@contrail/cli 1.0.6 → 1.0.9
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/apps/deploy-app/app-deployer.js +22 -4
- package/lib/cli.js +101 -17
- package/lib/types/interfaces.d.ts +1 -2
- package/lib/types/parse-optionSet.d.ts +2 -0
- package/lib/types/parse-optionSet.js +79 -0
- package/lib/types/types-extractor.js +12 -9
- package/lib/types/types-uploader.js +58 -1
- package/package.json +8 -3
- package/src/apps/deploy-app/app-deployer.ts +28 -4
- package/src/cli.ts +111 -17
- package/src/types/interfaces.ts +1 -2
- package/src/types/parse-optionSet.ts +96 -0
- package/src/types/types-extractor.ts +17 -12
- package/src/types/types-uploader.ts +64 -1
|
@@ -7,9 +7,11 @@ export class AppDeployer {
|
|
|
7
7
|
async deploy(appDeclaration) {
|
|
8
8
|
let app = await this.getExistingApp(appDeclaration);
|
|
9
9
|
if (!app) {
|
|
10
|
+
console.log(`%s Creating new app.`, chalk.yellow.bold('APP'));
|
|
10
11
|
app = await this.createNewApp(appDeclaration);
|
|
11
12
|
}
|
|
12
13
|
else {
|
|
14
|
+
console.log(`%s Updating existing app.`, chalk.yellow.bold('APP'));
|
|
13
15
|
app = await this.updateExistingApp(appDeclaration, app);
|
|
14
16
|
}
|
|
15
17
|
await new ActionsDeployer().deployActions(appDeclaration, app);
|
|
@@ -34,6 +36,14 @@ export class AppDeployer {
|
|
|
34
36
|
return apps.find(app => app.identifier === appDeclaration.appIdentifier);
|
|
35
37
|
}
|
|
36
38
|
async createNewApp(appDeclaration) {
|
|
39
|
+
let configProperties;
|
|
40
|
+
if (appDeclaration === null || appDeclaration === void 0 ? void 0 : appDeclaration.appConfiguration) {
|
|
41
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(appDeclaration.appConfiguration);
|
|
42
|
+
}
|
|
43
|
+
let triggerKeyDefinitions;
|
|
44
|
+
if (appDeclaration === null || appDeclaration === void 0 ? void 0 : appDeclaration.triggers) {
|
|
45
|
+
triggerKeyDefinitions = TriggerKeyDefinitionParser.buildTriggersArray(appDeclaration);
|
|
46
|
+
}
|
|
37
47
|
const newApp = {
|
|
38
48
|
identifier: appDeclaration.appIdentifier,
|
|
39
49
|
name: appDeclaration.appName,
|
|
@@ -43,14 +53,22 @@ export class AppDeployer {
|
|
|
43
53
|
thumbnail: appDeclaration.thumbnail,
|
|
44
54
|
eventWorkflowTriggerKeyMapping: appDeclaration.eventWorkflowTriggerKeyMapping,
|
|
45
55
|
keywords: appDeclaration.keywords,
|
|
46
|
-
configProperties
|
|
47
|
-
triggerKeyDefinitions
|
|
56
|
+
configProperties,
|
|
57
|
+
triggerKeyDefinitions,
|
|
48
58
|
};
|
|
49
59
|
const app = await new Entities().create({ entityName: 'app', object: newApp });
|
|
50
60
|
console.log(`%s Created new app: ${app.name}`, chalk.yellow.bold('APP'));
|
|
51
61
|
return app;
|
|
52
62
|
}
|
|
53
63
|
async updateExistingApp(appDeclaration, existingApp) {
|
|
64
|
+
let configProperties;
|
|
65
|
+
if (appDeclaration === null || appDeclaration === void 0 ? void 0 : appDeclaration.appConfiguration) {
|
|
66
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(appDeclaration.appConfiguration);
|
|
67
|
+
}
|
|
68
|
+
let triggerKeyDefinitions;
|
|
69
|
+
if (appDeclaration === null || appDeclaration === void 0 ? void 0 : appDeclaration.triggers) {
|
|
70
|
+
triggerKeyDefinitions = TriggerKeyDefinitionParser.buildTriggersArray(appDeclaration);
|
|
71
|
+
}
|
|
54
72
|
const updates = {
|
|
55
73
|
name: appDeclaration.appName,
|
|
56
74
|
public: appDeclaration.visibility === 'public',
|
|
@@ -59,8 +77,8 @@ export class AppDeployer {
|
|
|
59
77
|
thumbnail: appDeclaration.thumbnail,
|
|
60
78
|
eventWorkflowTriggerKeyMapping: appDeclaration.eventWorkflowTriggerKeyMapping,
|
|
61
79
|
keywords: appDeclaration.keywords,
|
|
62
|
-
configProperties
|
|
63
|
-
triggerKeyDefinitions
|
|
80
|
+
configProperties,
|
|
81
|
+
triggerKeyDefinitions,
|
|
64
82
|
};
|
|
65
83
|
const app = await new Entities().update({ entityName: 'app', id: existingApp.id, object: updates });
|
|
66
84
|
console.log(`%s Updated app: ${app.name}`, chalk.yellow.bold('APP'));
|
package/lib/cli.js
CHANGED
|
@@ -2,35 +2,119 @@ import { create } from './apps/create-app/create';
|
|
|
2
2
|
import { deployApp, removeApp } from './apps/deploy-app/deploy';
|
|
3
3
|
import { getTypes } from './types/types-extractor';
|
|
4
4
|
import { uploadTypes } from './types/types-uploader';
|
|
5
|
+
import { parseOptionSet } from './types/parse-optionSet';
|
|
6
|
+
const cliUsage = require('command-line-usage');
|
|
5
7
|
/**
|
|
6
8
|
* Built using documentation here:
|
|
7
9
|
* https://www.twilio.com/blog/how-to-build-a-cli-with-node-js
|
|
8
10
|
*/
|
|
9
11
|
export async function cli(args) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
const fullCommand = args[2];
|
|
13
|
+
let domain;
|
|
14
|
+
let command;
|
|
15
|
+
const commandParts = fullCommand.split('.');
|
|
16
|
+
if (!(commandParts === null || commandParts === void 0 ? void 0 : commandParts.length)) {
|
|
17
|
+
printIllegalCommand();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if ((commandParts === null || commandParts === void 0 ? void 0 : commandParts.length) < 2 && commandParts[0].toLowerCase() !== 'help' && commandParts[0].toLowerCase() !== '-help' && commandParts[0].toLowerCase() !== '--help') {
|
|
21
|
+
printIllegalCommand();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
domain = commandParts[0];
|
|
25
|
+
command = commandParts[1];
|
|
26
|
+
switch (domain) {
|
|
27
|
+
case 'app': {
|
|
28
|
+
switch (command) {
|
|
29
|
+
case 'create': {
|
|
30
|
+
create(args);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case 'deploy': {
|
|
34
|
+
deployApp(args);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'remove': {
|
|
38
|
+
removeApp(args);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
default: {
|
|
42
|
+
printIllegalCommand();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
break;
|
|
23
46
|
}
|
|
24
|
-
case '
|
|
25
|
-
|
|
47
|
+
case 'types': {
|
|
48
|
+
switch (command) {
|
|
49
|
+
case 'getAll': {
|
|
50
|
+
getTypes(args);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case 'upload': {
|
|
54
|
+
uploadTypes(args);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'parseOptionSet': {
|
|
58
|
+
parseOptionSet(args);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
default: {
|
|
62
|
+
printIllegalCommand();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
26
65
|
break;
|
|
27
66
|
}
|
|
28
|
-
case '
|
|
29
|
-
|
|
67
|
+
case 'help':
|
|
68
|
+
case '-help':
|
|
69
|
+
case '--help': {
|
|
70
|
+
printHelp();
|
|
30
71
|
break;
|
|
31
72
|
}
|
|
32
73
|
default: {
|
|
33
|
-
|
|
74
|
+
printIllegalCommand();
|
|
34
75
|
}
|
|
35
76
|
}
|
|
36
77
|
}
|
|
78
|
+
function printHelp() {
|
|
79
|
+
const sections = [
|
|
80
|
+
{
|
|
81
|
+
header: 'VibeIQ CLI',
|
|
82
|
+
content: 'From your friends at VibeIQ!',
|
|
83
|
+
},
|
|
84
|
+
...helpSections,
|
|
85
|
+
];
|
|
86
|
+
const usage = cliUsage(sections);
|
|
87
|
+
console.log(usage);
|
|
88
|
+
}
|
|
89
|
+
function printIllegalCommand() {
|
|
90
|
+
console.log('Illegal command.');
|
|
91
|
+
const usage = cliUsage(helpSections);
|
|
92
|
+
console.log(usage);
|
|
93
|
+
}
|
|
94
|
+
const helpSections = [
|
|
95
|
+
{
|
|
96
|
+
header: 'General Usage',
|
|
97
|
+
content: [
|
|
98
|
+
'Commands are run by pairing a domain with a command, joining them using a "."',
|
|
99
|
+
'e.g. app.create',
|
|
100
|
+
'',
|
|
101
|
+
'All <File Path> options should use the file path relative to the folder the command is being run in and include the file ext.',
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
header: 'App Commands - Domain: app',
|
|
106
|
+
content: [
|
|
107
|
+
'create Create a new app workspace.',
|
|
108
|
+
'deploy Deploy your app.',
|
|
109
|
+
'remove Remove your app.',
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
header: 'Type Commands - Domain: types',
|
|
114
|
+
content: [
|
|
115
|
+
'getAll Get all OptionSets and Types for your Org.',
|
|
116
|
+
'upload Upload OptionSets and Types for your Org.',
|
|
117
|
+
'parseOptionSet <File Path> Parse a CSV file into OptionSets.',
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
];
|
|
@@ -13,7 +13,7 @@ export interface ReducedTypeProperties {
|
|
|
13
13
|
label: string;
|
|
14
14
|
editable: boolean;
|
|
15
15
|
core: boolean;
|
|
16
|
-
|
|
16
|
+
typePropertyOptionSetName: string;
|
|
17
17
|
formula: string;
|
|
18
18
|
referencedTypeRootSlug: string;
|
|
19
19
|
defaultValue: string;
|
|
@@ -21,7 +21,6 @@ export interface ReducedTypeProperties {
|
|
|
21
21
|
inherited: boolean;
|
|
22
22
|
}
|
|
23
23
|
export interface ReducedOptionSet {
|
|
24
|
-
id: string;
|
|
25
24
|
name: string;
|
|
26
25
|
isArchived: boolean;
|
|
27
26
|
optionSet: any[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { StringUtil } from '@contrail/util';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { getSeparatorByPlatform } from '../common';
|
|
5
|
+
import * as csvToJson from 'csvtojson';
|
|
6
|
+
const yaml = require('js-yaml');
|
|
7
|
+
export async function parseOptionSet(args) {
|
|
8
|
+
try {
|
|
9
|
+
if ((args === null || args === void 0 ? void 0 : args.length) < 4) {
|
|
10
|
+
throw new Error('File name must be included.');
|
|
11
|
+
}
|
|
12
|
+
console.log(`%s Parsing`, chalk.yellow.bold('OPTION SETS'));
|
|
13
|
+
await parseOptionSets(args[3]);
|
|
14
|
+
console.log(`%s Parsed`, chalk.yellow.bold('OPTION SETS'));
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
console.log(`%s Parsing Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
console.log('%s Parsing Complete.', chalk.green.bold('COMPLETE'));
|
|
21
|
+
}
|
|
22
|
+
async function loadDataFromCSV(fileName) {
|
|
23
|
+
const CURRENT_DIR = process.cwd();
|
|
24
|
+
try {
|
|
25
|
+
const separator = getSeparatorByPlatform();
|
|
26
|
+
const csvData = await csvToJson.default().fromFile(CURRENT_DIR + separator + fileName);
|
|
27
|
+
return csvData;
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
throw new Error(`ERROR PARSING CSV: ${e}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function parseOptionSets(fileName) {
|
|
34
|
+
const csvData = await loadDataFromCSV(fileName);
|
|
35
|
+
const removedFields = new Set();
|
|
36
|
+
const valuesByFieldName = {};
|
|
37
|
+
for (const row of csvData) {
|
|
38
|
+
for (const [key, value] of Object.entries(row)) {
|
|
39
|
+
// Dont parse boolean fields - later log removed fields
|
|
40
|
+
if (value.toLowerCase() === 'false' || value.toLowerCase() === 'true') {
|
|
41
|
+
removedFields.add(key);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!value) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!valuesByFieldName[key]) {
|
|
48
|
+
valuesByFieldName[key] = new Set();
|
|
49
|
+
}
|
|
50
|
+
valuesByFieldName[key].add(value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const removed of removedFields.values()) {
|
|
54
|
+
console.log(`Removed Boolean Field: ${removed}`);
|
|
55
|
+
}
|
|
56
|
+
const reducedOptionSets = [];
|
|
57
|
+
for (const [key, value] of Object.entries(valuesByFieldName)) {
|
|
58
|
+
const optionSet = [];
|
|
59
|
+
for (const entry of value.values()) {
|
|
60
|
+
optionSet.push({
|
|
61
|
+
value: StringUtil.convertToCamelCase(entry),
|
|
62
|
+
display: isUpperCase(entry) ? entry : StringUtil.convertToTitleCase(entry),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const reducedOptionSet = {
|
|
66
|
+
name: StringUtil.convertToTitleCase(key),
|
|
67
|
+
isArchived: false,
|
|
68
|
+
optionSet,
|
|
69
|
+
};
|
|
70
|
+
reducedOptionSets.push(reducedOptionSet);
|
|
71
|
+
}
|
|
72
|
+
const yamlStr = yaml.dump(reducedOptionSets);
|
|
73
|
+
const CURRENT_DIR = process.cwd();
|
|
74
|
+
const separator = getSeparatorByPlatform();
|
|
75
|
+
fs.writeFileSync(CURRENT_DIR + separator + 'parsed_option_sets.yml', yamlStr, 'utf8');
|
|
76
|
+
}
|
|
77
|
+
function isUpperCase(str) {
|
|
78
|
+
return str === str.toUpperCase();
|
|
79
|
+
}
|
|
@@ -9,10 +9,13 @@ export async function getTypes(args) {
|
|
|
9
9
|
const userConfig = await authUser();
|
|
10
10
|
console.log(`%s Getting all types for orgId: ${userConfig.orgId}`, chalk.blue.bold('START'));
|
|
11
11
|
console.log(`%s Retrieving`, chalk.yellow.bold('OPTION SETS'));
|
|
12
|
-
await
|
|
12
|
+
const optionSets = await new Entities().get({
|
|
13
|
+
entityName: 'type-property-option-set',
|
|
14
|
+
});
|
|
15
|
+
await getOptionSets(optionSets);
|
|
13
16
|
console.log(`%s Retrieved`, chalk.yellow.bold('OPTION SETS'));
|
|
14
17
|
console.log(`%s Retrieving`, chalk.yellow.bold('TYPES'));
|
|
15
|
-
await getReducedTypes();
|
|
18
|
+
await getReducedTypes(optionSets);
|
|
16
19
|
console.log(`%s Retrieved`, chalk.yellow.bold('TYPES'));
|
|
17
20
|
}
|
|
18
21
|
catch (e) {
|
|
@@ -21,15 +24,11 @@ export async function getTypes(args) {
|
|
|
21
24
|
}
|
|
22
25
|
console.log('%s Types Retrieval Complete.', chalk.green.bold('COMPLETE'));
|
|
23
26
|
}
|
|
24
|
-
async function getOptionSets() {
|
|
25
|
-
const optionSets = await new Entities().get({
|
|
26
|
-
entityName: 'type-property-option-set',
|
|
27
|
-
});
|
|
27
|
+
async function getOptionSets(optionSets) {
|
|
28
28
|
let reducedOptionSets = [];
|
|
29
29
|
for (const optionSet of optionSets) {
|
|
30
30
|
const reducedOptionSet = {
|
|
31
31
|
name: optionSet.name,
|
|
32
|
-
id: optionSet.id,
|
|
33
32
|
isArchived: optionSet.isArchived,
|
|
34
33
|
optionSet: optionSet.optionSet,
|
|
35
34
|
};
|
|
@@ -44,7 +43,7 @@ async function getOptionSets() {
|
|
|
44
43
|
const yamlStr = yaml.dump(optionSetsHolder);
|
|
45
44
|
fs.writeFileSync(CURRENT_DIR + separator + 'option_sets.yml', yamlStr, 'utf8');
|
|
46
45
|
}
|
|
47
|
-
async function getReducedTypes() {
|
|
46
|
+
async function getReducedTypes(optionSets) {
|
|
48
47
|
const types = await new Entities().get({
|
|
49
48
|
entityName: 'type',
|
|
50
49
|
});
|
|
@@ -56,6 +55,10 @@ async function getReducedTypes() {
|
|
|
56
55
|
id: type.id,
|
|
57
56
|
relations: ['typeProperties'],
|
|
58
57
|
});
|
|
58
|
+
const optionSetNameById = {};
|
|
59
|
+
for (const optionSet of optionSets) {
|
|
60
|
+
optionSetNameById[optionSet.id] = optionSet.name;
|
|
61
|
+
}
|
|
59
62
|
const reducedTypeProperties = [];
|
|
60
63
|
for (const typeProperty of typeWithProperties.typeProperties) {
|
|
61
64
|
const reducedTypeProperty = {
|
|
@@ -66,7 +69,7 @@ async function getReducedTypes() {
|
|
|
66
69
|
label: typeProperty.label,
|
|
67
70
|
editable: typeProperty.editable,
|
|
68
71
|
core: typeProperty.core,
|
|
69
|
-
|
|
72
|
+
typePropertyOptionSetName: optionSetNameById[typeProperty.typePropertyOptionSetId],
|
|
70
73
|
formula: typeProperty.formula,
|
|
71
74
|
referencedTypeRootSlug: typeProperty.referencedTypeRootSlug,
|
|
72
75
|
defaultValue: typeProperty.defaultValue,
|
|
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { authUser } from '../auth/login';
|
|
6
6
|
import { getSeparatorByPlatform } from '../common';
|
|
7
|
+
import * as _ from 'lodash';
|
|
7
8
|
const yaml = require('js-yaml');
|
|
8
9
|
export async function uploadTypes(args) {
|
|
9
10
|
try {
|
|
@@ -119,6 +120,13 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
119
120
|
for (const newPropertyType of newPropertyTypes) {
|
|
120
121
|
newPropertyTypesBySlug[newPropertyType.slug] = newPropertyType;
|
|
121
122
|
}
|
|
123
|
+
const optionSets = await new Entities().get({
|
|
124
|
+
entityName: 'type-property-option-set',
|
|
125
|
+
});
|
|
126
|
+
const optionSetIdByName = {};
|
|
127
|
+
for (const optionSet of optionSets) {
|
|
128
|
+
optionSetIdByName[optionSet.name] = optionSet.id;
|
|
129
|
+
}
|
|
122
130
|
for (const newPropertyType of newPropertyTypes) {
|
|
123
131
|
try {
|
|
124
132
|
const found = currentPropertyTypeBySlug[newPropertyType.slug];
|
|
@@ -133,7 +141,7 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
133
141
|
label: newPropertyType.label,
|
|
134
142
|
editable: newPropertyType.editable,
|
|
135
143
|
core: newPropertyType.core,
|
|
136
|
-
typePropertyOptionSetId: newPropertyType.
|
|
144
|
+
typePropertyOptionSetId: optionSetIdByName[newPropertyType.typePropertyOptionSetName],
|
|
137
145
|
formula: newPropertyType.formula,
|
|
138
146
|
referencedTypeRootSlug: newPropertyType.referencedTypeRootSlug,
|
|
139
147
|
defaultValue: newPropertyType.defaultValue,
|
|
@@ -145,6 +153,26 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
145
153
|
},
|
|
146
154
|
});
|
|
147
155
|
}
|
|
156
|
+
else if (found && !newPropertyType.inherited && !typePropertiesEqual(found, newPropertyType, optionSetIdByName)) {
|
|
157
|
+
console.log('Updating Type-Property: ', newPropertyType.slug);
|
|
158
|
+
await new Entities().update({
|
|
159
|
+
entityName: 'type-property',
|
|
160
|
+
id: found.id,
|
|
161
|
+
object: {
|
|
162
|
+
isArchived: newPropertyType.isArchived,
|
|
163
|
+
propertyType: newPropertyType.propertyType,
|
|
164
|
+
slug: newPropertyType.slug,
|
|
165
|
+
label: newPropertyType.label,
|
|
166
|
+
editable: newPropertyType.editable,
|
|
167
|
+
core: newPropertyType.core,
|
|
168
|
+
typePropertyOptionSetId: optionSetIdByName[newPropertyType.typePropertyOptionSetName],
|
|
169
|
+
formula: newPropertyType.formula,
|
|
170
|
+
referencedTypeRootSlug: newPropertyType.referencedTypeRootSlug,
|
|
171
|
+
defaultValue: newPropertyType.defaultValue,
|
|
172
|
+
propertyLevel: newPropertyType.propertyLevel,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
148
176
|
}
|
|
149
177
|
catch (err) {
|
|
150
178
|
console.log('Error Creating Properties: ', err);
|
|
@@ -169,3 +197,32 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
169
197
|
}
|
|
170
198
|
}
|
|
171
199
|
}
|
|
200
|
+
function typePropertiesEqual(typeProperty1, typeProperty2, optionSetIdByName) {
|
|
201
|
+
const reducedProperty1 = {
|
|
202
|
+
isArchived: typeProperty1.isArchived,
|
|
203
|
+
propertyType: typeProperty1.propertyType,
|
|
204
|
+
slug: typeProperty1.slug,
|
|
205
|
+
label: typeProperty1.label,
|
|
206
|
+
editable: typeProperty1.editable,
|
|
207
|
+
core: typeProperty1.core,
|
|
208
|
+
typePropertyOptionSetId: optionSetIdByName[typeProperty1.typePropertyOptionSetName],
|
|
209
|
+
formula: typeProperty1.formula,
|
|
210
|
+
referencedTypeRootSlug: typeProperty1.referencedTypeRootSlug,
|
|
211
|
+
defaultValue: typeProperty1.defaultValue,
|
|
212
|
+
propertyLevel: typeProperty1.propertyLevel,
|
|
213
|
+
};
|
|
214
|
+
const reducedProperty2 = {
|
|
215
|
+
isArchived: typeProperty2.isArchived,
|
|
216
|
+
propertyType: typeProperty2.propertyType,
|
|
217
|
+
slug: typeProperty2.slug,
|
|
218
|
+
label: typeProperty2.label,
|
|
219
|
+
editable: typeProperty2.editable,
|
|
220
|
+
core: typeProperty2.core,
|
|
221
|
+
typePropertyOptionSetId: optionSetIdByName[typeProperty2.typePropertyOptionSetName],
|
|
222
|
+
formula: typeProperty2.formula,
|
|
223
|
+
referencedTypeRootSlug: typeProperty2.referencedTypeRootSlug,
|
|
224
|
+
defaultValue: typeProperty2.defaultValue,
|
|
225
|
+
propertyLevel: typeProperty2.propertyLevel,
|
|
226
|
+
};
|
|
227
|
+
return _.isEqual(reducedProperty1, reducedProperty2);
|
|
228
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrail/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "VibeIQ's Contrail Platform CLI.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,27 +24,32 @@
|
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@contrail/sdk": "^1.0.91",
|
|
27
|
+
"@contrail/util": "^1.0.17",
|
|
27
28
|
"amazon-cognito-identity-js": "^5.2.3",
|
|
28
29
|
"arg": "^4.0.1",
|
|
29
30
|
"aws-sdk": "^2.1044.0",
|
|
30
31
|
"chalk": "^4.0.0",
|
|
32
|
+
"command-line-usage": "^6.1.1",
|
|
33
|
+
"csvtojson": "^2.0.10",
|
|
31
34
|
"esm": "^3.2.25",
|
|
32
35
|
"execa": "^5.0.0",
|
|
33
36
|
"inquirer": "^8.2.0",
|
|
34
37
|
"js-yaml": "^4.1.0",
|
|
35
38
|
"listr": "^0.14.3",
|
|
39
|
+
"lodash": "^4.17.21",
|
|
36
40
|
"ncp": "^2.0.0",
|
|
37
41
|
"pkg-install": "^1.0.0"
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"@types/jest": "^23.3.14",
|
|
45
|
+
"@types/lodash": "^4.14.178",
|
|
46
|
+
"@types/node": "^14.6.4",
|
|
41
47
|
"jest": "^23.6.0",
|
|
42
48
|
"prettier": "^1.19.1",
|
|
43
49
|
"ts-jest": "^23.10.5",
|
|
44
50
|
"tslint": "^5.11.0",
|
|
45
51
|
"tslint-config-prettier": "^1.18.0",
|
|
46
|
-
"typescript": "^3.0.1"
|
|
47
|
-
"@types/node": "^14.6.4"
|
|
52
|
+
"typescript": "^3.0.1"
|
|
48
53
|
},
|
|
49
54
|
"jest": {
|
|
50
55
|
"moduleFileExtensions": [
|
|
@@ -9,8 +9,10 @@ export class AppDeployer {
|
|
|
9
9
|
public async deploy(appDeclaration: AppDeclaration) {
|
|
10
10
|
let app = await this.getExistingApp(appDeclaration);
|
|
11
11
|
if (!app) {
|
|
12
|
+
console.log(`%s Creating new app.`, chalk.yellow.bold('APP'));
|
|
12
13
|
app = await this.createNewApp(appDeclaration);
|
|
13
14
|
} else {
|
|
15
|
+
console.log(`%s Updating existing app.`, chalk.yellow.bold('APP'));
|
|
14
16
|
app = await this.updateExistingApp(appDeclaration, app);
|
|
15
17
|
}
|
|
16
18
|
await new ActionsDeployer().deployActions(appDeclaration, app);
|
|
@@ -39,6 +41,17 @@ export class AppDeployer {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
private async createNewApp(appDeclaration: AppDeclaration) {
|
|
44
|
+
|
|
45
|
+
let configProperties;
|
|
46
|
+
if (appDeclaration?.appConfiguration) {
|
|
47
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(appDeclaration.appConfiguration);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let triggerKeyDefinitions;
|
|
51
|
+
if (appDeclaration?.triggers) {
|
|
52
|
+
triggerKeyDefinitions = TriggerKeyDefinitionParser.buildTriggersArray(appDeclaration);
|
|
53
|
+
}
|
|
54
|
+
|
|
42
55
|
const newApp = {
|
|
43
56
|
identifier: appDeclaration.appIdentifier,
|
|
44
57
|
name: appDeclaration.appName,
|
|
@@ -48,8 +61,8 @@ export class AppDeployer {
|
|
|
48
61
|
thumbnail: appDeclaration.thumbnail,
|
|
49
62
|
eventWorkflowTriggerKeyMapping: appDeclaration.eventWorkflowTriggerKeyMapping,
|
|
50
63
|
keywords: appDeclaration.keywords,
|
|
51
|
-
configProperties
|
|
52
|
-
triggerKeyDefinitions
|
|
64
|
+
configProperties,
|
|
65
|
+
triggerKeyDefinitions,
|
|
53
66
|
};
|
|
54
67
|
const app = await new Entities().create({ entityName: 'app', object: newApp });
|
|
55
68
|
console.log(`%s Created new app: ${app.name}`, chalk.yellow.bold('APP'));
|
|
@@ -57,6 +70,17 @@ export class AppDeployer {
|
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
public async updateExistingApp(appDeclaration: AppDeclaration, existingApp) {
|
|
73
|
+
|
|
74
|
+
let configProperties;
|
|
75
|
+
if (appDeclaration?.appConfiguration) {
|
|
76
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(appDeclaration.appConfiguration);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let triggerKeyDefinitions;
|
|
80
|
+
if (appDeclaration?.triggers) {
|
|
81
|
+
triggerKeyDefinitions = TriggerKeyDefinitionParser.buildTriggersArray(appDeclaration);
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
const updates = {
|
|
61
85
|
name: appDeclaration.appName,
|
|
62
86
|
public: appDeclaration.visibility === 'public',
|
|
@@ -65,8 +89,8 @@ export class AppDeployer {
|
|
|
65
89
|
thumbnail: appDeclaration.thumbnail,
|
|
66
90
|
eventWorkflowTriggerKeyMapping: appDeclaration.eventWorkflowTriggerKeyMapping,
|
|
67
91
|
keywords: appDeclaration.keywords,
|
|
68
|
-
configProperties
|
|
69
|
-
triggerKeyDefinitions
|
|
92
|
+
configProperties,
|
|
93
|
+
triggerKeyDefinitions,
|
|
70
94
|
};
|
|
71
95
|
const app = await new Entities().update({ entityName: 'app', id: existingApp.id, object: updates });
|
|
72
96
|
console.log(`%s Updated app: ${app.name}`, chalk.yellow.bold('APP'));
|
package/src/cli.ts
CHANGED
|
@@ -3,36 +3,130 @@ import { create } from './apps/create-app/create';
|
|
|
3
3
|
import { deployApp, removeApp } from './apps/deploy-app/deploy';
|
|
4
4
|
import { getTypes } from './types/types-extractor';
|
|
5
5
|
import { uploadTypes } from './types/types-uploader';
|
|
6
|
+
import { parseOptionSet } from './types/parse-optionSet';
|
|
7
|
+
const cliUsage = require('command-line-usage');
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* Built using documentation here:
|
|
8
11
|
* https://www.twilio.com/blog/how-to-build-a-cli-with-node-js
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
14
|
export async function cli(args) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
const fullCommand = args[2];
|
|
16
|
+
let domain;
|
|
17
|
+
let command;
|
|
18
|
+
const commandParts = fullCommand.split('.');
|
|
19
|
+
|
|
20
|
+
if (!commandParts?.length) {
|
|
21
|
+
printIllegalCommand();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (commandParts?.length < 2 && commandParts[0].toLowerCase() !== 'help' && commandParts[0].toLowerCase() !== '-help' && commandParts[0].toLowerCase() !== '--help') {
|
|
26
|
+
printIllegalCommand();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
domain = commandParts[0];
|
|
31
|
+
command = commandParts[1];
|
|
32
|
+
|
|
33
|
+
switch (domain) {
|
|
34
|
+
case 'app': {
|
|
35
|
+
switch (command) {
|
|
36
|
+
case 'create': {
|
|
37
|
+
create(args);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case 'deploy': {
|
|
41
|
+
deployApp(args);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'remove': {
|
|
45
|
+
removeApp(args);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
default: {
|
|
49
|
+
printIllegalCommand();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
24
52
|
break;
|
|
25
53
|
}
|
|
26
|
-
case '
|
|
27
|
-
|
|
54
|
+
case 'types': {
|
|
55
|
+
switch (command) {
|
|
56
|
+
case 'getAll': {
|
|
57
|
+
getTypes(args);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'upload': {
|
|
61
|
+
uploadTypes(args);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'parseOptionSet': {
|
|
65
|
+
parseOptionSet(args);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
default: {
|
|
69
|
+
printIllegalCommand();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
28
72
|
break;
|
|
29
73
|
}
|
|
30
|
-
case '
|
|
31
|
-
|
|
74
|
+
case 'help':
|
|
75
|
+
case '-help':
|
|
76
|
+
case '--help': {
|
|
77
|
+
printHelp();
|
|
32
78
|
break;
|
|
33
79
|
}
|
|
34
80
|
default: {
|
|
35
|
-
|
|
81
|
+
printIllegalCommand();
|
|
36
82
|
}
|
|
37
83
|
}
|
|
38
84
|
}
|
|
85
|
+
|
|
86
|
+
function printHelp() {
|
|
87
|
+
const sections = [
|
|
88
|
+
{
|
|
89
|
+
header: 'VibeIQ CLI',
|
|
90
|
+
content: 'From your friends at VibeIQ!',
|
|
91
|
+
},
|
|
92
|
+
...helpSections,
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const usage = cliUsage(sections);
|
|
96
|
+
console.log(usage);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function printIllegalCommand() {
|
|
100
|
+
console.log('Illegal command.');
|
|
101
|
+
|
|
102
|
+
const usage = cliUsage(helpSections);
|
|
103
|
+
console.log(usage);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const helpSections = [
|
|
107
|
+
{
|
|
108
|
+
header: 'General Usage',
|
|
109
|
+
content: [
|
|
110
|
+
'Commands are run by pairing a domain with a command, joining them using a "."',
|
|
111
|
+
'e.g. app.create',
|
|
112
|
+
'',
|
|
113
|
+
'All <File Path> options should use the file path relative to the folder the command is being run in and include the file ext.',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
header: 'App Commands - Domain: app',
|
|
118
|
+
content: [
|
|
119
|
+
'create Create a new app workspace.',
|
|
120
|
+
'deploy Deploy your app.',
|
|
121
|
+
'remove Remove your app.',
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
header: 'Type Commands - Domain: types',
|
|
126
|
+
content: [
|
|
127
|
+
'getAll Get all OptionSets and Types for your Org.',
|
|
128
|
+
'upload Upload OptionSets and Types for your Org.',
|
|
129
|
+
'parseOptionSet <File Path> Parse a CSV file into OptionSets.',
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
];
|
package/src/types/interfaces.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface ReducedTypeProperties {
|
|
|
14
14
|
label: string;
|
|
15
15
|
editable: boolean;
|
|
16
16
|
core: boolean;
|
|
17
|
-
|
|
17
|
+
typePropertyOptionSetName: string;
|
|
18
18
|
formula: string;
|
|
19
19
|
referencedTypeRootSlug: string;
|
|
20
20
|
defaultValue: string;
|
|
@@ -23,7 +23,6 @@ export interface ReducedTypeProperties {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface ReducedOptionSet {
|
|
26
|
-
id: string;
|
|
27
26
|
name: string;
|
|
28
27
|
isArchived: boolean;
|
|
29
28
|
optionSet: any[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { StringUtil } from '@contrail/util';
|
|
2
|
+
import { ReducedOptionSet } from './interfaces';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { getSeparatorByPlatform } from '../common';
|
|
6
|
+
import * as csvToJson from 'csvtojson';
|
|
7
|
+
|
|
8
|
+
const yaml = require('js-yaml');
|
|
9
|
+
|
|
10
|
+
export async function parseOptionSet(args) {
|
|
11
|
+
try {
|
|
12
|
+
if (args?.length < 4) {
|
|
13
|
+
throw new Error('File name must be included.');
|
|
14
|
+
}
|
|
15
|
+
console.log(`%s Parsing`, chalk.yellow.bold('OPTION SETS'));
|
|
16
|
+
await parseOptionSets(args[3]);
|
|
17
|
+
console.log(`%s Parsed`, chalk.yellow.bold('OPTION SETS'));
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
console.log(`%s Parsing Failed. ${e}`, chalk.red.bold('FAIL'));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log('%s Parsing Complete.', chalk.green.bold('COMPLETE'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function loadDataFromCSV(fileName) {
|
|
29
|
+
const CURRENT_DIR = process.cwd();
|
|
30
|
+
try {
|
|
31
|
+
const separator = getSeparatorByPlatform();
|
|
32
|
+
const csvData = await csvToJson.default().fromFile(CURRENT_DIR + separator + fileName);
|
|
33
|
+
|
|
34
|
+
return csvData;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
throw new Error(`ERROR PARSING CSV: ${e}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function parseOptionSets(fileName) {
|
|
41
|
+
const csvData = await loadDataFromCSV(fileName);
|
|
42
|
+
|
|
43
|
+
const removedFields = new Set<string>();
|
|
44
|
+
const valuesByFieldName = {};
|
|
45
|
+
for (const row of csvData) {
|
|
46
|
+
for (const [key, value] of Object.entries(row)) {
|
|
47
|
+
|
|
48
|
+
// Dont parse boolean fields - later log removed fields
|
|
49
|
+
if ((value as string).toLowerCase() === 'false' || (value as string).toLowerCase() === 'true') {
|
|
50
|
+
removedFields.add(key);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!value) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!valuesByFieldName[key]) {
|
|
59
|
+
valuesByFieldName[key] = new Set<string>();
|
|
60
|
+
}
|
|
61
|
+
(valuesByFieldName[key] as Set<string>).add(value as string);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const removed of removedFields.values()) {
|
|
66
|
+
console.log(`Removed Boolean Field: ${removed}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const reducedOptionSets = [];
|
|
70
|
+
for (const [key, value] of Object.entries(valuesByFieldName)) {
|
|
71
|
+
const optionSet = [];
|
|
72
|
+
for (const entry of (value as Set<string>).values()) {
|
|
73
|
+
optionSet.push({
|
|
74
|
+
value: StringUtil.convertToCamelCase(entry),
|
|
75
|
+
display: isUpperCase(entry) ? entry : StringUtil.convertToTitleCase(entry), // if all uppercase dont convert
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const reducedOptionSet: ReducedOptionSet = {
|
|
80
|
+
name: StringUtil.convertToTitleCase(key),
|
|
81
|
+
isArchived: false,
|
|
82
|
+
optionSet,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
reducedOptionSets.push(reducedOptionSet);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const yamlStr = yaml.dump(reducedOptionSets);
|
|
89
|
+
const CURRENT_DIR = process.cwd();
|
|
90
|
+
const separator = getSeparatorByPlatform();
|
|
91
|
+
fs.writeFileSync(CURRENT_DIR + separator + 'parsed_option_sets.yml', yamlStr, 'utf8');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isUpperCase(str) {
|
|
95
|
+
return str === str.toUpperCase();
|
|
96
|
+
}
|
|
@@ -11,13 +11,18 @@ export async function getTypes(args) {
|
|
|
11
11
|
try {
|
|
12
12
|
const userConfig = await authUser();
|
|
13
13
|
console.log(`%s Getting all types for orgId: ${userConfig.orgId}`, chalk.blue.bold('START'));
|
|
14
|
-
|
|
15
14
|
console.log(`%s Retrieving`, chalk.yellow.bold('OPTION SETS'));
|
|
16
|
-
await getOptionSets();
|
|
17
|
-
console.log(`%s Retrieved`, chalk.yellow.bold('OPTION SETS'));
|
|
18
15
|
|
|
16
|
+
const optionSets = await new Entities().get({
|
|
17
|
+
entityName: 'type-property-option-set',
|
|
18
|
+
});
|
|
19
|
+
await getOptionSets(optionSets);
|
|
20
|
+
|
|
21
|
+
console.log(`%s Retrieved`, chalk.yellow.bold('OPTION SETS'));
|
|
19
22
|
console.log(`%s Retrieving`, chalk.yellow.bold('TYPES'));
|
|
20
|
-
|
|
23
|
+
|
|
24
|
+
await getReducedTypes(optionSets);
|
|
25
|
+
|
|
21
26
|
console.log(`%s Retrieved`, chalk.yellow.bold('TYPES'));
|
|
22
27
|
|
|
23
28
|
}
|
|
@@ -29,16 +34,11 @@ export async function getTypes(args) {
|
|
|
29
34
|
console.log('%s Types Retrieval Complete.', chalk.green.bold('COMPLETE'));
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
async function getOptionSets() {
|
|
33
|
-
const optionSets = await new Entities().get({
|
|
34
|
-
entityName: 'type-property-option-set',
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
+
async function getOptionSets(optionSets) {
|
|
37
38
|
let reducedOptionSets = [];
|
|
38
39
|
for (const optionSet of optionSets) {
|
|
39
40
|
const reducedOptionSet: ReducedOptionSet = {
|
|
40
41
|
name: optionSet.name,
|
|
41
|
-
id: optionSet.id,
|
|
42
42
|
isArchived: optionSet.isArchived,
|
|
43
43
|
optionSet: optionSet.optionSet,
|
|
44
44
|
};
|
|
@@ -59,7 +59,7 @@ async function getOptionSets() {
|
|
|
59
59
|
fs.writeFileSync(CURRENT_DIR + separator + 'option_sets.yml', yamlStr, 'utf8');
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
async function getReducedTypes() {
|
|
62
|
+
async function getReducedTypes(optionSets) {
|
|
63
63
|
const types = await new Entities().get({
|
|
64
64
|
entityName: 'type',
|
|
65
65
|
});
|
|
@@ -74,6 +74,11 @@ async function getReducedTypes() {
|
|
|
74
74
|
relations: ['typeProperties'],
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
const optionSetNameById = {};
|
|
78
|
+
for (const optionSet of optionSets) {
|
|
79
|
+
optionSetNameById[optionSet.id] = optionSet.name;
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
const reducedTypeProperties = [];
|
|
78
83
|
for (const typeProperty of typeWithProperties.typeProperties) {
|
|
79
84
|
const reducedTypeProperty: ReducedTypeProperties = {
|
|
@@ -84,7 +89,7 @@ async function getReducedTypes() {
|
|
|
84
89
|
label: typeProperty.label,
|
|
85
90
|
editable: typeProperty.editable,
|
|
86
91
|
core: typeProperty.core,
|
|
87
|
-
|
|
92
|
+
typePropertyOptionSetName: optionSetNameById[typeProperty.typePropertyOptionSetId],
|
|
88
93
|
formula: typeProperty.formula,
|
|
89
94
|
referencedTypeRootSlug: typeProperty.referencedTypeRootSlug,
|
|
90
95
|
defaultValue: typeProperty.defaultValue,
|
|
@@ -5,6 +5,7 @@ import chalk from 'chalk';
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import { authUser } from '../auth/login';
|
|
7
7
|
import { getSeparatorByPlatform } from '../common';
|
|
8
|
+
import * as _ from 'lodash';
|
|
8
9
|
|
|
9
10
|
const yaml = require('js-yaml');
|
|
10
11
|
|
|
@@ -141,6 +142,15 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
141
142
|
newPropertyTypesBySlug[newPropertyType.slug] = newPropertyType;
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
const optionSets = await new Entities().get({
|
|
146
|
+
entityName: 'type-property-option-set',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const optionSetIdByName = {};
|
|
150
|
+
for (const optionSet of optionSets) {
|
|
151
|
+
optionSetIdByName[optionSet.name] = optionSet.id;
|
|
152
|
+
}
|
|
153
|
+
|
|
144
154
|
for (const newPropertyType of newPropertyTypes) {
|
|
145
155
|
try {
|
|
146
156
|
const found = currentPropertyTypeBySlug[newPropertyType.slug];
|
|
@@ -158,7 +168,7 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
158
168
|
label: newPropertyType.label,
|
|
159
169
|
editable: newPropertyType.editable,
|
|
160
170
|
core: newPropertyType.core,
|
|
161
|
-
typePropertyOptionSetId: newPropertyType.
|
|
171
|
+
typePropertyOptionSetId: optionSetIdByName[newPropertyType.typePropertyOptionSetName],
|
|
162
172
|
formula: newPropertyType.formula,
|
|
163
173
|
referencedTypeRootSlug: newPropertyType.referencedTypeRootSlug,
|
|
164
174
|
defaultValue: newPropertyType.defaultValue,
|
|
@@ -170,6 +180,26 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
170
180
|
},
|
|
171
181
|
});
|
|
172
182
|
}
|
|
183
|
+
else if (found && !newPropertyType.inherited && !typePropertiesEqual(found, newPropertyType, optionSetIdByName)) {
|
|
184
|
+
console.log('Updating Type-Property: ', newPropertyType.slug);
|
|
185
|
+
await new Entities().update({
|
|
186
|
+
entityName: 'type-property',
|
|
187
|
+
id: found.id,
|
|
188
|
+
object: {
|
|
189
|
+
isArchived: newPropertyType.isArchived,
|
|
190
|
+
propertyType: newPropertyType.propertyType,
|
|
191
|
+
slug: newPropertyType.slug,
|
|
192
|
+
label: newPropertyType.label,
|
|
193
|
+
editable: newPropertyType.editable,
|
|
194
|
+
core: newPropertyType.core,
|
|
195
|
+
typePropertyOptionSetId: optionSetIdByName[newPropertyType.typePropertyOptionSetName],
|
|
196
|
+
formula: newPropertyType.formula,
|
|
197
|
+
referencedTypeRootSlug: newPropertyType.referencedTypeRootSlug,
|
|
198
|
+
defaultValue: newPropertyType.defaultValue,
|
|
199
|
+
propertyLevel: newPropertyType.propertyLevel,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
173
203
|
|
|
174
204
|
} catch (err) {
|
|
175
205
|
console.log('Error Creating Properties: ', err);
|
|
@@ -195,4 +225,37 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
195
225
|
throw new Error(`Error Deleting Properties: ${err}`);
|
|
196
226
|
}
|
|
197
227
|
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function typePropertiesEqual(typeProperty1, typeProperty2, optionSetIdByName): boolean {
|
|
231
|
+
|
|
232
|
+
const reducedProperty1 = {
|
|
233
|
+
isArchived: typeProperty1.isArchived,
|
|
234
|
+
propertyType: typeProperty1.propertyType,
|
|
235
|
+
slug: typeProperty1.slug,
|
|
236
|
+
label: typeProperty1.label,
|
|
237
|
+
editable: typeProperty1.editable,
|
|
238
|
+
core: typeProperty1.core,
|
|
239
|
+
typePropertyOptionSetId: optionSetIdByName[typeProperty1.typePropertyOptionSetName],
|
|
240
|
+
formula: typeProperty1.formula,
|
|
241
|
+
referencedTypeRootSlug: typeProperty1.referencedTypeRootSlug,
|
|
242
|
+
defaultValue: typeProperty1.defaultValue,
|
|
243
|
+
propertyLevel: typeProperty1.propertyLevel,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const reducedProperty2 = {
|
|
247
|
+
isArchived: typeProperty2.isArchived,
|
|
248
|
+
propertyType: typeProperty2.propertyType,
|
|
249
|
+
slug: typeProperty2.slug,
|
|
250
|
+
label: typeProperty2.label,
|
|
251
|
+
editable: typeProperty2.editable,
|
|
252
|
+
core: typeProperty2.core,
|
|
253
|
+
typePropertyOptionSetId: optionSetIdByName[typeProperty2.typePropertyOptionSetName],
|
|
254
|
+
formula: typeProperty2.formula,
|
|
255
|
+
referencedTypeRootSlug: typeProperty2.referencedTypeRootSlug,
|
|
256
|
+
defaultValue: typeProperty2.defaultValue,
|
|
257
|
+
propertyLevel: typeProperty2.propertyLevel,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return _.isEqual(reducedProperty1, reducedProperty2);
|
|
198
261
|
}
|