Skip to content

Commit 1b72c5b

Browse files
committed
feat(cli-repl): add flag to control deep inspect behavior
1 parent 7202eb2 commit 1b72c5b

File tree

9 files changed

+139
-24
lines changed

9 files changed

+139
-24
lines changed

packages/arg-parser/src/cli-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface CliOptions {
1919
csfleLibraryPath?: string;
2020
cryptSharedLibPath?: string;
2121
db?: string;
22+
deepInspect?: boolean; // defaults to true
2223
eval?: string[];
2324
exposeAsyncRewriter?: boolean; // internal testing only
2425
gssapiServiceName?: string;

packages/cli-repl/src/arg-parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const OPTIONS = {
5858
'apiDeprecationErrors',
5959
'apiStrict',
6060
'buildInfo',
61+
'deepInspect',
6162
'exposeAsyncRewriter',
6263
'help',
6364
'ipv6',

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import type {
5353
DevtoolsProxyOptions,
5454
} from '@mongodb-js/devtools-proxy-support';
5555
import { useOrCreateAgent } from '@mongodb-js/devtools-proxy-support';
56+
import { fullDepthInspectOptions } from './format-output';
5657

5758
/**
5859
* Connecting text key.
@@ -210,10 +211,11 @@ export class CliRepl implements MongoshIOProvider {
210211
if (jsContext === 'auto' || !jsContext) {
211212
jsContext = willEnterInteractiveMode ? 'repl' : 'plain-vm';
212213
}
214+
const deepInspect = this.cliOptions.deepInspect ?? willEnterInteractiveMode;
213215

214216
this.mongoshRepl = new MongoshNodeRepl({
215217
...options,
216-
shellCliOptions: { ...this.cliOptions, jsContext, quiet },
218+
shellCliOptions: { ...this.cliOptions, jsContext, quiet, deepInspect },
217219
nodeReplOptions: options.nodeReplOptions ?? {
218220
terminal: process.env.MONGOSH_FORCE_TERMINAL ? true : undefined,
219221
},
@@ -738,7 +740,12 @@ export class CliRepl implements MongoshIOProvider {
738740
formattedResult = formatForJSONOutput(e, this.cliOptions.json);
739741
}
740742
} else {
741-
formattedResult = this.mongoshRepl.writer(lastEvalResult);
743+
formattedResult = this.mongoshRepl.writer(
744+
lastEvalResult,
745+
this.cliOptions.deepInspect !== false
746+
? fullDepthInspectOptions
747+
: undefined
748+
);
742749
}
743750
this.output.write(formattedResult + '\n');
744751
}

packages/cli-repl/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export const USAGE = `
4343
--retryWrites[=true|false] ${i18n.__(
4444
'cli-repl.args.retryWrites'
4545
)}
46+
--deep-inspect[=true|false] ${i18n.__(
47+
'cli-repl.args.deepInspect'
48+
)}
4649
4750
${clr(
4851
i18n.__('cli-repl.args.authenticationOptions'),

packages/cli-repl/src/format-output.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const CONTROL_CHAR_REGEXP_ALLOW_SIMPLE =
3636
// eslint-disable-next-line no-control-regex
3737
/[\x00-\x08\x0B-\x1F\x7F-\x9F]/;
3838

39-
const fullDepthInspectOptions = {
39+
export const fullDepthInspectOptions = {
4040
depth: Infinity,
4141
maxArrayLength: Infinity,
4242
maxStringLength: Infinity,

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ class MongoshNodeRepl implements EvaluationListener {
955955
/**
956956
* Format the result to a string so it can be written to the output stream.
957957
*/
958-
writer(result: any): string {
958+
writer(result: any, extraFormatOptions?: Partial<FormatOptions>): string {
959959
// This checks for error instances.
960960
// The writer gets called immediately by the internal `repl.eval`
961961
// in case of errors.
@@ -975,7 +975,8 @@ class MongoshNodeRepl implements EvaluationListener {
975975
this.rawValueToShellResult.get(result) ?? {
976976
type: null,
977977
printable: result,
978-
}
978+
},
979+
extraFormatOptions
979980
);
980981
}
981982

packages/e2e-tests/test/e2e-bson.spec.ts

Lines changed: 114 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -589,30 +589,128 @@ describe('BSON e2e', function () {
589589
});
590590
});
591591
describe('inspect nesting depth', function () {
592-
it('inspects a full bson document when it is read from the server', async function () {
592+
const deepAndNestedDefinition = `({
593+
a: { b: { c: { d: { e: { f: { g: { h: "foundme" } } } } } } },
594+
array: [...Array(100000).keys()].map(i => ({ num: i })),
595+
str: 'All work and no playmakes Jack a dull boy'.repeat(4096) + 'The End'
596+
})`;
597+
const checkForDeepOutput = (output: string, wantFullOutput: boolean) => {
598+
if (wantFullOutput) {
599+
expect(output).not.to.include('[Object');
600+
expect(output).not.to.include('more items');
601+
expect(output).to.include('foundme');
602+
expect(output).to.include('num: 99999');
603+
expect(output).to.include('The End');
604+
} else {
605+
expect(output).to.include('[Object');
606+
expect(output).to.include('more items');
607+
expect(output).not.to.include('foundme');
608+
expect(output).not.to.include('num: 99999');
609+
expect(output).not.to.include('The End');
610+
}
611+
};
612+
613+
beforeEach(async function () {
593614
await shell.executeLine(`use ${dbName}`);
594-
await shell.executeLine(`deepAndNested = ({
595-
a: { b: { c: { d: { e: { f: { g: { h: "foundme" } } } } } } },
596-
array: [...Array(100000).keys()].map(i => ({ num: i })),
597-
str: 'All work and no play makes Jack a dull boy'.repeat(4096) + 'The End'
598-
});`);
615+
await shell.executeLine(`deepAndNested = ${deepAndNestedDefinition}`);
599616
await shell.executeLine(`db.coll.insertOne(deepAndNested)`);
617+
});
618+
619+
it('inspects a full bson document when it is read from the server (interactive mode)', async function () {
600620
// Deeply nested object from the server should be fully printed
601621
const output = await shell.executeLine('db.coll.findOne()');
602-
expect(output).not.to.include('[Object');
603-
expect(output).not.to.include('more items');
604-
expect(output).to.include('foundme');
605-
expect(output).to.include('num: 99999');
606-
expect(output).to.include('The End');
622+
checkForDeepOutput(output, true);
607623
// Same object doesn't need to be fully printed if created by the user
608624
const output2 = await shell.executeLine('deepAndNested');
609-
expect(output2).to.include('[Object');
610-
expect(output2).to.include('more items');
611-
expect(output2).not.to.include('foundme');
612-
expect(output2).not.to.include('num: 99999');
613-
expect(output2).not.to.include('The End');
625+
checkForDeepOutput(output2, false);
626+
shell.assertNoErrors();
627+
});
628+
629+
it('can explicitly disable full-depth nesting (interactive mode)', async function () {
630+
shell.kill();
631+
shell = this.startTestShell({
632+
args: [await testServer.connectionString(), '--deepInspect=false'],
633+
});
634+
await shell.executeLine(`use ${dbName}`);
635+
const output = await shell.executeLine('db.coll.findOne()');
636+
checkForDeepOutput(output, false);
637+
shell.assertNoErrors();
638+
});
639+
640+
it('does not deeply inspect objects in non-interactive mode for intermediate output', async function () {
641+
shell.kill();
642+
shell = this.startTestShell({
643+
args: [
644+
await testServer.connectionString(),
645+
'--eval',
646+
`use(${JSON.stringify(dbName)}); print(db.coll.findOne()); 0`,
647+
],
648+
});
649+
await shell.waitForSuccessfulExit();
650+
checkForDeepOutput(shell.output, false);
651+
shell.assertNoErrors();
652+
shell = this.startTestShell({
653+
args: [
654+
await testServer.connectionString(),
655+
'--eval',
656+
`print(${deepAndNestedDefinition}); 0`,
657+
],
658+
});
659+
await shell.waitForSuccessfulExit();
660+
checkForDeepOutput(shell.output, false);
661+
shell.assertNoErrors();
662+
});
663+
664+
it('inspect full objects in non-interactive mode for final output', async function () {
665+
shell.kill();
666+
shell = this.startTestShell({
667+
args: [
668+
await testServer.connectionString(),
669+
'--eval',
670+
`use(${JSON.stringify(dbName)}); db.coll.findOne();`,
671+
],
672+
});
673+
await shell.waitForSuccessfulExit();
674+
checkForDeepOutput(shell.output, true);
675+
shell.assertNoErrors();
676+
shell = this.startTestShell({
677+
args: [
678+
await testServer.connectionString(),
679+
'--eval',
680+
deepAndNestedDefinition,
681+
],
682+
});
683+
await shell.waitForSuccessfulExit();
684+
checkForDeepOutput(shell.output, true);
614685
shell.assertNoErrors();
615686
});
687+
688+
it('can explicitly disable full-depth nesting (non-interactive mode)', async function () {
689+
shell.kill();
690+
shell = this.startTestShell({
691+
args: [
692+
await testServer.connectionString(),
693+
'--deepInspect=false',
694+
'--eval',
695+
`use(${JSON.stringify(dbName)}); db.coll.findOne();`,
696+
],
697+
});
698+
await shell.waitForSuccessfulExit();
699+
checkForDeepOutput(shell.output, false);
700+
shell.assertNoErrors();
701+
shell = this.startTestShell({
702+
args: [
703+
await testServer.connectionString(),
704+
'--deepInspect=false',
705+
'--eval',
706+
deepAndNestedDefinition,
707+
],
708+
});
709+
await shell.waitForSuccessfulExit();
710+
checkForDeepOutput(shell.output, false);
711+
shell.assertNoErrors();
712+
});
713+
616714
it('can parse serverStatus back to its original form', async function () {
617715
// Dates get special treatment but that doesn't currently apply
618716
// to mongosh's util.inspect that's available to users

packages/i18n/src/locales/en_US.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const translations: Catalog = {
2222
quiet: 'Silence output from the shell during the connection process',
2323
shell: 'Run the shell after executing files',
2424
nodb: "Don't connect to mongod on startup - no 'db address' [arg] expected",
25+
deepInspect:
26+
'Force full depth inspection of server results (default: true if in interactive mode)',
2527
norc: "Will not run the '.mongoshrc.js' file on start up",
2628
eval: 'Evaluate javascript',
2729
json: 'Print result of --eval as Extended JSON, including errors',

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import { deepInspectServiceProviderWrapper } from './deep-inspect/service-provid
5858
*/
5959
export interface ShellCliOptions {
6060
nodb?: boolean;
61+
deepInspect?: boolean;
6162
}
6263

6364
/**
@@ -204,9 +205,10 @@ export class ShellInstanceState {
204205
cliOptions: ShellCliOptions = {},
205206
bsonLibrary: BSONLibrary = initialServiceProvider.bsonLibrary
206207
) {
207-
this.initialServiceProvider = deepInspectServiceProviderWrapper(
208-
initialServiceProvider
209-
);
208+
this.initialServiceProvider =
209+
cliOptions.deepInspect === false
210+
? initialServiceProvider
211+
: deepInspectServiceProviderWrapper(initialServiceProvider);
210212
this.bsonLibrary = bsonLibrary;
211213
this.messageBus = messageBus;
212214
this.shellApi = new ShellApi(this);

0 commit comments

Comments
 (0)