Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-3699): add support for comment field #3167

Merged
merged 54 commits into from Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bbbd074
Add 24 new crud test files
baileympearson Mar 8, 2022
87546ed
Add remaining 4 tests
baileympearson Mar 8, 2022
e0eb13f
feat: Add support for comment field in update operations
baileympearson Mar 9, 2022
49a9c48
Sync change streams test with latest specs
baileympearson Mar 9, 2022
911f5c4
fix: bring in latest versions of comment tests
baileympearson Mar 9, 2022
ac9fd81
fix: uncomment atomic operators check in update.ts
baileympearson Mar 9, 2022
a5e4de6
resolve merge conflicts
baileympearson Mar 14, 2022
704764a
finish conflict
baileympearson Mar 17, 2022
07df766
fix: skip failing change stream tests
baileympearson Mar 14, 2022
d7d7d28
fix: address linting issues
baileympearson Mar 14, 2022
6bb15ec
fix: skip change streams pre 4.4 test
baileympearson Mar 14, 2022
fec0040
docs: add docstring comments to 'comment' option
baileympearson Mar 14, 2022
c3e7fc1
fix: fix lint error in doc string
baileympearson Mar 14, 2022
f95e8f2
fix: check for 'undefined' in place of a falsy value
baileympearson Mar 15, 2022
097a5f1
fix: remove comment from DeleteStatement
baileympearson Mar 16, 2022
8433c67
fix: support change streams tests
baileympearson Mar 17, 2022
a1137c4
feat: support comment in change streams
baileympearson Mar 17, 2022
0cfbc2a
convert change stream node specific test to TS
baileympearson Mar 17, 2022
71f1775
fix: only add comment to getMore on server versions 4.4 and above
baileympearson Mar 18, 2022
8d4267c
fix: remove comment from get more for failiing test
baileympearson Mar 18, 2022
2cdbf3b
Fix broken unified change stream tests
baileympearson Mar 21, 2022
d9d09ee
fix: address lint warning in entities.ts
baileympearson Mar 21, 2022
9744897
fix: fix unit tests for get_more opreation
baileympearson Mar 22, 2022
960e887
chore: remove old node_specific change stream tests
baileympearson Mar 22, 2022
016652a
fix: misc changes before re-request review
baileympearson Mar 22, 2022
2c705ea
test: sync change stream, aggregate and find spec tests
baileympearson Mar 25, 2022
d999bcc
misc changes
baileympearson Mar 27, 2022
48cd535
test: add test for comments with falsy value
baileympearson Mar 27, 2022
d16f651
fix: add run-on requirements to falsy values test
baileympearson Mar 28, 2022
0da642f
fix: remove legacy (<3.2) logic from cursors
baileympearson Mar 28, 2022
14f2ea2
fix: update comment falsy values tests with more falsy values
baileympearson Mar 28, 2022
2f810ac
fix: Symbol() instead of Symbol.for()
baileympearson Mar 28, 2022
127f71b
fix: add back if statement for failing tests
baileympearson Mar 28, 2022
eb5069d
Add programatic unified test run for falsey comments
nbbeeken Mar 28, 2022
b160c55
fix: revert usage of applyComment
baileympearson Mar 28, 2022
d3c20c5
test: use unified runner for falsy values test
baileympearson Mar 28, 2022
056ddae
fix: address lint issue
baileympearson Mar 29, 2022
cf145bd
fix: add runonrequirement to falsy values tests
baileympearson Mar 29, 2022
0a7e38d
chore: working builder - needs fixup
baileympearson Mar 29, 2022
fba5e13
fix: allow falsy values in change stream options
baileympearson Mar 29, 2022
d0dbaab
fix: add change stream falsy tests
baileympearson Mar 29, 2022
f1b15f9
wip - address comments pt 1
baileympearson Mar 30, 2022
a74b530
feat: add comment support to listCollections and listIndexes
baileympearson Mar 30, 2022
f180064
fix: reorganize tests && add tests for listDatabases
baileympearson Mar 30, 2022
42147ee
fix: move sinon stubbing into tests and revert changes to connection …
baileympearson Mar 30, 2022
0bcdfc0
fix: address comments
baileympearson Mar 30, 2022
a5523e0
chore: add test case for positive and negative zero
baileympearson Mar 30, 2022
459344d
fix: run on requireemnts in change stream falsy tests
baileympearson Mar 30, 2022
8aac554
fix: use correct run on requirement for list* tests
baileympearson Mar 30, 2022
ece725d
wip
baileympearson Mar 30, 2022
9def648
fix: don't run chnage stream tests on shareded topology
baileympearson Mar 31, 2022
cd2707d
fix: remove only
baileympearson Mar 31, 2022
8518dcd
fix: address Neal's latest comments
baileympearson Mar 31, 2022
157c75d
fix: address Daria's comments
baileympearson Apr 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/change_stream.ts
Expand Up @@ -57,6 +57,7 @@ const CURSOR_OPTIONS = [
'maxAwaitTimeMS',
'collation',
'readPreference',
'comment',
...CHANGE_STREAM_OPTIONS
] as const;

Expand Down Expand Up @@ -411,6 +412,15 @@ export interface ChangeStreamCursorOptions extends AbstractCursorOptions {
startAtOperationTime?: OperationTime;
resumeAfter?: ResumeToken;
startAfter?: boolean;
dariakp marked this conversation as resolved.
Show resolved Hide resolved
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
dariakp marked this conversation as resolved.
Show resolved Hide resolved
}

/** @internal */
Expand Down Expand Up @@ -617,7 +627,7 @@ function applyKnownOptions(source: Document, options: ReadonlyArray<string>) {
const result: Document = {};

for (const option of options) {
if (source[option]) {
if (option in source) {
dariakp marked this conversation as resolved.
Show resolved Hide resolved
result[option] = source[option];
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/cmap/connection.ts
Expand Up @@ -124,7 +124,15 @@ export interface GetMoreOptions extends CommandOptions {
batchSize?: number;
maxTimeMS?: number;
maxAwaitTimeMS?: number;
comment?: Document | string;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
dariakp marked this conversation as resolved.
Show resolved Hide resolved
}

/** @public */
Expand Down Expand Up @@ -574,6 +582,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
if (typeof options.maxAwaitTimeMS === 'number') {
getMoreCmd.maxTimeMS = options.maxAwaitTimeMS;
}
// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (options.comment !== undefined) {
dariakp marked this conversation as resolved.
Show resolved Hide resolved
getMoreCmd.comment = options.comment;
}

const commandOptions = Object.assign(
{
Expand Down
134 changes: 78 additions & 56 deletions src/cursor/abstract_cursor.ts
Expand Up @@ -42,6 +42,8 @@ const kInitialized = Symbol('initialized');
const kClosed = Symbol('closed');
/** @internal */
const kKilled = Symbol('killed');
/** @internal */
nbbeeken marked this conversation as resolved.
Show resolved Hide resolved
const kInit = Symbol('kInit');

/** @public */
export const CURSOR_FLAGS = [
Expand Down Expand Up @@ -77,7 +79,15 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
readConcern?: ReadConcernLike;
batchSize?: number;
maxTimeMS?: number;
comment?: Document | string;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
tailable?: boolean;
awaitData?: boolean;
noCursorTimeout?: boolean;
Expand Down Expand Up @@ -162,7 +172,9 @@ export abstract class AbstractCursor<
this[kOptions].batchSize = options.batchSize;
}

if (options.comment != null) {
// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (options.comment !== undefined) {
this[kOptions].comment = options.comment;
}

Expand Down Expand Up @@ -620,6 +632,65 @@ export abstract class AbstractCursor<

executeOperation(this, getMoreOperation, callback);
}

/**
* @internal
*
* This function is exposed for the unified test runner's createChangeStream
* operation. We cannot refactor to use the abstract _initialize method without
* a significant refactor.
*/
[kInit](callback: Callback<TSchema | null>): void {
if (this[kSession] == null) {
if (this[kTopology].shouldCheckForSessionSupport()) {
return this[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => {
if (err) return callback(err);
return this[kInit](callback);
});
} else if (this[kTopology].hasSessionSupport()) {
this[kSession] = this[kTopology].startSession({ owner: this, explicit: false });
}
dariakp marked this conversation as resolved.
Show resolved Hide resolved
}

this._initialize(this[kSession], (err, state) => {
if (state) {
const response = state.response;
this[kServer] = state.server;
this[kSession] = state.session;

if (response.cursor) {
this[kId] =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: response.cursor.id;

if (response.cursor.ns) {
this[kNamespace] = ns(response.cursor.ns);
}

this[kDocuments] = response.cursor.firstBatch;
}

// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is often the case for explain commands
// for example
if (this[kId] == null) {
this[kId] = Long.ZERO;
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
this[kDocuments] = [state.response as TODO_NODE_3286];
}
}

// the cursor is now initialized, even if an error occurred or it is dead
this[kInitialized] = true;

if (err || cursorIsDead(this)) {
return cleanupCursor(this, { error: err }, () => callback(err, nextDocument(this)));
}

callback();
});
}
}

function nextDocument<T>(cursor: AbstractCursor): T | null | undefined {
Expand Down Expand Up @@ -653,61 +724,12 @@ function next<T>(cursor: AbstractCursor, blocking: boolean, callback: Callback<T

if (cursorId == null) {
// All cursors must operate within a session, one must be made implicitly if not explicitly provided
if (cursor[kSession] == null) {
if (cursor[kTopology].shouldCheckForSessionSupport()) {
return cursor[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => {
if (err) return callback(err);
return next(cursor, blocking, callback);
});
} else if (cursor[kTopology].hasSessionSupport()) {
cursor[kSession] = cursor[kTopology].startSession({ owner: cursor, explicit: false });
}
}

cursor._initialize(cursor[kSession], (err, state) => {
if (state) {
const response = state.response;
cursor[kServer] = state.server;
cursor[kSession] = state.session;

if (response.cursor) {
cursor[kId] =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: response.cursor.id;

if (response.cursor.ns) {
cursor[kNamespace] = ns(response.cursor.ns);
}

cursor[kDocuments] = response.cursor.firstBatch;
} else {
// NOTE: This is for support of older servers (<3.2) which do not use commands
cursor[kId] =
typeof response.cursorId === 'number'
? Long.fromNumber(response.cursorId)
: response.cursorId;
cursor[kDocuments] = response.documents;
}

// When server responses return without a cursor document, we close this cursor
// and return the raw server response. This is often the case for explain commands
// for example
if (cursor[kId] == null) {
cursor[kId] = Long.ZERO;
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
cursor[kDocuments] = [state.response as TODO_NODE_3286];
}
}

// the cursor is now initialized, even if an error occurred or it is dead
cursor[kInitialized] = true;

if (err || cursorIsDead(cursor)) {
return cleanupCursor(cursor, { error: err }, () => callback(err, nextDocument(cursor)));
cursor[kInit]((err, value) => {
if (err) return callback(err);
if (value) {
return callback(undefined, value);
}

next(cursor, blocking, callback);
return next(cursor, blocking, callback);
});

return;
Expand Down
4 changes: 2 additions & 2 deletions src/db.ts
Expand Up @@ -44,9 +44,9 @@ import { DbStatsOperation, DbStatsOptions } from './operations/stats';
import { ReadConcern } from './read_concern';
import { ReadPreference, ReadPreferenceLike } from './read_preference';
import {
allowOptions,
Callback,
DEFAULT_PK_FACTORY,
filterOptions,
getTopology,
MongoDBNamespace,
resolveOptions
Expand Down Expand Up @@ -149,7 +149,7 @@ export class Db {
options = options ?? {};

// Filter the options
options = filterOptions(options, DB_OPTIONS_ALLOW_LIST);
options = allowOptions(options, DB_OPTIONS_ALLOW_LIST);

// Ensure we have a valid db name
validateDatabaseName(databaseName);
Expand Down
19 changes: 17 additions & 2 deletions src/operations/aggregate.ts
Expand Up @@ -2,8 +2,7 @@ import type { Document } from '../bson';
import { MongoInvalidArgumentError } from '../error';
import type { Server } from '../sdam/server';
import type { ClientSession } from '../sessions';
import type { Callback } from '../utils';
import { maxWireVersion, MongoDBNamespace } from '../utils';
import { Callback, maxWireVersion, MongoDBNamespace } from '../utils';
import { CollationOptions, CommandOperation, CommandOperationOptions } from './command';
import { Aspect, defineAspects, Hint } from './operation';

Expand Down Expand Up @@ -31,6 +30,16 @@ export interface AggregateOptions extends CommandOperationOptions {
hint?: Hint;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
dariakp marked this conversation as resolved.
Show resolved Hide resolved

out?: string;
}

Expand Down Expand Up @@ -121,6 +130,12 @@ export class AggregateOperation<T = Document> extends CommandOperation<T> {
command.let = options.let;
}

// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (options.comment !== undefined) {
command.comment = options.comment;
}

command.cursor = options.cursor || {};
if (options.batchSize && !this.hasWriteStage) {
command.cursor.batchSize = options.batchSize;
Expand Down
11 changes: 9 additions & 2 deletions src/operations/command.ts
Expand Up @@ -45,8 +45,15 @@ export interface CommandOperationOptions
/** Collation */
collation?: CollationOptions;
maxTimeMS?: number;
/** A user-provided comment to attach to this command */
comment?: string | Document;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
/** Should retry failed writes */
retryWrites?: boolean;

Expand Down
23 changes: 15 additions & 8 deletions src/operations/delete.ts
Expand Up @@ -12,8 +12,15 @@ import { Aspect, defineAspects, Hint } from './operation';
export interface DeleteOptions extends CommandOperationOptions, WriteConcernOptions {
/** If true, when an insert fails, don't execute the remaining writes. If false, continue with remaining inserts when one fails. */
ordered?: boolean;
/** A user-provided comment to attach to this command */
comment?: string | Document;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
dariakp marked this conversation as resolved.
Show resolved Hide resolved
/** Specifies the collation to use for the operation */
collation?: CollationOptions;
/** Specify that the update query should only consider plans using the hinted index */
Expand Down Expand Up @@ -43,8 +50,6 @@ export interface DeleteStatement {
collation?: CollationOptions;
/** A document or string that specifies the index to use to support the query predicate. */
hint?: Hint;
/** A user-provided comment to attach to this command */
comment?: string | Document;
}

/** @internal */
Expand Down Expand Up @@ -80,6 +85,12 @@ export class DeleteOperation extends CommandOperation<Document> {
command.let = options.let;
}

// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (options.comment !== undefined) {
command.comment = options.comment;
}

if (options.explain != null && maxWireVersion(server) < 3) {
return callback
? callback(
Expand Down Expand Up @@ -175,10 +186,6 @@ export function makeDeleteStatement(
op.hint = options.hint;
}

if (options.comment) {
op.comment = options.comment;
}

return op;
}

Expand Down
15 changes: 12 additions & 3 deletions src/operations/find.ts
Expand Up @@ -46,8 +46,15 @@ export interface FindOptions<TSchema extends Document = Document> extends Comman
min?: Document;
/** The exclusive upper bound for a specific index */
max?: Document;
/** You can put a $comment field on a query to make looking in the profiler logs simpler. */
comment?: string | Document;
/**
* Comment to apply to the operation.
*
* In server versions pre-4.4, 'comment' must be string. A server
* error will be thrown if any other type is provided.
*
* In server versions 4.4 and above, 'comment' can be any valid BSON type.
*/
comment?: any;
dariakp marked this conversation as resolved.
Show resolved Hide resolved
/** Number of milliseconds to wait before aborting the query. */
maxTimeMS?: number;
/** The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true */
Expand Down Expand Up @@ -241,7 +248,9 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp
findCommand.singleBatch = options.singleBatch;
}

if (options.comment) {
// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (options.comment !== undefined) {
findCommand.comment = options.comment;
}

Expand Down