mirror of
https://github.com/bvanroll/yahoo-thing.git
synced 2025-08-29 20:12:46 +00:00
229 lines
6.0 KiB
JavaScript
229 lines
6.0 KiB
JavaScript
'use strict';
|
|
const KillCursor = require('../connection/commands').KillCursor;
|
|
const GetMore = require('../connection/commands').GetMore;
|
|
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
|
|
/** 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'
|
|
]);
|
|
|
|
// helper methods
|
|
const extractCommandName = command => Object.keys(command)[0];
|
|
const namespace = command => command.ns;
|
|
const databaseName = command => command.ns.split('.')[0];
|
|
const collectionName = command => command.ns.split('.')[1];
|
|
const generateConnectionId = pool => `${pool.options.host}:${pool.options.port}`;
|
|
const maybeRedact = (commandName, result) => (SENSITIVE_COMMANDS.has(commandName) ? {} : 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',
|
|
returnFieldsSelector: 'projection'
|
|
};
|
|
|
|
const OP_QUERY_KEYS = [
|
|
'tailable',
|
|
'oplogReplay',
|
|
'noCursorTimeout',
|
|
'awaitData',
|
|
'partial',
|
|
'exhaust'
|
|
];
|
|
|
|
/**
|
|
* Extract the actual command from the query, possibly upconverting if it's a legacy
|
|
* format
|
|
*
|
|
* @param {Object} command the command
|
|
*/
|
|
const extractCommand = command => {
|
|
if (command instanceof GetMore) {
|
|
return {
|
|
getMore: command.cursorId,
|
|
collection: collectionName(command),
|
|
batchSize: command.numberToReturn
|
|
};
|
|
}
|
|
|
|
if (command instanceof KillCursor) {
|
|
return {
|
|
killCursors: collectionName(command),
|
|
cursors: command.cursorIds
|
|
};
|
|
}
|
|
|
|
if (command.query && command.query.$query) {
|
|
let result;
|
|
if (command.ns === 'admin.$cmd') {
|
|
// upconvert legacy command
|
|
result = Object.assign({}, command.query.$query);
|
|
} else {
|
|
// upconvert legacy find command
|
|
result = { find: collectionName(command) };
|
|
Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
|
|
if (typeof command.query[key] !== 'undefined')
|
|
result[LEGACY_FIND_QUERY_MAP[key]] = command.query[key];
|
|
});
|
|
}
|
|
|
|
Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
|
|
if (typeof command[key] !== 'undefined') result[LEGACY_FIND_OPTIONS_MAP[key]] = command[key];
|
|
});
|
|
|
|
OP_QUERY_KEYS.forEach(key => {
|
|
if (command[key]) result[key] = command[key];
|
|
});
|
|
|
|
if (typeof command.pre32Limit !== 'undefined') {
|
|
result.limit = command.pre32Limit;
|
|
}
|
|
|
|
if (command.query.$explain) {
|
|
return { explain: result };
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return command.query ? command.query : command;
|
|
};
|
|
|
|
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.result;
|
|
};
|
|
|
|
/** 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 cmd = extractCommand(command);
|
|
const commandName = extractCommandName(cmd);
|
|
|
|
// NOTE: remove in major revision, this is not spec behavior
|
|
if (SENSITIVE_COMMANDS.has(commandName)) {
|
|
this.commandObj = {};
|
|
this.commandObj[commandName] = true;
|
|
}
|
|
|
|
Object.assign(this, {
|
|
command: cmd,
|
|
databaseName: databaseName(command),
|
|
commandName,
|
|
requestId: command.requestId,
|
|
connectionId: generateConnectionId(pool)
|
|
});
|
|
}
|
|
}
|
|
|
|
/** 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 cmd = extractCommand(command);
|
|
const commandName = extractCommandName(cmd);
|
|
|
|
Object.assign(this, {
|
|
duration: calculateDurationInMs(started),
|
|
commandName,
|
|
reply: maybeRedact(commandName, extractReply(command, reply)),
|
|
requestId: command.requestId,
|
|
connectionId: generateConnectionId(pool)
|
|
});
|
|
}
|
|
}
|
|
|
|
/** 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 cmd = extractCommand(command);
|
|
const commandName = extractCommandName(cmd);
|
|
|
|
Object.assign(this, {
|
|
duration: calculateDurationInMs(started),
|
|
commandName,
|
|
failure: maybeRedact(commandName, error),
|
|
requestId: command.requestId,
|
|
connectionId: generateConnectionId(pool)
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
CommandStartedEvent,
|
|
CommandSucceededEvent,
|
|
CommandFailedEvent
|
|
};
|