Skip to content

Commit 8694df7

Browse files
Merge pull request #166 from contentstack/staging
Adding rollback and streaming feature, Upgrade packages
2 parents b7f6b0b + a7a76f9 commit 8694df7

15 files changed

Lines changed: 1827 additions & 1212 deletions

File tree

.talismanrc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ fileignoreconfig:
44
- filecontent
55
- filename: src/commands/launch/index.test.ts
66
checksum: 9db6c02ad35a0367343cd753b916dd64db4a9efd24838201d2e1113ed19c9b62
7+
- filename: src/commands/launch/rollback.test.ts
8+
checksum: a1010882456f315a918afe2777f90472985e9966bd308c5311ac0de318b14e8c
9+
- filename: test/unit/commands/rollback.test.ts
10+
checksum: d1f931f2d9a397131409399ad6463653e28b5a2224e870b641d9ba57c4418f18
711
- filename: package-lock.json
8-
checksum: 43c0eecc2192095c8fb5bc524b7dafa33a6141ddd3923d41ffb15ec025bea9a9
12+
checksum: d24dfc90fb69ded83dfe4f6839cd17801e65a1f84be04244fb917e6bef6b5cc6
913
version: "1.0"

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/cli-launch",
3-
"version": "1.9.9",
3+
"version": "1.10.0",
44
"description": "Launch related operations",
55
"author": "Contentstack CLI",
66
"bin": {
@@ -115,7 +115,7 @@
115115
"@eslint/eslintrc": {
116116
"ajv": "^6.12.6"
117117
},
118-
"qs": "^6.14.2",
118+
"qs": "^6.15.2",
119119
"tmp": "^0.2.4"
120120
},
121121
"engines": {

src/adapters/file-upload.test.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,241 @@ describe('FileUpload Adapter', () => {
304304
);
305305
expect(serverCommandCalls.length).toBe(0);
306306
});
307+
308+
it('should prompt Enable Streaming Responses after server command when response-mode omitted for OTHER preset',
309+
async () => {
310+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('test-project');
311+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('Default');
312+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm run build');
313+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('./dist');
314+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm start');
315+
(cliux.inquire as jest.Mock).mockResolvedValueOnce(true);
316+
317+
const createSignedUploadUrlMock = jest
318+
.spyOn(FileUpload.prototype as any, 'createSignedUploadUrl')
319+
.mockResolvedValue({ uploadUid: 'test-upload-uid' });
320+
const archiveMock = jest
321+
.spyOn(FileUpload.prototype as any, 'archive')
322+
.mockResolvedValue({ zipName: 'test.zip', zipPath: '/path/to/test.zip', projectName: 'test-project' });
323+
const uploadFileMock = jest
324+
.spyOn(FileUpload.prototype as any, 'uploadFile')
325+
.mockResolvedValue(undefined);
326+
327+
const fileUploadInstance = new FileUpload({
328+
config: {
329+
flags: {
330+
'server-command': undefined,
331+
'response-mode': undefined,
332+
},
333+
framework: 'OTHER',
334+
supportedFrameworksForServerCommands: ['ANGULAR', 'OTHER', 'REMIX', 'NUXT'],
335+
outputDirectories: { OTHER: './dist' },
336+
},
337+
log: logMock,
338+
exit: exitMock,
339+
} as any);
340+
341+
await fileUploadInstance.prepareAndUploadNewProjectFile();
342+
343+
expect(cliux.inquire).toHaveBeenCalledWith({
344+
type: 'confirm',
345+
name: 'enableStreamingResponse',
346+
message: 'Enable Streaming Responses',
347+
default: false,
348+
});
349+
expect(fileUploadInstance.config.isStreamingEnabled).toBe(true);
350+
351+
createSignedUploadUrlMock.mockRestore();
352+
archiveMock.mockRestore();
353+
uploadFileMock.mockRestore();
354+
});
355+
356+
it('should not prompt Enable Streaming Response when response-mode flag is streaming', async () => {
357+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('test-project');
358+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('Default');
359+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm run build');
360+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('./dist');
361+
362+
const createSignedUploadUrlMock = jest
363+
.spyOn(FileUpload.prototype as any, 'createSignedUploadUrl')
364+
.mockResolvedValue({ uploadUid: 'test-upload-uid' });
365+
const archiveMock = jest
366+
.spyOn(FileUpload.prototype as any, 'archive')
367+
.mockResolvedValue({ zipName: 'test.zip', zipPath: '/path/to/test.zip', projectName: 'test-project' });
368+
const uploadFileMock = jest
369+
.spyOn(FileUpload.prototype as any, 'uploadFile')
370+
.mockResolvedValue(undefined);
371+
372+
const fileUploadInstance = new FileUpload({
373+
config: {
374+
flags: {
375+
'server-command': 'npm start',
376+
'response-mode': 'streaming',
377+
},
378+
framework: 'OTHER',
379+
supportedFrameworksForServerCommands: ['ANGULAR', 'OTHER', 'REMIX', 'NUXT'],
380+
outputDirectories: { OTHER: './dist' },
381+
},
382+
log: logMock,
383+
exit: exitMock,
384+
} as any);
385+
386+
await fileUploadInstance.prepareAndUploadNewProjectFile();
387+
388+
const enableStreamingCalls = (cliux.inquire as jest.Mock).mock.calls.filter(
389+
(call) => call[0]?.name === 'enableStreamingResponse',
390+
);
391+
expect(enableStreamingCalls.length).toBe(0);
392+
expect(fileUploadInstance.config.isStreamingEnabled).toBe(true);
393+
394+
createSignedUploadUrlMock.mockRestore();
395+
archiveMock.mockRestore();
396+
uploadFileMock.mockRestore();
397+
});
398+
399+
it('should not prompt Enable Streaming Response when response-mode flag is buffered', async () => {
400+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('test-project');
401+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('Default');
402+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm run build');
403+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('./dist');
404+
405+
const createSignedUploadUrlMock = jest
406+
.spyOn(FileUpload.prototype as any, 'createSignedUploadUrl')
407+
.mockResolvedValue({ uploadUid: 'test-upload-uid' });
408+
const archiveMock = jest
409+
.spyOn(FileUpload.prototype as any, 'archive')
410+
.mockResolvedValue({ zipName: 'test.zip', zipPath: '/path/to/test.zip', projectName: 'test-project' });
411+
const uploadFileMock = jest
412+
.spyOn(FileUpload.prototype as any, 'uploadFile')
413+
.mockResolvedValue(undefined);
414+
415+
const fileUploadInstance = new FileUpload({
416+
config: {
417+
flags: {
418+
'server-command': 'npm start',
419+
'response-mode': 'buffered',
420+
},
421+
framework: 'OTHER',
422+
supportedFrameworksForServerCommands: ['ANGULAR', 'OTHER', 'REMIX', 'NUXT'],
423+
outputDirectories: { OTHER: './dist' },
424+
},
425+
log: logMock,
426+
exit: exitMock,
427+
} as any);
428+
429+
await fileUploadInstance.prepareAndUploadNewProjectFile();
430+
431+
const enableStreamingCalls = (cliux.inquire as jest.Mock).mock.calls.filter(
432+
(call) => call[0]?.name === 'enableStreamingResponse',
433+
);
434+
expect(enableStreamingCalls.length).toBe(0);
435+
expect(fileUploadInstance.config.isStreamingEnabled).toBe(false);
436+
437+
createSignedUploadUrlMock.mockRestore();
438+
archiveMock.mockRestore();
439+
uploadFileMock.mockRestore();
440+
});
441+
442+
it('should prompt Enable Streaming Responses for Gatsby when flag is not provided', async () => {
443+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('test-project');
444+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('Default');
445+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm run build');
446+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('./public');
447+
(cliux.inquire as jest.Mock).mockResolvedValueOnce(true);
448+
449+
const createSignedUploadUrlMock = jest
450+
.spyOn(FileUpload.prototype as any, 'createSignedUploadUrl')
451+
.mockResolvedValue({ uploadUid: 'test-upload-uid' });
452+
const archiveMock = jest
453+
.spyOn(FileUpload.prototype as any, 'archive')
454+
.mockResolvedValue({ zipName: 'test.zip', zipPath: '/path/to/test.zip', projectName: 'test-project' });
455+
const uploadFileMock = jest
456+
.spyOn(FileUpload.prototype as any, 'uploadFile')
457+
.mockResolvedValue(undefined);
458+
459+
const fileUploadInstance = new FileUpload({
460+
config: {
461+
flags: {
462+
'response-mode': undefined,
463+
},
464+
framework: 'GATSBY',
465+
supportedFrameworksForServerCommands: ['ANGULAR', 'OTHER', 'REMIX', 'NUXT'],
466+
outputDirectories: { GATSBY: './public' },
467+
},
468+
log: logMock,
469+
exit: exitMock,
470+
} as any);
471+
472+
const handleEnvImportFlowMock = jest
473+
.spyOn(fileUploadInstance, 'handleEnvImportFlow' as any)
474+
.mockResolvedValue(undefined);
475+
476+
await fileUploadInstance.prepareAndUploadNewProjectFile();
477+
478+
const serverCommandCalls = (cliux.inquire as jest.Mock).mock.calls.filter(
479+
(call) => call[0]?.name === 'serverCommand',
480+
);
481+
expect(serverCommandCalls.length).toBe(0);
482+
expect(cliux.inquire).toHaveBeenCalledWith({
483+
type: 'confirm',
484+
name: 'enableStreamingResponse',
485+
message: 'Enable Streaming Responses',
486+
default: false,
487+
});
488+
expect(fileUploadInstance.config.isStreamingEnabled).toBe(true);
489+
490+
createSignedUploadUrlMock.mockRestore();
491+
archiveMock.mockRestore();
492+
uploadFileMock.mockRestore();
493+
handleEnvImportFlowMock.mockRestore();
494+
});
495+
496+
it('should apply response-mode flag for Gatsby without prompt', async () => {
497+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('test-project');
498+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('Default');
499+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('npm run build');
500+
(cliux.inquire as jest.Mock).mockResolvedValueOnce('./public');
501+
502+
const createSignedUploadUrlMock = jest
503+
.spyOn(FileUpload.prototype as any, 'createSignedUploadUrl')
504+
.mockResolvedValue({ uploadUid: 'test-upload-uid' });
505+
const archiveMock = jest
506+
.spyOn(FileUpload.prototype as any, 'archive')
507+
.mockResolvedValue({ zipName: 'test.zip', zipPath: '/path/to/test.zip', projectName: 'test-project' });
508+
const uploadFileMock = jest
509+
.spyOn(FileUpload.prototype as any, 'uploadFile')
510+
.mockResolvedValue(undefined);
511+
512+
const fileUploadInstance = new FileUpload({
513+
config: {
514+
flags: {
515+
'response-mode': 'buffered',
516+
},
517+
framework: 'GATSBY',
518+
supportedFrameworksForServerCommands: ['ANGULAR', 'OTHER', 'REMIX', 'NUXT'],
519+
outputDirectories: { GATSBY: './public' },
520+
},
521+
log: logMock,
522+
exit: exitMock,
523+
} as any);
524+
525+
const handleEnvImportFlowMock = jest
526+
.spyOn(fileUploadInstance, 'handleEnvImportFlow' as any)
527+
.mockResolvedValue(undefined);
528+
529+
await fileUploadInstance.prepareAndUploadNewProjectFile();
530+
531+
const enableStreamingCalls = (cliux.inquire as jest.Mock).mock.calls.filter(
532+
(call) => call[0]?.name === 'enableStreamingResponse',
533+
);
534+
expect(enableStreamingCalls.length).toBe(0);
535+
expect(fileUploadInstance.config.isStreamingEnabled).toBe(false);
536+
537+
createSignedUploadUrlMock.mockRestore();
538+
archiveMock.mockRestore();
539+
uploadFileMock.mockRestore();
540+
handleEnvImportFlowMock.mockRestore();
541+
});
307542
});
308543
});
309544

src/adapters/file-upload.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,15 @@ export default class FileUpload extends BaseClass {
114114
* @memberof FileUpload
115115
*/
116116
async createNewProject(uploadUid: string): Promise<void> {
117-
const { framework, projectName, buildCommand, outputDirectory, environmentName, serverCommand } = this.config;
117+
const {
118+
framework,
119+
projectName,
120+
buildCommand,
121+
outputDirectory,
122+
environmentName,
123+
serverCommand,
124+
isStreamingEnabled
125+
} = this.config;
118126
await this.apolloClient
119127
.mutate({
120128
mutation: importProjectMutation,
@@ -130,6 +138,7 @@ export default class FileUpload extends BaseClass {
130138
environmentVariables: map(this.envVariables, ({ key, value }) => ({ key, value })),
131139
buildCommand: buildCommand === undefined || buildCommand === null ? 'npm run build' : buildCommand,
132140
...(serverCommand && serverCommand.trim() !== '' ? { serverCommand } : {}),
141+
isStreamingEnabled: isStreamingEnabled ?? false,
133142
},
134143
},
135144
skipGitData: true,
@@ -167,6 +176,7 @@ export default class FileUpload extends BaseClass {
167176
'variable-type': variableType,
168177
'env-variables': envVariables,
169178
'server-command': serverCommand,
179+
'response-mode': responseMode,
170180
alias,
171181
} = this.config.flags;
172182
const { token, apiKey } = configHandler.get(`tokens.${alias}`) ?? {};
@@ -239,6 +249,16 @@ export default class FileUpload extends BaseClass {
239249
this.config.serverCommand = serverCommand;
240250
}
241251
}
252+
if (!responseMode) {
253+
this.config.isStreamingEnabled = (await cliux.inquire({
254+
type: 'confirm',
255+
name: 'enableStreamingResponse',
256+
message: 'Enable Streaming Responses',
257+
default: false,
258+
})) as boolean;
259+
} else {
260+
this.config.isStreamingEnabled = responseMode === 'streaming';
261+
}
242262
this.config.variableType = variableType as unknown as string;
243263
this.config.envVariables = envVariables;
244264
await this.handleEnvImportFlow();

0 commit comments

Comments
 (0)