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

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.
@@ -42,11 +42,12 @@ export class Api {
42
42
  * @returns {object}
43
43
  */
44
44
  async getAppByKey(appKey) {
45
- const { data: [app] } = await this.#api.get('v2/apps', {
45
+ const { data } = await this.#api.get('v2/apps', {
46
46
  searchParams: { per_page: 2, search: appKey }
47
47
  }).json();
48
48
 
49
- if (!app || app.attributes.app_key != appKey) {
49
+ const app = data.find(app => app.attributes.app_key === appKey);
50
+ if (!app) {
50
51
  throw new Error(`App with key - ${appKey}, not exist`);
51
52
  }
52
53
 
@@ -64,11 +65,6 @@ export class Api {
64
65
  */
65
66
  async getAppDatasetsOperationWrite(appId) {
66
67
  const { data } = await this.#api.get(`v2/apps/${appId}/app_datasets?dataset_operation=write&fields[]=app_dataset.dataset_name`).json();
67
-
68
- if (!data.length) {
69
- throw new Error(`App with ID - ${appId}, has no datasets`);
70
- }
71
-
72
68
  return data;
73
69
  }
74
70
 
@@ -138,13 +134,14 @@ export class Api {
138
134
  *
139
135
  * @param {string} appId
140
136
  * @param {string} version
137
+ * @param {number} interval
141
138
  * @param {string} wellId
142
139
  * @param {string[]} appDatasetsNames
143
140
  * @param {string} streamId
144
141
  *
145
142
  * @returns {object}
146
143
  */
147
- async queueAppRun(appId, version, wellId, appDatasetsNames, streamId) {
144
+ async queueAppRun(appId, version, interval, wellId, appDatasetsNames, streamId) {
148
145
  const json = {
149
146
  app_run: {
150
147
  app_stream_id: streamId,
@@ -153,7 +150,6 @@ export class Api {
153
150
  datasets: appDatasetsNames,
154
151
  settings: {
155
152
  end: 0,
156
- interval: null,
157
153
  invokes: null,
158
154
  records_per_event: 300,
159
155
  start: 0,
@@ -161,6 +157,10 @@ export class Api {
161
157
  },
162
158
  };
163
159
 
160
+ if (interval) {
161
+ json.app_run.settings.interval = interval;
162
+ }
163
+
164
164
  const { data } = await this.#api.post(`v2/apps/${appId}/app_runs`, { json }).json();
165
165
 
166
166
  return data;
@@ -279,6 +279,42 @@ export class Api {
279
279
  };
280
280
  }
281
281
 
282
+ /**
283
+ * Get app packages
284
+ *
285
+ * @param {string} appId
286
+ *
287
+ * @returns { Promise<object[]>}
288
+ */
289
+ async getAppPackages(appId) {
290
+ const { data } = await this.#api.get(`v2/apps/${appId}/packages`).json();
291
+ return data;
292
+ }
293
+
294
+ /**
295
+ * Connect app to the stream
296
+ *
297
+ * @public
298
+ *
299
+ * @param {string} appId
300
+ * @param {string} streamId
301
+ * @param {object} settings
302
+ * @param {string} status
303
+ *
304
+ * @returns { Promise<object> }
305
+ */
306
+ async connectAppToStream(appId, streamId, settings, status='active') {
307
+ const json = {
308
+ app_stream_id: streamId,
309
+ app_id: appId,
310
+ status,
311
+ settings: settings,
312
+ scheduler_type: settings.scheduler_type
313
+ };
314
+
315
+ return this.#api.post('v1/app_connections', { json }).json();
316
+ }
317
+
282
318
  /**
283
319
  * @param {string} appId
284
320
  * @param {string} id
@@ -1,5 +1,5 @@
1
1
  import { PREPARE_FLOW } from './prepare.js';
2
- import { RERUN_STEPS } from './steps/rerun.js';
2
+ import { RERUN_STEPS } from './steps/rerun/rerun.js';
3
3
  import { SETUP_API_CLIENT_STEP } from './steps/release/get-config.js';
4
4
 
5
5
  export const RERUN_FLOW = {
@@ -1,55 +1,69 @@
1
1
  import _ from 'lodash';
2
2
  import chalk from 'chalk';
3
3
 
4
- import { logger } from '../../helpers/logger.js';
4
+ import { logger } from '../../../helpers/logger.js';
5
5
 
6
6
  export const CREATE_TASK_STEP = {
7
7
  message: 'Creating tasks...',
8
8
  /**
9
9
  *
10
10
  * @param {object} param0
11
- * @param {import('../lib/api').Api} param0.api
11
+ * @param {import('../../lib/api').Api} param0.api
12
12
  */
13
13
  fn: async ({
14
- appId,
14
+ app,
15
15
  assets,
16
16
  appDatasetsNames,
17
17
  mappedAssetsToStreams,
18
18
  mappedAssetsToWells,
19
19
  api,
20
20
  version,
21
+ interval,
21
22
  CORVA_API_ENV,
22
23
  }) => {
23
24
  if (!assets.length) {
24
25
  logger.write(
25
- `\n\n${chalk.yellow.bold('There is not a asset ID to create new task')}`
26
+ `\n\n${chalk.yellow.bold('There is no asset ID to create a new task')}`
26
27
  );
27
28
  }
28
29
 
29
30
  for (const assetId of assets) {
31
+ const wellId = mappedAssetsToWells.get(parseInt(assetId)).id;
32
+ const streamId = mappedAssetsToStreams.get(parseInt(assetId)).id;
33
+
34
+ if (!wellId || !streamId) {
35
+ logger.write(
36
+ `\n\n${chalk.red.underline.bold(
37
+ `Skipped creating a task for asset ID - ${assetId}, well ID or stream ID is missing`
38
+ )}\n\n`
39
+ );
40
+ continue;
41
+ }
42
+
30
43
  try {
31
44
  const result = await api.queueAppRun(
32
- appId,
45
+ app.id,
33
46
  version,
34
- assetId,
47
+ interval,
48
+ wellId,
35
49
  appDatasetsNames,
36
- mappedAssetsToStreams.get(parseInt(assetId))
50
+ streamId,
37
51
  ).catch(e => { console.log(e.response.body); throw e });
38
52
 
39
53
  logger.write(
40
- `\n Created new task with ID ${chalk.yellow(result.id)} for asset ID - ${chalk.green(
54
+ `\n Created a new task with ID ${chalk.yellow(result.id)} for asset ID - ${chalk.green(
41
55
  assetId
42
56
  )}`
43
57
  );
44
58
 
45
59
  logger.write(
46
60
  `\n Task link - https://app${CORVA_API_ENV === 'production' ? '.' : `.${CORVA_API_ENV}.`
47
- }corva.ai/dev-center/apps/${appId}/runner`
61
+ }corva.ai/dev-center/apps/${app.id}/runner`
48
62
  );
49
63
  } catch (e) {
50
64
  logger.write(
51
65
  `\n\n${chalk.red.underline.bold(
52
- `Could not rerun app for asset ID - ${assetId}, an error occured: ${e.message}`
66
+ `Could not rerun app for asset ID - ${assetId}, an error occurred: ${e.message}`
53
67
  )}\n\n`
54
68
  );
55
69
  }
@@ -0,0 +1,51 @@
1
+ import _ from 'lodash';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+
5
+ import { logger } from '../../../helpers/logger.js';
6
+
7
+ export const ENSURE_APP_IN_STREAM_TASK_STEP = {
8
+ message: 'Ensure that app in the stream...',
9
+ /**
10
+ *
11
+ * @param {object} context
12
+ */
13
+ fn: async (context) => {
14
+ const {mappedAssetsToStreams, app, api, manifest } = context;
15
+
16
+ await Promise.all(
17
+ [...mappedAssetsToStreams].map(([assetId, stream]) => {
18
+ return ensureAppIsInTheStream(assetId, stream, app.id, api, manifest);
19
+ })
20
+ );
21
+
22
+ return context;
23
+ },
24
+ };
25
+
26
+ /**
27
+ * Ensure if the app is in the stream, if no, add app to the stream
28
+ *
29
+ * @param {string} assetId
30
+ * @param {object} stream
31
+ * @param {string} appId
32
+ * @param {import('../lib/api').Api} api
33
+ * @param {object} manifest
34
+ *
35
+ * @returns {void}
36
+ */
37
+ const ensureAppIsInTheStream = async (assetId, stream, appId, api, manifest) => {
38
+ const connectedApp = stream.app_connections.find((connection) => connection.app_id == appId);
39
+ if (connectedApp) {
40
+ logger.write(
41
+ `\n\n${chalk.black.underline.bold(`App has already connected to the stream ID: ${stream.id}, for asset ID: ${assetId}`)}`
42
+ );
43
+ return;
44
+ }
45
+
46
+ const newConnection = await api.connectAppToStream(appId, stream.id, manifest.settings.app);
47
+
48
+ logger.write(
49
+ `\n\n${chalk.black.underline.bold(`Added app to the stream, connect ID - ${chalk.green(newConnection.id)}, for asset ID: ${assetId}`)}`
50
+ );
51
+ }
@@ -0,0 +1,92 @@
1
+ import _ from 'lodash';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+
5
+ import { logger } from '../../../helpers/logger.js';
6
+
7
+ export const GET_APP_VERSION_TASK_STEP = {
8
+ message: 'Get app version...',
9
+ /**
10
+ *
11
+ * @param {object} context
12
+ */
13
+ fn: async (context) => {
14
+ const {app, options, api } = context;
15
+ const { appVersion } = options;
16
+
17
+ const currentAppVersion = await getAppVersion(app.id, appVersion, api);
18
+
19
+ return {
20
+ ...context,
21
+ version: currentAppVersion
22
+ };
23
+ },
24
+ };
25
+
26
+ /**
27
+ * CLI prompt question - Please choose the app version (optional).
28
+ *
29
+ * @param {string} appId
30
+ * @param {string} optionAppVersion
31
+ * @param {import('../lib/api').Api} api
32
+ *
33
+ * @returns {Promise<{number}>}
34
+ */
35
+ const getAppVersion = async (appId, optionAppVersion, api) => {
36
+ logger.write('\n Checking app versions...');
37
+ const appPackages = await api.getAppPackages(appId);
38
+
39
+ if (!appPackages.length) {
40
+ throw new Error('No app versions found');
41
+ }
42
+
43
+ const confirmUseVersionMessage = (version) => {
44
+ logger.write(
45
+ `\n\n${chalk.black.underline.bold(`Use app version - ${chalk.green(version)}`)}`
46
+ );
47
+ };
48
+
49
+ // try to get version from CLI option
50
+ if (optionAppVersion) {
51
+ const currentAppPackageByOption = appPackages.find((appPackage) => appPackage.attributes.version == optionAppVersion);
52
+ if (currentAppPackageByOption) {
53
+ const version = currentAppPackageByOption.attributes.version;
54
+ confirmUseVersionMessage(version);
55
+ return version;
56
+ }
57
+
58
+ logger.write(
59
+ `\n\n${chalk.black.underline.bold(`Could not find app version - ${chalk.green(optionAppVersion)}`)}`
60
+ );
61
+ }
62
+
63
+ // if there is only on version, use it.
64
+ if (appPackages.length === 1) {
65
+ const version = appPackages[0].attributes.version;
66
+ confirmUseVersionMessage(version);
67
+ return version;
68
+ }
69
+
70
+ // if there are several versions, show versions list to user
71
+ const choices = appPackages.map((appPackage) => {
72
+ return {
73
+ value: appPackage.attributes.version,
74
+ name: appPackage.attributes.version,
75
+ };
76
+ })
77
+
78
+ logger.log('\n')
79
+
80
+ return inquirer
81
+ .prompt([
82
+ {
83
+ message: 'Please choose the app version?',
84
+ name: 'option',
85
+ type: 'list',
86
+ choices,
87
+ },
88
+ ])
89
+ .then((answers) => {
90
+ return answers.option;
91
+ });
92
+ };
@@ -0,0 +1,138 @@
1
+ import _ from 'lodash';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import { StepError } from '../../lib/step-error.js';
5
+ import { logger } from '../../../helpers/logger.js';
6
+
7
+ const MAX_ASSET_IDS_COUNT = 10;
8
+
9
+ export const PREPARE_DATA_TASK_STEP = {
10
+ message: 'Preparing and checking data...',
11
+ fn: async (context) => {
12
+ const { manifest, options, api } = context;
13
+ let { assets, interval } = options;
14
+
15
+ assets = _.uniq(assets);
16
+
17
+ if (assets.length > MAX_ASSET_IDS_COUNT) {
18
+ throw new StepError(`Please no more than ${MAX_ASSET_IDS_COUNT} asset ids!`);
19
+ }
20
+
21
+ if (!['scheduler', 'stream'].includes(manifest.manifest.application.type)) {
22
+ throw new StepError('Rerun command supports only "scheduler" or "stream" apps');
23
+ }
24
+
25
+ if (!(await promptAreYouSure())) {
26
+ throw new StepError('Command stopped');
27
+ }
28
+
29
+ const app = await api.getAppByKey(manifest.manifest.application.key.toLowerCase());
30
+ const appDatasets = await api.getAppDatasetsOperationWrite(app.id);
31
+ const appDatasetsNames = appDatasets.map((dataset) => dataset.attributes.dataset_name);
32
+
33
+ const existingAppRuns = await getExistAppRuns(app.id, assets, appDatasetsNames, api);
34
+ if (existingAppRuns.size) {
35
+ for (let appRunAssetId of existingAppRuns.keys()) {
36
+ // remove asset ID if similar rerun already exists
37
+ assets = assets.filter((assetId) => appRunAssetId != assetId);
38
+
39
+ const run = existingAppRuns.get(appRunAssetId);
40
+
41
+ logger.write(
42
+ `\n\n${chalk.yellow.bold(
43
+ `A similar rerun with ID ${run.id}, for asset ID ${appRunAssetId} - already exists. Will be skipped!`
44
+ )}`
45
+ );
46
+ }
47
+ }
48
+
49
+ return {
50
+ assets,
51
+ app,
52
+ options,
53
+ manifest,
54
+ appDatasetsNames,
55
+ interval: getInterval(manifest.manifest, interval)
56
+ };
57
+ },
58
+ };
59
+
60
+ /**
61
+ * CLI prompt question - Are you sure you want to do this?
62
+ *
63
+ * @returns
64
+ */
65
+ const promptAreYouSure = async () => {
66
+ logger.log('\n')
67
+ const answers = await inquirer.prompt([
68
+ {
69
+ message:
70
+ 'This command will create an additional load on the server, which may take a long time. \n Are you sure you want to do this?',
71
+ name: 'option',
72
+ type: 'list',
73
+ choices: [
74
+ { value: true, name: 'Yes' },
75
+ { value: false, name: 'No' },
76
+ ],
77
+ default: false,
78
+ },
79
+ ]);
80
+ return answers.option;
81
+ };
82
+
83
+ /**
84
+ * Check if current runs already exist
85
+ *
86
+ * @param {string} appId
87
+ * @param {string[]} assets
88
+ * @param {string[]} appDatasetsNames
89
+ * @param {object} api
90
+ *
91
+ * @returns {map}
92
+ */
93
+ const getExistAppRuns = async (appId, assets, appDatasetsNames, api) => {
94
+ const appRuns = new Map();
95
+ const existingAppRuns = await api.getAppRuns(appId);
96
+
97
+ // go through all assets ids
98
+ for (const assetId of assets) {
99
+ const runsForCurrentAsset = existingAppRuns.filter(
100
+ (run) => run.attributes.well_asset_id == assetId
101
+ );
102
+ // go through all existing runs for current asset ID
103
+ for (const run of runsForCurrentAsset) {
104
+ const currentAppDatasetsNames = run.attributes.app_run_datasets.map(
105
+ (dataset) => dataset.name
106
+ );
107
+ // if datasets names matched for the same asset ID - then run already exist
108
+ if (appDatasetsNames.sort().toString() === currentAppDatasetsNames.sort().toString()) {
109
+ appRuns.set(assetId, run);
110
+ }
111
+ }
112
+ }
113
+
114
+ return appRuns;
115
+ };
116
+
117
+ /**
118
+ * Get interval
119
+ *
120
+ * @param {object} manifest
121
+ * @param {number} intervalOption
122
+ *
123
+ * @returns {number | null}
124
+ */
125
+ const getInterval = (manifest, intervalOption) => {
126
+ if (manifest.application.type !== 'scheduler') {
127
+ return null;
128
+ }
129
+
130
+ const DEFAULT_INTERVAL = 3600;
131
+ const interval = intervalOption || DEFAULT_INTERVAL;
132
+
133
+ logger.write(
134
+ `\n\n${chalk.black.underline.bold(`Use interval - ${chalk.green(interval)}`)}`
135
+ );
136
+
137
+ return parseInt(interval);
138
+ };
@@ -0,0 +1,182 @@
1
+ import _ from 'lodash';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+
5
+ import { logger } from '../../../helpers/logger.js';
6
+
7
+ export const PREPARE_WELL_AND_STREAM_TASK_STEP = {
8
+ message: 'Prepare well and stream data...',
9
+ /**
10
+ *
11
+ * @param {object} context
12
+ */
13
+ fn: async (context) => {
14
+ let { assets } = context;
15
+ const { api } = context;
16
+
17
+ const { mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete } =
18
+ await prepareWellAndStreamData(assets, api);
19
+
20
+ if (assetsToDelete.length) {
21
+ // remove asset ID if could not found stream or well
22
+ assets = assets.filter((assetId) => !assetsToDelete.includes(assetId));
23
+ }
24
+
25
+ return {
26
+ ...context,
27
+ assets,
28
+ mappedAssetsToStreams,
29
+ mappedAssetsToWells
30
+ };
31
+ },
32
+ };
33
+
34
+ /**
35
+ * CLI prompt question - Please choose the stream
36
+ *
37
+ * @param {object[]} streams
38
+ *
39
+ * @returns
40
+ */
41
+ const getStreamWithPrompt = async (streams) => {
42
+ let stream = {};
43
+
44
+ const choices = streams.map((stream) => {
45
+ return {
46
+ value: stream,
47
+ name: stream.name,
48
+ };
49
+ });
50
+
51
+ if (!choices.length) {
52
+ throw new Error('No completed streams')
53
+ }
54
+
55
+ if (choices.length === 1) {
56
+ const [stream] = choices;
57
+
58
+ logger.write(
59
+ `\n\n${chalk.black.underline.bold(`Process stream - ${chalk.green(stream.name)}`)}`
60
+ );
61
+ return stream.value
62
+ }
63
+
64
+ logger.log('\n')
65
+
66
+ await inquirer
67
+ .prompt([
68
+ {
69
+ message: 'Please choose stream?',
70
+ name: 'option',
71
+ type: 'list',
72
+ choices,
73
+ },
74
+ ])
75
+ .then((answers) => {
76
+ stream = answers.option;
77
+ });
78
+
79
+ return stream;
80
+ };
81
+
82
+ /**
83
+ * CLI prompt question - Please choose the well
84
+ *
85
+ * @param {object[]} wells
86
+ * @param {import('../lib/api').Api} api
87
+ *
88
+ * @returns
89
+ */
90
+ const getWellWithPrompt = async (wells, api) => {
91
+ let well = {};
92
+
93
+ const choices = await Promise.all(
94
+ wells.map(async (well) => {
95
+ const assetDetails = await api.getAssetById(well.data.attributes.asset_id);
96
+ well.data.name = assetDetails.attributes.name;
97
+ return {
98
+ value: well.data,
99
+ name: well.data.name,
100
+ };
101
+ })
102
+ );
103
+
104
+ if (!choices.length) {
105
+ throw new Error('No wells found');
106
+ }
107
+
108
+ if (choices.length === 1) {
109
+ const [well] = choices;
110
+
111
+ logger.write(
112
+ `\n\n${chalk.black.underline.bold(`Process well - ${chalk.green(well.name)}`)}`
113
+ );
114
+ return well.value
115
+ }
116
+
117
+ logger.log('\n')
118
+
119
+ await inquirer
120
+ .prompt([
121
+ {
122
+ message: 'Please choose the well?',
123
+ name: 'option',
124
+ type: 'list',
125
+ choices,
126
+ },
127
+ ])
128
+ .then((answers) => {
129
+ well = answers.option;
130
+ });
131
+
132
+ return well;
133
+ };
134
+
135
+ /**
136
+ * Get stream and well data for assets
137
+ *
138
+ * @param {string[]} assets
139
+ * @param {string} appId
140
+ * @param {import('../lib/api').Api} api
141
+ * @param {object} manifest
142
+ *
143
+ * @returns {object}
144
+ */
145
+ const prepareWellAndStreamData = async (assets, api) => {
146
+ const mappedAssetsToWells = new Map();
147
+ const mappedAssetsToStreams = new Map();
148
+ const assetsToDelete = [];
149
+
150
+ for (const assetId of assets) {
151
+ try {
152
+ logger.write(
153
+ `\n\n${chalk.black.underline.bold(`Process asset ID - ${chalk.green(assetId)}`)}`
154
+ );
155
+
156
+ logger.write('\n Loading wells...');
157
+
158
+ const wells = await api.getWellByAssetId(assetId);
159
+ const well = await getWellWithPrompt(wells, api);
160
+ mappedAssetsToWells.set(well.attributes.asset_id, well);
161
+
162
+ logger.write('\n Loading streams...');
163
+
164
+ const streams = await api.getStreamByAssetId(assetId);
165
+ const stream = await getStreamWithPrompt(streams);
166
+ mappedAssetsToStreams.set(stream.asset_id, stream);
167
+ } catch (e) {
168
+ logger.write(
169
+ `\n\n${chalk.red.underline.bold(
170
+ `Skipped the asset ID - ${assetId}, an error occurred: ${e.message}`
171
+ )}`
172
+ );
173
+
174
+ assetsToDelete.push(assetId);
175
+
176
+ if (mappedAssetsToWells.has(assetId)) mappedAssetsToWells.delete(assetId);
177
+ if (mappedAssetsToStreams.has(assetId)) mappedAssetsToStreams.delete(assetId);
178
+ }
179
+ }
180
+
181
+ return { mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete };
182
+ };
@@ -0,0 +1,13 @@
1
+ import { CREATE_TASK_STEP } from './create-task.js';
2
+ import { PREPARE_DATA_TASK_STEP } from './prepare-data.js';
3
+ import { GET_APP_VERSION_TASK_STEP } from './get-app-version.js';
4
+ import { ENSURE_APP_IN_STREAM_TASK_STEP } from './ensure-that-app-in-stream.js';
5
+ import { PREPARE_WELL_AND_STREAM_TASK_STEP } from './prepare-well-and-stream-data.js';
6
+
7
+ export const RERUN_STEPS = [
8
+ PREPARE_DATA_TASK_STEP,
9
+ GET_APP_VERSION_TASK_STEP,
10
+ PREPARE_WELL_AND_STREAM_TASK_STEP,
11
+ ENSURE_APP_IN_STREAM_TASK_STEP,
12
+ CREATE_TASK_STEP
13
+ ];
package/lib/main.js CHANGED
@@ -200,6 +200,8 @@ export async function run() {
200
200
  .addOption(envOption)
201
201
  .addOption(silentOption)
202
202
  .addOption(new Option('--assets [assets...]', 'Assets ids list', []))
203
+ .addOption(new Option('--app-version [number]', 'App version (exp. 1, 2, 3)'))
204
+ .addOption(new Option('--interval [number]', 'Interval for scheduler apps (exp. 1200)'))
203
205
  .action(async (dirName, options) => {
204
206
  await runFlow(RERUN_FLOW, { dirName, options });
205
207
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corva/create-app",
3
- "version": "0.41.0-2",
3
+ "version": "0.42.0-0",
4
4
  "private": false,
5
5
  "description": "Create app to use it in CORVA.AI",
6
6
  "keywords": [
@@ -26,6 +26,6 @@ Releases the app into Corva
26
26
 
27
27
  ## Documentation
28
28
 
29
- - [Dev Center documentation](https://app.corva.ai/dev-center/docs/frontend) – information about development process
30
- - [Component Library](https://app.corva.ai/dev-center/docs/frontend/storybook)
31
- - [Datasets documentation](https://app.corva.ai/dev-center/docs/datasets)
29
+ - [Dev Center documentation](https://dc-docs.corva.ai/) – information about development process
30
+ - [Component Library](https://dc-docs.corva.ai/docs/Frontend/Data%20visualization/Components%20library)
31
+ - [Datasets documentation](https://dc-docs.corva.ai/docs/Datasets/Link%20App%20to%20Dataset)
@@ -26,6 +26,6 @@ Releases the app into Corva
26
26
 
27
27
  ## Documentation
28
28
 
29
- - [Dev Center documentation](https://app.corva.ai/dev-center/docs/frontend) – information about development process
30
- - [Component Library](https://app.corva.ai/dev-center/docs/frontend/storybook)
31
- - [Datasets documentation](https://app.corva.ai/dev-center/docs/datasets)
29
+ - [Dev Center documentation](https://dc-docs.corva.ai/) – information about development process
30
+ - [Component Library](https://dc-docs.corva.ai/docs/Frontend/Data%20visualization/Components%20library)
31
+ - [Datasets documentation](https://dc-docs.corva.ai/docs/Datasets/Link%20App%20to%20Dataset)
@@ -1,266 +0,0 @@
1
- import _ from 'lodash';
2
- import chalk from 'chalk';
3
- import inquirer from 'inquirer';
4
- import { StepError } from '../lib/step-error.js';
5
- import { logger } from '../../helpers/logger.js';
6
-
7
- const MAX_ASSET_IDS_COUNT = 10;
8
-
9
- export const PREPARE_DATA_STEP = {
10
- message: 'Preparing data for tasks...',
11
- fn: async (context) => {
12
- const { manifest, options, api, pkg } = context;
13
- let { assets } = options;
14
-
15
- assets = _.uniq(assets);
16
-
17
- if (assets.length > MAX_ASSET_IDS_COUNT) {
18
- throw new StepError(`Please no more than ${MAX_ASSET_IDS_COUNT} asset ids!`);
19
- }
20
-
21
- if (!['scheduler', 'stream'].includes(manifest.manifest.application.type)) {
22
- throw new StepError('Rerun command supports only "scheduler" or "stream" apps');
23
- }
24
-
25
- if (!(await promptAreYouSure())) {
26
- throw new StepError('Command stopped');
27
- }
28
-
29
- const app = await api.getAppByKey(manifest.manifest.application.key.toLowerCase());
30
- const appDatasets = await api.getAppDatasetsOperationWrite(app.id);
31
- const appDatasetsNames = appDatasets.map((dataset) => dataset.attributes.dataset_name);
32
-
33
- const existingAppRuns = await getExistAppRuns(app.id, assets, appDatasetsNames, api);
34
- if (existingAppRuns.size) {
35
- for (let appRunAssetId of existingAppRuns.keys()) {
36
- // remove asset ID if similar rerun already exists
37
- assets = assets.filter((assetId) => appRunAssetId != assetId);
38
-
39
- const run = existingAppRuns.get(appRunAssetId);
40
-
41
- logger.write(
42
- `\n\n${chalk.yellow.bold(
43
- `Similar rerun with ID ${run.id}, for asset ID ${appRunAssetId} - already exist. Will be skipped!`
44
- )}`
45
- );
46
- }
47
- }
48
-
49
- const { mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete } =
50
- await prepareWellAndStreamData(assets, api);
51
-
52
- if (assetsToDelete.length) {
53
- // remove asset ID if could not found stream or well
54
- assets = assets.filter((assetId) => !assetsToDelete.includes(assetId));
55
- }
56
-
57
- return {
58
- appId: app.id,
59
- assets,
60
- appDatasetsNames,
61
- mappedAssetsToStreams,
62
- mappedAssetsToWells,
63
- version: pkg.version,
64
- };
65
- },
66
- };
67
-
68
- /**
69
- * CLI prompt question - Are you sure you want to do this?
70
- *
71
- * @returns
72
- */
73
- const promptAreYouSure = async () => {
74
- const answers = await inquirer.prompt([
75
- {
76
- message:
77
- '\n This command will create additional load on the server, which may take a long time. \n Are you sure you want to do this?',
78
- name: 'option',
79
- type: 'list',
80
- choices: [
81
- { value: true, name: 'Yes' },
82
- { value: false, name: 'No' },
83
- ],
84
- default: false,
85
- },
86
- ]);
87
- return answers.option;
88
- };
89
-
90
- /**
91
- * CLI prompt question - Please choose the stream
92
- *
93
- * @param {object[]} streams
94
- *
95
- * @returns
96
- */
97
- const getStreamWithPrompt = async (streams) => {
98
- let stream = {};
99
-
100
- const choices = streams.map((stream) => {
101
- return {
102
- value: stream,
103
- name: stream.name,
104
- };
105
- });
106
-
107
- if (!choices.length) {
108
- throw new Error('No completed streams')
109
- }
110
-
111
- if (choices.length === 1) {
112
- const [stream] = choices;
113
-
114
- logger.write(
115
- `\n\n${chalk.black.underline.bold(`Process stream - ${chalk.green(stream.name)}`)}`
116
- );
117
- return stream.value
118
- }
119
-
120
- await inquirer
121
- .prompt([
122
- {
123
- message: '\n Please choose stream?',
124
- name: 'option',
125
- type: 'list',
126
- choices,
127
- },
128
- ])
129
- .then((answers) => {
130
- stream = answers.option;
131
- });
132
-
133
- return stream;
134
- };
135
-
136
- /**
137
- * CLI prompt question - Please choose the well
138
- *
139
- * @param {object[]} wells
140
- * @param {import('../lib/api').Api} api
141
- *
142
- * @returns
143
- */
144
- const getWellWithPrompt = async (wells, api) => {
145
- let well = {};
146
-
147
- const choices = await Promise.all(
148
- wells.map(async (well) => {
149
- const assetDetails = await api.getAssetById(well.data.attributes.asset_id);
150
- well.data.name = assetDetails.attributes.name;
151
- return {
152
- value: well.data,
153
- name: well.data.name,
154
- };
155
- })
156
- );
157
-
158
- if (!choices.length) {
159
- throw new Error('No wells found');
160
- }
161
-
162
- if (choices.length === 1) {
163
- const [well] = choices;
164
-
165
- logger.write(
166
- `\n\n${chalk.black.underline.bold(`Process well - ${chalk.green(well.name)}`)}`
167
- );
168
- return well.value
169
- }
170
-
171
- await inquirer
172
- .prompt([
173
- {
174
- message: '\n Please choose the well?',
175
- name: 'option',
176
- type: 'list',
177
- choices,
178
- },
179
- ])
180
- .then((answers) => {
181
- well = answers.option;
182
- });
183
-
184
- return well;
185
- };
186
-
187
- /**
188
- * Get stream and well data for assets
189
- *
190
- * @param {string[]} assets
191
- * @param {import('../lib/api').Api} api
192
- *
193
- * @returns {object}
194
- */
195
- const prepareWellAndStreamData = async (assets, api) => {
196
- const mappedAssetsToWells = new Map();
197
- const mappedAssetsToStreams = new Map();
198
- const assetsToDelete = [];
199
-
200
- for (const assetId of assets) {
201
- try {
202
- logger.write(
203
- `\n\n${chalk.black.underline.bold(`Process asset ID - ${chalk.green(assetId)}`)}`
204
- );
205
-
206
- logger.write('\n Loading wells...');
207
-
208
- const wells = await api.getWellByAssetId(assetId);
209
- const well = await getWellWithPrompt(wells, api);
210
- mappedAssetsToWells.set(well.attributes.asset_id, well.id);
211
-
212
- logger.write('\n Loading streams...');
213
-
214
- const streams = await api.getStreamByAssetId(assetId);
215
- const stream = await getStreamWithPrompt(streams);
216
- mappedAssetsToStreams.set(stream.asset_id, stream.id);
217
- } catch (e) {
218
- logger.write(
219
- `\n\n${chalk.red.underline.bold(
220
- `Skipped the asset ID - ${assetId}, an error occured: ${e.message}`
221
- )}`
222
- );
223
-
224
- assetsToDelete.push(assetId);
225
-
226
- if (mappedAssetsToWells.has(assetId)) mappedAssetsToWells.delete(assetId);
227
- if (mappedAssetsToStreams.has(assetId)) mappedAssetsToStreams.delete(assetId);
228
- }
229
- }
230
-
231
- return { mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete };
232
- };
233
-
234
- /**
235
- * Check if current runs already exist
236
- *
237
- * @param {string} appId
238
- * @param {string[]} assets
239
- * @param {string[]} appDatasetsNames
240
- * @param {object} api
241
- *
242
- * @returns {map}
243
- */
244
- const getExistAppRuns = async (appId, assets, appDatasetsNames, api) => {
245
- const appRuns = new Map();
246
- const existingAppRuns = await api.getAppRuns(appId);
247
-
248
- // go through all assets ids
249
- for (const assetId of assets) {
250
- const runsForCurrentAsset = existingAppRuns.filter(
251
- (run) => run.attributes.well_asset_id == assetId
252
- );
253
- // go through all existing runs for current asset ID
254
- for (const run of runsForCurrentAsset) {
255
- const currentAppDatasetsNames = run.attributes.app_run_datasets.map(
256
- (dataset) => dataset.name
257
- );
258
- // if datasets names matched for the same asset ID - then run already exist
259
- if (appDatasetsNames.sort().toString() === currentAppDatasetsNames.sort().toString()) {
260
- appRuns.set(assetId, run);
261
- }
262
- }
263
- }
264
-
265
- return appRuns;
266
- };
@@ -1,4 +0,0 @@
1
- import { PREPARE_DATA_STEP } from './rerun-prepare-data.js';
2
- import { CREATE_TASK_STEP } from './rerun-create-task.js';
3
-
4
- export const RERUN_STEPS = [PREPARE_DATA_STEP, CREATE_TASK_STEP];