command_monitoring_events.js 8.2 KB

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