You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							242 lines
						
					
					
						
							8.0 KiB
						
					
					
				
			
		
		
	
	
							242 lines
						
					
					
						
							8.0 KiB
						
					
					
				| "use strict";
 | |
| Object.defineProperty(exports, "__esModule", { value: true });
 | |
| exports.CommandFailedEvent = exports.CommandSucceededEvent = exports.CommandStartedEvent = void 0;
 | |
| const constants_1 = require("../constants");
 | |
| const utils_1 = require("../utils");
 | |
| const commands_1 = require("./commands");
 | |
| /**
 | |
|  * An event indicating the start of a given
 | |
|  * @public
 | |
|  * @category Event
 | |
|  */
 | |
| class CommandStartedEvent {
 | |
|     /**
 | |
|      * Create a started event
 | |
|      *
 | |
|      * @internal
 | |
|      * @param pool - the pool that originated the command
 | |
|      * @param command - the command
 | |
|      */
 | |
|     constructor(connection, command) {
 | |
|         const cmd = extractCommand(command);
 | |
|         const commandName = extractCommandName(cmd);
 | |
|         const { address, connectionId, serviceId } = extractConnectionDetails(connection);
 | |
|         // TODO: remove in major revision, this is not spec behavior
 | |
|         if (SENSITIVE_COMMANDS.has(commandName)) {
 | |
|             this.commandObj = {};
 | |
|             this.commandObj[commandName] = true;
 | |
|         }
 | |
|         this.address = address;
 | |
|         this.connectionId = connectionId;
 | |
|         this.serviceId = serviceId;
 | |
|         this.requestId = command.requestId;
 | |
|         this.databaseName = databaseName(command);
 | |
|         this.commandName = commandName;
 | |
|         this.command = maybeRedact(commandName, cmd, cmd);
 | |
|     }
 | |
|     /* @internal */
 | |
|     get hasServiceId() {
 | |
|         return !!this.serviceId;
 | |
|     }
 | |
| }
 | |
| exports.CommandStartedEvent = CommandStartedEvent;
 | |
| /**
 | |
|  * An event indicating the success of a given command
 | |
|  * @public
 | |
|  * @category Event
 | |
|  */
 | |
| class CommandSucceededEvent {
 | |
|     /**
 | |
|      * Create a succeeded event
 | |
|      *
 | |
|      * @internal
 | |
|      * @param pool - the pool that originated the command
 | |
|      * @param command - the command
 | |
|      * @param reply - the reply for this command from the server
 | |
|      * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
 | |
|      */
 | |
|     constructor(connection, command, reply, started) {
 | |
|         const cmd = extractCommand(command);
 | |
|         const commandName = extractCommandName(cmd);
 | |
|         const { address, connectionId, serviceId } = extractConnectionDetails(connection);
 | |
|         this.address = address;
 | |
|         this.connectionId = connectionId;
 | |
|         this.serviceId = serviceId;
 | |
|         this.requestId = command.requestId;
 | |
|         this.commandName = commandName;
 | |
|         this.duration = (0, utils_1.calculateDurationInMs)(started);
 | |
|         this.reply = maybeRedact(commandName, cmd, extractReply(command, reply));
 | |
|     }
 | |
|     /* @internal */
 | |
|     get hasServiceId() {
 | |
|         return !!this.serviceId;
 | |
|     }
 | |
| }
 | |
| exports.CommandSucceededEvent = CommandSucceededEvent;
 | |
| /**
 | |
|  * An event indicating the failure of a given command
 | |
|  * @public
 | |
|  * @category Event
 | |
|  */
 | |
| class CommandFailedEvent {
 | |
|     /**
 | |
|      * Create a failure event
 | |
|      *
 | |
|      * @internal
 | |
|      * @param pool - the pool that originated the command
 | |
|      * @param command - the command
 | |
|      * @param error - the generated error or a server error response
 | |
|      * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
 | |
|      */
 | |
|     constructor(connection, command, error, started) {
 | |
|         const cmd = extractCommand(command);
 | |
|         const commandName = extractCommandName(cmd);
 | |
|         const { address, connectionId, serviceId } = extractConnectionDetails(connection);
 | |
|         this.address = address;
 | |
|         this.connectionId = connectionId;
 | |
|         this.serviceId = serviceId;
 | |
|         this.requestId = command.requestId;
 | |
|         this.commandName = commandName;
 | |
|         this.duration = (0, utils_1.calculateDurationInMs)(started);
 | |
|         this.failure = maybeRedact(commandName, cmd, error);
 | |
|     }
 | |
|     /* @internal */
 | |
|     get hasServiceId() {
 | |
|         return !!this.serviceId;
 | |
|     }
 | |
| }
 | |
| exports.CommandFailedEvent = CommandFailedEvent;
 | |
| /** Commands that we want to redact because of the sensitive nature of their contents */
 | |
| const SENSITIVE_COMMANDS = new Set([
 | |
|     'authenticate',
 | |
|     'saslStart',
 | |
|     'saslContinue',
 | |
|     'getnonce',
 | |
|     'createUser',
 | |
|     'updateUser',
 | |
|     'copydbgetnonce',
 | |
|     'copydbsaslstart',
 | |
|     'copydb'
 | |
| ]);
 | |
| const HELLO_COMMANDS = new Set(['hello', constants_1.LEGACY_HELLO_COMMAND, constants_1.LEGACY_HELLO_COMMAND_CAMEL_CASE]);
 | |
| // helper methods
 | |
| const extractCommandName = (commandDoc) => Object.keys(commandDoc)[0];
 | |
| const namespace = (command) => command.ns;
 | |
| const databaseName = (command) => command.ns.split('.')[0];
 | |
| const collectionName = (command) => command.ns.split('.')[1];
 | |
| const maybeRedact = (commandName, commandDoc, result) => SENSITIVE_COMMANDS.has(commandName) ||
 | |
|     (HELLO_COMMANDS.has(commandName) && commandDoc.speculativeAuthenticate)
 | |
|     ? {}
 | |
|     : result;
 | |
| const LEGACY_FIND_QUERY_MAP = {
 | |
|     $query: 'filter',
 | |
|     $orderby: 'sort',
 | |
|     $hint: 'hint',
 | |
|     $comment: 'comment',
 | |
|     $maxScan: 'maxScan',
 | |
|     $max: 'max',
 | |
|     $min: 'min',
 | |
|     $returnKey: 'returnKey',
 | |
|     $showDiskLoc: 'showRecordId',
 | |
|     $maxTimeMS: 'maxTimeMS',
 | |
|     $snapshot: 'snapshot'
 | |
| };
 | |
| const LEGACY_FIND_OPTIONS_MAP = {
 | |
|     numberToSkip: 'skip',
 | |
|     numberToReturn: 'batchSize',
 | |
|     returnFieldSelector: 'projection'
 | |
| };
 | |
| const OP_QUERY_KEYS = [
 | |
|     'tailable',
 | |
|     'oplogReplay',
 | |
|     'noCursorTimeout',
 | |
|     'awaitData',
 | |
|     'partial',
 | |
|     'exhaust'
 | |
| ];
 | |
| /** Extract the actual command from the query, possibly up-converting if it's a legacy format */
 | |
| function extractCommand(command) {
 | |
|     if (command instanceof commands_1.Msg) {
 | |
|         return (0, utils_1.deepCopy)(command.command);
 | |
|     }
 | |
|     if (command.query?.$query) {
 | |
|         let result;
 | |
|         if (command.ns === 'admin.$cmd') {
 | |
|             // up-convert legacy command
 | |
|             result = Object.assign({}, command.query.$query);
 | |
|         }
 | |
|         else {
 | |
|             // up-convert legacy find command
 | |
|             result = { find: collectionName(command) };
 | |
|             Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
 | |
|                 if (command.query[key] != null) {
 | |
|                     result[LEGACY_FIND_QUERY_MAP[key]] = (0, utils_1.deepCopy)(command.query[key]);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
 | |
|             const legacyKey = key;
 | |
|             if (command[legacyKey] != null) {
 | |
|                 result[LEGACY_FIND_OPTIONS_MAP[legacyKey]] = (0, utils_1.deepCopy)(command[legacyKey]);
 | |
|             }
 | |
|         });
 | |
|         OP_QUERY_KEYS.forEach(key => {
 | |
|             if (command[key]) {
 | |
|                 result[key] = command[key];
 | |
|             }
 | |
|         });
 | |
|         if (command.pre32Limit != null) {
 | |
|             result.limit = command.pre32Limit;
 | |
|         }
 | |
|         if (command.query.$explain) {
 | |
|             return { explain: result };
 | |
|         }
 | |
|         return result;
 | |
|     }
 | |
|     const clonedQuery = {};
 | |
|     const clonedCommand = {};
 | |
|     if (command.query) {
 | |
|         for (const k in command.query) {
 | |
|             clonedQuery[k] = (0, utils_1.deepCopy)(command.query[k]);
 | |
|         }
 | |
|         clonedCommand.query = clonedQuery;
 | |
|     }
 | |
|     for (const k in command) {
 | |
|         if (k === 'query')
 | |
|             continue;
 | |
|         clonedCommand[k] = (0, utils_1.deepCopy)(command[k]);
 | |
|     }
 | |
|     return command.query ? clonedQuery : clonedCommand;
 | |
| }
 | |
| function extractReply(command, reply) {
 | |
|     if (!reply) {
 | |
|         return reply;
 | |
|     }
 | |
|     if (command instanceof commands_1.Msg) {
 | |
|         return (0, utils_1.deepCopy)(reply.result ? reply.result : reply);
 | |
|     }
 | |
|     // is this a legacy find command?
 | |
|     if (command.query && command.query.$query != null) {
 | |
|         return {
 | |
|             ok: 1,
 | |
|             cursor: {
 | |
|                 id: (0, utils_1.deepCopy)(reply.cursorId),
 | |
|                 ns: namespace(command),
 | |
|                 firstBatch: (0, utils_1.deepCopy)(reply.documents)
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     return (0, utils_1.deepCopy)(reply.result ? reply.result : reply);
 | |
| }
 | |
| function extractConnectionDetails(connection) {
 | |
|     let connectionId;
 | |
|     if ('id' in connection) {
 | |
|         connectionId = connection.id;
 | |
|     }
 | |
|     return {
 | |
|         address: connection.address,
 | |
|         serviceId: connection.serviceId,
 | |
|         connectionId
 | |
|     };
 | |
| }
 | |
| //# sourceMappingURL=command_monitoring_events.js.map
 |