'use strict'; const KillCursor = require('../connection/commands').KillCursor; const GetMore = require('../connection/commands').GetMore; const calculateDurationInMs = require('../../utils').calculateDurationInMs; const extractCommand = require('../../command_utils').extractCommand; // helper methods const namespace = command => command.ns; const databaseName = command => command.ns.split('.')[0]; const generateConnectionId = pool => pool.options ? `${pool.options.host}:${pool.options.port}` : pool.address; const isLegacyPool = pool => pool.s && pool.queue; const extractReply = (command, reply) => { if (command instanceof GetMore) { return { ok: 1, cursor: { id: reply.message.cursorId, ns: namespace(command), nextBatch: reply.message.documents } }; } if (command instanceof KillCursor) { return { ok: 1, cursorsUnknown: command.cursorIds }; } // is this a legacy find command? if (command.query && typeof command.query.$query !== 'undefined') { return { ok: 1, cursor: { id: reply.message.cursorId, ns: namespace(command), firstBatch: reply.message.documents } }; } return reply && reply.result ? reply.result : reply; }; const extractConnectionDetails = pool => { if (isLegacyPool(pool)) { return { connectionId: generateConnectionId(pool) }; } // APM in the modern pool is done at the `Connection` level, so we rename it here for // readability. const connection = pool; return { address: connection.address, connectionId: connection.id }; }; /** An event indicating the start of a given command */ class CommandStartedEvent { /** * Create a started event * * @param {Pool} pool the pool that originated the command * @param {Object} command the command */ constructor(pool, command) { const extractedCommand = extractCommand(command); const commandName = extractedCommand.name; const connectionDetails = extractConnectionDetails(pool); Object.assign(this, connectionDetails, { requestId: command.requestId, databaseName: databaseName(command), commandName, command: extractedCommand.shouldRedact ? {} : extractedCommand.cmd }); } } /** An event indicating the success of a given command */ class CommandSucceededEvent { /** * Create a succeeded event * * @param {Pool} pool the pool that originated the command * @param {Object} command the command * @param {Object} reply the reply for this command from the server * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration */ constructor(pool, command, reply, started) { const extractedCommand = extractCommand(command); const commandName = extractedCommand.name; const connectionDetails = extractConnectionDetails(pool); Object.assign(this, connectionDetails, { requestId: command.requestId, commandName, duration: calculateDurationInMs(started), reply: extractedCommand.shouldRedact ? {} : extractReply(command, reply) }); } } /** An event indicating the failure of a given command */ class CommandFailedEvent { /** * Create a failure event * * @param {Pool} pool the pool that originated the command * @param {Object} command the command * @param {MongoError|Object} error the generated error or a server error response * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration */ constructor(pool, command, error, started) { const extractedCommand = extractCommand(command); const commandName = extractedCommand.name; const connectionDetails = extractConnectionDetails(pool); Object.assign(this, connectionDetails, { requestId: command.requestId, commandName, duration: calculateDurationInMs(started), failure: extractedCommand.shouldRedact ? {} : error }); } } module.exports = { CommandStartedEvent, CommandSucceededEvent, CommandFailedEvent };