Skip to content

Commit 13a2890

Browse files
committed
fix: simplify mongo cursor typings across meteor and webui
1 parent 1d6e6d3 commit 13a2890

13 files changed

Lines changed: 148 additions & 238 deletions

File tree

meteor/__mocks__/mongo.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ProtectedString } from '@sofie-automation/corelib/dist/protectedString'
22
import { InMemoryMongoCollection } from '@sofie-automation/corelib/dist/memoryCollection'
3-
import { MongoReadOnlyCollection } from '@sofie-automation/meteor-lib/dist/collections/lib'
43
import { AsyncOnlyMongoCollection, AsyncOnlyReadOnlyMongoCollection } from '../server/collections/collection'
54
import { Collections } from '../server/collections/lib'
65

@@ -34,7 +33,7 @@ export namespace MongoMock {
3433

3534
/** The backing in-memory collection for a wrapped (mock) collection. */
3635
export function getInnerMockCollection<T extends { _id: ProtectedString<any> }>(
37-
collection: MongoReadOnlyCollection<T> | AsyncOnlyReadOnlyMongoCollection<T>
36+
collection: AsyncOnlyReadOnlyMongoCollection<T>
3837
): InMemoryMongoCollection<T> {
3938
return (collection as any).mockCollection
4039
}

meteor/server/api/deviceTriggers/StudioObserver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import { RundownContentObserver } from './RundownContentObserver'
2121
import { RundownsObserver } from './RundownsObserver'
2222
import { RundownPlaylists, Rundowns, ShowStyleBases } from '../../collections'
2323
import { PromiseDebounce } from '../../publications/lib/PromiseDebounce'
24-
import { MinimalMongoCursor } from '../../collections/implementations/asyncCollection'
2524
import { PieceInstancesObserver } from './PieceInstancesObserver'
25+
import { MinimalMongoCursor } from '../../collections/collection'
2626

2727
type RundownContentChangeHandler = (showStyleBaseId: ShowStyleBaseId, cache: ContentCache) => () => void
2828
type PieceInstancesChangeHandler = (showStyleBaseId: ShowStyleBaseId, cache: PieceInstancesContentCache) => () => void

meteor/server/collections/changeStream/changeStreamCursor.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,12 @@ import {
99
} from '@sofie-automation/corelib/dist/mongo'
1010
import { PromisifyCallbacks } from '@sofie-automation/shared-lib/dist/lib/types'
1111
import { observeChangesViaChangeStream, observeViaChangeStream, ObserveMultiplexerDeps } from './observeMultiplexer'
12-
import type { MinimalMongoCursor } from '../implementations/asyncCollection'
12+
import type { MinimalMongoCursor } from '../collection'
1313

1414
export interface ChangeStreamCursorConfig<TDoc extends { _id: ProtectedString<any> }> {
1515
collectionName: string
1616
selector: MongoQuery<TDoc>
1717
projection: MongoFieldSpecifier<TDoc> | undefined
18-
/** Fetch the matching documents (native find, with projection applied) */
19-
fetch: () => Promise<TDoc[]>
20-
/** Count the matching documents. When `applySkipLimit` is false, ignore any skip/limit in the options. */
21-
count: (applySkipLimit: boolean) => Promise<number>
2218
/** Build the deps for an observe multiplexer over this cursor's query */
2319
makeDeps: () => ObserveMultiplexerDeps<TDoc>
2420
}
@@ -27,8 +23,6 @@ export interface ChangeStreamCursorConfig<TDoc extends { _id: ProtectedString<an
2723
* A lazy "watcher" returned by `findWithCursor`. It exposes the async cursor methods the codebase uses
2824
* (`fetchAsync`/`countAsync`/`observeChangesAsync`/`observeAsync`) plus a plain `collectionName`.
2925
*
30-
* Unlike a Meteor cursor it has NO `_publishCursor`: publications are wired up by `meteorPublish`
31-
* (`driveSubscriptionFromCursor`).
3226
* Observing goes through the shared change-stream multiplexer, so identical queries are de-duplicated.
3327
*/
3428
export class ChangeStreamCursor<TDoc extends { _id: ProtectedString<any> }> implements MinimalMongoCursor<TDoc> {
@@ -40,14 +34,6 @@ export class ChangeStreamCursor<TDoc extends { _id: ProtectedString<any> }> impl
4034
this.collectionName = config.collectionName
4135
}
4236

43-
async fetchAsync(): Promise<TDoc[]> {
44-
return this.#config.fetch()
45-
}
46-
47-
async countAsync(applySkipLimit = true): Promise<number> {
48-
return this.#config.count(applySkipLimit)
49-
}
50-
5137
async observeChangesAsync(
5238
callbacks: PromisifyCallbacks<ObserveChangesCallbacks<TDoc>>,
5339
options?: ObserveChangesOptions

meteor/server/collections/collection.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ import { createMockCollection } from './implementations/mock'
1717
import { WrappedAsyncMongoCollection } from './implementations/asyncCollection'
1818
import { WrappedReadOnlyMongoCollection } from './implementations/readonlyWrapper'
1919
import { isInMockMode } from './mongoConnection'
20-
import { FieldNames, IndexSpecifier, UpdateOptions } from '@sofie-automation/meteor-lib/dist/collections/lib'
21-
import { MinimalMongoCursor } from './implementations/asyncCollection'
20+
import {
21+
FieldNames,
22+
IndexSpecifier,
23+
MongoLiveQueryHandle,
24+
UpdateOptions,
25+
} from '@sofie-automation/meteor-lib/dist/collections/lib'
2226
import { UserPermissions } from '@sofie-automation/meteor-lib/dist/userPermissions'
2327

2428
export interface CustomMongoAllowRules<DBInterface> {
@@ -86,6 +90,29 @@ function createWrappedCollection<DBInterface extends { _id: ProtectedString<any>
8690
}
8791
}
8892

93+
/**
94+
* A minimal mongo cursor, with only the async methods used by the codebase.
95+
* This is intentionally only the observe methods, kept for publication usage to avoid a larger refactor
96+
*/
97+
export interface MinimalMongoCursor<T extends { _id: ProtectedString<any> }> {
98+
readonly collectionName: string | null
99+
100+
/**
101+
* Watch a query. Receive callbacks as the result set changes.
102+
* @param callbacks Functions to call to deliver the result set as it changes
103+
*/
104+
observeAsync(callbacks: ObserveCallbacks<T>): Promise<MongoLiveQueryHandle>
105+
/**
106+
* Watch a query. Receive callbacks as the result set changes. Only the differences between the old and new documents are passed to the callbacks.
107+
* @param callbacks Functions to call to deliver the result set as it changes
108+
* @param options { nonMutatingCallbacks: boolean }
109+
*/
110+
observeChangesAsync(
111+
callbacks: ObserveChangesCallbacks<T>,
112+
options?: { nonMutatingCallbacks?: boolean | undefined }
113+
): Promise<MongoLiveQueryHandle>
114+
}
115+
89116
/**
90117
* A minimal Async only wrapping around the base Mongo.Collection type
91118
*/

meteor/server/collections/implementations/asyncCollection.ts

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ import {
88
} from '@sofie-automation/corelib/dist/mongo'
99
import { ProtectedString, unprotectString } from '@sofie-automation/corelib/dist/protectedString'
1010
import { Meteor } from 'meteor/meteor'
11-
import {
12-
UpdateOptions,
13-
IndexSpecifier,
14-
MongoCursor,
15-
FindOptions,
16-
} from '@sofie-automation/meteor-lib/dist/collections/lib'
11+
import { UpdateOptions, IndexSpecifier, FindOptions } from '@sofie-automation/meteor-lib/dist/collections/lib'
1712
import type {
1813
Collection as RawCollection,
1914
CreateIndexesOptions,
@@ -25,7 +20,7 @@ import { MongoFieldSpecifier } from '@sofie-automation/corelib/dist/mongo'
2520
import { profiler } from '../../api/profiler'
2621
import { logger } from '../../logging'
2722
import { PromisifyCallbacks } from '@sofie-automation/shared-lib/dist/lib/types'
28-
import { AsyncOnlyMongoCollection } from '../collection'
23+
import { AsyncOnlyMongoCollection, MinimalMongoCursor } from '../collection'
2924
import { getMongoDb } from '../mongoConnection'
3025
import {
3126
observeChangesViaChangeStream,
@@ -35,15 +30,6 @@ import {
3530
import { ChangeStreamCursor } from '../changeStream/changeStreamCursor'
3631
import { subscribeToCollectionChangeFeed } from '../changeStream/collectionChangeFeed'
3732

38-
/**
39-
* A stripped down version of a cursor, with only the async methods used by the codebase, plus the name of
40-
* the collection it came from (used by the publish layer to address DDP messages).
41-
*/
42-
export type MinimalMongoCursor<T extends { _id: ProtectedString<any> }> = Pick<
43-
MongoCursor<T>,
44-
'fetchAsync' | 'observeChangesAsync' | 'observeAsync' | 'countAsync'
45-
> & { readonly collectionName: string | null }
46-
4733
/**
4834
* Translate a meteor-lib {@link FindOptions} into the options the native `mongodb` driver accepts.
4935
* This is an explicit allow-list: only options known to be understood by the driver are forwarded.
@@ -168,22 +154,13 @@ export class WrappedAsyncMongoCollection<
168154

169155
async findWithCursor(
170156
selector?: MongoQuery<DBInterface> | DBInterface['_id'],
171-
options?: FindOptions<DBInterface>
157+
options?: Omit<FindOptions<DBInterface>, 'fields'>
172158
): Promise<MinimalMongoCursor<DBInterface>> {
173159
const sel = this.mongoSelector(selector)
174160
return new ChangeStreamCursor<DBInterface>({
175161
collectionName: this.name,
176162
selector: sel,
177163
projection: this.projectionOf(options),
178-
fetch: async () => this.findFetchAsync(sel, options),
179-
// When applySkipLimit is false, strip skip/limit so the count reflects the total matching set.
180-
count: async (applySkipLimit) =>
181-
this.countDocuments(
182-
sel,
183-
applySkipLimit
184-
? options
185-
: ({ ...options, skip: undefined, limit: undefined } as FindOptions<DBInterface>)
186-
),
187164
makeDeps: () => this.observeDeps(sel),
188165
})
189166
}

meteor/server/collections/implementations/mock.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import type { CreateIndexesOptions, IndexDescriptionInfo } from 'mongodb'
1313
import { InMemoryMongoCollection } from '@sofie-automation/corelib/dist/memoryCollection'
1414
import { PromisifyCallbacks } from '@sofie-automation/shared-lib/dist/lib/types'
1515
import { UpdateOptions, IndexSpecifier } from '@sofie-automation/meteor-lib/dist/collections/lib'
16-
import { AsyncOnlyMongoCollection } from '../collection'
17-
import { MinimalMongoCursor } from './asyncCollection'
16+
import { AsyncOnlyMongoCollection, MinimalMongoCursor } from '../collection'
1817

1918
/**
2019
* {@link WrappedMockCollection} only ever runs under jest, where `Meteor` is the mock that provides
@@ -93,17 +92,6 @@ export class WrappedMockCollection<
9392
await this.#sleep(0)
9493
return {
9594
collectionName: this.#core.name,
96-
fetchAsync: async () => {
97-
await this.#sleep(0)
98-
return this.#core.findFetch(selector, options)
99-
},
100-
countAsync: async (applySkipLimit = true) => {
101-
await this.#sleep(0)
102-
return this.#core.count(
103-
selector,
104-
applySkipLimit ? options : { ...options, skip: undefined, limit: undefined }
105-
)
106-
},
10795
observeAsync: async (callbacks) => {
10896
await this.#sleep(0)
10997
return this.#core.observe(callbacks, selector, options)

meteor/server/collections/implementations/readonlyWrapper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { ProtectedString } from '@sofie-automation/corelib/dist/protectedString'
22
import { Meteor } from 'meteor/meteor'
33
import type { IndexDescriptionInfo } from 'mongodb'
4-
import type { AsyncOnlyMongoCollection, AsyncOnlyReadOnlyMongoCollection } from '../collection'
5-
import type { MinimalMongoCursor } from './asyncCollection'
4+
import type { AsyncOnlyMongoCollection, AsyncOnlyReadOnlyMongoCollection, MinimalMongoCursor } from '../collection'
65

76
export class WrappedReadOnlyMongoCollection<
87
DBInterface extends { _id: ProtectedString<any> },

meteor/server/publications/lib/lib.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { AllPubSubCollections, AllPubSubTypes } from '@sofie-automation/meteor-l
33
import { extractFunctionSignature } from '../../lib'
44
import { protectStringObject, unprotectString } from '@sofie-automation/corelib/dist/protectedString'
55
import { MetricsGauge } from '@sofie-automation/corelib/dist/prometheus'
6-
import { MinimalMongoCursor } from '../../collections/implementations/asyncCollection'
6+
import { MinimalMongoCursor } from '../../collections/collection'
77
import { ChangeStreamCursor } from '../../collections/changeStream/changeStreamCursor'
88

99
export const MeteorPublicationSignatures: { [key: string]: string[] } = {}

meteor/server/publications/lib/observerChain.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Simplify } from 'type-fest'
44
import { assertNever } from '@sofie-automation/corelib/dist/lib'
55
import { logger } from '../../logging'
66
import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyError'
7-
import { MinimalMongoCursor } from '../../collections/implementations/asyncCollection'
7+
import { MinimalMongoCursor } from '../../collections/collection'
88

99
/**
1010
* https://stackoverflow.com/a/66011942

meteor/server/publications/organization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ meteorPublish(
7171

7272
return UserActionsLog.findWithCursor(selector, {
7373
limit: 10_000, // this is to prevent having a publication that produces a very large array
74+
sort: { timestamp: -1 },
7475
})
7576
}
7677
)

0 commit comments

Comments
 (0)