Skip to content

Commit 0e17bc2

Browse files
committed
fixup: add index signature support
1 parent cfbafad commit 0e17bc2

File tree

5 files changed

+116
-55
lines changed

5 files changed

+116
-55
lines changed

packages/shell-api/bin/api-postprocess.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ type MongodbServerSchema = {
116116
_id: ObjectId;
117117
foo: number;
118118
}
119+
},
120+
with: { schema: never },
121+
'with.dots': {
122+
schema: {
123+
_id: ObjectId;
124+
bar: string;
125+
}
119126
}
120127
}
121128
}

packages/shell-api/src/collection.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,36 @@ import PlanCache from './plan-cache';
9292
import ChangeStreamCursor from './change-stream-cursor';
9393
import { ShellApiErrors } from './error-codes';
9494

95+
export type Collection<
96+
M extends GenericServerSideSchema = GenericServerSideSchema,
97+
D extends GenericDatabaseSchema = M[keyof M],
98+
C extends GenericCollectionSchema = D[keyof D],
99+
N extends StringKey<D> = StringKey<D>
100+
> = CollectionImpl<M, D, C, N> & {
101+
[k in keyof D as k extends `${N}.${infer S}` ? S : never]: Collection<
102+
M,
103+
D,
104+
D[k]
105+
>;
106+
};
107+
95108
@shellApiClassDefault
96109
@addSourceToResults
97-
export class Collection<
110+
export class CollectionImpl<
98111
M extends GenericServerSideSchema = GenericServerSideSchema,
99112
D extends GenericDatabaseSchema = M[keyof M],
100-
C extends GenericCollectionSchema = D[keyof D]
113+
C extends GenericCollectionSchema = D[keyof D],
114+
N extends StringKey<D> = StringKey<D>
101115
> extends ShellApiWithMongoClass {
102116
_mongo: Mongo<M>;
103117
_database: Database<M, D>;
104-
_name: StringKey<D>;
105-
constructor(mongo: Mongo<M>, database: Database<M, D>, name: StringKey<D>) {
118+
_name: N;
119+
120+
_typeLaunder(): Collection<M, D> {
121+
return this as Collection<M, D>;
122+
}
123+
124+
constructor(mongo: Mongo<M>, database: Database<M, D>, name: N) {
106125
super();
107126
this._mongo = mongo;
108127
this._database = database;
@@ -1555,9 +1574,9 @@ export class Collection<
15551574
return `${this._database._name}.${this._name}`;
15561575
}
15571576

1558-
getName(): string {
1577+
getName(): N {
15591578
this._emitCollectionApiCall('getName');
1560-
return `${this._name}`;
1579+
return this._name;
15611580
}
15621581

15631582
@returnsPromise
@@ -1604,7 +1623,7 @@ export class Collection<
16041623
explain(verbosity: ExplainVerbosityLike = 'queryPlanner'): Explainable {
16051624
verbosity = validateExplainableVerbosity(verbosity);
16061625
this._emitCollectionApiCall('explain', { verbosity });
1607-
return new Explainable(this._mongo, this, verbosity);
1626+
return new Explainable(this._mongo, this._typeLaunder(), verbosity);
16081627
}
16091628

16101629
/**
@@ -1972,7 +1991,7 @@ export class Collection<
19721991
true,
19731992
await this._database._baseOptions()
19741993
);
1975-
return new Bulk(this, innerBulk, true);
1994+
return new Bulk(this._typeLaunder(), innerBulk, true);
19761995
}
19771996

19781997
@returnsPromise
@@ -1986,14 +2005,14 @@ export class Collection<
19862005
false,
19872006
await this._database._baseOptions()
19882007
);
1989-
return new Bulk(this, innerBulk);
2008+
return new Bulk(this._typeLaunder(), innerBulk);
19902009
}
19912010

19922011
@returnType('PlanCache')
19932012
@apiVersions([])
19942013
getPlanCache(): PlanCache {
19952014
this._emitCollectionApiCall('getPlanCache');
1996-
return new PlanCache(this);
2015+
return new PlanCache(this._typeLaunder());
19972016
}
19982017

19992018
@returnsPromise
@@ -2242,15 +2261,15 @@ export class Collection<
22422261
@apiVersions([1])
22432262
async hideIndex(index: string | Document): Promise<Document> {
22442263
this._emitCollectionApiCall('hideIndex');
2245-
return setHideIndex(this, index, true);
2264+
return setHideIndex(this._typeLaunder(), index, true);
22462265
}
22472266

22482267
@serverVersions(['4.4.0', ServerVersions.latest])
22492268
@returnsPromise
22502269
@apiVersions([1])
22512270
async unhideIndex(index: string | Document): Promise<Document> {
22522271
this._emitCollectionApiCall('unhideIndex');
2253-
return setHideIndex(this, index, false);
2272+
return setHideIndex(this._typeLaunder(), index, false);
22542273
}
22552274

22562275
@serverVersions(['7.0.0', ServerVersions.latest])

packages/shell-api/src/database.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type Mongo from './mongo';
2-
import Collection from './collection';
2+
import type Collection from './collection';
3+
import { CollectionImpl } from './collection';
34
import {
45
returnsPromise,
56
returnType,
@@ -70,26 +71,37 @@ type AuthDoc = {
7071
mechanism?: string;
7172
};
7273

74+
export type Database<
75+
M extends GenericServerSideSchema = GenericServerSideSchema,
76+
D extends GenericDatabaseSchema = GenericDatabaseSchema
77+
> = DatabaseImpl<M, D> & {
78+
[k in StringKey<D>]: Collection<M, D, D[k], k>;
79+
};
80+
7381
@shellApiClassDefault
74-
export default class Database<
82+
export class DatabaseImpl<
7583
M extends GenericServerSideSchema = GenericServerSideSchema,
76-
D extends GenericDatabaseSchema = M[keyof M]
84+
D extends GenericDatabaseSchema = GenericDatabaseSchema
7785
> extends ShellApiWithMongoClass {
7886
_mongo: Mongo<M>;
7987
_name: StringKey<M>;
80-
_collections: Record<string, Collection>;
88+
_collections: Record<string, Collection<M, D>>;
8189
_session: Session | undefined;
8290
_cachedCollectionNames: StringKey<D>[] = [];
8391
_cachedHello: Document | null = null;
8492

93+
_typeLaunder(): Database<M, D> {
94+
return this as Database<M, D>;
95+
}
96+
8597
constructor(mongo: Mongo<M>, name: StringKey<M>, session?: Session) {
8698
super();
8799
this._mongo = mongo;
88100
this._name = name;
89-
const collections: Record<string, Collection> = Object.create(null);
101+
const collections: Record<string, Collection<M, D>> = Object.create(null);
90102
this._collections = collections;
91103
this._session = session;
92-
const proxy = new Proxy(this, {
104+
const proxy = new Proxy(this._typeLaunder(), {
93105
get: (target, prop): any => {
94106
if (prop in target) {
95107
return (target as any)[prop];
@@ -104,7 +116,11 @@ export default class Database<
104116
}
105117

106118
if (!collections[prop]) {
107-
collections[prop] = new Collection(mongo, proxy, prop);
119+
collections[prop] = new CollectionImpl(
120+
mongo,
121+
proxy,
122+
prop
123+
)._typeLaunder();
108124
}
109125

110126
return collections[prop];
@@ -449,13 +465,13 @@ export default class Database<
449465
assertArgsDefinedType([db], ['string'], 'Database.getSiblingDB');
450466
this._emitDatabaseApiCall('getSiblingDB', { db });
451467
if (this._session) {
452-
return this._session.getDatabase(db);
468+
return this._session.getDatabase(db) as Database<M, M[K]>;
453469
}
454470
return this._mongo._getDb(db);
455471
}
456472

457473
@returnType('Collection')
458-
getCollection<K extends StringKey<D>>(coll: K): Collection<M, D, D[K]> {
474+
getCollection<K extends StringKey<D>>(coll: K): Collection<M, D, D[K], K> {
459475
assertArgsDefinedType([coll], ['string'], 'Database.getColl');
460476
this._emitDatabaseApiCall('getCollection', { coll });
461477
if (!isValidCollectionName(coll)) {
@@ -465,13 +481,17 @@ export default class Database<
465481
);
466482
}
467483

468-
const collections: Record<string, Collection> = this._collections;
484+
const collections: Record<string, Collection<M, D>> = this._collections;
469485

470486
if (!collections[coll]) {
471-
collections[coll] = new Collection(this._mongo, this, coll);
487+
collections[coll] = new CollectionImpl(
488+
this._mongo,
489+
this._typeLaunder(),
490+
coll
491+
)._typeLaunder();
472492
}
473493

474-
return collections[coll];
494+
return collections[coll] as Collection<M, D, D[K], K>;
475495
}
476496

477497
@returnsPromise
@@ -1471,7 +1491,7 @@ export default class Database<
14711491
async printShardingStatus(verbose = false): Promise<CommandResult> {
14721492
this._emitDatabaseApiCall('printShardingStatus', { verbose });
14731493
const result = await getPrintableShardStatus(
1474-
await getConfigDB(this),
1494+
await getConfigDB(this._typeLaunder()),
14751495
verbose
14761496
);
14771497
return new CommandResult('StatsResult', result);
@@ -1760,3 +1780,5 @@ export default class Database<
17601780
});
17611781
}
17621782
}
1783+
1784+
export default Database;

packages/shell-api/src/mongo.ts

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,31 @@ import {
1919
topologies,
2020
deprecated,
2121
} from './decorators';
22-
import type {
23-
ChangeStreamOptions,
24-
ClientSessionOptions,
25-
CommandOperationOptions,
26-
Document,
27-
ListDatabasesOptions,
28-
ReadConcernLevel,
29-
ReadPreference,
30-
ReadPreferenceLike,
31-
ReadPreferenceMode,
32-
ServiceProvider,
33-
TransactionOptions,
34-
MongoClientOptions,
35-
AutoEncryptionOptions as SPAutoEncryption,
36-
ServerApi,
37-
ServerApiVersion,
38-
WriteConcern,
22+
import {
23+
type ChangeStreamOptions,
24+
type ClientSessionOptions,
25+
type CommandOperationOptions,
26+
type Document,
27+
type ListDatabasesOptions,
28+
type ReadConcernLevel,
29+
type ReadPreference,
30+
type ReadPreferenceLike,
31+
type ReadPreferenceMode,
32+
type ServiceProvider,
33+
type TransactionOptions,
34+
type MongoClientOptions,
35+
type AutoEncryptionOptions as SPAutoEncryption,
36+
type ServerApi,
37+
type ServerApiVersion,
38+
type WriteConcern,
3939
} from '@mongosh/service-provider-core';
4040
import type { ConnectionInfo } from '@mongosh/arg-parser';
4141
import {
4242
mapCliToDriver,
4343
generateConnectionInfoFromCliArgs,
4444
} from '@mongosh/arg-parser';
45-
import type Collection from './collection';
46-
import Database from './database';
45+
import type { Database } from './database';
46+
import { DatabaseImpl } from './database';
4747
import type ShellInstanceState from './shell-instance-state';
4848
import { CommandResult } from './result';
4949
import { redactURICredentials } from '@mongosh/history';
@@ -62,6 +62,7 @@ import { KeyVault, ClientEncryption } from './field-level-encryption';
6262
import { ShellApiErrors } from './error-codes';
6363
import type { LogEntry } from './log-entry';
6464
import { parseAnyLogEntry } from './log-entry';
65+
import type { Collection } from './collection';
6566

6667
/* Utility, inverse of Readonly<T> */
6768
type Mutable<T> = {
@@ -262,9 +263,9 @@ export default class Mongo<
262263
}
263264

264265
if (!(name in this._databases)) {
265-
this._databases[name] = new Database(this, name);
266+
this._databases[name] = new DatabaseImpl(this, name)._typeLaunder();
266267
}
267-
return this._databases[name];
268+
return this._databases[name] as Database<M, M[K]>;
268269
}
269270

270271
@returnType('Database')
@@ -286,7 +287,11 @@ export default class Mongo<
286287
CommonErrors.InvalidArgument
287288
);
288289
}
289-
return this._getDb(db as StringKey<M>).getCollection(coll);
290+
return this._getDb(db as StringKey<M>).getCollection(coll) as Collection<
291+
M,
292+
M[KD],
293+
M[KD][KC]
294+
>;
290295
}
291296

292297
getURI(): string {

packages/shell-api/src/session.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,25 @@ import type {
1414
} from '@mongosh/service-provider-core';
1515
import { asPrintable } from './enums';
1616
import type Mongo from './mongo';
17-
import Database from './database';
17+
import type Database from './database';
18+
import { DatabaseImpl } from './database';
1819
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
20+
import type { GenericServerSideSchema, StringKey } from './helpers';
1921
import { assertArgsDefinedType, isValidDatabaseName } from './helpers';
2022

2123
@shellApiClassDefault
2224
@classPlatforms(['CLI'])
23-
export default class Session extends ShellApiWithMongoClass {
25+
export default class Session<
26+
M extends GenericServerSideSchema = GenericServerSideSchema
27+
> extends ShellApiWithMongoClass {
2428
public id: ServerSessionId | undefined;
2529
public _session: ClientSession;
2630
public _options: ClientSessionOptions;
27-
public _mongo: Mongo;
28-
private _databases: Record<string, Database>;
31+
public _mongo: Mongo<M>;
32+
private _databases: Record<string, Database<M>>;
2933

3034
constructor(
31-
mongo: Mongo,
35+
mongo: Mongo<M>,
3236
options: ClientSessionOptions,
3337
session: ClientSession
3438
) {
@@ -47,7 +51,7 @@ export default class Session extends ShellApiWithMongoClass {
4751
return this._session.id;
4852
}
4953

50-
getDatabase(name: string): Database {
54+
getDatabase<K extends StringKey<M>>(name: K): Database<M, M[K]> {
5155
assertArgsDefinedType([name], ['string'], 'Session.getDatabase');
5256

5357
if (!isValidDatabaseName(name)) {
@@ -58,9 +62,13 @@ export default class Session extends ShellApiWithMongoClass {
5862
}
5963

6064
if (!(name in this._databases)) {
61-
this._databases[name] = new Database(this._mongo, name, this);
65+
this._databases[name] = new DatabaseImpl(
66+
this._mongo,
67+
name,
68+
this
69+
)._typeLaunder();
6270
}
63-
return this._databases[name];
71+
return this._databases[name] as Database<M, M[K]>;
6472
}
6573

6674
advanceOperationTime(ts: TimestampType): void {

0 commit comments

Comments
 (0)