@@ -26,29 +26,37 @@ type MongoDBAutocompleterOptions = {
26
26
} ;
27
27
28
28
class DatabaseSchema {
29
- private collectionNames : string [ ] ;
29
+ private collectionNames : Set < string > ;
30
30
private collectionSchemas : Record < string , JSONSchema > ;
31
31
32
32
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 ( ) ;
34
39
this . collectionSchemas = Object . create ( null ) ;
35
40
}
36
41
37
42
setCollectionNames ( collectionNames : string [ ] ) : void {
38
- this . collectionNames = collectionNames ;
43
+ this . collectionNames = new Set ( collectionNames ) ;
39
44
}
40
45
41
46
setCollectionSchema ( collectionName : string , schema : JSONSchema ) : void {
47
+ this . collectionNames . add ( collectionName ) ;
42
48
this . collectionSchemas [ collectionName ] = schema ;
43
49
}
44
50
45
51
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
+ ) ;
52
60
53
61
return `{
54
62
${ collectionProperties . join ( '\n' ) }
@@ -57,35 +65,28 @@ class DatabaseSchema {
57
65
}
58
66
59
67
class ConnectionSchema {
60
- private readonly databaseNames : string [ ] ;
61
- private databaseName : string ;
68
+ private readonly databaseNames : Set < string > ;
62
69
private readonly databaseSchemas : Record < string , DatabaseSchema > ;
63
70
64
71
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 ( ) ;
67
78
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 ;
78
79
}
79
80
80
81
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
+ }
83
86
}
84
87
85
88
setDatabaseCollectionNames ( databaseName : string , collectionNames : string [ ] ) {
86
- if ( ! this . databaseSchemas [ databaseName ] ) {
87
- throw new Error ( `expected ${ databaseName } to be known` ) ;
88
- }
89
+ this . addDatabase ( databaseName ) ;
89
90
this . databaseSchemas [ databaseName ] . setCollectionNames ( collectionNames ) ;
90
91
}
91
92
@@ -94,33 +95,38 @@ class ConnectionSchema {
94
95
collectionName : string ,
95
96
collectionSchema : JSONSchema
96
97
) {
97
- if ( ! this . databaseSchemas [ databaseName ] ) {
98
- throw new Error ( `expected ${ databaseName } to be known` ) ;
99
- }
98
+ this . addDatabase ( databaseName ) ;
100
99
this . databaseSchemas [ databaseName ] . setCollectionSchema (
101
100
collectionName ,
102
101
collectionSchema
103
102
) ;
104
103
}
105
104
106
105
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
+ ) ;
113
114
114
115
return `{
115
116
${ databaseProperties . join ( '\n' ) }
116
117
}` ;
117
118
}
118
119
}
119
120
121
+ type AutocompleteOptions = {
122
+ connectionId : string ;
123
+ databaseName : string ;
124
+ position ?: number ;
125
+ } ;
126
+
120
127
export default class MongoDBAutocompleter {
121
128
private readonly context : AutocompletionContext ;
122
129
private connectionSchemas : Record < string , ConnectionSchema > ;
123
- private currentConnectionKey : string | undefined ;
124
130
private readonly autocompleter : Autocompleter ;
125
131
126
132
constructor ( { context, autocompleterOptions } : MongoDBAutocompleterOptions ) {
@@ -141,30 +147,11 @@ export default class MongoDBAutocompleter {
141
147
this . autocompleter . updateCode ( { 'shell-api.d.ts' : loadShellAPI ( ) } ) ;
142
148
}
143
149
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 ( ) ;
166
153
}
167
- return this . connectionSchemas [ this . currentConnectionKey ] ;
154
+ return this . connectionSchemas [ connectionId ] ;
168
155
}
169
156
170
157
getConnectionCode ( connectionKey : string ) : string {
@@ -183,20 +170,15 @@ declare global {
183
170
` ;
184
171
}
185
172
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 ) {
192
174
return `
193
175
/// <reference path="shell-api.d.ts" />
194
- /// <reference path="${ this . currentConnectionKey } .d.ts" />
176
+ /// <reference path="${ connectionId } .d.ts" />
195
177
196
178
export {}; // turns this into an "external module"
197
179
198
180
declare global {
199
- type CurrentDatabaseSchema = Connection${ this . currentConnectionKey } .ServerSchema['${ databaseName } '];
181
+ type CurrentDatabaseSchema = Connection${ connectionId } .ServerSchema['${ databaseName } '];
200
182
201
183
var db: CurrentDatabaseSchema;
202
184
var use: (collection: string) => CurrentDatabaseSchema;
@@ -206,12 +188,8 @@ declare global {
206
188
207
189
async autocomplete (
208
190
code : string ,
209
- position ?: number
191
+ { connectionId , databaseName , position } : AutocompleteOptions
210
192
) : Promise < AutoCompletion [ ] > {
211
- if ( ! this . currentConnectionKey ) {
212
- throw new Error ( 'No active connection' ) ;
213
- }
214
-
215
193
if ( typeof position === 'undefined' ) {
216
194
position = code . length ;
217
195
}
@@ -237,40 +215,40 @@ declare global {
237
215
[ ] ;
238
216
239
217
schema = await this . context . schemaInformationForAggregation (
240
- this . currentConnectionKey ,
241
- this . getActiveConnection ( ) . getCurrentDatabaseName ( ) ,
218
+ connectionId ,
219
+ databaseName ,
242
220
collectionName ,
243
221
pipelineToDryRun as Pipeline
244
222
) ;
245
223
} else {
246
224
schema = await this . context . schemaInformationForCollection (
247
- this . currentConnectionKey ,
248
- this . getActiveConnection ( ) . getCurrentDatabaseName ( ) ,
225
+ connectionId ,
226
+ databaseName ,
249
227
collectionName
250
228
) ;
251
229
}
252
230
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 ) ;
256
233
257
234
const collectionNames = await this . context . collectionsForDatabase (
258
- this . currentConnectionKey ,
235
+ connectionId ,
259
236
databaseName
260
237
) ;
261
- activeConnection . setDatabaseCollectionNames ( databaseName , collectionNames ) ;
238
+ connection . setDatabaseCollectionNames ( databaseName , collectionNames ) ;
262
239
263
240
// TODO: the problem with doing it this way is that, while the collection
264
241
// schema might be cached, we'll be generating TypeScript for it (and every
265
242
// other collection in the db and in fact every db in the server) every
266
243
// time.
267
244
this . autocompleter . updateCode ( {
268
- [ `${ this . currentConnectionKey } .d.ts` ] : this . getConnectionCode (
269
- this . currentConnectionKey
270
- ) ,
245
+ [ `${ connectionId } .d.ts` ] : this . getConnectionCode ( connectionId ) ,
271
246
} ) ;
272
247
this . autocompleter . updateCode ( {
273
- 'current-globals.d.ts' : this . getCurrentGlobalsCode ( ) ,
248
+ 'current-globals.d.ts' : this . getCurrentGlobalsCode (
249
+ connectionId ,
250
+ databaseName
251
+ ) ,
274
252
} ) ;
275
253
276
254
return this . autocompleter . autocomplete ( code , position ) ;
0 commit comments