@contrail/cli 2.0.4 → 2.0.6

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 CHANGED
@@ -15,7 +15,7 @@ $ npm install -g @contrail/cli
15
15
  $ contrail COMMAND
16
16
  running command...
17
17
  $ contrail (--version)
18
- @contrail/cli/2.0.4 darwin-arm64 node-v18.13.0
18
+ @contrail/cli/2.0.6 darwin-arm64 node-v18.13.0
19
19
  $ contrail --help [COMMAND]
20
20
  USAGE
21
21
  $ contrail COMMAND
@@ -40,6 +40,8 @@ To get instructions on how to set up autocomplete for your shell, run `contrail
40
40
  * [`contrail config list`](#contrail-config-list)
41
41
  * [`contrail config remove`](#contrail-config-remove)
42
42
  * [`contrail config set`](#contrail-config-set)
43
+ * [`contrail groups export`](#contrail-groups-export)
44
+ * [`contrail groups upload GROUPSFILE`](#contrail-groups-upload-groupsfile)
43
45
  * [`contrail help [COMMANDS]`](#contrail-help-commands)
44
46
  * [`contrail instance export`](#contrail-instance-export)
45
47
  * [`contrail instance import`](#contrail-instance-import)
@@ -137,9 +139,10 @@ Install an app from the marketplace
137
139
 
138
140
  ```
139
141
  USAGE
140
- $ contrail app install [-i <value>] [-w]
142
+ $ contrail app install [-i <value>] [-w] [-h]
141
143
 
142
144
  FLAGS
145
+ -h, --showHidden Show the option to install public but hidden apps
143
146
  -i, --appIdentifier=<value> The identifier of the app to install
144
147
  -w, --installWorkflowTemplates Install the app's accompanying workflows
145
148
 
@@ -322,6 +325,43 @@ EXAMPLES
322
325
  $ contrail config set --orgId 1234
323
326
  ```
324
327
 
328
+ ## `contrail groups export`
329
+
330
+ Export all groups in the org.
331
+
332
+ ```
333
+ USAGE
334
+ $ contrail groups export [-d <value>] [--overwrite]
335
+
336
+ FLAGS
337
+ -d, --outDir=<value> [default: groups.yml] The location for the file to be saved
338
+ --overwrite Replaces existing files if they exist
339
+
340
+ DESCRIPTION
341
+ Export all groups in the org.
342
+
343
+ EXAMPLES
344
+ $ contrail groups export
345
+ ```
346
+
347
+ ## `contrail groups upload GROUPSFILE`
348
+
349
+ Import all groups in the groups.yml file
350
+
351
+ ```
352
+ USAGE
353
+ $ contrail groups upload GROUPSFILE
354
+
355
+ ARGUMENTS
356
+ GROUPSFILE The .yml file containing the groups
357
+
358
+ DESCRIPTION
359
+ Import all groups in the groups.yml file
360
+
361
+ EXAMPLES
362
+ $ contrail groups import <GROUPS_FILE>
363
+ ```
364
+
325
365
  ## `contrail help [COMMANDS]`
326
366
 
327
367
  Display help for contrail.
@@ -720,11 +760,11 @@ Get all Types and associated data for your Org
720
760
 
721
761
  ```
722
762
  USAGE
723
- $ contrail types getAll [--outDir <value>] [--overwrite]
763
+ $ contrail types getAll [-d <value>] [--overwrite]
724
764
 
725
765
  FLAGS
726
- --outDir=<value> [default: .] The directory to which to export the type data. Defaults to the current directory
727
- --overwrite Replaces any existing export files in outDir
766
+ -d, --outDir=<value> [default: .] The directory to which to export the type data. Defaults to the current directory
767
+ --overwrite Replaces any existing export files in outDir
728
768
 
729
769
  DESCRIPTION
730
770
  Get all Types and associated data for your Org
@@ -1048,11 +1088,11 @@ Export all users in the org.
1048
1088
 
1049
1089
  ```
1050
1090
  USAGE
1051
- $ contrail users export [-f <value>] [--overwrite]
1091
+ $ contrail users export [-d <value>] [--overwrite]
1052
1092
 
1053
1093
  FLAGS
1054
- -f, --fileOut=<value> The location for the file to be saved
1055
- --overwrite Replaces existing files if they exist
1094
+ -d, --outDir=<value> [default: users.yml] The location for the file to be saved
1095
+ --overwrite Replaces existing files if they exist
1056
1096
 
1057
1097
  DESCRIPTION
1058
1098
  Export all users in the org.
@@ -10,4 +10,4 @@ export declare function upgradeAppVersion(appOrg: any, appVersionId: any): Promi
10
10
  export declare function deleteAppOrg(appOrgId: string): Promise<AppOrg>;
11
11
  export declare function getInstalledApps(): Promise<AppOrg[]>;
12
12
  export declare function installAppToOrg(app: any, configProperties: any): Promise<AppOrg>;
13
- export declare function getAvailableApps(): Promise<App[]>;
13
+ export declare function getAvailableApps(options?: any): Promise<App[]>;
package/dist/api/apps.js CHANGED
@@ -74,7 +74,7 @@ async function installAppToOrg(app, configProperties) {
74
74
  });
75
75
  }
76
76
  exports.installAppToOrg = installAppToOrg;
77
- async function getAvailableApps() {
77
+ async function getAvailableApps(options) {
78
78
  const orgId = auth_1.Auth.config.orgId;
79
79
  const apps = [];
80
80
  apps.push(...await new sdk_1.Entities().get({
@@ -89,6 +89,7 @@ async function getAvailableApps() {
89
89
  criteria: {
90
90
  orgId,
91
91
  private: false,
92
+ ...options
92
93
  },
93
94
  }));
94
95
  return apps;
@@ -6,6 +6,7 @@ export default class InstallApp extends Command {
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>;
9
10
  };
10
11
  run(): Promise<void>;
11
12
  }
@@ -10,9 +10,9 @@ const apps_1 = require("../../api/apps");
10
10
  const workflow_template_definition_deployer_1 = require("../../helpers/workflow-template-definitions/workflow-template-definition-deployer");
11
11
  class InstallApp extends core_1.Command {
12
12
  async run() {
13
- const { args, flags } = await this.parse(InstallApp);
13
+ const { flags } = await this.parse(InstallApp);
14
14
  await (0, login_1.authUser)();
15
- const apps = await (0, apps_1.getAvailableApps)();
15
+ const apps = await (0, apps_1.getAvailableApps)({ hidden: flags.showHidden });
16
16
  if (!apps.length) {
17
17
  throw new Error('No apps are available to install...');
18
18
  }
@@ -94,6 +94,7 @@ InstallApp.strict = true;
94
94
  InstallApp.flags = {
95
95
  appIdentifier: core_1.Flags.string({ char: 'i', description: 'The identifier of the app to install', required: false }),
96
96
  installWorkflowTemplates: core_1.Flags.boolean({ char: 'w', description: 'Install the app\'s accompanying workflows', required: false }),
97
+ showHidden: core_1.Flags.boolean({ char: 'h', description: 'Show the option to install public but hidden apps', required: false }),
97
98
  };
98
99
  async function getConfigProperties(app) {
99
100
  const config = {};
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export declare const DEFAULT_FILE_LOCATION = "groups.yml";
3
+ export default class ExportGroups extends Command {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ outDir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
+ overwrite: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
12
+ export declare function exportGroups(fileLocation: string, overwrite: boolean): Promise<void>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exportGroups = exports.DEFAULT_FILE_LOCATION = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const sdk_1 = require("@contrail/sdk");
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ const fs_1 = tslib_1.__importDefault(require("fs"));
9
+ const login_1 = require("../../auth/login");
10
+ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
11
+ const core_1 = require("@oclif/core");
12
+ const file_helpers_1 = require("../../helpers/file-helpers");
13
+ exports.DEFAULT_FILE_LOCATION = 'groups.yml';
14
+ class ExportGroups extends core_1.Command {
15
+ async run() {
16
+ var _a;
17
+ const { flags } = await this.parse(ExportGroups);
18
+ try {
19
+ await (0, login_1.authUser)();
20
+ this.log('%s Getting Groups.', chalk_1.default.blue.bold('START'));
21
+ await exportGroups((_a = flags.outDir) !== null && _a !== void 0 ? _a : exports.DEFAULT_FILE_LOCATION, flags.overwrite);
22
+ this.log('%s Done', chalk_1.default.yellow.bold('GROUPS'));
23
+ }
24
+ catch (e) {
25
+ this.log(`%s Groups download Failed. ${e}`, chalk_1.default.red.bold('FAIL'));
26
+ return;
27
+ }
28
+ this.log('%s Groups download Complete.', chalk_1.default.green.bold('COMPLETE'));
29
+ }
30
+ }
31
+ exports.default = ExportGroups;
32
+ ExportGroups.description = 'Export all groups in the org.';
33
+ ExportGroups.examples = [
34
+ `$ contrail groups export
35
+ `,
36
+ ];
37
+ ExportGroups.flags = {
38
+ outDir: core_1.Flags.string({ char: 'd', description: 'The location for the file to be saved', required: false, default: exports.DEFAULT_FILE_LOCATION }),
39
+ overwrite: core_1.Flags.boolean({ description: 'Replaces existing files if they exist', required: false, default: false })
40
+ };
41
+ async function exportGroups(fileLocation, overwrite) {
42
+ const allGroups = await new sdk_1.Entities().get({
43
+ entityName: 'group', relations: ['groupPrincipals']
44
+ });
45
+ const allUsersInOrg = await new sdk_1.Entities().get({
46
+ entityName: 'user-org',
47
+ });
48
+ const groups = [];
49
+ for (const group of allGroups) {
50
+ const groupPrincipals = getGroupPrincipals(group, allGroups, allUsersInOrg);
51
+ groups.push({
52
+ name: group.name,
53
+ groupPrincipals,
54
+ });
55
+ }
56
+ await saveData(groups, fileLocation, overwrite);
57
+ }
58
+ exports.exportGroups = exportGroups;
59
+ async function saveData(data, fileLocation, overwrite) {
60
+ const yamlStr = js_yaml_1.default.dump(data);
61
+ const CURRENT_DIR = process.cwd();
62
+ const pathToWrite = path.resolve(path.join(CURRENT_DIR, fileLocation));
63
+ if (fs_1.default.existsSync(pathToWrite)) {
64
+ await (0, file_helpers_1.safeFileOverwrite)(pathToWrite, overwrite);
65
+ }
66
+ fs_1.default.writeFileSync(pathToWrite, yamlStr, 'utf8');
67
+ }
68
+ function getGroupPrincipals(group, allGroups, orgUsers) {
69
+ const principals = [];
70
+ for (const principal of group.groupPrincipals) {
71
+ const [type, id] = principal.principalReference.split(':');
72
+ if (type === 'user') {
73
+ const user = orgUsers.find(x => x.user.id === id);
74
+ if (user) {
75
+ principals.push({
76
+ type,
77
+ email: user.user.email
78
+ });
79
+ }
80
+ else {
81
+ console.log('couldn\'t find user', id);
82
+ }
83
+ }
84
+ else if (type === 'group') {
85
+ const nestedGroup = allGroups.find(x => x.id === id);
86
+ principals.push({
87
+ type: 'group',
88
+ name: nestedGroup.name
89
+ });
90
+ }
91
+ }
92
+ return principals;
93
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ImportGroups extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ groupsFile: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
10
+ export declare function uploadGroups(fileLocation: string): Promise<void>;
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadGroups = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const sdk_1 = require("@contrail/sdk");
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ const fs_1 = tslib_1.__importDefault(require("fs"));
9
+ const login_1 = require("../../auth/login");
10
+ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
11
+ const core_1 = require("@oclif/core");
12
+ class ImportGroups extends core_1.Command {
13
+ async run() {
14
+ const { args } = await this.parse(ImportGroups);
15
+ try {
16
+ await (0, login_1.authUser)();
17
+ this.log('%s Uploading Groups.', chalk_1.default.blue.bold('START'));
18
+ this.log('%s Uploading', chalk_1.default.yellow.bold('GROUP'));
19
+ await uploadGroups(args.groupsFile);
20
+ this.log('%s Uploaded', chalk_1.default.yellow.bold('GROUP'));
21
+ }
22
+ catch (e) {
23
+ this.log(`%s Group Upload Failed. ${e}`, chalk_1.default.red.bold('FAIL'));
24
+ return;
25
+ }
26
+ this.log('%s Group Upload Complete.', chalk_1.default.green.bold('COMPLETE'));
27
+ }
28
+ }
29
+ exports.default = ImportGroups;
30
+ ImportGroups.description = 'Import all groups in the groups.yml file';
31
+ ImportGroups.examples = [
32
+ `$ contrail groups import <GROUPS_FILE>
33
+ `,
34
+ ];
35
+ ImportGroups.args = {
36
+ groupsFile: core_1.Args.string({ description: 'The .yml file containing the groups', required: true }),
37
+ };
38
+ function getGroupsFile(fileLocation) {
39
+ const CURRENT_DIR = process.cwd();
40
+ try {
41
+ const fullPath = path.join(CURRENT_DIR, fileLocation);
42
+ const doc = js_yaml_1.default.load(fs_1.default.readFileSync(fullPath, 'utf8'));
43
+ return doc;
44
+ }
45
+ catch (e) {
46
+ throw new Error(`ERROR PARSING YAML: ${e}`);
47
+ }
48
+ }
49
+ async function uploadGroups(fileLocation) {
50
+ const existingGroups = await new sdk_1.Entities().get({
51
+ entityName: 'group', relations: ['groupPrincipals']
52
+ });
53
+ const groupsToImport = getGroupsFile(fileLocation);
54
+ const orgUsers = await new sdk_1.Entities().get({
55
+ entityName: 'user-org',
56
+ });
57
+ const toDelete = existingGroups.filter(x => !groupsToImport.map(y => y.name).includes(x.name));
58
+ const toCreate = groupsToImport.filter(x => !existingGroups.map(y => y.name).includes(x.name));
59
+ const toUpdate = existingGroups.filter(x => groupsToImport.map(y => y.name).includes(x.name));
60
+ const createdGroups = [];
61
+ // it's important to create all the groups first, to resolve any downstream principal dependencies
62
+ for (const group of toCreate) {
63
+ if (!group.groupPrincipals || !group.name) {
64
+ console.log('skipping invalid group');
65
+ }
66
+ createdGroups.push(await new sdk_1.Entities().create({
67
+ entityName: 'group', object: { name: group.name }
68
+ }));
69
+ }
70
+ const allGroupsAfterCreate = existingGroups.concat(createdGroups);
71
+ for (const group of toCreate) {
72
+ const createdGroup = createdGroups.find(x => x.name === group.name);
73
+ if (!createdGroup) {
74
+ continue;
75
+ }
76
+ await createGroupPrincipals(createdGroup, group.groupPrincipals, orgUsers, allGroupsAfterCreate);
77
+ }
78
+ for (const group of toUpdate) {
79
+ if (!group.groupPrincipals || !group.name) {
80
+ console.log('skipping invalid group');
81
+ }
82
+ await updateGroup(group.groupPrincipals, orgUsers, group, allGroupsAfterCreate);
83
+ }
84
+ for (const group of toDelete) {
85
+ if (!group.groupPrincipals || !group.name) {
86
+ console.log('skipping invalid group');
87
+ }
88
+ await new sdk_1.Entities().delete({ entityName: 'group', id: group.id });
89
+ }
90
+ }
91
+ exports.uploadGroups = uploadGroups;
92
+ async function updateGroup(principals, orgUsers, existingGroup, allGroupsAfterCreate) {
93
+ const existingPrincipals = existingGroup.groupPrincipals;
94
+ for (const principal of principals) {
95
+ if (principal.type === 'user') {
96
+ const user = orgUsers.map(x => x.user).find(x => x.email === (principal === null || principal === void 0 ? void 0 : principal.email));
97
+ if (!user) {
98
+ console.log(`user ${principal.email} is not in this org's user list`);
99
+ }
100
+ const principalId = `user:${user.id}`;
101
+ if (existingPrincipals.map(x => x.principalReference).includes(principalId)) {
102
+ continue;
103
+ }
104
+ await new sdk_1.Entities().update({
105
+ entityName: 'group-principal', object: {
106
+ principalReference: `user:${user.id}`,
107
+ groupId: existingGroup.id
108
+ }
109
+ });
110
+ }
111
+ else if (principal.type === 'group') {
112
+ const group = allGroupsAfterCreate.find(x => x.name === principal.name);
113
+ if (!group) {
114
+ console.log(`group ${principal.name} is not in this org's group list`);
115
+ }
116
+ const principalId = `group:${principal.name}`;
117
+ if (existingPrincipals.map(x => x.principalReference).includes(principalId)) {
118
+ continue;
119
+ }
120
+ await new sdk_1.Entities().update({
121
+ entityName: 'group-principal', object: {
122
+ principalReference: `group:${group.id}`,
123
+ groupId: existingGroup.id
124
+ }
125
+ });
126
+ }
127
+ }
128
+ }
129
+ async function createGroupPrincipals(group, principals, orgUsers, allGroupsAfterCreate) {
130
+ for (const principal of principals) {
131
+ if (principal.type === 'user') {
132
+ try {
133
+ const user = orgUsers.map(x => x.user).find(x => x.email === (principal === null || principal === void 0 ? void 0 : principal.email));
134
+ if (!user) {
135
+ console.log(`user ${principal.email} is not in this org's user list`);
136
+ }
137
+ await new sdk_1.Entities().create({
138
+ entityName: 'group-principal', object: {
139
+ principalReference: `user:${user.id}`,
140
+ groupId: group.id
141
+ }
142
+ });
143
+ }
144
+ catch (e) {
145
+ console.log('could not create group principal');
146
+ }
147
+ }
148
+ else if (principal.type === 'group') {
149
+ try {
150
+ const nestedGroup = allGroupsAfterCreate.find(x => x.name === principal.name);
151
+ if (!nestedGroup) {
152
+ console.log(`group ${principal.name} is not in this org's group list`);
153
+ }
154
+ await new sdk_1.Entities().create({
155
+ entityName: 'group-principal', object: {
156
+ principalReference: `group:${nestedGroup.id}`,
157
+ groupId: group.id
158
+ }
159
+ });
160
+ }
161
+ catch (e) {
162
+ console.log('could not create nested group');
163
+ }
164
+ }
165
+ }
166
+ }
@@ -24,7 +24,7 @@ GetAllTypes.examples = [
24
24
  `,
25
25
  ];
26
26
  GetAllTypes.flags = {
27
- outDir: core_1.Flags.string({ description: 'The directory to which to export the type data. Defaults to the current directory', required: false, default: '.' }),
27
+ outDir: core_1.Flags.string({ char: 'd', description: 'The directory to which to export the type data. Defaults to the current directory', required: false, default: '.' }),
28
28
  overwrite: core_1.Flags.boolean({ description: 'Replaces any existing export files in outDir', required: false, default: false })
29
29
  };
30
30
  async function getAll(outDir, overwrite) {
@@ -182,7 +182,12 @@ function reduceTypes(dataHolder) {
182
182
  slug: type.slug,
183
183
  label: type.label,
184
184
  isArchived: type.isArchived,
185
- typeProperties: reducedTypeProperties,
185
+ typeProperties: reducedTypeProperties.map(x => {
186
+ delete x['id'];
187
+ return {
188
+ ...x
189
+ };
190
+ }),
186
191
  };
187
192
  reducedTypes.push(reducedType);
188
193
  }
@@ -131,8 +131,8 @@ async function uploadRuleSetLinkObjects(loadedRuleSetLinks) {
131
131
  const hash = `${loadedLink.type}|${loadedLink.ruleSet}`;
132
132
  reducedLinksByHashes[hash] = loadedLink;
133
133
  }
134
- console.debug('current rule set links:', currentLinksHashes);
135
- console.debug('reduced rule set links:', reducedLinksByHashes);
134
+ // console.debug('reduced rule set links:', reducedLinksByHashes);
135
+ // console.debug('current rule set links:', currentLinksHashes);
136
136
  for (const loadedRuleSetLink of loadedRuleSetLinks) {
137
137
  try {
138
138
  const found = currentLinksHashes[`${loadedRuleSetLink.type}|${loadedRuleSetLink.ruleSet}`];
@@ -69,8 +69,8 @@ async function uploadTPPolicyObjects(reducedTypePropertyPolicies) {
69
69
  for (const loadedTPP of reducedTypePropertyPolicies) {
70
70
  loadedTPPByName[loadedTPP.label] = loadedTPP;
71
71
  }
72
- console.debug('current tp policy:', currentTPPolicies);
73
- console.debug('reduced tp policy:', reducedTypePropertyPolicies);
72
+ // console.debug('current tp policy:', currentTPPolicies);
73
+ // console.debug('reduced tp policy:', reducedTypePropertyPolicies);
74
74
  for (const [label, loadedTPP] of Object.entries(loadedTPPByName)) {
75
75
  try {
76
76
  const found = currentTPPByLabel[label];
@@ -1,9 +1,10 @@
1
1
  import { Command } from '@oclif/core';
2
+ export declare const DEFAULT_FILE_LOCATION = "users.yml";
2
3
  export default class ExportUsers extends Command {
3
4
  static description: string;
4
5
  static examples: string[];
5
6
  static flags: {
6
- fileOut: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
7
+ outDir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
7
8
  overwrite: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
9
  };
9
10
  run(): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.exportUsers = void 0;
3
+ exports.exportUsers = exports.DEFAULT_FILE_LOCATION = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path = tslib_1.__importStar(require("path"));
6
6
  const sdk_1 = require("@contrail/sdk");
@@ -10,6 +10,7 @@ const login_1 = require("../../auth/login");
10
10
  const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
11
11
  const core_1 = require("@oclif/core");
12
12
  const file_helpers_1 = require("../../helpers/file-helpers");
13
+ exports.DEFAULT_FILE_LOCATION = 'users.yml';
13
14
  class ExportUsers extends core_1.Command {
14
15
  async run() {
15
16
  var _a;
@@ -17,7 +18,7 @@ class ExportUsers extends core_1.Command {
17
18
  try {
18
19
  await (0, login_1.authUser)();
19
20
  this.log('%s Getting Users.', chalk_1.default.blue.bold('START'));
20
- await exportUsers((_a = flags.fileOut) !== null && _a !== void 0 ? _a : 'users.yml', flags.overwrite);
21
+ await exportUsers((_a = flags.outDir) !== null && _a !== void 0 ? _a : exports.DEFAULT_FILE_LOCATION, flags.overwrite);
21
22
  this.log('%s Done', chalk_1.default.yellow.bold('USERS'));
22
23
  }
23
24
  catch (e) {
@@ -34,7 +35,7 @@ ExportUsers.examples = [
34
35
  `,
35
36
  ];
36
37
  ExportUsers.flags = {
37
- fileOut: core_1.Flags.string({ char: 'f', description: 'The location for the file to be saved', required: false }),
38
+ outDir: core_1.Flags.string({ char: 'd', description: 'The location for the file to be saved', required: false, default: exports.DEFAULT_FILE_LOCATION }),
38
39
  overwrite: core_1.Flags.boolean({ description: 'Replaces existing files if they exist', required: false, default: false })
39
40
  };
40
41
  async function exportUsers(fileLocation, overwrite) {
@@ -54,15 +55,15 @@ async function exportUsers(fileLocation, overwrite) {
54
55
  ...userInOrg.user
55
56
  });
56
57
  }
57
- saveData({ users: users }, fileLocation, overwrite);
58
+ await saveData(users, fileLocation, overwrite);
58
59
  }
59
60
  exports.exportUsers = exportUsers;
60
- function saveData(data, fileLocation, overwrite) {
61
+ async function saveData(data, fileLocation, overwrite) {
61
62
  const yamlStr = js_yaml_1.default.dump(data);
62
63
  const CURRENT_DIR = process.cwd();
63
64
  const pathToWrite = path.resolve(path.join(CURRENT_DIR, fileLocation));
64
65
  if (fs_1.default.existsSync(pathToWrite)) {
65
- (0, file_helpers_1.safeFileOverwrite)(pathToWrite, overwrite);
66
+ await (0, file_helpers_1.safeFileOverwrite)(pathToWrite, overwrite);
66
67
  }
67
68
  fs_1.default.writeFileSync(pathToWrite, yamlStr, 'utf8');
68
69
  }
@@ -11,7 +11,7 @@ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
11
11
  const core_1 = require("@oclif/core");
12
12
  class ImportUsers extends core_1.Command {
13
13
  async run() {
14
- const { args, flags } = await this.parse(ImportUsers);
14
+ const { args } = await this.parse(ImportUsers);
15
15
  try {
16
16
  await (0, login_1.authUser)();
17
17
  this.log('%s Uploading Users.', chalk_1.default.blue.bold('START'));
@@ -38,10 +38,9 @@ ImportUsers.args = {
38
38
  function getUsersFile(fileLocation) {
39
39
  const CURRENT_DIR = process.cwd();
40
40
  try {
41
- const fullPath = path.resolve(path.join(CURRENT_DIR, fileLocation));
42
- console.log('Getting file from path: ', fullPath);
41
+ const fullPath = path.join(CURRENT_DIR, fileLocation);
43
42
  const doc = js_yaml_1.default.load(fs_1.default.readFileSync(fullPath, 'utf8'));
44
- return doc.users;
43
+ return doc;
45
44
  }
46
45
  catch (e) {
47
46
  throw new Error(`ERROR PARSING YAML: ${e}`);
@@ -42,6 +42,7 @@ class AppDeployer {
42
42
  identifier: appDeclaration.appIdentifier,
43
43
  name: appDeclaration.appName,
44
44
  public: appDeclaration.visibility === 'public',
45
+ hidden: appDeclaration.hidden,
45
46
  description: appDeclaration.appDescription,
46
47
  publisher: appDeclaration.publisher,
47
48
  thumbnail: appDeclaration.thumbnail,
@@ -67,6 +68,7 @@ class AppDeployer {
67
68
  const updates = {
68
69
  name: appDeclaration.appName,
69
70
  public: appDeclaration.visibility === 'public',
71
+ hidden: appDeclaration.hidden,
70
72
  description: appDeclaration.appDescription,
71
73
  publisher: appDeclaration.publisher,
72
74
  thumbnail: appDeclaration.thumbnail,
@@ -6,6 +6,7 @@ export interface AppDeclaration {
6
6
  appName: string;
7
7
  appDescription?: string;
8
8
  visibility: 'private' | 'public';
9
+ hidden: boolean;
9
10
  publisher: string;
10
11
  version: string;
11
12
  eventWorkflowTriggerKeyMapping: string;
@@ -2,6 +2,7 @@ appIdentifier: "@your-co/app-name" # Change to be your company root domain and
2
2
  appName: "Your App Name" # Give your app a name
3
3
  appDescription: "Your app description." # Give your app a description
4
4
  visibility: private # This should be private unless you want to develop an app for all VibeIQ customers to use
5
+ hidden: false # can be used with a public app to prevent it from showing on the marketplace, but still installable by other orgs
5
6
  publisher: "Your company name" # Your company name, used for public apps
6
7
  version: 1.0
7
8
  dockerImageRef: CHANGE_ME # For VibeIQ Deployment... for use by VibeIQ employees only
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.0.4",
2
+ "version": "2.0.6",
3
3
  "commands": {
4
4
  "app:create": {
5
5
  "id": "app:create",
@@ -78,6 +78,14 @@
78
78
  "description": "Install the app's accompanying workflows",
79
79
  "required": false,
80
80
  "allowNo": false
81
+ },
82
+ "showHidden": {
83
+ "name": "showHidden",
84
+ "type": "boolean",
85
+ "char": "h",
86
+ "description": "Show the option to install public but hidden apps",
87
+ "required": false,
88
+ "allowNo": false
81
89
  }
82
90
  },
83
91
  "args": {}
@@ -287,6 +295,57 @@
287
295
  },
288
296
  "args": {}
289
297
  },
298
+ "groups:export": {
299
+ "id": "groups:export",
300
+ "description": "Export all groups in the org.",
301
+ "strict": true,
302
+ "pluginName": "@contrail/cli",
303
+ "pluginAlias": "@contrail/cli",
304
+ "pluginType": "core",
305
+ "aliases": [],
306
+ "examples": [
307
+ "$ contrail groups export\n"
308
+ ],
309
+ "flags": {
310
+ "outDir": {
311
+ "name": "outDir",
312
+ "type": "option",
313
+ "char": "d",
314
+ "description": "The location for the file to be saved",
315
+ "required": false,
316
+ "multiple": false,
317
+ "default": "groups.yml"
318
+ },
319
+ "overwrite": {
320
+ "name": "overwrite",
321
+ "type": "boolean",
322
+ "description": "Replaces existing files if they exist",
323
+ "required": false,
324
+ "allowNo": false
325
+ }
326
+ },
327
+ "args": {}
328
+ },
329
+ "groups:upload": {
330
+ "id": "groups:upload",
331
+ "description": "Import all groups in the groups.yml file",
332
+ "strict": true,
333
+ "pluginName": "@contrail/cli",
334
+ "pluginAlias": "@contrail/cli",
335
+ "pluginType": "core",
336
+ "aliases": [],
337
+ "examples": [
338
+ "$ contrail groups import <GROUPS_FILE>\n"
339
+ ],
340
+ "flags": {},
341
+ "args": {
342
+ "groupsFile": {
343
+ "name": "groupsFile",
344
+ "description": "The .yml file containing the groups",
345
+ "required": true
346
+ }
347
+ }
348
+ },
290
349
  "instance:export": {
291
350
  "id": "instance:export",
292
351
  "description": "Exports an data of an instance including types and users",
@@ -468,6 +527,7 @@
468
527
  "outDir": {
469
528
  "name": "outDir",
470
529
  "type": "option",
530
+ "char": "d",
471
531
  "description": "The directory to which to export the type data. Defaults to the current directory",
472
532
  "required": false,
473
533
  "multiple": false,
@@ -870,13 +930,14 @@
870
930
  "$ contrail users export\n"
871
931
  ],
872
932
  "flags": {
873
- "fileOut": {
874
- "name": "fileOut",
933
+ "outDir": {
934
+ "name": "outDir",
875
935
  "type": "option",
876
- "char": "f",
936
+ "char": "d",
877
937
  "description": "The location for the file to be saved",
878
938
  "required": false,
879
- "multiple": false
939
+ "multiple": false,
940
+ "default": "users.yml"
880
941
  },
881
942
  "overwrite": {
882
943
  "name": "overwrite",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/cli",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "VibeIQ's Contrail Platform CLI.",
5
5
  "bin": {
6
6
  "contrail-cli": "./bin/run",