@corva/create-app 0.42.0-0 → 0.42.0-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +2 -0
  2. package/lib/bump-version.option.js +12 -5
  3. package/lib/flow.js +1 -1
  4. package/lib/flows/attach.js +8 -0
  5. package/lib/flows/lib/api.js +109 -79
  6. package/lib/flows/lib/notification.js +139 -0
  7. package/lib/flows/lib/waitForMs.js +1 -1
  8. package/lib/flows/release.js +1 -1
  9. package/lib/flows/steps/attach/add-app-to-stream.js +24 -0
  10. package/lib/flows/steps/attach/get-all-live-assets.js +142 -0
  11. package/lib/flows/steps/attach/index.js +9 -0
  12. package/lib/flows/steps/attach/prepare-data.js +20 -0
  13. package/lib/flows/steps/release/add-label.js +2 -3
  14. package/lib/flows/steps/release/add-notes.js +2 -2
  15. package/lib/flows/steps/release/get-config.js +4 -1
  16. package/lib/flows/steps/release/prepare-data.js +3 -3
  17. package/lib/flows/steps/release/publish.js +6 -4
  18. package/lib/flows/steps/release/remove-failed-upload.js +18 -7
  19. package/lib/flows/steps/release/upload-zip-to-corva.js +9 -6
  20. package/lib/flows/steps/release/wait-for-build.js +4 -4
  21. package/lib/flows/steps/rerun/create-task.js +12 -15
  22. package/lib/flows/steps/rerun/ensure-that-app-in-stream.js +33 -25
  23. package/lib/flows/steps/rerun/get-app-version.js +14 -12
  24. package/lib/flows/steps/rerun/prepare-data.js +6 -8
  25. package/lib/flows/steps/rerun/prepare-well-and-stream-data.js +29 -36
  26. package/lib/flows/steps/rerun/rerun.js +5 -5
  27. package/lib/helpers/logger.js +22 -22
  28. package/lib/main.js +61 -31
  29. package/package.json +4 -3
  30. package/templates/python/scheduler/README.md +1 -1
  31. package/templates/python/stream/README.md +1 -1
  32. package/templates/python/task/README.md +1 -1
package/README.md CHANGED
@@ -27,6 +27,7 @@ Commands:
27
27
  zip [options] <project-directory> [patterns...] Bundle app
28
28
  release [options] <project-directory> [patterns...] Release app
29
29
  rerun [options] <project-directory> Rerun app
30
+ attach [options] <project-directory> Add app to live assets streams
30
31
  help [command] display help for command
31
32
  ```
32
33
 
@@ -176,6 +177,7 @@ Options:
176
177
  --label [string] Put a label on the release (choices: "BETA", "STABLE")
177
178
  --remove-on-fail [boolean] Remove release if it fails during deployment (default: false)
178
179
  --silent [boolean] Only log result of the operation (default: false)
180
+ --remove-on-success App package (.zip) will not be deleted after upload (default: true)
179
181
  ```
180
182
 
181
183
  ### Examples
@@ -21,19 +21,26 @@ export const bumpVersionOption = new Option(flags, description)
21
21
  .choices(choices)
22
22
  .argParser(argParser);
23
23
 
24
- export const apiKeyOption = new Option('--api-key [string]',
25
- 'Pre generated API key for authorization during app upload')
24
+ export const apiKeyOption = new Option(
25
+ '--api-key [string]',
26
+ 'Pre generated API key for authorization during app upload'
27
+ );
28
+
29
+ export const appVersion = new Option('--app-version [number]', 'App version (exp. 1, 2, 3)');
26
30
 
27
31
  export const envOption = new Option('--env [string]', 'Environment to use')
28
32
  .choices(['qa', 'staging', 'production'])
29
- .default('qa')
33
+ .default('qa');
30
34
 
31
- export const silentOption = new Option('--silent [boolean]', 'Only log result of the operation').default(false)
35
+ export const silentOption = new Option(
36
+ '--silent [boolean]',
37
+ 'Only log result of the operation'
38
+ ).default(false);
32
39
 
33
40
  export const bumpVersionOptionDeprecated = new Option(
34
41
  flags,
35
42
  chalk.bgYellow`DEPRECATED` +
36
- ` Use with ${chalk.cyan`zip`} or ${chalk.cyan`release`} command instead`
43
+ ` Use with ${chalk.cyan`zip`} or ${chalk.cyan`release`} command instead`
37
44
  )
38
45
  .choices(choices)
39
46
  .argParser(argParser);
package/lib/flow.js CHANGED
@@ -14,7 +14,7 @@ export const runFlow = async (flow, context, indent = '') => {
14
14
  )}\n`
15
15
  );
16
16
 
17
- context.progress = () => logger.write('.')
17
+ context.progress = () => logger.write('.');
18
18
 
19
19
  const result = await runSteps(flow.steps, context, indent + ' ');
20
20
 
@@ -0,0 +1,8 @@
1
+ import { PREPARE_FLOW } from './prepare.js';
2
+ import { SETUP_API_CLIENT_STEP } from './steps/release/get-config.js';
3
+ import { ATTACH_STEPS } from './steps/attach/index.js';
4
+
5
+ export const ATTACH_FLOW = {
6
+ name: 'attach',
7
+ steps: [PREPARE_FLOW, SETUP_API_CLIENT_STEP, ...ATTACH_STEPS],
8
+ };
@@ -1,7 +1,6 @@
1
1
  import got from 'got';
2
2
  import Debug from 'debug';
3
3
  import { StepError } from './step-error.js';
4
- import _ from 'lodash/fp.js';
5
4
 
6
5
  const debug = Debug('cca:api');
7
6
 
@@ -11,8 +10,8 @@ const debug = Debug('cca:api');
11
10
  */
12
11
  export class Api {
13
12
  /**
14
- * @type {import('got').Got}
15
- */
13
+ * @type {import('got').Got}
14
+ */
16
15
  #api;
17
16
 
18
17
  constructor(envName, apiKey, authToken = null) {
@@ -27,9 +26,9 @@ export class Api {
27
26
  'Accept': 'application/json',
28
27
  },
29
28
  hooks: {
30
- beforeRequest: [debug]
31
- }
32
- })
29
+ beforeRequest: [debug],
30
+ },
31
+ });
33
32
  }
34
33
 
35
34
  /**
@@ -42,11 +41,13 @@ export class Api {
42
41
  * @returns {object}
43
42
  */
44
43
  async getAppByKey(appKey) {
45
- const { data } = await this.#api.get('v2/apps', {
46
- searchParams: { per_page: 2, search: appKey }
47
- }).json();
44
+ const { data } = await this.#api
45
+ .get('v2/apps', {
46
+ searchParams: { per_page: 2, search: appKey },
47
+ })
48
+ .json();
48
49
 
49
- const app = data.find(app => app.attributes.app_key === appKey);
50
+ const app = data.find((app) => app.attributes.app_key === appKey);
50
51
  if (!app) {
51
52
  throw new Error(`App with key - ${appKey}, not exist`);
52
53
  }
@@ -64,7 +65,11 @@ export class Api {
64
65
  * @returns {object}
65
66
  */
66
67
  async getAppDatasetsOperationWrite(appId) {
67
- const { data } = await this.#api.get(`v2/apps/${appId}/app_datasets?dataset_operation=write&fields[]=app_dataset.dataset_name`).json();
68
+ const { data } = await this.#api
69
+ .get(
70
+ `v2/apps/${appId}/app_datasets?dataset_operation=write&fields[]=app_dataset.dataset_name`
71
+ )
72
+ .json();
68
73
  return data;
69
74
  }
70
75
 
@@ -84,27 +89,42 @@ export class Api {
84
89
  }
85
90
 
86
91
  /**
87
- * Get asset streams
92
+ * Get assets streams by ids
88
93
  *
89
94
  * @public
90
95
  *
91
- * @param {string} assetId
96
+ * @param {string[]} assetIds
97
+ * @param {string[]} segments
98
+ * @param {string[]} statuses
92
99
  *
93
- * @returns {object[]}
100
+ * @returns {Promise<object[]>}
94
101
  */
95
- async getStreamByAssetId(assetId) {
96
- const data = await this.#api.get(`v1/app_streams`, {
97
- searchParams: {
98
- asset_id: assetId,
99
- segment: 'drilling',
100
- status: 'complete'
101
- }
102
- }).json();
103
-
104
- if (!data[0]) {
105
- throw new Error(`Could not found streams for asset ID - ${assetId}`);
106
- }
102
+ async getStreamsByAssetIds(assetIds, segments, statuses = ['complete']) {
103
+ const searchParams = new URLSearchParams([
104
+ ...assetIds.map((assetId) => ['asset_id[]', assetId]),
105
+ ...segments.map((segment) => ['segment[]', segment]),
106
+ ...statuses.map((status) => ['status[]', status]),
107
+ ]);
108
+
109
+ return this.#api.get(`v1/app_streams`, { searchParams }).json();
110
+ }
107
111
 
112
+ /**
113
+ * Get all assets
114
+ *
115
+ * @public
116
+ *
117
+ * @param {string} status
118
+ * @param {number} perPage
119
+ *
120
+ * @returns {Promise<object[]>}
121
+ */
122
+ async getAllAssets(status = 'active', perPage = 1000) {
123
+ const { data } = await this.#api
124
+ .get(`v2/assets`, {
125
+ searchParams: { status, per_page: perPage },
126
+ })
127
+ .json();
108
128
  return data;
109
129
  }
110
130
 
@@ -118,7 +138,9 @@ export class Api {
118
138
  * @returns {object[]}
119
139
  */
120
140
  async getWellByAssetId(assetId) {
121
- const { wells } = await this.#api.post('v2/assets/resolve', { json: { assets: [assetId], } }).json();
141
+ const { wells } = await this.#api
142
+ .post('v2/assets/resolve', { json: { assets: [assetId] } })
143
+ .json();
122
144
 
123
145
  if (!wells.length) {
124
146
  throw new Error(`Could not found wells by asset ID - ${assetId}`);
@@ -176,7 +198,11 @@ export class Api {
176
198
  * @returns {object[]}
177
199
  */
178
200
  async getAppRuns(appId) {
179
- const { data } = await this.#api.get(`v2/apps/${appId}/app_runs?page=1&per_page=500&status[]=pending&status[]=in_progress&status[]=running`).json();
201
+ const { data } = await this.#api
202
+ .get(
203
+ `v2/apps/${appId}/app_runs?page=1&per_page=500&status[]=pending&status[]=in_progress&status[]=running`
204
+ )
205
+ .json();
180
206
 
181
207
  return data;
182
208
  }
@@ -195,10 +221,12 @@ export class Api {
195
221
  const uploadURL = `v2/apps/${appKey}/packages/upload`;
196
222
 
197
223
  try {
198
- const { data } = await this.#api.post(uploadURL, {
199
- headers: formData.getHeaders(),
200
- body: formData,
201
- }).json();
224
+ const { data } = await this.#api
225
+ .post(uploadURL, {
226
+ headers: formData.getHeaders(),
227
+ body: formData,
228
+ })
229
+ .json();
202
230
 
203
231
  return {
204
232
  id: data.id,
@@ -209,22 +237,22 @@ export class Api {
209
237
  `${JSON.parse(e.response.body).message || ''} \nPOST: ${uploadURL} failed.`,
210
238
  { cause: e }
211
239
  );
212
-
213
240
  }
214
241
  }
215
242
 
216
-
217
243
  /**
218
- * @param {string} appId
219
- * @param {string} id
220
- * @param {string} notes
221
- *
222
- * @returns { Promise<{id: number, status: string}>}
223
- */
244
+ * @param {string} appId
245
+ * @param {string} id
246
+ * @param {string} notes
247
+ *
248
+ * @returns { Promise<{id: number, status: string}>}
249
+ */
224
250
  async addNotes(appId, id, notes) {
225
- const { data } = await this.#api.patch(`v2/apps/${appId}/packages/${id}`, {
226
- json: { package: { notes } },
227
- }).json();
251
+ const { data } = await this.#api
252
+ .patch(`v2/apps/${appId}/packages/${id}`, {
253
+ json: { package: { notes } },
254
+ })
255
+ .json();
228
256
 
229
257
  return {
230
258
  id: data.id,
@@ -233,11 +261,11 @@ export class Api {
233
261
  }
234
262
 
235
263
  /**
236
- * @param {string} appId
237
- * @param {string} uploadId
238
- *
239
- * @returns { Promise<{id: number, status: string}>}
240
- */
264
+ * @param {string} appId
265
+ * @param {string} uploadId
266
+ *
267
+ * @returns { Promise<{id: number, status: string}>}
268
+ */
241
269
  async checkApp(appId, uploadId) {
242
270
  const { data } = await this.#api.get(`v2/apps/${appId}/packages/${uploadId}`).json();
243
271
 
@@ -248,30 +276,32 @@ export class Api {
248
276
  }
249
277
 
250
278
  /**
251
- * @param {string} appId
252
- * @param {string} uploadId
253
- *
254
- * @returns { Promise<void>}
255
- */
279
+ * @param {string} appId
280
+ * @param {string} uploadId
281
+ *
282
+ * @returns { Promise<void>}
283
+ */
256
284
  deleteAppUpload(appId, uploadId) {
257
- return this.#api.del(`v2/apps/${appId}/packages/${uploadId}`).json();
285
+ return this.#api.delete(`v2/apps/${appId}/packages/${uploadId}`).json();
258
286
  }
259
287
 
260
288
  /**
261
- * @param {string} appId
262
- * @param {string} id
263
- *
264
- * @returns { Promise<{id: number, status: string}>}
265
- */
289
+ * @param {string} appId
290
+ * @param {string} id
291
+ *
292
+ * @returns { Promise<{id: number, status: string}>}
293
+ */
266
294
  async publishApp(appId, id) {
267
295
  console.log(`v2/apps/${appId}/packages/${id}`, {
268
296
  json: { package: { status: 'published' } },
269
- })
270
- const { data } = await this.#api.patch(`v2/apps/${appId}/packages/${id}`, {
271
- json: { package: { status: 'published' } },
272
- }).json();
297
+ });
298
+ const { data } = await this.#api
299
+ .patch(`v2/apps/${appId}/packages/${id}`, {
300
+ json: { package: { status: 'published' } },
301
+ })
302
+ .json();
273
303
 
274
- console.log('done')
304
+ console.log('done');
275
305
 
276
306
  return {
277
307
  id: data.id,
@@ -279,13 +309,13 @@ export class Api {
279
309
  };
280
310
  }
281
311
 
282
- /**
283
- * Get app packages
284
- *
285
- * @param {string} appId
286
- *
287
- * @returns { Promise<object[]>}
288
- */
312
+ /**
313
+ * Get app packages
314
+ *
315
+ * @param {string} appId
316
+ *
317
+ * @returns { Promise<object[]>}
318
+ */
289
319
  async getAppPackages(appId) {
290
320
  const { data } = await this.#api.get(`v2/apps/${appId}/packages`).json();
291
321
  return data;
@@ -303,25 +333,25 @@ export class Api {
303
333
  *
304
334
  * @returns { Promise<object> }
305
335
  */
306
- async connectAppToStream(appId, streamId, settings, status='active') {
336
+ async connectAppToStream(appId, streamId, settings, status = 'active') {
307
337
  const json = {
308
338
  app_stream_id: streamId,
309
339
  app_id: appId,
310
340
  status,
311
341
  settings: settings,
312
- scheduler_type: settings.scheduler_type
342
+ scheduler_type: settings.scheduler_type,
313
343
  };
314
344
 
315
345
  return this.#api.post('v1/app_connections', { json }).json();
316
346
  }
317
347
 
318
348
  /**
319
- * @param {string} appId
320
- * @param {string} id
321
- * @param {'BETA' | 'PROD'} label
322
- *
323
- * @returns { Promise<{id: number, status: string}>}
324
- */
349
+ * @param {string} appId
350
+ * @param {string} id
351
+ * @param {'BETA' | 'PROD'} label
352
+ *
353
+ * @returns { Promise<{id: number, status: string}>}
354
+ */
325
355
  async putLabel(appId, id, label) {
326
356
  await this.#api.patch(`v2/apps/${appId}/packages/${id}`, {
327
357
  json: { package: { label } },
@@ -0,0 +1,139 @@
1
+ import chalk from 'chalk';
2
+ import terminalLink from 'terminal-link';
3
+
4
+ import { logger } from '../../helpers/logger.js';
5
+
6
+ /**
7
+ * Console notification
8
+ *
9
+ */
10
+ export class Notification {
11
+ #prefixUrl;
12
+
13
+ constructor(envName) {
14
+ this.#prefixUrl = `https://app${envName === 'production' ? '.' : `.${envName}.`}corva.ai`;
15
+ }
16
+
17
+ /**
18
+ * Create link
19
+ *
20
+ * @private
21
+ *
22
+ * @param {string} title
23
+ * @param {string} url
24
+ *
25
+ * @returns {string}
26
+ */
27
+ _createLink(title, url) {
28
+ return this._addLinkDecoration(terminalLink(title, url));
29
+ }
30
+
31
+ /**
32
+ * Print to console line break
33
+ *
34
+ * @public
35
+ *
36
+ * @returns {void}
37
+ */
38
+ printLineBreak() {
39
+ logger.write('\n');
40
+ }
41
+
42
+ /**
43
+ * Print to console text
44
+ *
45
+ * @public
46
+ *
47
+ * @param {string} text
48
+ *
49
+ * @returns {void}
50
+ */
51
+ print(text) {
52
+ this.printLineBreak();
53
+ logger.write(text);
54
+ }
55
+
56
+ /**
57
+ * Add a link styles
58
+ *
59
+ * @private
60
+ *
61
+ * @param {string} context
62
+ *
63
+ * @returns {string}
64
+ */
65
+ _addLinkDecoration(context) {
66
+ return chalk.blue.underline(context);
67
+ }
68
+
69
+ /**
70
+ * Add an error styles
71
+ *
72
+ * @private
73
+ *
74
+ * @param {string} context
75
+ *
76
+ * @returns {string}
77
+ */
78
+ _addErrorDecoration(context) {
79
+ return chalk.red.bold(context);
80
+ }
81
+
82
+ /**
83
+ * Get stream link
84
+ *
85
+ * @public
86
+ *
87
+ * @param {string} title
88
+ * @param {string} streamId
89
+ *
90
+ * @returns {string}
91
+ */
92
+ getStreamLink(title, streamId) {
93
+ const streamUrl = `${this.#prefixUrl}/config/streams/${streamId}`;
94
+ return this._createLink(title, streamUrl);
95
+ }
96
+
97
+ /**
98
+ * Print stream link
99
+ *
100
+ * @public
101
+ *
102
+ * @param {string} title
103
+ * @param {string} streamId
104
+ *
105
+ * @returns {avoid}
106
+ */
107
+ printStreamLink(title, streamId) {
108
+ const link = this.getStreamLink(title, streamId);
109
+ this.print(link);
110
+ }
111
+
112
+ /**
113
+ * Get asset link
114
+ *
115
+ * @public
116
+ *
117
+ * @param {string} title
118
+ * @param {string} assetId
119
+ *
120
+ * @returns {string}
121
+ */
122
+ getAssetLink(title, assetId) {
123
+ const assetUrl = `${this.#prefixUrl}/assets/${assetId}`;
124
+ return this._createLink(title, assetUrl);
125
+ }
126
+
127
+ /**
128
+ * Print an error
129
+ *
130
+ * @public
131
+ *
132
+ * @param {string} error
133
+ *
134
+ * @returns {void}
135
+ */
136
+ printError(error) {
137
+ this.print(this._addErrorDecoration(error));
138
+ }
139
+ }
@@ -1,3 +1,3 @@
1
1
  export async function waitForMs(ms) {
2
- return new Promise(resolve => setTimeout(resolve, ms));
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
3
  }
@@ -21,6 +21,6 @@ export const RELEASE_FLOW = {
21
21
  REMOVE_FAILED_UPLOAD_STEP,
22
22
  PUBLISH_PACKAGE_STEP,
23
23
  ADD_LABEL_STEP,
24
- ADD_NOTES_STEP
24
+ ADD_NOTES_STEP,
25
25
  ],
26
26
  };
@@ -0,0 +1,24 @@
1
+ import _ from 'lodash';
2
+
3
+ export const ADD_APP_TO_STREAM_TASK_STEP = {
4
+ message: 'Add app to streams...',
5
+ fn: async (context) => {
6
+ const { assetsStreamsDataToProcess, api, app, manifest, notification } = context;
7
+
8
+ let counter = 0;
9
+ for (const assetStreamsData of assetsStreamsDataToProcess) {
10
+ for (const stream of assetStreamsData.selectedStreams) {
11
+ notification.printStreamLink(stream.name, stream.id);
12
+ try {
13
+ await api.connectAppToStream(app.id, stream.id, manifest.manifest.settings.app);
14
+ ++counter;
15
+ } catch (e) {
16
+ notification.printError(
17
+ `Could not add app to the stream, an error occurred: ${e.message}`
18
+ );
19
+ }
20
+ }
21
+ }
22
+ notification.print(`The app has been added to ${counter} - stream(s)`);
23
+ },
24
+ };
@@ -0,0 +1,142 @@
1
+ import _ from 'lodash';
2
+ import inquirer from 'inquirer';
3
+
4
+ export const GET_ALL_LIVE_ASSETS_TASK_STEP = {
5
+ message: 'Fetching all live assets information...',
6
+ fn: async (context) => {
7
+ const { manifest, api, app, notification } = context;
8
+
9
+ const assets = await api.getAllAssets();
10
+
11
+ notification.print(`${assets.length} - asset was found`);
12
+
13
+ const selectedAssets = await getAssetsListWithPrompt(assets, notification);
14
+
15
+ const assetsIds = selectedAssets.map((asset) => asset.id);
16
+ const streams = await api.getStreamsByAssetIds(
17
+ assetsIds,
18
+ manifest.manifest.application.segments
19
+ );
20
+
21
+ const mappedAssetStreamsData = mapAssetsStreamsData(selectedAssets, streams);
22
+
23
+ const assetsStreamsDataToProcess = await getAssetsStreamsWithPrompt(
24
+ mappedAssetStreamsData,
25
+ app.id,
26
+ notification
27
+ );
28
+
29
+ return {
30
+ ...context,
31
+ assetsStreamsDataToProcess,
32
+ };
33
+ },
34
+ };
35
+
36
+ /**
37
+ * CLI get assets list
38
+ *
39
+ * @param {object[]} assets
40
+ * @param {import('../../lib/notification').Notification} notification
41
+ *
42
+ * @returns {Promise<object[]>}
43
+ */
44
+ const getAssetsListWithPrompt = (assets, notification) => {
45
+ const choices = assets.map((asset) => {
46
+ return {
47
+ value: asset,
48
+ name: notification.getAssetLink(asset.attributes.name, asset.id),
49
+ checked: true,
50
+ };
51
+ });
52
+
53
+ return inquirer
54
+ .prompt([
55
+ {
56
+ message: 'Please choose assets?',
57
+ name: 'option',
58
+ type: 'checkbox',
59
+ choices,
60
+ },
61
+ ])
62
+ .then((res) => res.option);
63
+ };
64
+
65
+ /**
66
+ * Map asset and stream data
67
+ *
68
+ * @param {object[]} assets
69
+ * @param {object[]} streamsData
70
+ *
71
+ * @returns {object[]}
72
+ */
73
+ const mapAssetsStreamsData = (assets, streamsData) => {
74
+ return assets.map((asset) => {
75
+ const currentStreams = streamsData.filter((stream) => stream.asset_id === parseInt(asset.id));
76
+ return {
77
+ assetId: asset.id,
78
+ assetName: asset.attributes.name,
79
+ streams: currentStreams,
80
+ selectedStreams: [],
81
+ };
82
+ });
83
+ };
84
+
85
+ /**
86
+ * CLI get streams list to process
87
+ *
88
+ * @param {object[]} mappedAssetsStreamsData
89
+ * @param {number} appId
90
+ * @param {import('../../lib/notification').Notification} notification
91
+ *
92
+ * @returns {Promise<object[]>}
93
+ */
94
+ const getAssetsStreamsWithPrompt = async (mappedAssetsStreamsData, appId, notification) => {
95
+ notification.printLineBreak();
96
+ for (let mappedData of mappedAssetsStreamsData) {
97
+ const currentStreamsCount = mappedData.streams.length;
98
+
99
+ if (!currentStreamsCount) {
100
+ continue;
101
+ }
102
+
103
+ const streamsWithoutCurrentApp = mappedData.streams.filter((stream) => {
104
+ return !stream.app_connections.find(
105
+ (appConnection) => appConnection.app_id === parseInt(appId)
106
+ );
107
+ });
108
+
109
+ if (!streamsWithoutCurrentApp.length) {
110
+ continue;
111
+ }
112
+
113
+ if (streamsWithoutCurrentApp.length === 1) {
114
+ mappedData.selectedStreams = streamsWithoutCurrentApp;
115
+ continue;
116
+ }
117
+
118
+ const choices = streamsWithoutCurrentApp.map((stream) => {
119
+ return {
120
+ value: stream,
121
+ name: notification.getStreamLink(stream.name, stream.id),
122
+ checked: true,
123
+ };
124
+ });
125
+
126
+ const link = notification.getAssetLink(mappedData.assetName, mappedData.assetId);
127
+ const selectedStreams = await inquirer
128
+ .prompt([
129
+ {
130
+ message: `Please choose streams for the asset - ${link}?`,
131
+ name: 'option',
132
+ type: 'checkbox',
133
+ choices,
134
+ },
135
+ ])
136
+ .then((res) => res.option);
137
+
138
+ mappedData.selectedStreams = selectedStreams;
139
+ }
140
+
141
+ return mappedAssetsStreamsData;
142
+ };