@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 +48 -8
- package/dist/api/apps.d.ts +1 -1
- package/dist/api/apps.js +2 -1
- package/dist/commands/app/install.d.ts +1 -0
- package/dist/commands/app/install.js +3 -2
- package/dist/commands/groups/export.d.ts +12 -0
- package/dist/commands/groups/export.js +93 -0
- package/dist/commands/groups/upload.d.ts +10 -0
- package/dist/commands/groups/upload.js +166 -0
- package/dist/commands/types/getAll.js +7 -2
- package/dist/commands/types/uploadRuleSets.js +2 -2
- package/dist/commands/types/uploadTypePropertyPolicies.js +2 -2
- package/dist/commands/users/export.d.ts +2 -1
- package/dist/commands/users/export.js +7 -6
- package/dist/commands/users/upload.js +3 -4
- package/dist/helpers/app/app-deployer.js +2 -0
- package/dist/interfaces.d.ts +1 -0
- package/dist/templates/typescript/app.yml +1 -0
- package/oclif.manifest.json +66 -5
- package/package.json +1 -1
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.
|
|
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 [
|
|
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
|
|
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 [-
|
|
1091
|
+
$ contrail users export [-d <value>] [--overwrite]
|
|
1052
1092
|
|
|
1053
1093
|
FLAGS
|
|
1054
|
-
-
|
|
1055
|
-
--overwrite
|
|
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.
|
package/dist/api/apps.d.ts
CHANGED
|
@@ -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 {
|
|
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('
|
|
135
|
-
console.debug('
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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
|
|
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,
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -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
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.0.
|
|
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
|
-
"
|
|
874
|
-
"name": "
|
|
933
|
+
"outDir": {
|
|
934
|
+
"name": "outDir",
|
|
875
935
|
"type": "option",
|
|
876
|
-
"char": "
|
|
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",
|