@corva/create-app 0.41.0-1 → 0.41.0-3
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.
- package/lib/flows/lib/api.js +45 -9
- package/lib/flows/lib/waitForMs.js +3 -0
- package/lib/flows/rerun.js +1 -1
- package/lib/flows/steps/release/wait-for-build.js +1 -1
- package/lib/flows/steps/{rerun-create-task.js → rerun/create-task.js} +24 -10
- package/lib/flows/steps/rerun/ensure-that-app-in-stream.js +51 -0
- package/lib/flows/steps/rerun/get-app-version.js +92 -0
- package/lib/flows/steps/rerun/prepare-data.js +138 -0
- package/lib/flows/steps/rerun/prepare-well-and-stream-data.js +182 -0
- package/lib/flows/steps/rerun/rerun.js +13 -0
- package/lib/main.js +2 -0
- package/package.json +1 -1
- package/templates/javascript/ui/README.md +3 -3
- package/templates/typescript/ui/README.md +3 -3
- package/lib/flows/steps/rerun-prepare-data.js +0 -266
- package/lib/flows/steps/rerun.js +0 -4
package/lib/flows/lib/api.js
CHANGED
|
@@ -42,11 +42,12 @@ export class Api {
|
|
|
42
42
|
* @returns {object}
|
|
43
43
|
*/
|
|
44
44
|
async getAppByKey(appKey) {
|
|
45
|
-
const { data
|
|
45
|
+
const { data } = await this.#api.get('v2/apps', {
|
|
46
46
|
searchParams: { per_page: 2, search: appKey }
|
|
47
47
|
}).json();
|
|
48
48
|
|
|
49
|
-
|
|
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
|
package/lib/flows/rerun.js
CHANGED
|
@@ -1,55 +1,69 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
import { logger } from '
|
|
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('
|
|
11
|
+
* @param {import('../../lib/api').Api} param0.api
|
|
12
12
|
*/
|
|
13
13
|
fn: async ({
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
45
|
+
app.id,
|
|
33
46
|
version,
|
|
34
|
-
|
|
47
|
+
interval,
|
|
48
|
+
wellId,
|
|
35
49
|
appDatasetsNames,
|
|
36
|
-
|
|
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/${
|
|
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
|
|
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
|
@@ -26,6 +26,6 @@ Releases the app into Corva
|
|
|
26
26
|
|
|
27
27
|
## Documentation
|
|
28
28
|
|
|
29
|
-
- [Dev Center documentation](https://
|
|
30
|
-
- [Component Library](https://
|
|
31
|
-
- [Datasets documentation](https://
|
|
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://
|
|
30
|
-
- [Component Library](https://
|
|
31
|
-
- [Datasets documentation](https://
|
|
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
|
-
};
|
package/lib/flows/steps/rerun.js
DELETED