-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(NODE-4059): ChangeStreamDocument not fully typed to specification #3191
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
Conversation
ae8f97c
to
7aaf33d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions and suggestions
src/change_stream.ts
Outdated
@@ -84,16 +85,16 @@ export interface ResumeOptions { | |||
} | |||
|
|||
/** | |||
* Represents the logical starting point for a new or resuming {@link https://docs.mongodb.com/manual/changeStreams/#std-label-change-stream-resume| Change Stream} on the server. | |||
* Represents the logical starting point for a new or resuming on the server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Represents the logical starting point for a new or resuming on the server. | |
* Represents the logical starting point for a new ChangeStream or resuming a ChangeStream on the server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
src/change_stream.ts
Outdated
export interface ChangeStreamDocument<TSchema extends Document = Document> { | ||
export interface ChangeStreamFullNameSpace { | ||
/** The namespace */ | ||
ns: { db: string; coll: string }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice further down in the rename
interface, you reuse { db: string; coll: string }
. Ultimately, { db: string; coll: string }
is the namespace. Could we create the type as
interface ChangeStreamFullNamespace { db: string; coll: string }
so we can reuse it where needed? Then we can add ns: ChangeStreamFullNamespace
to the ChangeStreamDocumentCommon
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this, we can't add it to common because it's missing from a number of the events
* this will contain all the components of the shard key in order, | ||
* followed by the _id if the _id isn’t part of the shard key. | ||
* The transaction number. | ||
* Only present if the operation is part of a multi-document transaction. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we note here that the type of transaction number depends on the promoteLongs
flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it's more friction that it's worth to include this in the type annotation and instead document it as a possiblity based on the option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So include this as part of the documentation for the promoteLongs
flag? I'm okay with that
src/change_stream.ts
Outdated
* @public | ||
* @see https://www.mongodb.com/docs/manual/reference/change-events/#insert-event | ||
*/ | ||
export interface InsertChangeStreamDocument<TSchema extends Document = Document> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think ChangeStream<Command>Result
might make more sense than <Command>ChangeStreamDocument
as a general format for these interfaces but I don't feel too strongly. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed them all, I'm still using Document at the end because I don't want to change the name of the ChangeStreamDocument
type and we should keep these consistent, but reordering the indicator makes sense.
entity.collection.collectionName, | ||
patchCollectionOptions(entity.collection.collectionOptions) | ||
); | ||
const collection = await db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: is this logic added to handle the scenario where a user has two entities with the same collection name in a single test suite? If so, would it make more sense to flip the logic - list collections while querying for the collection, and then create it if it doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until this PR we haven't used the unified runner to run a drop db / rename collection. They both require the collection to exist in advance of the operations stage of the test. I could instead adjust the test to perform an insert to create the collection, but this seems like a reasonable adjustment to the runner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that makes sense. I was mainly wondering if the logic would be easier to follow if we flipped it around to look for collections first. But I don't feel super strongly about this, feel free to ignore this thought
let collection = await db.listCollections({ collectionName: entitiy.collection.collectionName })
.map(({ name }) => db.collection(name))
.toArray()[0]
if (!collection) {
collection = db.createCollection( .... )
}
@@ -233,6 +233,6 @@ const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option | |||
) | |||
.toJSON(); | |||
|
|||
describe('listDatabases w/ comment option', () => { | |||
describe('listDatabases with comment option', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-blocking: Feel free to use the new toMocha
method you added on enumerate_databases
and enumerate_indexes
if you'd like, but not necessary for the review!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
86bb7f2
to
b3610e5
Compare
Hi, me again, hope it's okay. I'm actually really excited for this particular change as I've been using changestreams for a while, though looking at it, I do have a question: I think this change would break typescript validation if someone is trying to access to |
Hi @PaulEndri always appreciate the feedback, something I hadn't originally considered when starting this work was the impact that the pipeline has on the change stream document as well, since currently the generic only controls the schema there isn't a way to account for a possible change like client.watchFor([{ $match: { operationType: 'insert' } }]) might be able to filter the actual change stream document type returned. Any ideas are welcome additions to the conversation 🙂 |
I'm proposing we add a second optional generic argument that can override the type of the whole change stream document. Unfortunately you can't skip arguments so the schema will need to be passed in first, but the second argument can be extended from our existing types making adding/omitting properties somewhat ergonomic. The impact on users should still be the same as the change proposed so far where certain properties need narrowing to access. Those who want a solution that doesn't require runtime narrowing could use this second argument to strictly type their changes or pass in type InsertChangeWithComment = ChangeStreamInsertDocument<Schema> & { comment: string };
const pipelineChangeStream = collection.watch<Schema, InsertChangeWithComment>([
{ $addFields: { comment: 'big changes' } },
{ $match: { operationType: 'insert' } },
]);
pipelineChangeStream.addListener('change', change => {
expectType<string>(change.comment);
// No need to narrow in code because the generics did that for us!
expectType<Schema>(change.fullDocument);
}); |
981f105
to
68cbcac
Compare
Waiting on #3215 to slim this PR down |
853f968
to
b022793
Compare
c10b98e
to
f2bfbaf
Compare
f2bfbaf
to
acebbfe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Description
What is changing?
ChangeStreamDocument had some errors and friction in type info. I've restructured it to be a descriminated union of the possible change stream events which lends itself well to TS.
This does introduce compilation changes since properties that used to be optional now do not exist depending on the change stream event type. We could consider mitigating this by bringing back the properties but typing them as something that indicates they shouldn't have been used with a particular event (for example delete has no fullDocument field)
What is the motivation for this change?
Type hinting on change stream documents helps sort through the variety of events that can occur.
Double check the following
npm run check:lint
script<type>(NODE-xxxx)<!>: <description>