Skip to content

Commit 16242e0

Browse files
committed
backport the circular ref fix
1 parent 30ad07e commit 16242e0

File tree

3 files changed

+98
-89
lines changed

3 files changed

+98
-89
lines changed

packages/cli-repl/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"@mongosh/shell-evaluator": "^3.11.0",
7878
"@mongosh/snippet-manager": "^3.11.0",
7979
"@mongosh/types": "3.6.2",
80-
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.0",
80+
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.2",
8181
"@segment/analytics-node": "^1.3.0",
8282
"ansi-escape-sequences": "^5.1.2",
8383
"askcharacter": "^2.0.4",

packages/cli-repl/src/mongosh-repl.ts

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import crypto from 'crypto';
12
import completer from '@mongosh/autocomplete';
23
import { MongoshInternalError, MongoshWarning } from '@mongosh/errors';
34
import { changeHistory } from '@mongosh/history';
@@ -7,6 +8,7 @@ import type {
78
} from '@mongosh/service-provider-core';
89
import type {
910
EvaluationListener,
11+
Mongo,
1012
OnLoadResult,
1113
ShellCliOptions,
1214
} from '@mongosh/shell-api';
@@ -50,6 +52,9 @@ import { installPasteSupport } from './repl-paste-support';
5052
import util from 'util';
5153

5254
import { MongoDBAutocompleter } from '@mongodb-js/mongodb-ts-autocomplete';
55+
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
56+
import type { JSONSchema } from 'mongodb-schema';
57+
import { analyzeDocuments } from 'mongodb-schema';
5358

5459
declare const __non_webpack_require__: any;
5560

@@ -433,6 +438,88 @@ class MongoshNodeRepl implements EvaluationListener {
433438
this.runtimeState().context.history = history;
434439
}
435440

441+
// TODO: probably better to have this on instanceState
442+
public _getMongoByConnectionId(
443+
instanceState: ShellInstanceState,
444+
connectionId: string
445+
): Mongo {
446+
for (const mongo of instanceState.mongos) {
447+
if (connectionIdFromURI(mongo.getURI()) === connectionId) {
448+
return mongo;
449+
}
450+
}
451+
throw new Error(`mongo with connection id ${connectionId} not found`);
452+
}
453+
454+
public getAutocompletionContext(
455+
instanceState: ShellInstanceState
456+
): AutocompletionContext {
457+
return {
458+
currentDatabaseAndConnection: () => {
459+
return {
460+
connectionId: connectionIdFromURI(
461+
instanceState.currentDb.getMongo().getURI()
462+
),
463+
databaseName: instanceState.currentDb.getName(),
464+
};
465+
},
466+
databasesForConnection: async (
467+
connectionId: string
468+
): Promise<string[]> => {
469+
const mongo = this._getMongoByConnectionId(instanceState, connectionId);
470+
try {
471+
const dbNames = await mongo._getDatabaseNamesForCompletion();
472+
return dbNames.filter(
473+
(name: string) => !CONTROL_CHAR_REGEXP.test(name)
474+
);
475+
} catch (err: any) {
476+
// TODO: move this code to a method in the shell instance so we don't
477+
// have to hardcode the error code or export it.
478+
if (err?.code === 'SHAPI-10004' || err?.codeName === 'Unauthorized') {
479+
return [];
480+
}
481+
throw err;
482+
}
483+
},
484+
collectionsForDatabase: async (
485+
connectionId: string,
486+
databaseName: string
487+
): Promise<string[]> => {
488+
const mongo = this._getMongoByConnectionId(instanceState, connectionId);
489+
try {
490+
const collectionNames = await mongo
491+
._getDb(databaseName)
492+
._getCollectionNamesForCompletion();
493+
return collectionNames.filter(
494+
(name: string) => !CONTROL_CHAR_REGEXP.test(name)
495+
);
496+
} catch (err: any) {
497+
// TODO: move this code to a method in the shell instance so we don't
498+
// have to hardcode the error code or export it.
499+
if (err?.code === 'SHAPI-10004' || err?.codeName === 'Unauthorized') {
500+
return [];
501+
}
502+
throw err;
503+
}
504+
},
505+
schemaInformationForCollection: async (
506+
connectionId: string,
507+
databaseName: string,
508+
collectionName: string
509+
): Promise<JSONSchema> => {
510+
const mongo = this._getMongoByConnectionId(instanceState, connectionId);
511+
const docs = await mongo
512+
._getDb(databaseName)
513+
.getCollection(collectionName)
514+
._getSampleDocsForCompletion();
515+
const schemaAccessor = await analyzeDocuments(docs);
516+
517+
const schema = await schemaAccessor.getMongoDBJsonSchema();
518+
return schema;
519+
},
520+
};
521+
}
522+
436523
private async finishInitializingNodeRepl(): Promise<void> {
437524
const { repl, instanceState } = this.runtimeState();
438525
if (!repl) return;
@@ -445,7 +532,8 @@ class MongoshNodeRepl implements EvaluationListener {
445532
line: string
446533
) => Promise<[string[], string, 'exclusive'] | [string[], string]>;
447534
if (process.env.USE_NEW_AUTOCOMPLETE) {
448-
const autocompletionContext = instanceState.getAutocompletionContext();
535+
const autocompletionContext =
536+
this.getAutocompletionContext(instanceState);
449537
newMongoshCompleter = new MongoDBAutocompleter({
450538
context: autocompletionContext,
451539
});
@@ -1347,4 +1435,12 @@ async function enterAsynchronousExecutionForPrompt(): Promise<void> {
13471435
await new Promise(setImmediate);
13481436
}
13491437

1438+
function connectionIdFromURI(uri: string): string {
1439+
// turn the uri into something we can safely use as part of a "filename"
1440+
// inside autocomplete
1441+
const hash = crypto.createHash('sha256');
1442+
hash.update(uri);
1443+
return hash.digest('hex');
1444+
}
1445+
13501446
export default MongoshNodeRepl;

packages/shell-api/src/shell-instance-state.ts

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import crypto from 'crypto';
21
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
32
import type {
43
AutoEncryptionOptions,
@@ -38,10 +37,6 @@ import constructShellBson from './shell-bson';
3837
import { Streams } from './streams';
3938
import { ShellLog } from './shell-log';
4039

41-
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
42-
import type { JSONSchema } from 'mongodb-schema';
43-
import { analyzeDocuments } from 'mongodb-schema';
44-
4540
/**
4641
* The subset of CLI options that is relevant for the shell API's behavior itself.
4742
*/
@@ -140,14 +135,6 @@ export interface ShellPlugin {
140135
// eslint-disable-next-line no-control-regex
141136
const CONTROL_CHAR_REGEXP = /[\x00-\x1F\x7F-\x9F]/g;
142137

143-
function connectionIdFromURI(uri: string): string {
144-
// turn the uri into something we can safely use as part of a "filename"
145-
// inside autocomplete
146-
const hash = crypto.createHash('sha256');
147-
hash.update(uri);
148-
return hash.digest('hex');
149-
}
150-
151138
/**
152139
* Anything to do with the state of the shell API and API objects is stored here.
153140
*
@@ -415,80 +402,6 @@ export class ShellInstanceState {
415402
this.evaluationListener = listener;
416403
}
417404

418-
public _getMongoByConnectionId(connectionId: string): Mongo {
419-
for (const mongo of this.mongos) {
420-
if (connectionIdFromURI(mongo.getURI()) === connectionId) {
421-
return mongo;
422-
}
423-
}
424-
throw new Error(`mongo with connection id ${connectionId} not found`);
425-
}
426-
427-
public getAutocompletionContext(): AutocompletionContext {
428-
return {
429-
currentDatabaseAndConnection: () => {
430-
return {
431-
connectionId: connectionIdFromURI(this.currentDb.getMongo().getURI()),
432-
databaseName: this.currentDb.getName(),
433-
};
434-
},
435-
databasesForConnection: async (
436-
connectionId: string
437-
): Promise<string[]> => {
438-
const mongo = this._getMongoByConnectionId(connectionId);
439-
try {
440-
const dbNames = await mongo._getDatabaseNamesForCompletion();
441-
return dbNames.filter((name) => !CONTROL_CHAR_REGEXP.test(name));
442-
} catch (err: any) {
443-
if (
444-
err?.code === ShellApiErrors.NotConnected ||
445-
err?.codeName === 'Unauthorized'
446-
) {
447-
return [];
448-
}
449-
throw err;
450-
}
451-
},
452-
collectionsForDatabase: async (
453-
connectionId: string,
454-
databaseName: string
455-
): Promise<string[]> => {
456-
const mongo = this._getMongoByConnectionId(connectionId);
457-
try {
458-
const collectionNames = await mongo
459-
._getDb(databaseName)
460-
._getCollectionNamesForCompletion();
461-
return collectionNames.filter(
462-
(name) => !CONTROL_CHAR_REGEXP.test(name)
463-
);
464-
} catch (err: any) {
465-
if (
466-
err?.code === ShellApiErrors.NotConnected ||
467-
err?.codeName === 'Unauthorized'
468-
) {
469-
return [];
470-
}
471-
throw err;
472-
}
473-
},
474-
schemaInformationForCollection: async (
475-
connectionId: string,
476-
databaseName: string,
477-
collectionName: string
478-
): Promise<JSONSchema> => {
479-
const mongo = this._getMongoByConnectionId(connectionId);
480-
const docs = await mongo
481-
._getDb(databaseName)
482-
.getCollection(collectionName)
483-
._getSampleDocsForCompletion();
484-
const schemaAccessor = await analyzeDocuments(docs);
485-
486-
const schema = await schemaAccessor.getMongoDBJsonSchema();
487-
return schema;
488-
},
489-
};
490-
}
491-
492405
public getAutocompleteParameters(): AutocompleteParameters {
493406
return {
494407
topology: () => {

0 commit comments

Comments
 (0)