Skip to content

Commit 0a85da0

Browse files
feat(shell-api): add stream processor modify MONGOSH-1864 (#2245)
Co-authored-by: Anna Henningsen <[email protected]>
1 parent a1c1485 commit 0a85da0

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

packages/i18n/src/locales/en_US.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,6 +2771,9 @@ const translations: Catalog = {
27712771
description:
27722772
'Return stats captured from a named stream processor.',
27732773
},
2774+
modify: {
2775+
description: 'Modify a stream processor definition.',
2776+
},
27742777
},
27752778
},
27762779
},

packages/shell-api/src/stream-processor.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Document } from '@mongosh/service-provider-core';
2+
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
23

34
import type Mongo from './mongo';
45
import { asPrintable } from './enums';
@@ -57,6 +58,46 @@ export default class StreamProcessor extends ShellApiWithMongoClass {
5758
});
5859
}
5960

61+
/**
62+
* modify is used to modify a stream processor definition, like below:
63+
* Change the pipeline:
64+
* sp.name.modify(newPipeline)
65+
* Keep the same pipeline, change other options:
66+
* sp.name.modify({resumeFromCheckpoint: false})
67+
* Change the pipeline and set additional options:
68+
* sp.name.modify(newPipeline, {resumeFromCheckpoint: false})
69+
*/
70+
async modify(options: Document): Promise<Document>;
71+
async modify(pipeline: Document[], options?: Document): Promise<Document>;
72+
73+
@returnsPromise
74+
async modify(
75+
pipelineOrOptions: Document[] | Document,
76+
options?: Document
77+
): Promise<Document> {
78+
if (Array.isArray(pipelineOrOptions)) {
79+
options = { ...options, pipeline: pipelineOrOptions };
80+
} else if (typeof pipelineOrOptions === 'object') {
81+
if (options) {
82+
throw new MongoshInvalidInputError(
83+
'If the first argument to modify is an object, the second argument should not be specified.',
84+
CommonErrors.InvalidArgument
85+
);
86+
}
87+
options = { ...pipelineOrOptions };
88+
} else {
89+
throw new MongoshInvalidInputError(
90+
'The first argument to modify must be an array or object.',
91+
CommonErrors.InvalidArgument
92+
);
93+
}
94+
95+
return this._streams._runStreamCommand({
96+
modifyStreamProcessor: this.name,
97+
...options,
98+
});
99+
}
100+
60101
@returnsPromise
61102
async sample(options: Document = {}) {
62103
const r = await this._streams._runStreamCommand({

packages/shell-api/src/streams.spec.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type Mongo from './mongo';
55
import Database from './database';
66
import { Streams } from './streams';
77
import { InterruptFlag, MongoshInterruptedError } from './interruptor';
8+
import type { MongoshInvalidInputError } from '@mongosh/errors';
89

910
describe('Streams', function () {
1011
let mongo: Mongo;
@@ -162,4 +163,112 @@ describe('Streams', function () {
162163
).to.be.true;
163164
});
164165
});
166+
167+
describe('modify', function () {
168+
it('throws with invalid parameters', async function () {
169+
// Create the stream processor.
170+
const runCmdStub = sinon
171+
.stub(mongo._serviceProvider, 'runCommand')
172+
.resolves({ ok: 1 });
173+
const name = 'p1';
174+
const pipeline = [{ $match: { foo: 'bar' } }];
175+
const processor = await streams.createStreamProcessor(name, pipeline);
176+
expect(processor).to.eql(streams.getProcessor(name));
177+
const cmd = { createStreamProcessor: name, pipeline };
178+
expect(runCmdStub.calledOnceWithExactly('admin', cmd, {})).to.be.true;
179+
180+
// No arguments to modify.
181+
const caught = await processor
182+
.modify()
183+
.catch((e: MongoshInvalidInputError) => e);
184+
expect(caught.message).to.contain(
185+
'[COMMON-10001] The first argument to modify must be an array or object.'
186+
);
187+
188+
// A single numeric argument to modify.
189+
const caught2 = await processor
190+
.modify(1)
191+
.catch((e: MongoshInvalidInputError) => e);
192+
expect(caught2.message).to.contain(
193+
'[COMMON-10001] The first argument to modify must be an array or object.'
194+
);
195+
196+
// Two object arguments to modify.
197+
const caught3 = await processor
198+
.modify(
199+
{ resumeFromCheckpoint: false },
200+
{ dlq: { connectionName: 'foo' } }
201+
)
202+
.catch((e: MongoshInvalidInputError) => e);
203+
expect(caught3.message).to.contain(
204+
'[COMMON-10001] If the first argument to modify is an object, the second argument should not be specified.'
205+
);
206+
});
207+
208+
it('works with pipeline and options arguments', async function () {
209+
const runCmdStub = sinon
210+
.stub(mongo._serviceProvider, 'runCommand')
211+
.resolves({ ok: 1 });
212+
213+
// Create the stream processor.
214+
const name = 'p1';
215+
const pipeline = [{ $match: { foo: 'bar' } }];
216+
const processor = await streams.createStreamProcessor(name, pipeline);
217+
expect(processor).to.eql(streams.getProcessor(name));
218+
const cmd = { createStreamProcessor: name, pipeline };
219+
expect(runCmdStub.calledOnceWithExactly('admin', cmd, {})).to.be.true;
220+
221+
// Start the stream processor.
222+
await processor.start();
223+
expect(
224+
runCmdStub.calledWithExactly(
225+
'admin',
226+
{ startStreamProcessor: name },
227+
{}
228+
)
229+
).to.be.true;
230+
231+
// Stop the stream processor.
232+
await processor.stop();
233+
expect(
234+
runCmdStub.calledWithExactly('admin', { stopStreamProcessor: name }, {})
235+
).to.be.true;
236+
237+
// Modify the stream processor.
238+
const pipeline2 = [{ $match: { foo: 'baz' } }];
239+
processor.modify(pipeline2);
240+
expect(
241+
runCmdStub.calledWithExactly(
242+
'admin',
243+
{ modifyStreamProcessor: name, pipeline: pipeline2 },
244+
{}
245+
)
246+
).to.be.true;
247+
248+
// Modify the stream processor with extra options.
249+
const pipeline3 = [{ $match: { foo: 'bat' } }];
250+
processor.modify(pipeline3, { resumeFromCheckpoint: false });
251+
expect(
252+
runCmdStub.calledWithExactly(
253+
'admin',
254+
{
255+
modifyStreamProcessor: name,
256+
pipeline: pipeline3,
257+
resumeFromCheckpoint: false,
258+
},
259+
{}
260+
)
261+
).to.be.true;
262+
263+
// Modify the stream processor without changing pipeline.
264+
processor.modify({ resumeFromCheckpoint: false });
265+
expect(
266+
runCmdStub.calledWithExactly(
267+
'admin',
268+
{ modifyStreamProcessor: name, resumeFromCheckpoint: false },
269+
{}
270+
)
271+
).to.be.true;
272+
});
273+
});
165274
});

0 commit comments

Comments
 (0)