@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.
Files changed (34) hide show
  1. package/README.md +2 -3
  2. package/dist/api/apps.d.ts +1 -1
  3. package/dist/api/apps.js +3 -20
  4. package/dist/auth/login.d.ts +0 -1
  5. package/dist/auth/login.js +2 -12
  6. package/dist/commands/app/create.js +1 -2
  7. package/dist/commands/app/init.js +2 -1
  8. package/dist/commands/app/install.d.ts +0 -1
  9. package/dist/commands/app/install.js +1 -6
  10. package/dist/commands/types/getAll.js +7 -0
  11. package/dist/commands/types/mergeOptionSets.js +3 -1
  12. package/dist/commands/types/parseOptionSetsLocalFiles.d.ts +9 -7
  13. package/dist/commands/types/parseOptionSetsLocalFiles.js +33 -14
  14. package/dist/commands/types/uploadOptionSets.d.ts +1 -0
  15. package/dist/commands/types/uploadOptionSets.js +33 -20
  16. package/dist/helpers/app/app-config-validators.d.ts +16 -1
  17. package/dist/helpers/app/app-config-validators.js +73 -24
  18. package/dist/helpers/app/app-config-validators.spec.js +225 -20
  19. package/dist/helpers/app/app-deployer.js +12 -7
  20. package/dist/helpers/app/app-template-parsers.d.ts +4 -1
  21. package/dist/helpers/app/app-template-parsers.js +28 -1
  22. package/dist/helpers/types/parseOptionSetsLocalFiles.spec.d.ts +1 -0
  23. package/dist/helpers/types/parseOptionSetsLocalFiles.spec.js +285 -0
  24. package/dist/helpers/types/type-uploader.js +15 -7
  25. package/dist/helpers/types/uploadOptionSets.spec.d.ts +1 -0
  26. package/dist/helpers/types/uploadOptionSets.spec.js +19 -0
  27. package/dist/helpers/workflow-template-definitions/workflow-template-definition-deployer.js +4 -0
  28. package/dist/helpers/workflow-templates/helper.js +4 -0
  29. package/dist/interfaces.d.ts +26 -1
  30. package/dist/interfaces.js +10 -1
  31. package/dist/templates/typescript/app.yml +10 -2
  32. package/dist/templates/typescript/package-lock.json +13908 -0
  33. package/oclif.manifest.json +1 -9
  34. 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.3.3 darwin-arm64 node-v18.13.0
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] [-h]
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
 
@@ -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(options?: any): Promise<App[]>;
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(options) {
59
- const orgId = options?.orgId ?? auth_1.Auth.config.orgId;
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
- criteria: {
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;
@@ -1,3 +1,2 @@
1
1
  import { AuthContext } from './auth';
2
2
  export declare function authUser(): Promise<AuthContext>;
3
- export declare function getCurrentUser(): Promise<any>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCurrentUser = exports.authUser = void 0;
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, login_1.getCurrentUser)();
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, login_1.getCurrentUser)();
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)({ hidden: flags.showHidden });
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 3 columns:
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 second column contains the option display label (e.g. Men)
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 3 columns:
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 second column contains the option display label (e.g. Men)
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 !== 3 || row.some((x) => !x)) {
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
- optionSets[optionSetName][key] = label;
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, value] of Object.entries(optionSets[optionSetName])) {
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: value,
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;
@@ -8,3 +8,4 @@ export default class UploadOptionSets extends BaseCommand {
8
8
  run(): Promise<void>;
9
9
  }
10
10
  export declare function uploadOptionSets(dir: string): Promise<void>;
11
+ export declare function parseOptionSetValuesToString(optionSet: any): any;
@@ -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 orgId: ${userConfig.orgId}`);
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
- logger_1.default.info(`Creating: ${JSON.stringify(optionSet, null, 2)}`);
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: optionSet.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: optionSet.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
- try {
117
- const found = newOptionSetsBySlug[currentOptionSet.slug];
118
- if (!found) {
119
- logger_1.default.info(`Deleting: ${currentOptionSet.slug}`);
120
- await new sdk_1.Entities().delete({
121
- entityName: 'type-property-option-set',
122
- id: currentOptionSet.id,
123
- });
124
- }
125
- }
126
- catch (err) {
127
- logger_1.default.error(`Error Deleting OptionSets: ${err}`);
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
- error: string;
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 VALIDATORS = [appVersionValidator, visibilityValidator];
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 error = validator(appConfig);
9
- if (error) {
10
- errors.push(error);
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
- return {
21
- error: 'Missing "version" field". Semantic App Version looks like "1.0.0". Use "contrail app version [patch|minor|major]" to update the app version.',
22
- };
41
+ errors.push(exports.ERROR_MESSAGES.missingVersionField);
42
+ return { errors, warnings };
23
43
  }
24
44
  if (typeof appDeclaration.version !== 'string') {
25
- return {
26
- error: '"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.',
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
- return {
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
- return {
40
- error: 'Invalid visibility. Visibility must be "public" or "private".',
41
- };
59
+ errors.push(exports.ERROR_MESSAGES.invalidVisibility);
42
60
  }
43
- if (appDeclaration.visibility === 'private' &&
44
- Object.prototype.hasOwnProperty.call(appDeclaration, 'hidden') &&
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
+ }