@contrail/cli 1.0.50 → 1.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cli.js +37 -3
- package/lib/projects/project-exporter.d.ts +2 -0
- package/lib/projects/project-exporter.js +163 -0
- package/lib/types/option-set-hierarchy-parser.d.ts +2 -0
- package/lib/types/option-set-hierarchy-parser.js +140 -0
- package/package.json +2 -2
- package/src/cli.ts +37 -3
- package/src/projects/project-exporter.ts +194 -0
- package/src/types/option-set-hierarchy-parser.ts +169 -0
package/lib/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getTypes } from './types/types-extractor';
|
|
|
4
4
|
import { uploadTypes } from './types/types-uploader';
|
|
5
5
|
import { UploadOptionSets } from './types/option-set-uploader';
|
|
6
6
|
import { ParseOptionSetsLocalFiles } from './types/option-set-parser';
|
|
7
|
+
import { ParseOptionSetsHierarchyLocalFiles, UploadOptionSetsHierarchyLocalFiles } from './types/option-set-hierarchy-parser';
|
|
7
8
|
import { MergeOptionSets } from './types/option-set-merger';
|
|
8
9
|
import { MergeTypeProperties } from './types/types-properties-merger';
|
|
9
10
|
import { ParseTypesFromFiles, ParseTypesFromLocalFiles, ParseTypesFromTemplateFromLocalFiles, ParseTypesFromTemplateAndDataFromLocalFiles } from './types/parse-types';
|
|
@@ -14,6 +15,7 @@ import { installApp, uninstallApp } from './apps/install-app/app-installer';
|
|
|
14
15
|
import { upgradeApp } from './apps/upgrade-app/app-upgrader';
|
|
15
16
|
import { cliInstallWorkflowTemplateDefinitions, listWorkflowTemplateDefinitions } from './workflow-template-definitions/workflow-template-definition-commands';
|
|
16
17
|
import { configureProject } from './projects/project-uploader';
|
|
18
|
+
import { ListProjects, ExportProject } from './projects/project-exporter';
|
|
17
19
|
import { Load, UploadAndLoad } from './loader-process/loader-process';
|
|
18
20
|
const cliUsage = require('command-line-usage');
|
|
19
21
|
/**
|
|
@@ -91,6 +93,14 @@ export async function cli(args) {
|
|
|
91
93
|
ParseOptionSetsLocalFiles(args);
|
|
92
94
|
break;
|
|
93
95
|
}
|
|
96
|
+
case 'parseOptionSetsHierarchyLocalFiles': {
|
|
97
|
+
ParseOptionSetsHierarchyLocalFiles(args);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case 'uploadOptionSetsHierarchyLocalFiles': {
|
|
101
|
+
UploadOptionSetsHierarchyLocalFiles(args);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
94
104
|
case 'parseTypesFromLocalFiles': {
|
|
95
105
|
ParseTypesFromLocalFiles(args);
|
|
96
106
|
break;
|
|
@@ -155,10 +165,18 @@ export async function cli(args) {
|
|
|
155
165
|
}
|
|
156
166
|
case 'projects': {
|
|
157
167
|
switch (command) {
|
|
158
|
-
case '
|
|
168
|
+
case 'load': {
|
|
159
169
|
await configureProject(args);
|
|
160
170
|
break;
|
|
161
171
|
}
|
|
172
|
+
case 'list': {
|
|
173
|
+
await ListProjects();
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'export': {
|
|
177
|
+
await ExportProject(args);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
162
180
|
default: {
|
|
163
181
|
printIllegalCommand();
|
|
164
182
|
}
|
|
@@ -307,8 +325,16 @@ const helpSections = [
|
|
|
307
325
|
{
|
|
308
326
|
header: 'Project Commands - Domain: projects',
|
|
309
327
|
content: [{
|
|
310
|
-
name: '
|
|
328
|
+
name: 'load <File Path>',
|
|
311
329
|
summary: 'Configure a project from a yml file.'
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'list',
|
|
333
|
+
summary: 'Get a list of all projects.'
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'export <Project Name>',
|
|
337
|
+
summary: 'Export a project.'
|
|
312
338
|
}
|
|
313
339
|
],
|
|
314
340
|
},
|
|
@@ -358,7 +384,15 @@ const helpSections = [
|
|
|
358
384
|
},
|
|
359
385
|
{
|
|
360
386
|
name: 'parseOptionSetsLocalFiles <File Paths>',
|
|
361
|
-
summary: 'Parse CSV files into OptionSets'
|
|
387
|
+
summary: 'Parse CSV files into OptionSets.'
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: 'parseOptionSetsHierarchyLocalFiles <File Paths>',
|
|
391
|
+
summary: 'Parse CSV files into OptionSetHierarchies.'
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'uploadOptionSetsHierarchyLocalFiles <File Paths> <name>',
|
|
395
|
+
summary: 'upload CSV files into OptionSetHierarchies.'
|
|
362
396
|
},
|
|
363
397
|
{
|
|
364
398
|
name: 'parseTypesFromLocalFiles <File Paths>',
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import { ObjectUtil } from '@contrail/util';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { authUser } from '../auth/login';
|
|
6
|
+
import { getSeparatorByPlatform } from '../common';
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
export async function ListProjects() {
|
|
9
|
+
try {
|
|
10
|
+
await authUser();
|
|
11
|
+
console.log('%s Getting Projects.', chalk.blue.bold('START'));
|
|
12
|
+
await listProjects();
|
|
13
|
+
console.log('%s Done', chalk.yellow.bold('PROJECTS'));
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
console.log(`%s Project download Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
console.log('%s Project download Complete.', chalk.green.bold('COMPLETE'));
|
|
20
|
+
}
|
|
21
|
+
export async function ExportProject(args) {
|
|
22
|
+
try {
|
|
23
|
+
await authUser();
|
|
24
|
+
console.log('%s Exporting Project.', chalk.blue.bold('START'));
|
|
25
|
+
console.log('%s Exporting', chalk.yellow.bold('PROJECT'));
|
|
26
|
+
const projectName = args[3];
|
|
27
|
+
await exportProject(projectName);
|
|
28
|
+
console.log('%s Exported', chalk.yellow.bold('PROJECT'));
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
console.log(`%s Project Export Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log('%s Project Export Complete.', chalk.green.bold('COMPLETE'));
|
|
35
|
+
}
|
|
36
|
+
async function listProjects() {
|
|
37
|
+
const allWorkspaces = await new Entities().get({
|
|
38
|
+
entityName: 'workspace',
|
|
39
|
+
criteria: { isArchived: false }
|
|
40
|
+
});
|
|
41
|
+
console.log('%s', chalk.green.bold('PROJECTS:'));
|
|
42
|
+
//console.log(allWorkspaces.map(w => Object.assign({}, {name: w.name, id: w.id}))); // if we want ids
|
|
43
|
+
console.log(allWorkspaces.map(w => w.name));
|
|
44
|
+
}
|
|
45
|
+
async function exportProject(name) {
|
|
46
|
+
const allWorkspaces = await new Entities().get({
|
|
47
|
+
entityName: 'workspace',
|
|
48
|
+
useSearch: false,
|
|
49
|
+
});
|
|
50
|
+
const workspace = allWorkspaces.find(w => w.name === name);
|
|
51
|
+
if (!workspace) {
|
|
52
|
+
throw new Error('Project was not found.');
|
|
53
|
+
}
|
|
54
|
+
const projectConfiguration = {
|
|
55
|
+
projectName: workspace.name,
|
|
56
|
+
identifier: workspace.identifier,
|
|
57
|
+
commonConfig: {},
|
|
58
|
+
assortments: [],
|
|
59
|
+
subFolders: []
|
|
60
|
+
};
|
|
61
|
+
const allAssortments = [];
|
|
62
|
+
await getAssortmentsAndSubFolders(workspace.id, projectConfiguration.assortments, projectConfiguration.subFolders, allAssortments);
|
|
63
|
+
await convertRelationshipsInFolders(projectConfiguration, allAssortments);
|
|
64
|
+
saveData({ projectConfiguration: projectConfiguration }, name);
|
|
65
|
+
}
|
|
66
|
+
async function getAssortmentsAndSubFolders(parentId, assortments, subFolders, allAssortments) {
|
|
67
|
+
const workspaceAssortments = await new Entities().get({
|
|
68
|
+
entityName: 'assortment',
|
|
69
|
+
criteria: {
|
|
70
|
+
workspaceId: parentId,
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const integrationAssortments = workspaceAssortments === null || workspaceAssortments === void 0 ? void 0 : workspaceAssortments.filter(a => a.assortmentType === 'INTEGRATION');
|
|
74
|
+
if ((integrationAssortments === null || integrationAssortments === void 0 ? void 0 : integrationAssortments.length) > 0) {
|
|
75
|
+
allAssortments.push(...integrationAssortments);
|
|
76
|
+
for (const a of integrationAssortments) {
|
|
77
|
+
const integrationAssortmentsCopy = ObjectUtil.cloneDeep(a);
|
|
78
|
+
deleteNonUserKeys(integrationAssortmentsCopy);
|
|
79
|
+
const converted = {
|
|
80
|
+
identifier: a.identifier,
|
|
81
|
+
name: a.name,
|
|
82
|
+
type: 'ASSORTMENT',
|
|
83
|
+
sourceAssortmentIdentifier: [],
|
|
84
|
+
sourceAssortmentIds: a.sourceAssortmentIds,
|
|
85
|
+
composedFromAssortmentIdentifiers: [],
|
|
86
|
+
composedFromAssortmentIds: a.composedFromAssortmentIds,
|
|
87
|
+
assortmentAtts: Object.assign({}, integrationAssortmentsCopy),
|
|
88
|
+
};
|
|
89
|
+
assortments.push(converted);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log('workspaceFolderLinks call');
|
|
93
|
+
const workspaceFolderLinks = await new Entities().get({
|
|
94
|
+
entityName: 'workspace-entity',
|
|
95
|
+
criteria: {
|
|
96
|
+
workspaceId: parentId,
|
|
97
|
+
entityType: 'workspace'
|
|
98
|
+
},
|
|
99
|
+
relations: ['entity']
|
|
100
|
+
});
|
|
101
|
+
const workspaceFolders = workspaceFolderLinks === null || workspaceFolderLinks === void 0 ? void 0 : workspaceFolderLinks.map(wfl => wfl.entity);
|
|
102
|
+
if ((workspaceFolders === null || workspaceFolders === void 0 ? void 0 : workspaceFolders.length) > 0) {
|
|
103
|
+
for (const folder of workspaceFolders) {
|
|
104
|
+
const converted = {
|
|
105
|
+
identifier: folder.identifier,
|
|
106
|
+
name: folder.name,
|
|
107
|
+
assortments: [],
|
|
108
|
+
subFolders: [],
|
|
109
|
+
};
|
|
110
|
+
await getAssortmentsAndSubFolders(folder.id, converted.assortments, converted.subFolders, allAssortments);
|
|
111
|
+
subFolders.push(converted);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function convertRelationshipsInFolders(folder, allAssortments) {
|
|
116
|
+
if (!(allAssortments.length > 0)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
convertRelationshipsToIdentifiers(folder.assortments, allAssortments);
|
|
120
|
+
for (const f of folder.subFolders) {
|
|
121
|
+
convertRelationshipsInFolders(f, allAssortments);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function convertRelationshipsToIdentifiers(assortments, allAssortments) {
|
|
125
|
+
if (!(allAssortments.length > 0)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
for (const assortment of assortments) {
|
|
129
|
+
if (assortment.sourceAssortmentIds) {
|
|
130
|
+
assortment.sourceAssortmentIdentifier = assortment.sourceAssortmentIds.map(id => { var _a; return (_a = allAssortments.find(a => a.id === id)) === null || _a === void 0 ? void 0 : _a.identifier; });
|
|
131
|
+
}
|
|
132
|
+
delete assortment.sourceAssortmentIds;
|
|
133
|
+
if (assortment.composedFromAssortmentIds) {
|
|
134
|
+
assortment.composedFromAssortmentIdentifiers = assortment.composedFromAssortmentIds.map(id => { var _a; return (_a = allAssortments.find(a => a.id === id)) === null || _a === void 0 ? void 0 : _a.identifier; });
|
|
135
|
+
}
|
|
136
|
+
delete assortment.composedFromAssortmentIds;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function deleteNonUserKeys(assortment) {
|
|
140
|
+
delete assortment.name;
|
|
141
|
+
delete assortment.identifier;
|
|
142
|
+
delete assortment.assortmentType;
|
|
143
|
+
delete assortment.createdById;
|
|
144
|
+
delete assortment.createdForReference;
|
|
145
|
+
delete assortment.createdOn;
|
|
146
|
+
delete assortment.entityType;
|
|
147
|
+
delete assortment.id;
|
|
148
|
+
delete assortment.orgId;
|
|
149
|
+
delete assortment.rootWorkspaceId;
|
|
150
|
+
delete assortment.composedFromAssortmentIds;
|
|
151
|
+
delete assortment.sourceAssortmentIds;
|
|
152
|
+
delete assortment.typeId;
|
|
153
|
+
delete assortment.typePath;
|
|
154
|
+
delete assortment.updatedById;
|
|
155
|
+
delete assortment.updatedOn;
|
|
156
|
+
delete assortment.workspaceId;
|
|
157
|
+
}
|
|
158
|
+
function saveData(data, name) {
|
|
159
|
+
const yamlStr = yaml.dump(data);
|
|
160
|
+
const CURRENT_DIR = process.cwd();
|
|
161
|
+
const separator = getSeparatorByPlatform();
|
|
162
|
+
fs.writeFileSync(CURRENT_DIR + separator + name + '-export.yml', yamlStr, 'utf8');
|
|
163
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { getSeparatorByPlatform } from '../common';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
const csv = require('csvtojson');
|
|
5
|
+
import { Entities } from '@contrail/sdk';
|
|
6
|
+
import { StringUtil } from '@contrail/util';
|
|
7
|
+
import { authUser } from '../auth/login';
|
|
8
|
+
export async function ParseOptionSetsHierarchyLocalFiles(args) {
|
|
9
|
+
try {
|
|
10
|
+
await authUser();
|
|
11
|
+
if ((args === null || args === void 0 ? void 0 : args.length) < 4) {
|
|
12
|
+
throw new Error('File name must be included.');
|
|
13
|
+
}
|
|
14
|
+
console.log('%s Importing local file', chalk.yellow.bold('OPTION SET HIERARCHY PARSE'));
|
|
15
|
+
const fileLocation = args[3];
|
|
16
|
+
console.log('Using File: ', fileLocation);
|
|
17
|
+
await parseAndSaveOptionSetHierarchiesLocalFiles(fileLocation);
|
|
18
|
+
console.log('%s Parsed', chalk.yellow.bold('OPTION SET HIERARCHY PARSE'));
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
console.log(`%s PARSE Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
console.log('%s PARSE Complete.', chalk.green.bold('COMPLETE'));
|
|
25
|
+
}
|
|
26
|
+
export async function UploadOptionSetsHierarchyLocalFiles(args) {
|
|
27
|
+
try {
|
|
28
|
+
await authUser();
|
|
29
|
+
if ((args === null || args === void 0 ? void 0 : args.length) < 4) {
|
|
30
|
+
throw new Error('File name must be included.');
|
|
31
|
+
}
|
|
32
|
+
console.log('%s Importing local file', chalk.yellow.bold('OPTION SET HIERARCHY UPLOAD'));
|
|
33
|
+
const fileLocation = args[3];
|
|
34
|
+
const name = args[4];
|
|
35
|
+
console.log('Using File: ', fileLocation);
|
|
36
|
+
console.log('Using Name: ', name);
|
|
37
|
+
await uploadOptionSetHierarchiesLocalFiles(fileLocation, name);
|
|
38
|
+
console.log('%s Uploaded', chalk.yellow.bold('OPTION SET HIERARCHY UPLOAD'));
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.log(`%s UPLOAD Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log('%s UPLOAD Complete.', chalk.green.bold('COMPLETE'));
|
|
45
|
+
}
|
|
46
|
+
async function loadOptionSetHierarchyCSV(fileLocation) {
|
|
47
|
+
const TARGET_DIR = process.cwd();
|
|
48
|
+
try {
|
|
49
|
+
const separator = getSeparatorByPlatform();
|
|
50
|
+
const fullPath = TARGET_DIR + separator + fileLocation;
|
|
51
|
+
console.log('Getting file from path: ', fullPath);
|
|
52
|
+
return await csv().fromFile(fullPath);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
throw new Error(`ERROR PARSING CSV: ${e}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function parseOptionSetHierarchiesLocalFiles(fileLocation) {
|
|
59
|
+
const uploadedOptionHierarchyData = await loadOptionSetHierarchyCSV(fileLocation);
|
|
60
|
+
const optionSetHierarchy = {
|
|
61
|
+
hierarchy: [],
|
|
62
|
+
maxDepth: 0,
|
|
63
|
+
name: 'No Name',
|
|
64
|
+
options: []
|
|
65
|
+
};
|
|
66
|
+
if (!((uploadedOptionHierarchyData === null || uploadedOptionHierarchyData === void 0 ? void 0 : uploadedOptionHierarchyData.length) > 0)) {
|
|
67
|
+
return optionSetHierarchy;
|
|
68
|
+
}
|
|
69
|
+
const keysInOrder = Object.keys(uploadedOptionHierarchyData[0]);
|
|
70
|
+
const currentOptionSets = await new Entities().get({
|
|
71
|
+
entityName: 'type-property-option-set',
|
|
72
|
+
});
|
|
73
|
+
const currentOptionSetsByName = {};
|
|
74
|
+
for (const currentOptionSet of currentOptionSets) {
|
|
75
|
+
currentOptionSetsByName[StringUtil.convertToCamelCase(currentOptionSet.name)] = currentOptionSet;
|
|
76
|
+
}
|
|
77
|
+
const optionSetIds = [];
|
|
78
|
+
for (const key of keysInOrder) {
|
|
79
|
+
if (currentOptionSetsByName[key]) {
|
|
80
|
+
optionSetIds.push(currentOptionSetsByName[key].id);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error(`No OptionSet found for ${key}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
optionSetHierarchy.maxDepth = keysInOrder.length;
|
|
87
|
+
optionSetHierarchy.hierarchy = optionSetIds;
|
|
88
|
+
for (const row of uploadedOptionHierarchyData) {
|
|
89
|
+
addRowToOptions(optionSetHierarchy, keysInOrder, row);
|
|
90
|
+
}
|
|
91
|
+
return optionSetHierarchy;
|
|
92
|
+
}
|
|
93
|
+
async function parseAndSaveOptionSetHierarchiesLocalFiles(fileLocation) {
|
|
94
|
+
const optionSetHierarchy = await parseOptionSetHierarchiesLocalFiles(fileLocation);
|
|
95
|
+
saveData(optionSetHierarchy.options);
|
|
96
|
+
}
|
|
97
|
+
async function uploadOptionSetHierarchiesLocalFiles(fileLocation, name) {
|
|
98
|
+
const optionSetHierarchy = await parseOptionSetHierarchiesLocalFiles(fileLocation);
|
|
99
|
+
optionSetHierarchy.name = name;
|
|
100
|
+
const currentOptionSetHierarchies = await new Entities().get({
|
|
101
|
+
entityName: 'option-set-hierarchy',
|
|
102
|
+
});
|
|
103
|
+
const existing = currentOptionSetHierarchies.find(osh => osh.name === name);
|
|
104
|
+
if (existing) {
|
|
105
|
+
console.log('Updating: ', optionSetHierarchy.name);
|
|
106
|
+
await new Entities().update({
|
|
107
|
+
entityName: 'option-set-hierarchy',
|
|
108
|
+
id: existing.id,
|
|
109
|
+
object: optionSetHierarchy,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log('Creating: ', optionSetHierarchy.name);
|
|
114
|
+
await new Entities().create({
|
|
115
|
+
entityName: 'option-set-hierarchy',
|
|
116
|
+
object: optionSetHierarchy,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function addRowToOptions(optionSetHierarchy, keysInOrder, row) {
|
|
121
|
+
let options = optionSetHierarchy.options;
|
|
122
|
+
for (let i = 0; i < keysInOrder.length; i++) {
|
|
123
|
+
const optionValues = options.map(op => op.value);
|
|
124
|
+
const value = row[keysInOrder[i]];
|
|
125
|
+
if (!optionValues.includes(value)) {
|
|
126
|
+
const obj = i === (keysInOrder.length - 1) ? { value: value } : { value: value, options: [] };
|
|
127
|
+
options.push(obj);
|
|
128
|
+
options = obj.options;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const optionObj = options.find(op => op.value === value);
|
|
132
|
+
options = optionObj.options;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function saveData(data) {
|
|
137
|
+
const CURRENT_DIR = process.cwd();
|
|
138
|
+
const separator = getSeparatorByPlatform();
|
|
139
|
+
fs.writeFileSync(CURRENT_DIR + separator + 'parsed_local_optionSetHierarchy.json', JSON.stringify(data), 'utf8');
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrail/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.53",
|
|
4
4
|
"description": "VibeIQ's Contrail Platform CLI.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@contrail/sdk": "^1.1.
|
|
26
|
+
"@contrail/sdk": "^1.1.52",
|
|
27
27
|
"@contrail/types": "^3.0.20",
|
|
28
28
|
"@contrail/util": "^1.0.17",
|
|
29
29
|
"amazon-cognito-identity-js": "^5.2.3",
|
package/src/cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { getTypes } from './types/types-extractor';
|
|
|
5
5
|
import { uploadTypes } from './types/types-uploader';
|
|
6
6
|
import { UploadOptionSets } from './types/option-set-uploader';
|
|
7
7
|
import { ParseOptionSetsLocalFiles } from './types/option-set-parser';
|
|
8
|
+
import { ParseOptionSetsHierarchyLocalFiles, UploadOptionSetsHierarchyLocalFiles } from './types/option-set-hierarchy-parser';
|
|
8
9
|
import { MergeOptionSets } from './types/option-set-merger';
|
|
9
10
|
import { MergeTypeProperties } from './types/types-properties-merger';
|
|
10
11
|
import { ParseTypesFromFiles, ParseTypesFromLocalFiles, ParseTypesFromTemplateFromLocalFiles, ParseTypesFromTemplateAndDataFromLocalFiles } from './types/parse-types';
|
|
@@ -15,6 +16,7 @@ import { installApp, uninstallApp } from './apps/install-app/app-installer';
|
|
|
15
16
|
import { upgradeApp } from './apps/upgrade-app/app-upgrader';
|
|
16
17
|
import { cliInstallWorkflowTemplateDefinitions, listWorkflowTemplateDefinitions } from './workflow-template-definitions/workflow-template-definition-commands';
|
|
17
18
|
import { configureProject } from './projects/project-uploader';
|
|
19
|
+
import { ListProjects, ExportProject } from './projects/project-exporter';
|
|
18
20
|
import { Load, UploadAndLoad } from './loader-process/loader-process';
|
|
19
21
|
|
|
20
22
|
const cliUsage = require('command-line-usage');
|
|
@@ -101,6 +103,14 @@ export async function cli(args) {
|
|
|
101
103
|
ParseOptionSetsLocalFiles(args);
|
|
102
104
|
break;
|
|
103
105
|
}
|
|
106
|
+
case 'parseOptionSetsHierarchyLocalFiles': {
|
|
107
|
+
ParseOptionSetsHierarchyLocalFiles(args);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'uploadOptionSetsHierarchyLocalFiles': {
|
|
111
|
+
UploadOptionSetsHierarchyLocalFiles(args);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
104
114
|
case 'parseTypesFromLocalFiles': {
|
|
105
115
|
ParseTypesFromLocalFiles(args);
|
|
106
116
|
break;
|
|
@@ -165,10 +175,18 @@ export async function cli(args) {
|
|
|
165
175
|
}
|
|
166
176
|
case 'projects': {
|
|
167
177
|
switch (command) {
|
|
168
|
-
case '
|
|
178
|
+
case 'load': {
|
|
169
179
|
await configureProject(args);
|
|
170
180
|
break;
|
|
171
181
|
}
|
|
182
|
+
case 'list': {
|
|
183
|
+
await ListProjects();
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case 'export': {
|
|
187
|
+
await ExportProject(args);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
172
190
|
default: {
|
|
173
191
|
printIllegalCommand();
|
|
174
192
|
}
|
|
@@ -322,8 +340,16 @@ const helpSections = [
|
|
|
322
340
|
{
|
|
323
341
|
header: 'Project Commands - Domain: projects',
|
|
324
342
|
content: [{
|
|
325
|
-
name: '
|
|
343
|
+
name: 'load <File Path>',
|
|
326
344
|
summary: 'Configure a project from a yml file.'
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'list',
|
|
348
|
+
summary: 'Get a list of all projects.'
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'export <Project Name>',
|
|
352
|
+
summary: 'Export a project.'
|
|
327
353
|
}
|
|
328
354
|
],
|
|
329
355
|
},
|
|
@@ -373,7 +399,15 @@ const helpSections = [
|
|
|
373
399
|
},
|
|
374
400
|
{
|
|
375
401
|
name: 'parseOptionSetsLocalFiles <File Paths>',
|
|
376
|
-
summary: 'Parse CSV files into OptionSets'
|
|
402
|
+
summary: 'Parse CSV files into OptionSets.'
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: 'parseOptionSetsHierarchyLocalFiles <File Paths>',
|
|
406
|
+
summary: 'Parse CSV files into OptionSetHierarchies.'
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: 'uploadOptionSetsHierarchyLocalFiles <File Paths> <name>',
|
|
410
|
+
summary: 'upload CSV files into OptionSetHierarchies.'
|
|
377
411
|
},
|
|
378
412
|
{
|
|
379
413
|
name: 'parseTypesFromLocalFiles <File Paths>',
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import { ObjectUtil } from '@contrail/util';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { authUser } from '../auth/login';
|
|
6
|
+
import { getSeparatorByPlatform } from '../common';
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
|
|
9
|
+
export async function ListProjects() {
|
|
10
|
+
try {
|
|
11
|
+
await authUser();
|
|
12
|
+
console.log('%s Getting Projects.', chalk.blue.bold('START'));
|
|
13
|
+
|
|
14
|
+
await listProjects();
|
|
15
|
+
console.log('%s Done', chalk.yellow.bold('PROJECTS'));
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
console.log(`%s Project download Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log('%s Project download Complete.', chalk.green.bold('COMPLETE'));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function ExportProject(args) {
|
|
27
|
+
try {
|
|
28
|
+
await authUser();
|
|
29
|
+
console.log('%s Exporting Project.', chalk.blue.bold('START'));
|
|
30
|
+
|
|
31
|
+
console.log('%s Exporting', chalk.yellow.bold('PROJECT'));
|
|
32
|
+
const projectName = args[3];
|
|
33
|
+
await exportProject(projectName);
|
|
34
|
+
console.log('%s Exported', chalk.yellow.bold('PROJECT'));
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
console.log(`%s Project Export Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log('%s Project Export Complete.', chalk.green.bold('COMPLETE'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function listProjects() {
|
|
46
|
+
const allWorkspaces = await new Entities().get({
|
|
47
|
+
entityName: 'workspace',
|
|
48
|
+
criteria: {isArchived: false}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log('%s', chalk.green.bold('PROJECTS:'));
|
|
52
|
+
//console.log(allWorkspaces.map(w => Object.assign({}, {name: w.name, id: w.id}))); // if we want ids
|
|
53
|
+
console.log(allWorkspaces.map(w => w.name));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function exportProject(name) {
|
|
57
|
+
const allWorkspaces = await new Entities().get({
|
|
58
|
+
entityName: 'workspace',
|
|
59
|
+
useSearch: false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const workspace = allWorkspaces.find(w => w.name === name);
|
|
63
|
+
if (!workspace) {
|
|
64
|
+
throw new Error('Project was not found.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const projectConfiguration = {
|
|
68
|
+
projectName: workspace.name,
|
|
69
|
+
identifier: workspace.identifier,
|
|
70
|
+
commonConfig: {},
|
|
71
|
+
assortments: [],
|
|
72
|
+
subFolders: []
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const allAssortments = [];
|
|
76
|
+
await getAssortmentsAndSubFolders(workspace.id, projectConfiguration.assortments, projectConfiguration.subFolders, allAssortments);
|
|
77
|
+
await convertRelationshipsInFolders(projectConfiguration, allAssortments);
|
|
78
|
+
saveData({projectConfiguration: projectConfiguration}, name);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function getAssortmentsAndSubFolders(parentId, assortments, subFolders, allAssortments) {
|
|
82
|
+
const workspaceAssortments = await new Entities().get({
|
|
83
|
+
entityName: 'assortment',
|
|
84
|
+
criteria: {
|
|
85
|
+
workspaceId: parentId,
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const integrationAssortments = workspaceAssortments?.filter(a => a.assortmentType === 'INTEGRATION');
|
|
90
|
+
|
|
91
|
+
if (integrationAssortments?.length > 0) {
|
|
92
|
+
allAssortments.push(...integrationAssortments);
|
|
93
|
+
for (const a of integrationAssortments) {
|
|
94
|
+
const integrationAssortmentsCopy = ObjectUtil.cloneDeep(a);
|
|
95
|
+
deleteNonUserKeys(integrationAssortmentsCopy);
|
|
96
|
+
const converted = {
|
|
97
|
+
identifier: a.identifier,
|
|
98
|
+
name: a.name,
|
|
99
|
+
type: 'ASSORTMENT',
|
|
100
|
+
sourceAssortmentIdentifier: [],
|
|
101
|
+
sourceAssortmentIds: a.sourceAssortmentIds,
|
|
102
|
+
composedFromAssortmentIdentifiers: [],
|
|
103
|
+
composedFromAssortmentIds: a.composedFromAssortmentIds,
|
|
104
|
+
assortmentAtts: Object.assign({}, integrationAssortmentsCopy),
|
|
105
|
+
};
|
|
106
|
+
assortments.push(converted);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('workspaceFolderLinks call');
|
|
111
|
+
|
|
112
|
+
const workspaceFolderLinks = await new Entities().get({
|
|
113
|
+
entityName: 'workspace-entity',
|
|
114
|
+
criteria: {
|
|
115
|
+
workspaceId: parentId,
|
|
116
|
+
entityType: 'workspace'
|
|
117
|
+
},
|
|
118
|
+
relations: ['entity']
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const workspaceFolders = workspaceFolderLinks?.map(wfl => wfl.entity);
|
|
122
|
+
|
|
123
|
+
if (workspaceFolders?.length > 0) {
|
|
124
|
+
for (const folder of workspaceFolders) {
|
|
125
|
+
const converted = {
|
|
126
|
+
identifier: folder.identifier,
|
|
127
|
+
name: folder.name,
|
|
128
|
+
assortments: [],
|
|
129
|
+
subFolders: [],
|
|
130
|
+
};
|
|
131
|
+
await getAssortmentsAndSubFolders(folder.id, converted.assortments, converted.subFolders, allAssortments);
|
|
132
|
+
subFolders.push(converted);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function convertRelationshipsInFolders(folder, allAssortments) {
|
|
139
|
+
if (!(allAssortments.length > 0)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
convertRelationshipsToIdentifiers(folder.assortments, allAssortments);
|
|
144
|
+
|
|
145
|
+
for (const f of folder.subFolders) {
|
|
146
|
+
convertRelationshipsInFolders(f, allAssortments);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function convertRelationshipsToIdentifiers(assortments, allAssortments) {
|
|
152
|
+
if (!(allAssortments.length > 0)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const assortment of assortments) {
|
|
157
|
+
if (assortment.sourceAssortmentIds) {
|
|
158
|
+
assortment.sourceAssortmentIdentifier = assortment.sourceAssortmentIds.map(id => allAssortments.find(a => a.id === id)?.identifier);
|
|
159
|
+
}
|
|
160
|
+
delete assortment.sourceAssortmentIds;
|
|
161
|
+
|
|
162
|
+
if (assortment.composedFromAssortmentIds) {
|
|
163
|
+
assortment.composedFromAssortmentIdentifiers = assortment.composedFromAssortmentIds.map(id => allAssortments.find(a => a.id === id)?.identifier);
|
|
164
|
+
}
|
|
165
|
+
delete assortment.composedFromAssortmentIds;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function deleteNonUserKeys(assortment) {
|
|
170
|
+
delete assortment.name;
|
|
171
|
+
delete assortment.identifier;
|
|
172
|
+
delete assortment.assortmentType;
|
|
173
|
+
delete assortment.createdById;
|
|
174
|
+
delete assortment.createdForReference;
|
|
175
|
+
delete assortment.createdOn;
|
|
176
|
+
delete assortment.entityType;
|
|
177
|
+
delete assortment.id;
|
|
178
|
+
delete assortment.orgId;
|
|
179
|
+
delete assortment.rootWorkspaceId;
|
|
180
|
+
delete assortment.composedFromAssortmentIds;
|
|
181
|
+
delete assortment.sourceAssortmentIds;
|
|
182
|
+
delete assortment.typeId;
|
|
183
|
+
delete assortment.typePath;
|
|
184
|
+
delete assortment.updatedById;
|
|
185
|
+
delete assortment.updatedOn;
|
|
186
|
+
delete assortment.workspaceId;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function saveData(data, name) {
|
|
190
|
+
const yamlStr = yaml.dump(data);
|
|
191
|
+
const CURRENT_DIR = process.cwd();
|
|
192
|
+
const separator = getSeparatorByPlatform();
|
|
193
|
+
fs.writeFileSync(CURRENT_DIR + separator + name + '-export.yml', yamlStr, 'utf8');
|
|
194
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { getSeparatorByPlatform } from '../common';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
const csv = require('csvtojson');
|
|
5
|
+
import { Entities } from '@contrail/sdk';
|
|
6
|
+
import { StringUtil } from '@contrail/util';
|
|
7
|
+
import { authUser } from '../auth/login';
|
|
8
|
+
|
|
9
|
+
export async function ParseOptionSetsHierarchyLocalFiles(args) {
|
|
10
|
+
try {
|
|
11
|
+
await authUser();
|
|
12
|
+
|
|
13
|
+
if (args?.length < 4) {
|
|
14
|
+
throw new Error('File name must be included.');
|
|
15
|
+
}
|
|
16
|
+
console.log('%s Importing local file', chalk.yellow.bold('OPTION SET HIERARCHY PARSE'));
|
|
17
|
+
const fileLocation = args[3];
|
|
18
|
+
console.log('Using File: ', fileLocation);
|
|
19
|
+
|
|
20
|
+
await parseAndSaveOptionSetHierarchiesLocalFiles(fileLocation);
|
|
21
|
+
console.log('%s Parsed', chalk.yellow.bold('OPTION SET HIERARCHY PARSE'));
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
console.log(`%s PARSE Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('%s PARSE Complete.', chalk.green.bold('COMPLETE'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function UploadOptionSetsHierarchyLocalFiles(args) {
|
|
33
|
+
try {
|
|
34
|
+
await authUser();
|
|
35
|
+
|
|
36
|
+
if (args?.length < 4) {
|
|
37
|
+
throw new Error('File name must be included.');
|
|
38
|
+
}
|
|
39
|
+
console.log('%s Importing local file', chalk.yellow.bold('OPTION SET HIERARCHY UPLOAD'));
|
|
40
|
+
const fileLocation = args[3];
|
|
41
|
+
const name = args[4];
|
|
42
|
+
console.log('Using File: ', fileLocation);
|
|
43
|
+
console.log('Using Name: ', name);
|
|
44
|
+
|
|
45
|
+
await uploadOptionSetHierarchiesLocalFiles(fileLocation, name);
|
|
46
|
+
console.log('%s Uploaded', chalk.yellow.bold('OPTION SET HIERARCHY UPLOAD'));
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.log(`%s UPLOAD Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log('%s UPLOAD Complete.', chalk.green.bold('COMPLETE'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function loadOptionSetHierarchyCSV(fileLocation) {
|
|
58
|
+
const TARGET_DIR = process.cwd();
|
|
59
|
+
try {
|
|
60
|
+
const separator = getSeparatorByPlatform();
|
|
61
|
+
|
|
62
|
+
const fullPath = TARGET_DIR + separator + fileLocation;
|
|
63
|
+
console.log('Getting file from path: ', fullPath);
|
|
64
|
+
|
|
65
|
+
return await csv().fromFile(fullPath);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
throw new Error(`ERROR PARSING CSV: ${e}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function parseOptionSetHierarchiesLocalFiles(fileLocation) {
|
|
72
|
+
const uploadedOptionHierarchyData = await loadOptionSetHierarchyCSV(fileLocation);
|
|
73
|
+
|
|
74
|
+
const optionSetHierarchy = {
|
|
75
|
+
hierarchy:[],
|
|
76
|
+
maxDepth: 0,
|
|
77
|
+
name: 'No Name',
|
|
78
|
+
options: []
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (!(uploadedOptionHierarchyData?.length > 0)) {
|
|
82
|
+
return optionSetHierarchy;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const keysInOrder = Object.keys(uploadedOptionHierarchyData[0]);
|
|
86
|
+
const currentOptionSets = await new Entities().get({
|
|
87
|
+
entityName: 'type-property-option-set',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const currentOptionSetsByName = {};
|
|
91
|
+
for (const currentOptionSet of currentOptionSets) {
|
|
92
|
+
currentOptionSetsByName[StringUtil.convertToCamelCase(currentOptionSet.name)] = currentOptionSet;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const optionSetIds = [];
|
|
96
|
+
for(const key of keysInOrder) {
|
|
97
|
+
if (currentOptionSetsByName[key]) {
|
|
98
|
+
optionSetIds.push(currentOptionSetsByName[key].id);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
throw new Error(`No OptionSet found for ${key}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
optionSetHierarchy.maxDepth = keysInOrder.length;
|
|
106
|
+
optionSetHierarchy.hierarchy = optionSetIds;
|
|
107
|
+
|
|
108
|
+
for (const row of uploadedOptionHierarchyData) {
|
|
109
|
+
addRowToOptions(optionSetHierarchy, keysInOrder, row);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return optionSetHierarchy;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function parseAndSaveOptionSetHierarchiesLocalFiles(fileLocation) {
|
|
116
|
+
const optionSetHierarchy = await parseOptionSetHierarchiesLocalFiles(fileLocation);
|
|
117
|
+
saveData(optionSetHierarchy.options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function uploadOptionSetHierarchiesLocalFiles(fileLocation, name) {
|
|
121
|
+
const optionSetHierarchy = await parseOptionSetHierarchiesLocalFiles(fileLocation);
|
|
122
|
+
optionSetHierarchy.name = name;
|
|
123
|
+
|
|
124
|
+
const currentOptionSetHierarchies = await new Entities().get({
|
|
125
|
+
entityName: 'option-set-hierarchy',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const existing = currentOptionSetHierarchies.find(osh => osh.name === name);
|
|
129
|
+
|
|
130
|
+
if (existing) {
|
|
131
|
+
console.log('Updating: ', optionSetHierarchy.name);
|
|
132
|
+
await new Entities().update({
|
|
133
|
+
entityName: 'option-set-hierarchy',
|
|
134
|
+
id: existing.id,
|
|
135
|
+
object: optionSetHierarchy,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log('Creating: ', optionSetHierarchy.name);
|
|
140
|
+
await new Entities().create({
|
|
141
|
+
entityName: 'option-set-hierarchy',
|
|
142
|
+
object: optionSetHierarchy,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function addRowToOptions(optionSetHierarchy, keysInOrder, row) {
|
|
148
|
+
let options: any[] = optionSetHierarchy.options;
|
|
149
|
+
|
|
150
|
+
for (let i = 0; i < keysInOrder.length; i++) {
|
|
151
|
+
const optionValues = options.map(op => op.value);
|
|
152
|
+
const value = row[keysInOrder[i]];
|
|
153
|
+
if (!optionValues.includes(value)) {
|
|
154
|
+
const obj = i === (keysInOrder.length - 1) ? {value: value} : {value: value, options: []};
|
|
155
|
+
options.push(obj);
|
|
156
|
+
options = obj.options;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const optionObj = options.find(op => op.value === value);
|
|
160
|
+
options = optionObj.options;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function saveData(data) {
|
|
166
|
+
const CURRENT_DIR = process.cwd();
|
|
167
|
+
const separator = getSeparatorByPlatform();
|
|
168
|
+
fs.writeFileSync(CURRENT_DIR + separator + 'parsed_local_optionSetHierarchy.json', JSON.stringify(data), 'utf8');
|
|
169
|
+
}
|