Skip to content

Commit 3f1513b

Browse files
committed
feat(shell-api): add shell plugin feature
1 parent e096fe1 commit 3f1513b

File tree

10 files changed

+569
-5
lines changed

10 files changed

+569
-5
lines changed

packages/shell-api/src/decorators.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import Help from './help';
1313
import { addHiddenDataProperty } from './helpers';
1414
import { checkInterrupted } from './interruptor';
15-
import { rephraseMongoError } from './mongo-errors';
1615

1716
const addSourceToResultsSymbol = Symbol.for('@@mongosh.addSourceToResults');
1817
const resultSource = Symbol.for('@@mongosh.resultSource');
@@ -171,7 +170,7 @@ function wrapWithApiChecks<T extends(...args: any[]) => any>(fn: T, className: s
171170
fn.call(this, ...args)
172171
]);
173172
} catch (e) {
174-
throw rephraseMongoError(e);
173+
throw internalState?.transformError(e) ?? e;
175174
} finally {
176175
if (interrupt) {
177176
interrupt.destroy();
@@ -187,7 +186,7 @@ function wrapWithApiChecks<T extends(...args: any[]) => any>(fn: T, className: s
187186
try {
188187
result = fn.call(this, ...args);
189188
} catch (e) {
190-
throw rephraseMongoError(e);
189+
throw internalState?.transformError(e) ?? e;
191190
}
192191
checkInterrupted(internalState);
193192
return result;

packages/shell-api/src/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import Database, { CollectionNamesWithTypes } from './database';
55
import Explainable from './explainable';
66
import ExplainableCursor from './explainable-cursor';
77
import Help, { HelpProperties } from './help';
8-
import ShellInternalState, { EvaluationListener, ShellCliOptions, OnLoadResult } from './shell-internal-state';
8+
import ShellInternalState, {
9+
EvaluationListener,
10+
ShellCliOptions,
11+
OnLoadResult,
12+
ShellPlugin
13+
} from './shell-internal-state';
914
import Shard from './shard';
1015
import ReplicaSet from './replica-set';
1116
import ShellApi from './shell-api';
@@ -62,5 +67,6 @@ export {
6267
ShellResult,
6368
ShellCliOptions,
6469
TypeSignature,
65-
OnLoadResult
70+
OnLoadResult,
71+
ShellPlugin
6672
};

packages/shell-api/src/mongo-errors.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShellPlugin } from './';
12

23
interface MongoErrorRephrase {
34
matchMessage?: RegExp | string;
@@ -37,3 +38,9 @@ export function rephraseMongoError(error: any): any {
3738
function isMongoError(error: any): boolean {
3839
return /^Mongo([A-Z].*)?Error$/.test(Object.getPrototypeOf(error)?.constructor?.name ?? '');
3940
}
41+
42+
export class TransformMongoErrorPlugin implements ShellPlugin {
43+
transformError(err: Error): Error {
44+
return rephraseMongoError(err);
45+
}
46+
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
import { InterruptFlag } from './interruptor';
3030
import NoDatabase from './no-db';
3131
import constructShellBson from './shell-bson';
32+
import { TransformMongoErrorPlugin } from './mongo-errors';
3233

3334
export interface ShellCliOptions {
3435
nodb?: boolean;
@@ -89,6 +90,10 @@ export interface EvaluationListener extends Partial<ConfigProvider<ShellUserConf
8990
startMongocryptd?: () => Promise<AutoEncryptionOptions['extraOptions']>;
9091
}
9192

93+
export interface ShellPlugin {
94+
transformError?: (err: Error) => Error;
95+
}
96+
9297
/**
9398
* Anything to do with the internal shell state is stored here.
9499
*/
@@ -114,6 +119,9 @@ export default class ShellInternalState {
114119
resume: (() => Promise<void>) | null
115120
}> | undefined;
116121

122+
private plugins: ShellPlugin[] = [ new TransformMongoErrorPlugin() ];
123+
private alreadyTransformedErrors = new WeakMap<Error, Error>();
124+
117125
constructor(initialServiceProvider: ServiceProvider, messageBus: any = new EventEmitter(), cliOptions: ShellCliOptions = {}) {
118126
this.initialServiceProvider = initialServiceProvider;
119127
this.messageBus = messageBus;
@@ -450,4 +458,26 @@ export default class ShellInternalState {
450458
serverType
451459
};
452460
}
461+
462+
registerPlugin(plugin: ShellPlugin): void {
463+
this.plugins.push(plugin);
464+
}
465+
466+
transformError(err: any): any {
467+
if (Object.prototype.toString.call(err) === '[object Error]') {
468+
if (this.alreadyTransformedErrors.has(err)) {
469+
return this.alreadyTransformedErrors.get(err);
470+
}
471+
const before = err;
472+
473+
for (const plugin of this.plugins) {
474+
if (plugin.transformError) {
475+
err = plugin.transformError(err);
476+
}
477+
}
478+
479+
this.alreadyTransformedErrors.set(before, err);
480+
}
481+
return err;
482+
}
453483
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { SnippetManager } from './snippet-manager';

packages/snippet-manager/lib/index.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/snippet-manager/lib/index.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ShellPlugin, ShellInternalState } from '@mongosh/shell-api';
2+
import type { SnippetShellUserConfig, MongoshBus } from '@mongosh/types';
3+
export interface SnippetOptions {
4+
installdir: string;
5+
internalState: ShellInternalState;
6+
}
7+
export interface ErrorMatcher {
8+
matches: RegExp[];
9+
message: string;
10+
}
11+
export interface SnippetDescription {
12+
name: string;
13+
snippetName: string;
14+
installSpec?: string;
15+
version: string;
16+
description: string;
17+
license: string;
18+
readme: string;
19+
errorMatchers?: ErrorMatcher[];
20+
}
21+
export interface SnippetIndexFile {
22+
indexFileVersion: 1;
23+
index: SnippetDescription[];
24+
metadata: {
25+
homepage: string;
26+
};
27+
sourceURL: string;
28+
}
29+
export declare class SnippetManager implements ShellPlugin {
30+
_internalState: ShellInternalState;
31+
installdir: string;
32+
repos: SnippetIndexFile[] | null;
33+
load: (filename: string) => Promise<void>;
34+
config: {
35+
get<T extends keyof SnippetShellUserConfig>(key: T): Promise<SnippetShellUserConfig[T]>;
36+
};
37+
print: (...args: any[]) => Promise<void>;
38+
npmArgv: string[];
39+
inflightFetchIndexPromise: Promise<SnippetIndexFile[]> | null;
40+
constructor({ installdir, internalState }: SnippetOptions);
41+
get messageBus(): MongoshBus;
42+
prepareNpm(): Promise<string[]>;
43+
prepareIndex(refreshMode?: 'force-refresh' | 'allow-cached'): Promise<SnippetIndexFile[]>;
44+
get snippets(): SnippetDescription[];
45+
registryBaseUrl(): Promise<string>;
46+
ensureSetup(): Promise<string[]>;
47+
editPackageJSON<T>(fn: (pjson: any) => T): Promise<T>;
48+
runNpm(...npmArgs: string[]): Promise<string>;
49+
search(): Promise<string>;
50+
loadAllSnippets(autoloadMode?: 'always' | 'only-autoload'): Promise<void>;
51+
runSnippetCommand(args: string[]): Promise<string>;
52+
runInstallLikeCmd(args: string[]): Promise<string>;
53+
helpText(snippet?: string): Promise<string>;
54+
showInfo(): Promise<string>;
55+
transformError(err: Error): Error;
56+
}

0 commit comments

Comments
 (0)