Skip to content

Commit b4288ed

Browse files
committed
simplify partially
1 parent 8a9bf2e commit b4288ed

File tree

2 files changed

+77
-91
lines changed

2 files changed

+77
-91
lines changed

packages/mongodb-ts-autocomplete/src/index.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ describe('MongoDBAutocompleter', function () {
4646
autocompleter = new MongoDBAutocompleter({
4747
context: autocompleterContext,
4848
});
49-
autocompleter.setConnectionKey('myConnection');
5049
});
5150

5251
it('completes a bson expression', async function () {
53-
const completions = await autocompleter.autocomplete('Ob');
52+
const completions = await autocompleter.autocomplete('Ob', {
53+
connectionId: 'myConnection',
54+
databaseName: 'myDatabase',
55+
});
5456
expect(completions.filter((c) => c.name === 'ObjectId')).to.deep.equal([
5557
{
5658
kind: 'function',
@@ -61,7 +63,10 @@ describe('MongoDBAutocompleter', function () {
6163
});
6264

6365
it('completes a collection name', async function () {
64-
const completions = await autocompleter.autocomplete('db.fo');
66+
const completions = await autocompleter.autocomplete('db.fo', {
67+
connectionId: 'myConnection',
68+
databaseName: 'myDatabase',
69+
});
6570
// Note that the types are all blank objects for now because we haven't
6671
// sampled any of these collections' schemas yet
6772
expect(completions).to.deep.equal([
@@ -89,7 +94,10 @@ describe('MongoDBAutocompleter', function () {
8994
});
9095

9196
it('completes a collection field name', async function () {
92-
const completions = await autocompleter.autocomplete('db.foo.find({ fo');
97+
const completions = await autocompleter.autocomplete('db.foo.find({ fo', {
98+
connectionId: 'myConnection',
99+
databaseName: 'myDatabase',
100+
});
93101

94102
expect(completions).to.deep.equal([
95103
{

packages/mongodb-ts-autocomplete/src/index.ts

Lines changed: 65 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,37 @@ type MongoDBAutocompleterOptions = {
2626
};
2727

2828
class DatabaseSchema {
29-
private collectionNames: string[];
29+
private collectionNames: Set<string>;
3030
private collectionSchemas: Record<string, JSONSchema>;
3131

3232
constructor() {
33-
this.collectionNames = [];
33+
// TODO: this is kinda the only real reason for this class: So we can keep
34+
// track of the known collectionNames for a database. It could be all the
35+
// names from a listCollections() or it could just be all the ones we've
36+
// auto-completed for. The schemas can come from the autocompletion context.
37+
// This can probably be added to the autocompletion context.
38+
this.collectionNames = new Set();
3439
this.collectionSchemas = Object.create(null);
3540
}
3641

3742
setCollectionNames(collectionNames: string[]): void {
38-
this.collectionNames = collectionNames;
43+
this.collectionNames = new Set(collectionNames);
3944
}
4045

4146
setCollectionSchema(collectionName: string, schema: JSONSchema): void {
47+
this.collectionNames.add(collectionName);
4248
this.collectionSchemas[collectionName] = schema;
4349
}
4450

4551
toTypescriptTypeDefinition(): string {
46-
const collectionProperties = this.collectionNames.map((collectionName) => {
47-
const def = this.collectionSchemas[collectionName]
48-
? toTypescriptTypeDefinition(this.collectionSchemas[collectionName])
49-
: `{}`;
50-
return ` '${collectionName}': ShellAPI.Collection<${def}>;`;
51-
});
52+
const collectionProperties = [...this.collectionNames.values()].map(
53+
(collectionName) => {
54+
const def = this.collectionSchemas[collectionName]
55+
? toTypescriptTypeDefinition(this.collectionSchemas[collectionName])
56+
: `{}`;
57+
return ` '${collectionName}': ShellAPI.Collection<${def}>;`;
58+
}
59+
);
5260

5361
return `{
5462
${collectionProperties.join('\n')}
@@ -57,35 +65,28 @@ class DatabaseSchema {
5765
}
5866

5967
class ConnectionSchema {
60-
private readonly databaseNames: string[];
61-
private databaseName: string;
68+
private readonly databaseNames: Set<string>;
6269
private readonly databaseSchemas: Record<string, DatabaseSchema>;
6370

6471
constructor() {
65-
this.databaseNames = [];
66-
this.databaseName = 'test';
72+
// TODO: this is kinda the only real reason for this class: So we can keep
73+
// track of the known databaseNames for a connection. It could be all the
74+
// names from a listDatabases() or it could just be all the ones we've
75+
// auto-completed for. The schemas can come from the autocompletion context.
76+
// This can probably be added to the autocompletion context.
77+
this.databaseNames = new Set();
6778
this.databaseSchemas = Object.create(null);
68-
69-
this.addDatabase(this.databaseName);
70-
}
71-
72-
setDatabaseName(databaseName: string) {
73-
this.databaseName = databaseName;
74-
}
75-
76-
getCurrentDatabaseName(): string {
77-
return this.databaseName;
7879
}
7980

8081
addDatabase(databaseName: string) {
81-
this.databaseNames.push(databaseName);
82-
this.databaseSchemas[databaseName] = new DatabaseSchema();
82+
this.databaseNames.add(databaseName);
83+
if (!this.databaseSchemas[databaseName]) {
84+
this.databaseSchemas[databaseName] = new DatabaseSchema();
85+
}
8386
}
8487

8588
setDatabaseCollectionNames(databaseName: string, collectionNames: string[]) {
86-
if (!this.databaseSchemas[databaseName]) {
87-
throw new Error(`expected ${databaseName} to be known`);
88-
}
89+
this.addDatabase(databaseName);
8990
this.databaseSchemas[databaseName].setCollectionNames(collectionNames);
9091
}
9192

@@ -94,33 +95,38 @@ class ConnectionSchema {
9495
collectionName: string,
9596
collectionSchema: JSONSchema
9697
) {
97-
if (!this.databaseSchemas[databaseName]) {
98-
throw new Error(`expected ${databaseName} to be known`);
99-
}
98+
this.addDatabase(databaseName);
10099
this.databaseSchemas[databaseName].setCollectionSchema(
101100
collectionName,
102101
collectionSchema
103102
);
104103
}
105104

106105
toTypescriptTypeDefinition(): string {
107-
const databaseProperties = this.databaseNames.map((databaseName) => {
108-
const def = this.databaseSchemas[databaseName]
109-
? this.databaseSchemas[databaseName].toTypescriptTypeDefinition()
110-
: `{}`;
111-
return ` '${databaseName}': ShellAPI.Database & ${def}`;
112-
});
106+
const databaseProperties = [...this.databaseNames.values()].map(
107+
(databaseName) => {
108+
const def = this.databaseSchemas[databaseName]
109+
? this.databaseSchemas[databaseName].toTypescriptTypeDefinition()
110+
: `{}`;
111+
return ` '${databaseName}': ShellAPI.Database & ${def}`;
112+
}
113+
);
113114

114115
return `{
115116
${databaseProperties.join('\n')}
116117
}`;
117118
}
118119
}
119120

121+
type AutocompleteOptions = {
122+
connectionId: string;
123+
databaseName: string;
124+
position?: number;
125+
};
126+
120127
export default class MongoDBAutocompleter {
121128
private readonly context: AutocompletionContext;
122129
private connectionSchemas: Record<string, ConnectionSchema>;
123-
private currentConnectionKey: string | undefined;
124130
private readonly autocompleter: Autocompleter;
125131

126132
constructor({ context, autocompleterOptions }: MongoDBAutocompleterOptions) {
@@ -141,30 +147,11 @@ export default class MongoDBAutocompleter {
141147
this.autocompleter.updateCode({ 'shell-api.d.ts': loadShellAPI() });
142148
}
143149

144-
setConnectionKey(connectionKey: string) {
145-
this.currentConnectionKey = connectionKey;
146-
this.connectionSchemas[this.currentConnectionKey] = new ConnectionSchema();
147-
this.autocompleter.updateCode({
148-
[`${connectionKey}.d.ts`]: this.getConnectionCode(
149-
this.currentConnectionKey
150-
),
151-
});
152-
153-
// do we need this? it will be rewritten before auto-completing anyway
154-
//this.autocompleter.updateCode({ 'current-globals.d.ts': this.getCurrentGlobalsCode() });
155-
}
156-
157-
setDatabaseName(databaseName: string): void {
158-
this.getActiveConnection().setDatabaseName(databaseName);
159-
// do we need this? it will be rewritten before auto-completing anyway
160-
//this.autocompleter.updateCode({ 'current-globals.d.ts': this.getCurrentGlobalsCode() });
161-
}
162-
163-
getActiveConnection(): ConnectionSchema {
164-
if (!this.currentConnectionKey) {
165-
throw new Error('No active connection');
150+
addConnection(connectionId: string): ConnectionSchema {
151+
if (!this.connectionSchemas[connectionId]) {
152+
this.connectionSchemas[connectionId] = new ConnectionSchema();
166153
}
167-
return this.connectionSchemas[this.currentConnectionKey];
154+
return this.connectionSchemas[connectionId];
168155
}
169156

170157
getConnectionCode(connectionKey: string): string {
@@ -183,20 +170,15 @@ declare global {
183170
`;
184171
}
185172

186-
getCurrentGlobalsCode() {
187-
if (!this.currentConnectionKey) {
188-
throw new Error('No active connection');
189-
}
190-
191-
const databaseName = this.getActiveConnection().getCurrentDatabaseName();
173+
getCurrentGlobalsCode(connectionId: string, databaseName: string) {
192174
return `
193175
/// <reference path="shell-api.d.ts" />
194-
/// <reference path="${this.currentConnectionKey}.d.ts" />
176+
/// <reference path="${connectionId}.d.ts" />
195177
196178
export {}; // turns this into an "external module"
197179
198180
declare global {
199-
type CurrentDatabaseSchema = Connection${this.currentConnectionKey}.ServerSchema['${databaseName}'];
181+
type CurrentDatabaseSchema = Connection${connectionId}.ServerSchema['${databaseName}'];
200182
201183
var db: CurrentDatabaseSchema;
202184
var use: (collection: string) => CurrentDatabaseSchema;
@@ -206,12 +188,8 @@ declare global {
206188

207189
async autocomplete(
208190
code: string,
209-
position?: number
191+
{ connectionId, databaseName, position }: AutocompleteOptions
210192
): Promise<AutoCompletion[]> {
211-
if (!this.currentConnectionKey) {
212-
throw new Error('No active connection');
213-
}
214-
215193
if (typeof position === 'undefined') {
216194
position = code.length;
217195
}
@@ -237,40 +215,40 @@ declare global {
237215
[];
238216

239217
schema = await this.context.schemaInformationForAggregation(
240-
this.currentConnectionKey,
241-
this.getActiveConnection().getCurrentDatabaseName(),
218+
connectionId,
219+
databaseName,
242220
collectionName,
243221
pipelineToDryRun as Pipeline
244222
);
245223
} else {
246224
schema = await this.context.schemaInformationForCollection(
247-
this.currentConnectionKey,
248-
this.getActiveConnection().getCurrentDatabaseName(),
225+
connectionId,
226+
databaseName,
249227
collectionName
250228
);
251229
}
252230

253-
const activeConnection = this.getActiveConnection();
254-
const databaseName = this.getActiveConnection().getCurrentDatabaseName();
255-
activeConnection.addCollectionSchema(databaseName, collectionName, schema);
231+
const connection = this.addConnection(connectionId);
232+
connection.addCollectionSchema(databaseName, collectionName, schema);
256233

257234
const collectionNames = await this.context.collectionsForDatabase(
258-
this.currentConnectionKey,
235+
connectionId,
259236
databaseName
260237
);
261-
activeConnection.setDatabaseCollectionNames(databaseName, collectionNames);
238+
connection.setDatabaseCollectionNames(databaseName, collectionNames);
262239

263240
// TODO: the problem with doing it this way is that, while the collection
264241
// schema might be cached, we'll be generating TypeScript for it (and every
265242
// other collection in the db and in fact every db in the server) every
266243
// time.
267244
this.autocompleter.updateCode({
268-
[`${this.currentConnectionKey}.d.ts`]: this.getConnectionCode(
269-
this.currentConnectionKey
270-
),
245+
[`${connectionId}.d.ts`]: this.getConnectionCode(connectionId),
271246
});
272247
this.autocompleter.updateCode({
273-
'current-globals.d.ts': this.getCurrentGlobalsCode(),
248+
'current-globals.d.ts': this.getCurrentGlobalsCode(
249+
connectionId,
250+
databaseName
251+
),
274252
});
275253

276254
return this.autocompleter.autocomplete(code, position);

0 commit comments

Comments
 (0)