@contrail/cli 2.3.3 → 2.4.0-alpha
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/README.md +2 -3
- package/dist/api/apps.d.ts +1 -1
- package/dist/api/apps.js +3 -20
- package/dist/auth/login.d.ts +0 -1
- package/dist/auth/login.js +2 -12
- package/dist/commands/app/create.js +1 -2
- package/dist/commands/app/init.js +2 -1
- package/dist/commands/app/install.d.ts +0 -1
- package/dist/commands/app/install.js +1 -6
- package/dist/commands/types/getAll.js +7 -0
- package/dist/commands/types/mergeOptionSets.js +3 -1
- package/dist/commands/types/parseOptionSetsLocalFiles.d.ts +9 -7
- package/dist/commands/types/parseOptionSetsLocalFiles.js +33 -14
- package/dist/commands/types/uploadOptionSets.d.ts +1 -0
- package/dist/commands/types/uploadOptionSets.js +33 -20
- package/dist/helpers/app/app-config-validators.d.ts +16 -1
- package/dist/helpers/app/app-config-validators.js +73 -24
- package/dist/helpers/app/app-config-validators.spec.js +225 -20
- package/dist/helpers/app/app-deployer.js +12 -7
- package/dist/helpers/app/app-template-parsers.d.ts +4 -1
- package/dist/helpers/app/app-template-parsers.js +28 -1
- package/dist/helpers/types/parseOptionSetsLocalFiles.spec.d.ts +1 -0
- package/dist/helpers/types/parseOptionSetsLocalFiles.spec.js +285 -0
- package/dist/helpers/types/type-uploader.js +15 -7
- package/dist/helpers/types/uploadOptionSets.spec.d.ts +1 -0
- package/dist/helpers/types/uploadOptionSets.spec.js +19 -0
- package/dist/helpers/workflow-template-definitions/workflow-template-definition-deployer.js +4 -0
- package/dist/helpers/workflow-templates/helper.js +4 -0
- package/dist/interfaces.d.ts +26 -1
- package/dist/interfaces.js +10 -1
- package/dist/templates/typescript/app.yml +10 -2
- package/dist/templates/typescript/package-lock.json +13908 -0
- package/oclif.manifest.json +1 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
|
|
|
17
17
|
$ contrail COMMAND
|
|
18
18
|
running command...
|
|
19
19
|
$ contrail (--version)
|
|
20
|
-
@contrail/cli/2.
|
|
20
|
+
@contrail/cli/2.4.0-alpha darwin-arm64 node-v22.3.0
|
|
21
21
|
$ contrail --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ contrail COMMAND
|
|
@@ -174,10 +174,9 @@ Install an app from the marketplace
|
|
|
174
174
|
|
|
175
175
|
```
|
|
176
176
|
USAGE
|
|
177
|
-
$ contrail app install [-i <value>] [-w]
|
|
177
|
+
$ contrail app install [-i <value>] [-w]
|
|
178
178
|
|
|
179
179
|
FLAGS
|
|
180
|
-
-h, --showHidden Show the option to install public but hidden apps
|
|
181
180
|
-i, --appIdentifier=<value> The identifier of the app to install
|
|
182
181
|
-w, --installWorkflowTemplates "Install the app's accompanying workflows"
|
|
183
182
|
|
package/dist/api/apps.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare function upgradeAppVersion(appOrg: any, appVersionId: any): Promi
|
|
|
7
7
|
export declare function deleteAppOrg(appOrgId: string): Promise<AppOrg>;
|
|
8
8
|
export declare function getInstalledApps(): Promise<AppOrg[]>;
|
|
9
9
|
export declare function installAppToOrg(app: any, configProperties: any): Promise<AppOrg>;
|
|
10
|
-
export declare function getAvailableApps(
|
|
10
|
+
export declare function getAvailableApps(): Promise<App[]>;
|
package/dist/api/apps.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getAvailableApps = exports.installAppToOrg = exports.getInstalledApps = exports.deleteAppOrg = exports.upgradeAppVersion = exports.getAppVersions = exports.getWorkflowTemplateDefinitionsForAppVersion = void 0;
|
|
4
|
-
const auth_1 = require("../auth/auth");
|
|
5
4
|
const sdk_1 = require("@contrail/sdk");
|
|
6
5
|
async function getWorkflowTemplateDefinitionsForAppVersion(options) {
|
|
7
6
|
return await new sdk_1.Entities().get({
|
|
@@ -55,26 +54,10 @@ async function installAppToOrg(app, configProperties) {
|
|
|
55
54
|
});
|
|
56
55
|
}
|
|
57
56
|
exports.installAppToOrg = installAppToOrg;
|
|
58
|
-
async function getAvailableApps(
|
|
59
|
-
const
|
|
60
|
-
const apps = [];
|
|
61
|
-
apps.push(...(await new sdk_1.Entities().get({
|
|
57
|
+
async function getAvailableApps() {
|
|
58
|
+
const apps = await new sdk_1.Entities().get({
|
|
62
59
|
entityName: 'app',
|
|
63
|
-
|
|
64
|
-
orgId,
|
|
65
|
-
private: true,
|
|
66
|
-
...options,
|
|
67
|
-
},
|
|
68
|
-
})));
|
|
69
|
-
apps.push(...(await new sdk_1.Entities().get({
|
|
70
|
-
entityName: 'app',
|
|
71
|
-
criteria: {
|
|
72
|
-
orgId,
|
|
73
|
-
private: false,
|
|
74
|
-
hidden: true,
|
|
75
|
-
...options,
|
|
76
|
-
},
|
|
77
|
-
})));
|
|
60
|
+
});
|
|
78
61
|
return apps;
|
|
79
62
|
}
|
|
80
63
|
exports.getAvailableApps = getAvailableApps;
|
package/dist/auth/login.d.ts
CHANGED
package/dist/auth/login.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.authUser = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const amazon_cognito_identity_js_1 = require("amazon-cognito-identity-js");
|
|
6
6
|
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
@@ -122,19 +122,9 @@ async function setAuthContext(user, params) {
|
|
|
122
122
|
(0, sdk_1.setConfig)(auth_1.Auth.config);
|
|
123
123
|
return refreshCurrentUserOrg();
|
|
124
124
|
}
|
|
125
|
-
async function getCurrentUser() {
|
|
126
|
-
return await sdk_1.Request.request('/users/current', {
|
|
127
|
-
method: 'GET',
|
|
128
|
-
headers: {
|
|
129
|
-
Accept: 'application/json',
|
|
130
|
-
'Content-Type': 'application/json',
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
exports.getCurrentUser = getCurrentUser;
|
|
135
125
|
async function refreshCurrentUserOrg() {
|
|
136
126
|
logger_1.default.debug('refreshing current user org...');
|
|
137
|
-
const currentUser = await getCurrentUser();
|
|
127
|
+
const currentUser = await (0, sdk_1.getCurrentUser)();
|
|
138
128
|
if (!currentUser) {
|
|
139
129
|
throw Error('Could not get current user');
|
|
140
130
|
}
|
|
@@ -15,7 +15,7 @@ class CreateApp extends basecommand_1.BaseCommand {
|
|
|
15
15
|
];
|
|
16
16
|
async run() {
|
|
17
17
|
await (0, login_1.authUser)();
|
|
18
|
-
const currentUser = await (0,
|
|
18
|
+
const currentUser = await (0, sdk_1.getCurrentUser)();
|
|
19
19
|
const username = currentUser.email.split('@')[0];
|
|
20
20
|
const defaultAppName = `Hello World by ${username}`;
|
|
21
21
|
const defaultAppIdentifier = `@${username}/hello-world`;
|
|
@@ -61,7 +61,6 @@ class CreateApp extends basecommand_1.BaseCommand {
|
|
|
61
61
|
name: answers.appName,
|
|
62
62
|
publisher: answers.appPublisher,
|
|
63
63
|
public: false,
|
|
64
|
-
hidden: false,
|
|
65
64
|
description: answers.appDescription,
|
|
66
65
|
frameworkVersion: 2,
|
|
67
66
|
};
|
|
@@ -5,6 +5,7 @@ const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
|
5
5
|
const login_1 = require("../../auth/login");
|
|
6
6
|
const basecommand_1 = require("../../basecommand");
|
|
7
7
|
const setup_template_1 = require("../../helpers/app/setup-template");
|
|
8
|
+
const sdk_1 = require("@contrail/sdk");
|
|
8
9
|
class CreateApp extends basecommand_1.BaseCommand {
|
|
9
10
|
static description = 'Create an app from a template';
|
|
10
11
|
static examples = [
|
|
@@ -13,7 +14,7 @@ class CreateApp extends basecommand_1.BaseCommand {
|
|
|
13
14
|
];
|
|
14
15
|
async run() {
|
|
15
16
|
await (0, login_1.authUser)();
|
|
16
|
-
const currentUser = await (0,
|
|
17
|
+
const currentUser = await (0, sdk_1.getCurrentUser)();
|
|
17
18
|
const username = currentUser.email.split('@')[0];
|
|
18
19
|
const defaultAppName = `Hello World by ${username}`;
|
|
19
20
|
const defaultAppIdentifier = `@${username}/hello-world`;
|
|
@@ -6,7 +6,6 @@ export default class InstallApp extends BaseCommand {
|
|
|
6
6
|
static flags: {
|
|
7
7
|
appIdentifier: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
8
|
installWorkflowTemplates: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
-
showHidden: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
9
|
};
|
|
11
10
|
run(): Promise<void>;
|
|
12
11
|
}
|
|
@@ -30,16 +30,11 @@ class InstallApp extends basecommand_1.BaseCommand {
|
|
|
30
30
|
description: '"Install the app\'s accompanying workflows"',
|
|
31
31
|
required: false,
|
|
32
32
|
}),
|
|
33
|
-
showHidden: core_1.Flags.boolean({
|
|
34
|
-
char: 'h',
|
|
35
|
-
description: 'Show the option to install public but hidden apps',
|
|
36
|
-
required: false,
|
|
37
|
-
}),
|
|
38
33
|
};
|
|
39
34
|
async run() {
|
|
40
35
|
const { flags } = await this.parse(InstallApp);
|
|
41
36
|
await (0, login_1.authUser)();
|
|
42
|
-
const apps = await (0, apps_1.getAvailableApps)(
|
|
37
|
+
const apps = await (0, apps_1.getAvailableApps)();
|
|
43
38
|
if (!apps.length) {
|
|
44
39
|
throw new Error('No apps are available to install...');
|
|
45
40
|
}
|
|
@@ -312,6 +312,9 @@ function reduceTypes(dataHolder) {
|
|
|
312
312
|
for (const type of dataHolder.types) {
|
|
313
313
|
const reducedTypeProperties = [];
|
|
314
314
|
for (const typeProperty of type.typeProperties) {
|
|
315
|
+
if (typeProperty.inherited) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
315
318
|
const reducedTypeProperty = {
|
|
316
319
|
slug: typeProperty.slug,
|
|
317
320
|
id: typeProperty.id,
|
|
@@ -325,10 +328,14 @@ function reduceTypes(dataHolder) {
|
|
|
325
328
|
formula: typeProperty.formula,
|
|
326
329
|
referencedTypePath: typeProperty.referencedTypePath,
|
|
327
330
|
referencedTypeRootSlug: typeProperty.referencedTypeRootSlug,
|
|
331
|
+
typePropertyUserListId: typeProperty.typePropertyUserListId,
|
|
332
|
+
referencedTypeRole: typeProperty.referencedTypeRole,
|
|
328
333
|
defaultValue: typeProperty.defaultValue,
|
|
329
334
|
propertyLevel: typeProperty.propertyLevel,
|
|
330
335
|
carryOverBehavior: typeProperty.carryOverBehavior,
|
|
331
336
|
carryOverDefault: typeProperty.carryOverDefault,
|
|
337
|
+
isUniqueProperty: typeProperty.isUniqueProperty,
|
|
338
|
+
isUniquePropertyInFamily: typeProperty.isUniquePropertyInFamily,
|
|
332
339
|
inherited: typeProperty.inherited,
|
|
333
340
|
formulaFunction: typeProperty.formulaFunction,
|
|
334
341
|
validationFunction: typeProperty.validationFunction,
|
|
@@ -55,6 +55,7 @@ async function mergeOptionSets(fileLocation) {
|
|
|
55
55
|
const currentOptionSets = currentOptionSetsFull.map((os) => Object.assign({}, {
|
|
56
56
|
name: os.name,
|
|
57
57
|
isArchived: os.isArchived,
|
|
58
|
+
slug: os.slug,
|
|
58
59
|
optionSet: os.optionSet,
|
|
59
60
|
}));
|
|
60
61
|
const currentOptionSetsByName = {};
|
|
@@ -101,9 +102,10 @@ function mergeOptionSet(existingOptionSet, uploadedOptionSet) {
|
|
|
101
102
|
for (const keyVal of existingOptionSet.optionSet) {
|
|
102
103
|
const value = keyVal.value;
|
|
103
104
|
const display = keyVal.display;
|
|
105
|
+
const disabled = keyVal?.disabled;
|
|
104
106
|
const found = merged.optionSet.find((keyVal) => keyVal.value === value);
|
|
105
107
|
if (!found) {
|
|
106
|
-
merged.optionSet.push({ value, display });
|
|
108
|
+
merged.optionSet.push({ value, display, disabled });
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -18,18 +18,20 @@ export declare function ParseOptionSetsToFolderStructure(args: any): Promise<voi
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function parseLabelsInColumn(uploadedOptionData: any): any[];
|
|
20
20
|
/**
|
|
21
|
-
* Expects a CSV file with an ignored header file and
|
|
21
|
+
* Expects a CSV file with an ignored header file and 4 columns:
|
|
22
22
|
* The first column contains the option set name (e.g. Gender)
|
|
23
23
|
* The second column contains the option keys (e.g. ge_men)
|
|
24
|
-
* The
|
|
24
|
+
* The third column contains the option display label (e.g. Men)
|
|
25
|
+
* The fourth column contains the disabled value (e.g. true / false)
|
|
25
26
|
*
|
|
26
27
|
* Sample file
|
|
27
|
-
* ignored header 1, ignored header 2, ignored header 3
|
|
28
|
-
* Gender, ge_men, Men
|
|
29
|
-
* Gender, ge_women, Women
|
|
30
|
-
* Brand, b_prada, Prada
|
|
31
|
-
* Brand, b_coach, Coach
|
|
28
|
+
* ignored header 1, ignored header 2, ignored header 3, ignored header 4
|
|
29
|
+
* Gender, ge_men, Men, true
|
|
30
|
+
* Gender, ge_women, Women, false
|
|
31
|
+
* Brand, b_prada, Prada, true
|
|
32
|
+
* Brand, b_coach, Coach, true
|
|
32
33
|
* @param uploadedOptionData
|
|
33
34
|
* @returns
|
|
34
35
|
*/
|
|
35
36
|
export declare function parseNameAndLabelAndKey(uploadedOptionData: any[]): any[];
|
|
37
|
+
export declare function parseDisabledValue(stringValue: string): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseNameAndLabelAndKey = exports.parseLabelsInColumn = exports.ParseOptionSetsToFolderStructure = void 0;
|
|
3
|
+
exports.parseDisabledValue = exports.parseNameAndLabelAndKey = exports.parseLabelsInColumn = exports.ParseOptionSetsToFolderStructure = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const path = tslib_1.__importStar(require("path"));
|
|
6
6
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
@@ -154,7 +154,7 @@ function parseLabelsInColumn(uploadedOptionData) {
|
|
|
154
154
|
const uniqueOptionSetValues = new Set();
|
|
155
155
|
for (const data of uploadedOptionData) {
|
|
156
156
|
const label = data[optionSetName];
|
|
157
|
-
uniqueOptionSetValues.add(label);
|
|
157
|
+
uniqueOptionSetValues.add(label.toString()); // Parse the value to string
|
|
158
158
|
}
|
|
159
159
|
const optionSet = [];
|
|
160
160
|
if (uniqueOptionSetValues) {
|
|
@@ -180,17 +180,18 @@ function parseLabelsInColumn(uploadedOptionData) {
|
|
|
180
180
|
}
|
|
181
181
|
exports.parseLabelsInColumn = parseLabelsInColumn;
|
|
182
182
|
/**
|
|
183
|
-
* Expects a CSV file with an ignored header file and
|
|
183
|
+
* Expects a CSV file with an ignored header file and 4 columns:
|
|
184
184
|
* The first column contains the option set name (e.g. Gender)
|
|
185
185
|
* The second column contains the option keys (e.g. ge_men)
|
|
186
|
-
* The
|
|
186
|
+
* The third column contains the option display label (e.g. Men)
|
|
187
|
+
* The fourth column contains the disabled value (e.g. true / false)
|
|
187
188
|
*
|
|
188
189
|
* Sample file
|
|
189
|
-
* ignored header 1, ignored header 2, ignored header 3
|
|
190
|
-
* Gender, ge_men, Men
|
|
191
|
-
* Gender, ge_women, Women
|
|
192
|
-
* Brand, b_prada, Prada
|
|
193
|
-
* Brand, b_coach, Coach
|
|
190
|
+
* ignored header 1, ignored header 2, ignored header 3, ignored header 4
|
|
191
|
+
* Gender, ge_men, Men, true
|
|
192
|
+
* Gender, ge_women, Women, false
|
|
193
|
+
* Brand, b_prada, Prada, true
|
|
194
|
+
* Brand, b_coach, Coach, true
|
|
194
195
|
* @param uploadedOptionData
|
|
195
196
|
* @returns
|
|
196
197
|
*/
|
|
@@ -198,7 +199,7 @@ function parseNameAndLabelAndKey(uploadedOptionData) {
|
|
|
198
199
|
const optionSets = {};
|
|
199
200
|
for (const csvRow of uploadedOptionData) {
|
|
200
201
|
const row = Object.values(csvRow);
|
|
201
|
-
if (row.length
|
|
202
|
+
if (!(row.length === 4 || row.length === 3) || row.some((x) => !x)) {
|
|
202
203
|
console.log('skipping bad row', JSON.stringify(row, null, 2));
|
|
203
204
|
continue;
|
|
204
205
|
}
|
|
@@ -208,15 +209,23 @@ function parseNameAndLabelAndKey(uploadedOptionData) {
|
|
|
208
209
|
}
|
|
209
210
|
const key = row[1];
|
|
210
211
|
const label = row[2];
|
|
211
|
-
|
|
212
|
+
let disabled = false;
|
|
213
|
+
if (row.length === 4) {
|
|
214
|
+
// as bad row is skipped above, testString is always a string
|
|
215
|
+
const testString = row[3];
|
|
216
|
+
disabled = parseDisabledValue(testString);
|
|
217
|
+
}
|
|
218
|
+
optionSets[optionSetName][key] = { label, disabled };
|
|
212
219
|
}
|
|
213
220
|
const result = [];
|
|
214
221
|
for (const optionSetName of Object.keys(optionSets)) {
|
|
215
222
|
const optionSet = [];
|
|
216
|
-
for (const [key,
|
|
223
|
+
for (const [key, obj] of Object.entries(optionSets[optionSetName])) {
|
|
224
|
+
const optionObj = obj; // Add type annotation
|
|
217
225
|
optionSet.push({
|
|
218
|
-
value: key,
|
|
219
|
-
display:
|
|
226
|
+
value: key.toString(),
|
|
227
|
+
display: optionObj?.label,
|
|
228
|
+
disabled: optionObj?.disabled, // Use the type annotated variable
|
|
220
229
|
});
|
|
221
230
|
}
|
|
222
231
|
const reducedOptionSet = {
|
|
@@ -230,3 +239,13 @@ function parseNameAndLabelAndKey(uploadedOptionData) {
|
|
|
230
239
|
return result;
|
|
231
240
|
}
|
|
232
241
|
exports.parseNameAndLabelAndKey = parseNameAndLabelAndKey;
|
|
242
|
+
function parseDisabledValue(stringValue) {
|
|
243
|
+
const testString = /^true$/i.test(stringValue?.trim());
|
|
244
|
+
const isDisabledFalse = testString === false;
|
|
245
|
+
const isDisabledTrue = testString === true;
|
|
246
|
+
if (!isDisabledFalse && !isDisabledTrue) {
|
|
247
|
+
console.warn(`Invalid value (${stringValue}) for disabled attribute. Must be either "true" or "false"`);
|
|
248
|
+
}
|
|
249
|
+
return isDisabledTrue;
|
|
250
|
+
}
|
|
251
|
+
exports.parseDisabledValue = parseDisabledValue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.uploadOptionSets = void 0;
|
|
3
|
+
exports.parseOptionSetValuesToString = exports.uploadOptionSets = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const sdk_1 = require("@contrail/sdk");
|
|
6
6
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
@@ -11,6 +11,7 @@ const core_1 = require("@oclif/core");
|
|
|
11
11
|
const basecommand_1 = require("../../basecommand");
|
|
12
12
|
const logger_1 = tslib_1.__importDefault(require("../../logger"));
|
|
13
13
|
const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
|
|
14
|
+
const lodash_1 = require("lodash");
|
|
14
15
|
class UploadOptionSets extends basecommand_1.BaseCommand {
|
|
15
16
|
static description = 'Upload OptionSets for your Org. OptionSets are loaded from .yml files in the option-sets directory.';
|
|
16
17
|
static examples = [
|
|
@@ -34,7 +35,7 @@ exports.default = UploadOptionSets;
|
|
|
34
35
|
async function uploadOptionSets(dir) {
|
|
35
36
|
try {
|
|
36
37
|
const userConfig = await (0, login_1.authUser)();
|
|
37
|
-
logger_1.default.info(`${chalk_1.default.blue.bold('START')} Getting all types for
|
|
38
|
+
logger_1.default.info(`${chalk_1.default.blue.bold('START')} Getting all types for org ${userConfig.orgSlug}`);
|
|
38
39
|
logger_1.default.info(`${chalk_1.default.yellow.bold('OPTION SETS')} Uploading`);
|
|
39
40
|
await uploadOS(dir);
|
|
40
41
|
logger_1.default.info(`${chalk_1.default.yellow.bold('OPTION SETS')} Uploaded`);
|
|
@@ -60,6 +61,17 @@ function loadOptionSets(fromDir) {
|
|
|
60
61
|
throw new Error(`ERROR PARSING YAML: ${e}`);
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
function parseOptionSetValuesToString(optionSet) {
|
|
65
|
+
return (0, lodash_1.cloneDeepWith)(optionSet, (value) => {
|
|
66
|
+
// If the value is an object or array, let cloneDeepWith handle it
|
|
67
|
+
if ((0, lodash_1.isObject)(value) || (0, lodash_1.isArray)(value)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
// Otherwise, convert the value to string
|
|
71
|
+
return String(value);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
exports.parseOptionSetValuesToString = parseOptionSetValuesToString;
|
|
63
75
|
async function uploadOS(dir) {
|
|
64
76
|
const optionSets = loadOptionSets(dir);
|
|
65
77
|
const currentOptionSets = await new sdk_1.Entities().get({
|
|
@@ -83,18 +95,20 @@ async function uploadOS(dir) {
|
|
|
83
95
|
logger_1.default.info(`No OptionSet for: ${optionSet.slug}`);
|
|
84
96
|
continue;
|
|
85
97
|
}
|
|
86
|
-
|
|
98
|
+
const parsedOptionSet = parseOptionSetValuesToString(optionSet.optionSet);
|
|
99
|
+
logger_1.default.info(`Creating: ${JSON.stringify(parsedOptionSet, null, 2)}`);
|
|
87
100
|
await new sdk_1.Entities().create({
|
|
88
101
|
entityName: 'type-property-option-set',
|
|
89
102
|
object: {
|
|
90
103
|
name: optionSet.name,
|
|
91
104
|
slug: optionSet.slug,
|
|
92
|
-
optionSet:
|
|
105
|
+
optionSet: parsedOptionSet,
|
|
93
106
|
isArchived: optionSet.isArchived,
|
|
94
107
|
},
|
|
95
108
|
});
|
|
96
109
|
}
|
|
97
110
|
else {
|
|
111
|
+
const parsedOptionSet = parseOptionSetValuesToString(optionSet.optionSet);
|
|
98
112
|
logger_1.default.info(`Updating: ${optionSet.slug}`);
|
|
99
113
|
await new sdk_1.Entities().update({
|
|
100
114
|
entityName: 'type-property-option-set',
|
|
@@ -102,7 +116,7 @@ async function uploadOS(dir) {
|
|
|
102
116
|
object: {
|
|
103
117
|
name: optionSet.name,
|
|
104
118
|
slug: optionSet.slug,
|
|
105
|
-
optionSet:
|
|
119
|
+
optionSet: parsedOptionSet,
|
|
106
120
|
isArchived: optionSet.isArchived,
|
|
107
121
|
},
|
|
108
122
|
});
|
|
@@ -112,19 +126,18 @@ async function uploadOS(dir) {
|
|
|
112
126
|
logger_1.default.error(`Error Uploading OptionSets: ${err}`);
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
|
-
for (const currentOptionSet of currentOptionSets) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
129
|
+
// for (const currentOptionSet of currentOptionSets) {
|
|
130
|
+
// try {
|
|
131
|
+
// const found = newOptionSetsBySlug[currentOptionSet.slug];
|
|
132
|
+
// if (!found) {
|
|
133
|
+
// logger.info(`Deleting: ${currentOptionSet.slug}`);
|
|
134
|
+
// await new Entities().delete({
|
|
135
|
+
// entityName: 'type-property-option-set',
|
|
136
|
+
// id: currentOptionSet.id,
|
|
137
|
+
// });
|
|
138
|
+
// }
|
|
139
|
+
// } catch (err) {
|
|
140
|
+
// logger.error(`Error Deleting OptionSets: ${err}`);
|
|
141
|
+
// }
|
|
142
|
+
// }
|
|
130
143
|
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { AppDeclaration } from '../../interfaces';
|
|
2
2
|
export interface AppConfigValidationError {
|
|
3
|
-
|
|
3
|
+
errors: string[];
|
|
4
|
+
warnings: string[];
|
|
4
5
|
}
|
|
6
|
+
export declare const ERROR_MESSAGES: {
|
|
7
|
+
missingVersionField: string;
|
|
8
|
+
invalidVersionField: string;
|
|
9
|
+
invalidAppVersion: string;
|
|
10
|
+
accessGrantsArray: string;
|
|
11
|
+
invalidOrgSlug: string;
|
|
12
|
+
emptyPermissionsArray: string;
|
|
13
|
+
invalidVisibility: string;
|
|
14
|
+
deprecatedHiddenField: string;
|
|
15
|
+
ignoredAccessGrants: string;
|
|
16
|
+
invalidPermission: (permission: string) => string;
|
|
17
|
+
invalidAccessGrants: (error: string) => string;
|
|
18
|
+
};
|
|
5
19
|
export declare function validateAppConfig(appConfig: AppDeclaration): void;
|
|
6
20
|
export declare function appVersionValidator(appDeclaration: AppDeclaration): AppConfigValidationError;
|
|
7
21
|
export declare function visibilityValidator(appDeclaration: AppDeclaration): AppConfigValidationError;
|
|
22
|
+
export declare function validateAccessGrants(appDeclaration: AppDeclaration): AppConfigValidationError;
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.visibilityValidator = exports.appVersionValidator = exports.validateAppConfig = void 0;
|
|
4
|
-
const
|
|
3
|
+
exports.validateAccessGrants = exports.visibilityValidator = exports.appVersionValidator = exports.validateAppConfig = exports.ERROR_MESSAGES = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const interfaces_1 = require("../../interfaces");
|
|
6
|
+
const logger_1 = tslib_1.__importDefault(require("../../logger"));
|
|
7
|
+
const VALIDATORS = [appVersionValidator, visibilityValidator, validateAccessGrants];
|
|
8
|
+
exports.ERROR_MESSAGES = {
|
|
9
|
+
missingVersionField: 'Missing "version" field". Semantic App Version looks like "1.0.0". Use "contrail app version [patch|minor|major]" to update the app version.',
|
|
10
|
+
invalidVersionField: '"version" field is not a string. Expecting a version like "1.0.0". Manually update the "version" field to be a string in the app.yml file.',
|
|
11
|
+
invalidAppVersion: 'Invalid app version. Semantic App Version looks like "1.0.0". Use "contrail app version [patch|minor|major]" to update the app version.',
|
|
12
|
+
accessGrantsArray: 'accessGrants must be an array.',
|
|
13
|
+
invalidOrgSlug: 'Invalid or missing orgSlug in one of the accessGrants objects.',
|
|
14
|
+
emptyPermissionsArray: 'Permissions must be a non-empty array in one of the accessGrants objects.',
|
|
15
|
+
invalidVisibility: 'Invalid visibility. Visibility must be "public" or "private".',
|
|
16
|
+
deprecatedHiddenField: 'The "hidden" field is deprecated.',
|
|
17
|
+
ignoredAccessGrants: 'App is set to public. accessGrants will be ignored.',
|
|
18
|
+
invalidPermission: (permission) => `Invalid permission found in one of the accessGrants objects: ${permission}.`,
|
|
19
|
+
invalidAccessGrants: (error) => `Invalid accessGrants. ${error}`,
|
|
20
|
+
};
|
|
5
21
|
function validateAppConfig(appConfig) {
|
|
6
22
|
const errors = [];
|
|
23
|
+
const warnings = [];
|
|
7
24
|
for (const validator of VALIDATORS) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
const validation = validator(appConfig);
|
|
26
|
+
errors.push(...validation.errors);
|
|
27
|
+
warnings.push(...validation.warnings);
|
|
28
|
+
}
|
|
29
|
+
if (warnings.length) {
|
|
30
|
+
logger_1.default.warn(warnings);
|
|
12
31
|
}
|
|
13
32
|
if (errors.length) {
|
|
14
33
|
throw new Error(`Invalid app config: \n ${errors.map((x) => x.error).join('\n')}`);
|
|
@@ -16,36 +35,66 @@ function validateAppConfig(appConfig) {
|
|
|
16
35
|
}
|
|
17
36
|
exports.validateAppConfig = validateAppConfig;
|
|
18
37
|
function appVersionValidator(appDeclaration) {
|
|
38
|
+
const errors = [];
|
|
39
|
+
const warnings = [];
|
|
19
40
|
if (!appDeclaration.version) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
};
|
|
41
|
+
errors.push(exports.ERROR_MESSAGES.missingVersionField);
|
|
42
|
+
return { errors, warnings };
|
|
23
43
|
}
|
|
24
44
|
if (typeof appDeclaration.version !== 'string') {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
45
|
+
errors.push(exports.ERROR_MESSAGES.invalidVersionField);
|
|
46
|
+
return { errors, warnings };
|
|
28
47
|
}
|
|
29
48
|
const parts = appDeclaration.version.split('.');
|
|
30
49
|
if (parts.length !== 3) {
|
|
31
|
-
|
|
32
|
-
error: 'Invalid app version. Semantic App Version looks like "1.0.0". Use "contrail app version [patch|minor|major]" to update the app version.',
|
|
33
|
-
};
|
|
50
|
+
errors.push(exports.ERROR_MESSAGES.invalidAppVersion);
|
|
34
51
|
}
|
|
52
|
+
return { errors, warnings };
|
|
35
53
|
}
|
|
36
54
|
exports.appVersionValidator = appVersionValidator;
|
|
37
55
|
function visibilityValidator(appDeclaration) {
|
|
56
|
+
const errors = [];
|
|
57
|
+
const warnings = [];
|
|
38
58
|
if (!['public', 'private'].includes(appDeclaration.visibility)) {
|
|
39
|
-
|
|
40
|
-
error: 'Invalid visibility. Visibility must be "public" or "private".',
|
|
41
|
-
};
|
|
59
|
+
errors.push(exports.ERROR_MESSAGES.invalidVisibility);
|
|
42
60
|
}
|
|
43
|
-
if (appDeclaration.
|
|
44
|
-
|
|
45
|
-
appDeclaration.hidden === true) {
|
|
46
|
-
return {
|
|
47
|
-
error: 'Apps can not be private and hidden.',
|
|
48
|
-
};
|
|
61
|
+
if (appDeclaration.hasOwnProperty('hidden')) {
|
|
62
|
+
warnings.push(exports.ERROR_MESSAGES.deprecatedHiddenField);
|
|
49
63
|
}
|
|
64
|
+
return { errors, warnings };
|
|
50
65
|
}
|
|
51
66
|
exports.visibilityValidator = visibilityValidator;
|
|
67
|
+
function validateAccessGrants(appDeclaration) {
|
|
68
|
+
const errors = [];
|
|
69
|
+
const warnings = [];
|
|
70
|
+
if (appDeclaration.visibility === 'public' && appDeclaration.hasOwnProperty('accessGrants')) {
|
|
71
|
+
warnings.push(exports.ERROR_MESSAGES.ignoredAccessGrants);
|
|
72
|
+
}
|
|
73
|
+
if (appDeclaration.visibility === 'private' && appDeclaration.hasOwnProperty('accessGrants')) {
|
|
74
|
+
const accessGrantsArray = appDeclaration.accessGrants;
|
|
75
|
+
if (!Array.isArray(accessGrantsArray)) {
|
|
76
|
+
errors.push(exports.ERROR_MESSAGES.accessGrantsArray);
|
|
77
|
+
return { errors, warnings };
|
|
78
|
+
}
|
|
79
|
+
for (const accessGrants of accessGrantsArray) {
|
|
80
|
+
if (!accessGrants?.orgSlug || accessGrants?.orgSlug.trim() === '') {
|
|
81
|
+
errors.push(exports.ERROR_MESSAGES.invalidOrgSlug);
|
|
82
|
+
}
|
|
83
|
+
if (!Array.isArray(accessGrants?.permissions) || accessGrants?.permissions.length === 0) {
|
|
84
|
+
errors.push(exports.ERROR_MESSAGES.emptyPermissionsArray);
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(accessGrants?.permissions)) {
|
|
87
|
+
for (const permission of accessGrants.permissions) {
|
|
88
|
+
if (!isValidPermission(permission)) {
|
|
89
|
+
errors.push(exports.ERROR_MESSAGES.invalidPermission(permission));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { errors, warnings };
|
|
96
|
+
}
|
|
97
|
+
exports.validateAccessGrants = validateAccessGrants;
|
|
98
|
+
function isValidPermission(permission) {
|
|
99
|
+
return Object.values(interfaces_1.AppAccessPermission).includes(permission);
|
|
100
|
+
}
|