command_monitoring_events.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.CommandFailedEvent = exports.CommandSucceededEvent = exports.CommandStartedEvent = void 0;
  4. const constants_1 = require("../constants");
  5. const utils_1 = require("../utils");
  6. const commands_1 = require("./commands");
  7. /**
  8. * An event indicating the start of a given
  9. * @public
  10. * @category Event
  11. */
  12. class CommandStartedEvent {
  13. /**
  14. * Create a started event
  15. *
  16. * @internal
  17. * @param pool - the pool that originated the command
  18. * @param command - the command
  19. */
  20. constructor(connection, command) {
  21. const cmd = extractCommand(command);
  22. const commandName = extractCommandName(cmd);
  23. const { address, connectionId, serviceId } = extractConnectionDetails(connection);
  24. // TODO: remove in major revision, this is not spec behavior
  25. if (SENSITIVE_COMMANDS.has(commandName)) {
  26. this.commandObj = {};
  27. this.commandObj[commandName] = true;
  28. }
  29. this.address = address;
  30. this.connectionId = connectionId;
  31. this.serviceId = serviceId;
  32. this.requestId = command.requestId;
  33. this.databaseName = databaseName(command);
  34. this.commandName = commandName;
  35. this.command = maybeRedact(commandName, cmd, cmd);
  36. }
  37. /* @internal */
  38. get hasServiceId() {
  39. return !!this.serviceId;
  40. }
  41. }
  42. exports.CommandStartedEvent = CommandStartedEvent;
  43. /**
  44. * An event indicating the success of a given command
  45. * @public
  46. * @category Event
  47. */
  48. class CommandSucceededEvent {
  49. /**
  50. * Create a succeeded event
  51. *
  52. * @internal
  53. * @param pool - the pool that originated the command
  54. * @param command - the command
  55. * @param reply - the reply for this command from the server
  56. * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
  57. */
  58. constructor(connection, command, reply, started) {
  59. const cmd = extractCommand(command);
  60. const commandName = extractCommandName(cmd);
  61. const { address, connectionId, serviceId } = extractConnectionDetails(connection);
  62. this.address = address;
  63. this.connectionId = connectionId;
  64. this.serviceId = serviceId;
  65. this.requestId = command.requestId;
  66. this.commandName = commandName;
  67. this.duration = (0, utils_1.calculateDurationInMs)(started);
  68. this.reply = maybeRedact(commandName, cmd, extractReply(command, reply));
  69. }
  70. /* @internal */
  71. get hasServiceId() {
  72. return !!this.serviceId;
  73. }
  74. }
  75. exports.CommandSucceededEvent = CommandSucceededEvent;
  76. /**
  77. * An event indicating the failure of a given command
  78. * @public
  79. * @category Event
  80. */
  81. class CommandFailedEvent {
  82. /**
  83. * Create a failure event
  84. *
  85. * @internal
  86. * @param pool - the pool that originated the command
  87. * @param command - the command
  88. * @param error - the generated error or a server error response
  89. * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
  90. */
  91. constructor(connection, command, error, started) {
  92. const cmd = extractCommand(command);
  93. const commandName = extractCommandName(cmd);
  94. const { address, connectionId, serviceId } = extractConnectionDetails(connection);
  95. this.address = address;
  96. this.connectionId = connectionId;
  97. this.serviceId = serviceId;
  98. this.requestId = command.requestId;
  99. this.commandName = commandName;
  100. this.duration = (0, utils_1.calculateDurationInMs)(started);
  101. this.failure = maybeRedact(commandName, cmd, error);
  102. }
  103. /* @internal */
  104. get hasServiceId() {
  105. return !!this.serviceId;
  106. }
  107. }
  108. exports.CommandFailedEvent = CommandFailedEvent;
  109. /** Commands that we want to redact because of the sensitive nature of their contents */
  110. const SENSITIVE_COMMANDS = new Set([
  111. 'authenticate',
  112. 'saslStart',
  113. 'saslContinue',
  114. 'getnonce',
  115. 'createUser',
  116. 'updateUser',
  117. 'copydbgetnonce',
  118. 'copydbsaslstart',
  119. 'copydb'
  120. ]);
  121. const HELLO_COMMANDS = new Set(['hello', constants_1.LEGACY_HELLO_COMMAND, constants_1.LEGACY_HELLO_COMMAND_CAMEL_CASE]);
  122. // helper methods
  123. const extractCommandName = (commandDoc) => Object.keys(commandDoc)[0];
  124. const namespace = (command) => command.ns;
  125. const databaseName = (command) => command.ns.split('.')[0];
  126. const collectionName = (command) => command.ns.split('.')[1];
  127. const maybeRedact = (commandName, commandDoc, result) => SENSITIVE_COMMANDS.has(commandName) ||
  128. (HELLO_COMMANDS.has(commandName) && commandDoc.speculativeAuthenticate)
  129. ? {}
  130. : result;
  131. const LEGACY_FIND_QUERY_MAP = {
  132. $query: 'filter',
  133. $orderby: 'sort',
  134. $hint: 'hint',
  135. $comment: 'comment',
  136. $maxScan: 'maxScan',
  137. $max: 'max',
  138. $min: 'min',
  139. $returnKey: 'returnKey',
  140. $showDiskLoc: 'showRecordId',
  141. $maxTimeMS: 'maxTimeMS',
  142. $snapshot: 'snapshot'
  143. };
  144. const LEGACY_FIND_OPTIONS_MAP = {
  145. numberToSkip: 'skip',
  146. numberToReturn: 'batchSize',
  147. returnFieldSelector: 'projection'
  148. };
  149. const OP_QUERY_KEYS = [
  150. 'tailable',
  151. 'oplogReplay',
  152. 'noCursorTimeout',
  153. 'awaitData',
  154. 'partial',
  155. 'exhaust'
  156. ];
  157. /** Extract the actual command from the query, possibly up-converting if it's a legacy format */
  158. function extractCommand(command) {
  159. var _a;
  160. if (command instanceof commands_1.GetMore) {
  161. return {
  162. getMore: (0, utils_1.deepCopy)(command.cursorId),
  163. collection: collectionName(command),
  164. batchSize: command.numberToReturn
  165. };
  166. }
  167. if (command instanceof commands_1.KillCursor) {
  168. return {
  169. killCursors: collectionName(command),
  170. cursors: (0, utils_1.deepCopy)(command.cursorIds)
  171. };
  172. }
  173. if (command instanceof commands_1.Msg) {
  174. return (0, utils_1.deepCopy)(command.command);
  175. }
  176. if ((_a = command.query) === null || _a === void 0 ? void 0 : _a.$query) {
  177. let result;
  178. if (command.ns === 'admin.$cmd') {
  179. // up-convert legacy command
  180. result = Object.assign({}, command.query.$query);
  181. }
  182. else {
  183. // up-convert legacy find command
  184. result = { find: collectionName(command) };
  185. Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
  186. if (command.query[key] != null) {
  187. result[LEGACY_FIND_QUERY_MAP[key]] = (0, utils_1.deepCopy)(command.query[key]);
  188. }
  189. });
  190. }
  191. Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
  192. const legacyKey = key;
  193. if (command[legacyKey] != null) {
  194. result[LEGACY_FIND_OPTIONS_MAP[legacyKey]] = (0, utils_1.deepCopy)(command[legacyKey]);
  195. }
  196. });
  197. OP_QUERY_KEYS.forEach(key => {
  198. const opKey = key;
  199. if (command[opKey]) {
  200. result[opKey] = command[opKey];
  201. }
  202. });
  203. if (command.pre32Limit != null) {
  204. result.limit = command.pre32Limit;
  205. }
  206. if (command.query.$explain) {
  207. return { explain: result };
  208. }
  209. return result;
  210. }
  211. const clonedQuery = {};
  212. const clonedCommand = {};
  213. if (command.query) {
  214. for (const k in command.query) {
  215. clonedQuery[k] = (0, utils_1.deepCopy)(command.query[k]);
  216. }
  217. clonedCommand.query = clonedQuery;
  218. }
  219. for (const k in command) {
  220. if (k === 'query')
  221. continue;
  222. clonedCommand[k] = (0, utils_1.deepCopy)(command[k]);
  223. }
  224. return command.query ? clonedQuery : clonedCommand;
  225. }
  226. function extractReply(command, reply) {
  227. if (command instanceof commands_1.KillCursor) {
  228. return {
  229. ok: 1,
  230. cursorsUnknown: command.cursorIds
  231. };
  232. }
  233. if (!reply) {
  234. return reply;
  235. }
  236. if (command instanceof commands_1.GetMore) {
  237. return {
  238. ok: 1,
  239. cursor: {
  240. id: (0, utils_1.deepCopy)(reply.cursorId),
  241. ns: namespace(command),
  242. nextBatch: (0, utils_1.deepCopy)(reply.documents)
  243. }
  244. };
  245. }
  246. if (command instanceof commands_1.Msg) {
  247. return (0, utils_1.deepCopy)(reply.result ? reply.result : reply);
  248. }
  249. // is this a legacy find command?
  250. if (command.query && command.query.$query != null) {
  251. return {
  252. ok: 1,
  253. cursor: {
  254. id: (0, utils_1.deepCopy)(reply.cursorId),
  255. ns: namespace(command),
  256. firstBatch: (0, utils_1.deepCopy)(reply.documents)
  257. }
  258. };
  259. }
  260. return (0, utils_1.deepCopy)(reply.result ? reply.result : reply);
  261. }
  262. function extractConnectionDetails(connection) {
  263. let connectionId;
  264. if ('id' in connection) {
  265. connectionId = connection.id;
  266. }
  267. return {
  268. address: connection.address,
  269. serviceId: connection.serviceId,
  270. connectionId
  271. };
  272. }
  273. //# sourceMappingURL=command_monitoring_events.js.map