@extrahorizon/exh-cli 1.5.0
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/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/build/commands/completion.d.ts +5 -0
- package/build/commands/completion.js +38 -0
- package/build/commands/data/schemas/delete.d.ts +10 -0
- package/build/commands/data/schemas/delete.js +29 -0
- package/build/commands/data/schemas/list.d.ts +8 -0
- package/build/commands/data/schemas/list.js +20 -0
- package/build/commands/data/schemas/sync/statusHelpers.d.ts +3 -0
- package/build/commands/data/schemas/sync/statusHelpers.js +49 -0
- package/build/commands/data/schemas/sync.d.ts +35 -0
- package/build/commands/data/schemas/sync.js +72 -0
- package/build/commands/data/schemas/util/listFilesInDir.d.ts +1 -0
- package/build/commands/data/schemas/util/listFilesInDir.js +25 -0
- package/build/commands/data/schemas/util/metaschema.json +455 -0
- package/build/commands/data/schemas/util/readJson.d.ts +1 -0
- package/build/commands/data/schemas/util/readJson.js +11 -0
- package/build/commands/data/schemas/util/schemaverify.d.ts +28 -0
- package/build/commands/data/schemas/util/schemaverify.js +202 -0
- package/build/commands/data/schemas/util/syncSchema.d.ts +20 -0
- package/build/commands/data/schemas/util/syncSchema.js +276 -0
- package/build/commands/data/schemas/util/tests/listFilesInDir.test.d.ts +1 -0
- package/build/commands/data/schemas/util/tests/listFilesInDir.test.js +40 -0
- package/build/commands/data/schemas/verify.d.ts +18 -0
- package/build/commands/data/schemas/verify.js +82 -0
- package/build/commands/data/schemas.d.ts +5 -0
- package/build/commands/data/schemas.js +10 -0
- package/build/commands/data.d.ts +5 -0
- package/build/commands/data.js +10 -0
- package/build/commands/dispatchers/sync.d.ts +21 -0
- package/build/commands/dispatchers/sync.js +24 -0
- package/build/commands/dispatchers.d.ts +5 -0
- package/build/commands/dispatchers.js +10 -0
- package/build/commands/login.d.ts +37 -0
- package/build/commands/login.js +59 -0
- package/build/commands/sync.d.ts +55 -0
- package/build/commands/sync.js +107 -0
- package/build/commands/tasks/createrepo.d.ts +22 -0
- package/build/commands/tasks/createrepo.js +70 -0
- package/build/commands/tasks/delete.d.ts +10 -0
- package/build/commands/tasks/delete.js +29 -0
- package/build/commands/tasks/list.d.ts +8 -0
- package/build/commands/tasks/list.js +27 -0
- package/build/commands/tasks/sync.d.ts +56 -0
- package/build/commands/tasks/sync.js +122 -0
- package/build/commands/tasks/taskConfig.d.ts +24 -0
- package/build/commands/tasks/taskConfig.js +169 -0
- package/build/commands/tasks/util.d.ts +1 -0
- package/build/commands/tasks/util.js +27 -0
- package/build/commands/tasks.d.ts +5 -0
- package/build/commands/tasks.js +10 -0
- package/build/commands/templates/delete.d.ts +19 -0
- package/build/commands/templates/delete.js +48 -0
- package/build/commands/templates/get.d.ts +19 -0
- package/build/commands/templates/get.js +37 -0
- package/build/commands/templates/list.d.ts +9 -0
- package/build/commands/templates/list.js +25 -0
- package/build/commands/templates/sync.d.ts +22 -0
- package/build/commands/templates/sync.js +65 -0
- package/build/commands/templates/util/buildTemplates.d.ts +2 -0
- package/build/commands/templates/util/buildTemplates.js +50 -0
- package/build/commands/templates/util/readTemplateFiles.d.ts +3 -0
- package/build/commands/templates/util/readTemplateFiles.js +52 -0
- package/build/commands/templates/util/templateService.d.ts +8 -0
- package/build/commands/templates/util/templateService.js +18 -0
- package/build/commands/templates/util/uploadTemplate.d.ts +2 -0
- package/build/commands/templates/util/uploadTemplate.js +20 -0
- package/build/commands/templates/util/utils.d.ts +4 -0
- package/build/commands/templates/util/utils.js +22 -0
- package/build/commands/templates.d.ts +5 -0
- package/build/commands/templates.js +10 -0
- package/build/constants.d.ts +13 -0
- package/build/constants.js +37 -0
- package/build/exh.d.ts +3 -0
- package/build/exh.js +65 -0
- package/build/helpers/error.d.ts +2 -0
- package/build/helpers/error.js +6 -0
- package/build/helpers/repoConfig.d.ts +2 -0
- package/build/helpers/repoConfig.js +60 -0
- package/build/helpers/util.d.ts +3 -0
- package/build/helpers/util.js +36 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +33 -0
- package/build/repositories/dispatchers.d.ts +8 -0
- package/build/repositories/dispatchers.js +31 -0
- package/build/repositories/functions.d.ts +24 -0
- package/build/repositories/functions.js +28 -0
- package/build/repositories/schemas.d.ts +44 -0
- package/build/repositories/schemas.js +86 -0
- package/build/services/dispatchers.d.ts +3 -0
- package/build/services/dispatchers.js +132 -0
- package/package.json +53 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _SchemaVerify_instances, _SchemaVerify_verifyMetaSchema, _SchemaVerify_verifyTransitionNames, _SchemaVerify_verifyProperties, _SchemaVerify_verifyStatuses, _SchemaVerify_verifyInputConditions, _SchemaVerify_verifyConditionTypes;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SchemaVerify = exports.TestId = void 0;
|
|
10
|
+
var TestId;
|
|
11
|
+
(function (TestId) {
|
|
12
|
+
TestId[TestId["META_SCHEMA"] = 1] = "META_SCHEMA";
|
|
13
|
+
TestId[TestId["PROPERTY_VERIFY"] = 2] = "PROPERTY_VERIFY";
|
|
14
|
+
TestId[TestId["INPUT_CONDITIONS"] = 3] = "INPUT_CONDITIONS";
|
|
15
|
+
TestId[TestId["STATUS_CHECK"] = 4] = "STATUS_CHECK";
|
|
16
|
+
TestId[TestId["CONDITION_TYPES"] = 5] = "CONDITION_TYPES";
|
|
17
|
+
TestId[TestId["TRANSITION_NAMES"] = 6] = "TRANSITION_NAMES";
|
|
18
|
+
})(TestId = exports.TestId || (exports.TestId = {}));
|
|
19
|
+
function transformAjvErrors(offsetPath, errors) {
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const error of errors) {
|
|
22
|
+
const path = offsetPath + error.instancePath;
|
|
23
|
+
if (!result[path]) {
|
|
24
|
+
result[path] = [];
|
|
25
|
+
}
|
|
26
|
+
result[path].push(`${error.message} ${error.params ? JSON.stringify(error.params) : ''}`);
|
|
27
|
+
}
|
|
28
|
+
return Object.keys(result).map(r => [r, ...result[r].map((m) => `\t-${m}`)]).flat();
|
|
29
|
+
}
|
|
30
|
+
class SchemaVerify {
|
|
31
|
+
constructor(ajv, schema, metaSchema) {
|
|
32
|
+
_SchemaVerify_instances.add(this);
|
|
33
|
+
this.ajv = ajv;
|
|
34
|
+
this.schema = schema;
|
|
35
|
+
this.metaSchema = metaSchema;
|
|
36
|
+
}
|
|
37
|
+
*RunChecks() {
|
|
38
|
+
yield { id: TestId.META_SCHEMA, test: 'Check if the schema is in the correct form', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyMetaSchema).call(this) };
|
|
39
|
+
yield { id: TestId.PROPERTY_VERIFY, test: 'Check if the properties object is valid JSON schema', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyProperties).call(this) };
|
|
40
|
+
yield { id: TestId.INPUT_CONDITIONS, test: 'Check if all input conditions of transitions are valid JSON schema', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyInputConditions).call(this) };
|
|
41
|
+
yield { id: TestId.STATUS_CHECK, test: 'Check if all statuses are accounted for', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyStatuses).call(this) };
|
|
42
|
+
yield { id: TestId.CONDITION_TYPES, test: 'Check if all condition types are used in the correct transitions', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyConditionTypes).call(this) };
|
|
43
|
+
yield { id: TestId.TRANSITION_NAMES, test: 'Check if all transition names are unique', ...__classPrivateFieldGet(this, _SchemaVerify_instances, "m", _SchemaVerify_verifyTransitionNames).call(this) };
|
|
44
|
+
}
|
|
45
|
+
validateTransition(transition, name) {
|
|
46
|
+
const errors = [];
|
|
47
|
+
const conditions = transition.conditions || [];
|
|
48
|
+
for (const [index, condition] of Object.entries(conditions)) {
|
|
49
|
+
if (condition.type === 'input') {
|
|
50
|
+
condition.configuration.$schema = 'http://json-schema.org/draft-07/schema#';
|
|
51
|
+
const isValidConfiguration = this.ajv.validateSchema(condition.configuration);
|
|
52
|
+
if (!isValidConfiguration) {
|
|
53
|
+
errors.push(...transformAjvErrors(`/creationTransition/conditions[${index}]/configuration`, this.ajv.errors));
|
|
54
|
+
}
|
|
55
|
+
const result = this.validateConditionPropertiesAgainstSchemaProperties(condition.configuration.properties, this.schema.properties, '');
|
|
56
|
+
result.forEach((error) => errors.push(`Transition - ${name} : property ${error}`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return errors;
|
|
60
|
+
}
|
|
61
|
+
validateConditionPropertiesAgainstSchemaProperties(conditionProperties, schemaProperties, path) {
|
|
62
|
+
const invalidPaths = new Set([]);
|
|
63
|
+
if (typeof conditionProperties !== 'object') {
|
|
64
|
+
return invalidPaths;
|
|
65
|
+
}
|
|
66
|
+
for (const key of Object.keys(conditionProperties)) {
|
|
67
|
+
const conditionProperty = conditionProperties[key];
|
|
68
|
+
const schemaProperty = schemaProperties?.[key];
|
|
69
|
+
if (key === 'type' && typeof conditionProperty === 'string') {
|
|
70
|
+
if (schemaProperty && conditionProperty !== schemaProperty) {
|
|
71
|
+
invalidPaths.add(`'${path}.type' does not match the value found in the schema properties`);
|
|
72
|
+
}
|
|
73
|
+
if (!schemaProperty) {
|
|
74
|
+
invalidPaths.add(`'${path}' is defined in the condition properties, but not defined in the schema properties`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
78
|
+
const result = this.validateConditionPropertiesAgainstSchemaProperties(conditionProperty, schemaProperty, currentPath);
|
|
79
|
+
result.forEach(error => invalidPaths.add(error));
|
|
80
|
+
}
|
|
81
|
+
return invalidPaths;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.SchemaVerify = SchemaVerify;
|
|
85
|
+
_SchemaVerify_instances = new WeakSet(), _SchemaVerify_verifyMetaSchema = function _SchemaVerify_verifyMetaSchema() {
|
|
86
|
+
const validate = this.ajv.compile(this.metaSchema);
|
|
87
|
+
if (!validate(this.schema)) {
|
|
88
|
+
return { ok: false, errors: transformAjvErrors('', validate.errors) };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, errors: [] };
|
|
91
|
+
}, _SchemaVerify_verifyTransitionNames = function _SchemaVerify_verifyTransitionNames() {
|
|
92
|
+
const transitionNames = new Set();
|
|
93
|
+
const nonUniqueNames = new Set();
|
|
94
|
+
for (const transition of (this.schema.transitions || [])) {
|
|
95
|
+
const { name } = transition;
|
|
96
|
+
if (transitionNames.has(name)) {
|
|
97
|
+
nonUniqueNames.add(name);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
transitionNames.add(name);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { ok: nonUniqueNames.size === 0, errors: [...nonUniqueNames].map(name => `Transition name '${name}' is not unique`) };
|
|
104
|
+
}, _SchemaVerify_verifyProperties = function _SchemaVerify_verifyProperties() {
|
|
105
|
+
if (this.schema.properties) {
|
|
106
|
+
const tmpSchema = {
|
|
107
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: this.schema.properties,
|
|
110
|
+
};
|
|
111
|
+
if (!this.ajv.validateSchema(tmpSchema)) {
|
|
112
|
+
return { ok: false, errors: transformAjvErrors('', this.ajv.errors) };
|
|
113
|
+
}
|
|
114
|
+
const errors = getIdInObjectArrayErrors(this.schema.properties);
|
|
115
|
+
if (errors.length > 0) {
|
|
116
|
+
return { ok: false, errors: errors.map(path => `The following id property is not allowed: ${path}`) };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { ok: true, errors: [] };
|
|
120
|
+
}, _SchemaVerify_verifyStatuses = function _SchemaVerify_verifyStatuses() {
|
|
121
|
+
let ok = true;
|
|
122
|
+
const errors = [];
|
|
123
|
+
let statusList = new Set();
|
|
124
|
+
if (this.schema.transitions) {
|
|
125
|
+
statusList = new Set([
|
|
126
|
+
...statusList.entries(),
|
|
127
|
+
...this.schema.transitions.map((t) => t.fromStatuses).flat(),
|
|
128
|
+
...this.schema.transitions.map((t) => t.toStatus),
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
if (this.schema.creationTransition) {
|
|
132
|
+
statusList = new Set([...statusList.values(), this.schema.creationTransition.toStatus]);
|
|
133
|
+
}
|
|
134
|
+
Object.keys(this.schema.statuses || []).forEach(status => {
|
|
135
|
+
if (!statusList.has(status)) {
|
|
136
|
+
errors.push(`Status '${status}' is defined in the schema statuses but not used in any transition`);
|
|
137
|
+
ok = false;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
for (const status of statusList) {
|
|
141
|
+
if (this.schema.statuses[status] === undefined) {
|
|
142
|
+
errors.push(`Status '${status}' is not defined in the status list`);
|
|
143
|
+
ok = false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { ok, errors };
|
|
147
|
+
}, _SchemaVerify_verifyInputConditions = function _SchemaVerify_verifyInputConditions() {
|
|
148
|
+
let ok = true;
|
|
149
|
+
const errors = [];
|
|
150
|
+
if (this.schema.creationTransition?.conditions?.length) {
|
|
151
|
+
const result = this.validateTransition(this.schema.creationTransition, 'creationTransition');
|
|
152
|
+
errors.push(...result);
|
|
153
|
+
}
|
|
154
|
+
if (this.schema.transitions?.length) {
|
|
155
|
+
for (const transition of this.schema.transitions) {
|
|
156
|
+
const result = this.validateTransition(transition, `${transition.name}`);
|
|
157
|
+
errors.push(...result);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (errors.length) {
|
|
161
|
+
ok = false;
|
|
162
|
+
}
|
|
163
|
+
return { ok, errors };
|
|
164
|
+
}, _SchemaVerify_verifyConditionTypes = function _SchemaVerify_verifyConditionTypes() {
|
|
165
|
+
let ok = true;
|
|
166
|
+
const errors = [];
|
|
167
|
+
if (this.schema.transitions) {
|
|
168
|
+
for (const [tIndex, transition] of this.schema.transitions.entries()) {
|
|
169
|
+
if (transition.type === 'manual' || !transition.conditions) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
for (const [cIndex, condition] of transition.conditions.entries()) {
|
|
173
|
+
if (['input', 'initiator'].includes(condition.type)) {
|
|
174
|
+
ok = false;
|
|
175
|
+
errors.push(`/transitions[${tIndex}]/conditions[${cIndex}] type cannot be ${condition.type} in a non-manual transition`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { ok, errors };
|
|
181
|
+
};
|
|
182
|
+
function getIdInObjectArrayErrors(properties, path = []) {
|
|
183
|
+
const pathsWithIdInArray = [];
|
|
184
|
+
let name;
|
|
185
|
+
let value;
|
|
186
|
+
for ([name, value] of Object.entries(properties)) {
|
|
187
|
+
while (value.type === 'array' && value.items.type === 'array') {
|
|
188
|
+
value = value.items;
|
|
189
|
+
name = `${name}.items`;
|
|
190
|
+
}
|
|
191
|
+
if (value.type === 'array' && value.items.type === 'object') {
|
|
192
|
+
if ('id' in value.items.properties) {
|
|
193
|
+
pathsWithIdInArray.push([...path, `${name}.items.properties`, 'id'].join('.'));
|
|
194
|
+
}
|
|
195
|
+
pathsWithIdInArray.push(...getIdInObjectArrayErrors(value.items.properties, [...path, `${name}.items.properties`]));
|
|
196
|
+
}
|
|
197
|
+
if (value.type === 'object') {
|
|
198
|
+
pathsWithIdInArray.push(...getIdInObjectArrayErrors(value.properties, [...path, `${name}.properties`]));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return pathsWithIdInArray;
|
|
202
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class SyncSchema {
|
|
2
|
+
#private;
|
|
3
|
+
private sdk;
|
|
4
|
+
private dry;
|
|
5
|
+
private cloudSchema;
|
|
6
|
+
private localSchema;
|
|
7
|
+
static createSchemaSync(sdk: any, dry?: boolean): SyncSchema;
|
|
8
|
+
constructor(sdk: any, dry?: boolean);
|
|
9
|
+
sync(target: any): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare type Changes = {
|
|
12
|
+
toAdd: string[];
|
|
13
|
+
toRemove: string[];
|
|
14
|
+
toUpdate: string[];
|
|
15
|
+
};
|
|
16
|
+
export declare function compareSchemaKey(localSchema: any, cloudSchema: any, key: string): {
|
|
17
|
+
toAdd: any[];
|
|
18
|
+
toRemove: any[];
|
|
19
|
+
toUpdate: any[];
|
|
20
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _SyncSchema_instances, _SyncSchema_syncRootAttributes, _SyncSchema_syncProperties, _SyncSchema_updateStatuses, _SyncSchema_syncCreationTransition, _SyncSchema_syncTransitions, _SyncSchema_pruneStatuses, _SyncSchema_syncIndexes;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.compareSchemaKey = exports.SyncSchema = void 0;
|
|
10
|
+
const chalk = require("chalk");
|
|
11
|
+
const _ = require("lodash");
|
|
12
|
+
const schemaRepository = require("../../../../repositories/schemas");
|
|
13
|
+
const statusHelpers_1 = require("../sync/statusHelpers");
|
|
14
|
+
class SyncSchema {
|
|
15
|
+
constructor(sdk, dry) {
|
|
16
|
+
_SyncSchema_instances.add(this);
|
|
17
|
+
this.cloudSchema = null;
|
|
18
|
+
this.localSchema = null;
|
|
19
|
+
this.sdk = sdk;
|
|
20
|
+
this.dry = dry;
|
|
21
|
+
}
|
|
22
|
+
static createSchemaSync(sdk, dry) {
|
|
23
|
+
return new SyncSchema(sdk, dry);
|
|
24
|
+
}
|
|
25
|
+
async sync(target) {
|
|
26
|
+
this.localSchema = target;
|
|
27
|
+
if (!this.localSchema.name) {
|
|
28
|
+
console.log('No schema name defined, skipping this file');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.log(`Syncing ${this.localSchema.name}`);
|
|
32
|
+
this.cloudSchema = await schemaRepository.fetchSchemaByName(this.sdk, this.localSchema.name);
|
|
33
|
+
if (!this.cloudSchema) {
|
|
34
|
+
if (this.dry) {
|
|
35
|
+
console.log(`\t-> Will be created: ${chalk.green(this.localSchema.name)}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.cloudSchema = await schemaRepository.createSchema(this.sdk, this.localSchema.name, this.localSchema.description);
|
|
39
|
+
}
|
|
40
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_syncRootAttributes).call(this);
|
|
41
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_syncProperties).call(this);
|
|
42
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_updateStatuses).call(this);
|
|
43
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_syncCreationTransition).call(this);
|
|
44
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_syncTransitions).call(this);
|
|
45
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_pruneStatuses).call(this);
|
|
46
|
+
await __classPrivateFieldGet(this, _SyncSchema_instances, "m", _SyncSchema_syncIndexes).call(this);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.SyncSchema = SyncSchema;
|
|
50
|
+
_SyncSchema_instances = new WeakSet(), _SyncSchema_syncRootAttributes = async function _SyncSchema_syncRootAttributes() {
|
|
51
|
+
const diff = diffRootAttributes(this.localSchema, this.cloudSchema);
|
|
52
|
+
if (this.dry) {
|
|
53
|
+
reportRootAttributesChanges(this.cloudSchema, diff);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (Object.keys(diff).length > 0) {
|
|
57
|
+
await schemaRepository.updateSchema(this.sdk, this.cloudSchema.id, diff);
|
|
58
|
+
}
|
|
59
|
+
}, _SyncSchema_syncProperties = async function _SyncSchema_syncProperties() {
|
|
60
|
+
console.log(JSON.stringify({ local: this.localSchema, cloud: this.cloudSchema }, null, 2));
|
|
61
|
+
const propertiesDiff = compareSchemaKey(this.localSchema, this.cloudSchema, 'properties');
|
|
62
|
+
if (this.dry) {
|
|
63
|
+
reportSchemaChanges(`Schema ${this.cloudSchema.name} - Properties`, propertiesDiff);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const { toAdd, toRemove, toUpdate } = propertiesDiff;
|
|
67
|
+
for (const key of toAdd) {
|
|
68
|
+
console.log(`properties: adding ${key}`);
|
|
69
|
+
await schemaRepository.createProperty(this.sdk, this.cloudSchema.id, {
|
|
70
|
+
name: key,
|
|
71
|
+
configuration: this.localSchema.properties[key],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (const key of toUpdate) {
|
|
75
|
+
console.log(`properties: updating ${key}`);
|
|
76
|
+
await schemaRepository.updateProperty(this.sdk, this.cloudSchema.id, key, this.localSchema.properties[key]);
|
|
77
|
+
}
|
|
78
|
+
for (const key of toRemove) {
|
|
79
|
+
console.log(`properties: removing ${key}`);
|
|
80
|
+
await schemaRepository.deleteProperty(this.sdk, this.cloudSchema.id, key);
|
|
81
|
+
}
|
|
82
|
+
}, _SyncSchema_updateStatuses = async function _SyncSchema_updateStatuses() {
|
|
83
|
+
const changes = (0, statusHelpers_1.compareStatuses)(this.localSchema, this.cloudSchema);
|
|
84
|
+
if (this.dry) {
|
|
85
|
+
reportSchemaChanges(`Schema ${this.cloudSchema.name} - Statuses`, changes);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { toAdd, toUpdate } = changes;
|
|
89
|
+
for (const key of toAdd) {
|
|
90
|
+
console.log(`statuses: adding ${key}`);
|
|
91
|
+
await schemaRepository.createStatus(this.sdk, this.cloudSchema.id, key, this.localSchema.statuses[key]);
|
|
92
|
+
}
|
|
93
|
+
for (const key of toUpdate) {
|
|
94
|
+
console.log(`statuses: updating ${key}`);
|
|
95
|
+
const data = (0, statusHelpers_1.calculateStatusUpdateData)(this.localSchema.statuses[key], this.cloudSchema.statuses[key]);
|
|
96
|
+
await schemaRepository.updateStatus(this.sdk, this.cloudSchema.id, key, data);
|
|
97
|
+
}
|
|
98
|
+
}, _SyncSchema_syncCreationTransition = async function _SyncSchema_syncCreationTransition() {
|
|
99
|
+
if (!this.localSchema.creationTransition) {
|
|
100
|
+
console.log(`Skipping creationTransition: No creationTransition defined in local ${this.localSchema.name} schema`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!_.isEqual(this.cloudSchema.creationTransition, this.localSchema.creationTransition)) {
|
|
104
|
+
if (this.dry) {
|
|
105
|
+
console.log('Update creation transition');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log('creation transition: updating');
|
|
109
|
+
await schemaRepository.updateCreationTransition(this.sdk, this.cloudSchema.id, this.localSchema.creationTransition);
|
|
110
|
+
}
|
|
111
|
+
}, _SyncSchema_syncTransitions = async function _SyncSchema_syncTransitions() {
|
|
112
|
+
const transitionsDiff = compareSchemaKey(this.localSchema, this.cloudSchema, 'transitions');
|
|
113
|
+
if (this.dry) {
|
|
114
|
+
reportSchemaChanges(`Schema ${this.cloudSchema.name} - Transitions`, transitionsDiff);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { toAdd, toRemove, toUpdate } = transitionsDiff;
|
|
118
|
+
for (const transition of toAdd) {
|
|
119
|
+
console.log(`transitions: adding ${transition.name}`);
|
|
120
|
+
await schemaRepository.createTransition(this.sdk, this.cloudSchema.id, transition);
|
|
121
|
+
}
|
|
122
|
+
for (const transition of toUpdate) {
|
|
123
|
+
console.log(`transitions: updating ${transition.name}`);
|
|
124
|
+
const currentTransition = findTransitionByName(transition.name, this.cloudSchema.transitions);
|
|
125
|
+
await schemaRepository.updateTransition(this.sdk, this.cloudSchema.id, currentTransition.id, transition);
|
|
126
|
+
}
|
|
127
|
+
for (const transition of toRemove) {
|
|
128
|
+
console.log(`transitions: removing ${transition.name}`);
|
|
129
|
+
await schemaRepository.deleteTransition(this.sdk, this.cloudSchema.id, transition.id);
|
|
130
|
+
}
|
|
131
|
+
}, _SyncSchema_pruneStatuses = async function _SyncSchema_pruneStatuses() {
|
|
132
|
+
if (this.dry) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const excessStatuses = _.difference(Object.keys(this.cloudSchema.statuses), Object.keys(this.localSchema.statuses));
|
|
136
|
+
for (const key of excessStatuses) {
|
|
137
|
+
console.log(`statuses: removing ${key}`);
|
|
138
|
+
await schemaRepository.deleteStatus(this.sdk, this.cloudSchema.id, key);
|
|
139
|
+
}
|
|
140
|
+
}, _SyncSchema_syncIndexes = async function _SyncSchema_syncIndexes() {
|
|
141
|
+
const { newIndexes, removedIndexes } = compareIndexes(this.localSchema, this.cloudSchema);
|
|
142
|
+
if (this.dry) {
|
|
143
|
+
reportIndexChanges(this.localSchema, { newIndexes, removedIndexes });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
for (const idx of removedIndexes) {
|
|
147
|
+
console.log(`Indexes: remove index ${idx.id}`);
|
|
148
|
+
await schemaRepository.deleteIndex(this.sdk, this.cloudSchema.id, idx.id);
|
|
149
|
+
}
|
|
150
|
+
for (const idx of newIndexes) {
|
|
151
|
+
console.log('\t-> Creating new index');
|
|
152
|
+
await schemaRepository.createIndex(this.sdk, this.cloudSchema.id, idx);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function deepDiff(object, other) {
|
|
156
|
+
return _.transform(object, (result, value, key) => {
|
|
157
|
+
if (!_.isEqual(value, other[key])) {
|
|
158
|
+
result[key] = _.isObject(value) && _.isObject(other[key]) ? deepDiff(value, other[key]) : value;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function diffRootAttributes(localSchema, cloudSchema) {
|
|
163
|
+
return deepDiff(_.pick(localSchema, 'description', 'defaultLimit', 'maximumLimit', 'createMode', 'readMode', 'updateMode', 'deleteMode', 'groupSyncMode'), _.pick(cloudSchema, 'description', 'defaultLimit', 'maximumLimit', 'createMode', 'readMode', 'updateMode', 'deleteMode', 'groupSyncMode'));
|
|
164
|
+
}
|
|
165
|
+
function reportRootAttributesChanges(cloudSchema, updatedValues) {
|
|
166
|
+
const changedKeys = Object.keys(updatedValues);
|
|
167
|
+
console.group(`Schema ${cloudSchema.name} - Root attributes`);
|
|
168
|
+
if (changedKeys.length < 1) {
|
|
169
|
+
console.log('No update required.');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
changedKeys.forEach(key => {
|
|
173
|
+
console.log(`${chalk.yellow(key)}:\t ${chalk.red(cloudSchema[key])} => ${chalk.green(updatedValues[key])}`);
|
|
174
|
+
});
|
|
175
|
+
console.groupEnd();
|
|
176
|
+
}
|
|
177
|
+
function reportSchemaChanges(group, changes) {
|
|
178
|
+
const { toAdd, toRemove, toUpdate } = changes;
|
|
179
|
+
console.group(group);
|
|
180
|
+
if (!toAdd.length && !toRemove.length && !toUpdate.length) {
|
|
181
|
+
console.log('No update required');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
toAdd.forEach(key => console.log(`Will be added: ${chalk.green(getIdentifier(key))}`));
|
|
185
|
+
toRemove.forEach(key => console.log(`Will be removed: ${chalk.red(getIdentifier(key))}`));
|
|
186
|
+
toUpdate.forEach(key => console.log(`Will be updated: ${chalk.yellow(getIdentifier(key))}`));
|
|
187
|
+
console.groupEnd();
|
|
188
|
+
}
|
|
189
|
+
function reportIndexChanges(schema, indexChanges) {
|
|
190
|
+
const { newIndexes, removedIndexes } = indexChanges;
|
|
191
|
+
const changes = compareArraysByName(newIndexes, removedIndexes);
|
|
192
|
+
reportSchemaChanges(`Schema ${schema.name} - Indexes`, {
|
|
193
|
+
toAdd: changes.toAdd.map(v => v.name),
|
|
194
|
+
toRemove: changes.toRemove.map(v => v.name),
|
|
195
|
+
toUpdate: changes.toUpdate.map(v => v.name),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function compareSchemaKey(localSchema, cloudSchema, key) {
|
|
199
|
+
if (!localSchema[key] && !cloudSchema[key]) {
|
|
200
|
+
return {
|
|
201
|
+
toAdd: [],
|
|
202
|
+
toRemove: [],
|
|
203
|
+
toUpdate: [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const isLocalPropertyArray = Array.isArray(localSchema[key]);
|
|
207
|
+
const isCloudPropertyArray = !localSchema[key] && Array.isArray(cloudSchema[key]);
|
|
208
|
+
if (isLocalPropertyArray || isCloudPropertyArray) {
|
|
209
|
+
return compareArraysByName(localSchema[key], cloudSchema[key]);
|
|
210
|
+
}
|
|
211
|
+
return compareSchemas(localSchema[key], cloudSchema[key]);
|
|
212
|
+
}
|
|
213
|
+
exports.compareSchemaKey = compareSchemaKey;
|
|
214
|
+
function compareSchemas(localSchema, cloudSchema) {
|
|
215
|
+
const toAdd = _.difference(Object.keys(localSchema), Object.keys(cloudSchema));
|
|
216
|
+
const toRemove = _.difference(Object.keys(cloudSchema), Object.keys(localSchema));
|
|
217
|
+
const existingLocalProperties = _(localSchema).omit(toAdd).value();
|
|
218
|
+
const existingCloudProperties = _(cloudSchema).omit(toRemove).value();
|
|
219
|
+
const schemaDiff = deepDiff(existingLocalProperties, existingCloudProperties);
|
|
220
|
+
const toUpdate = Object.keys(schemaDiff);
|
|
221
|
+
return {
|
|
222
|
+
toAdd,
|
|
223
|
+
toRemove,
|
|
224
|
+
toUpdate,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function compareArraysByName(localSchema, cloudSchema) {
|
|
228
|
+
const toAdd = _.differenceBy(localSchema, cloudSchema, 'name');
|
|
229
|
+
const toRemove = _.differenceBy(cloudSchema, localSchema, 'name');
|
|
230
|
+
const existingLocalProperties = _.differenceBy(localSchema, toAdd, 'name');
|
|
231
|
+
const existingCloudProperties = _.differenceBy(cloudSchema, toRemove, 'name');
|
|
232
|
+
const toUpdate = _.differenceWith(existingLocalProperties, existingCloudProperties, (targetSchema, currentSchema) => _.isEqual(_.omit(targetSchema, ['id']), _.omit(currentSchema, ['id'])));
|
|
233
|
+
return {
|
|
234
|
+
toAdd,
|
|
235
|
+
toRemove,
|
|
236
|
+
toUpdate,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function findTransitionByName(name, transitions) {
|
|
240
|
+
return _.find(transitions, transition => transition.name === name);
|
|
241
|
+
}
|
|
242
|
+
function compareIndexes(localSchema, cloudSchema) {
|
|
243
|
+
const localIndexes = localSchema?.indexes ?? [];
|
|
244
|
+
const indexesManagedByUser = cloudSchema.indexes
|
|
245
|
+
.filter((index) => index.system === false)
|
|
246
|
+
.map((idx) => ({ idx, marked: false }));
|
|
247
|
+
const newIndexes = [];
|
|
248
|
+
for (const localIndex of localIndexes) {
|
|
249
|
+
let existsInCloud = false;
|
|
250
|
+
for (const cloudIndex of indexesManagedByUser) {
|
|
251
|
+
const cloudIndexContents = _.omit(cloudIndex.idx, ['name', 'id', 'system']);
|
|
252
|
+
const localIndexContents = _.omit({ options: {}, ...localIndex }, ['name']);
|
|
253
|
+
if (_.isEqual(cloudIndexContents, localIndexContents)) {
|
|
254
|
+
existsInCloud = true;
|
|
255
|
+
cloudIndex.marked = true;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!existsInCloud) {
|
|
260
|
+
newIndexes.push(localIndex);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const removedIndexes = indexesManagedByUser
|
|
264
|
+
.filter((index) => index.marked === false)
|
|
265
|
+
.map((index) => index.idx);
|
|
266
|
+
return { removedIndexes, newIndexes };
|
|
267
|
+
}
|
|
268
|
+
function getIdentifier(value) {
|
|
269
|
+
if (typeof value !== 'object') {
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
if (value.id) {
|
|
273
|
+
return value.id;
|
|
274
|
+
}
|
|
275
|
+
return value.name;
|
|
276
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const mockFs = require("mock-fs");
|
|
5
|
+
const listFilesInDir_1 = require("../listFilesInDir");
|
|
6
|
+
describe('listFilesInDir', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockFs({
|
|
9
|
+
'path/to/fake/dir': {
|
|
10
|
+
'some-file.txt': 'file content here',
|
|
11
|
+
'first-json-file.json': 'json file here',
|
|
12
|
+
'a-directory': {
|
|
13
|
+
'some-file.txt': 'file content here',
|
|
14
|
+
'second-json-file.json': 'json file here',
|
|
15
|
+
},
|
|
16
|
+
'empty-directory': {},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
mockFs.restore();
|
|
22
|
+
});
|
|
23
|
+
it('Returns a flat array of only json files', () => {
|
|
24
|
+
expect((0, listFilesInDir_1.flatListFiles)('path/to/fake/dir', '.json')).toStrictEqual([
|
|
25
|
+
path.join('path/to/fake/dir/a-directory/second-json-file.json'),
|
|
26
|
+
path.join('path/to/fake/dir/first-json-file.json'),
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
it('Returns a flat array of all files', () => {
|
|
30
|
+
expect((0, listFilesInDir_1.flatListFiles)('path/to/fake/dir')).toStrictEqual([
|
|
31
|
+
path.join('path/to/fake/dir/a-directory/second-json-file.json'),
|
|
32
|
+
path.join('path/to/fake/dir/a-directory/some-file.txt'),
|
|
33
|
+
path.join('path/to/fake/dir/first-json-file.json'),
|
|
34
|
+
path.join('path/to/fake/dir/some-file.txt'),
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
it('Returns an empty flat array of jpg files', () => {
|
|
38
|
+
expect((0, listFilesInDir_1.flatListFiles)('path/to/fake/dir', '.jpg')).toStrictEqual([]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="yargs" />
|
|
2
|
+
export declare const command = "verify";
|
|
3
|
+
export declare const desc = "Syntactically verify a local schema";
|
|
4
|
+
export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs").Omit<{}, "file" | "dir"> & import("yargs").InferredOptionTypes<{
|
|
5
|
+
file: {
|
|
6
|
+
describe: string;
|
|
7
|
+
type: "string";
|
|
8
|
+
};
|
|
9
|
+
dir: {
|
|
10
|
+
describe: string;
|
|
11
|
+
type: "string";
|
|
12
|
+
};
|
|
13
|
+
}>>;
|
|
14
|
+
export declare const handler: ({ file, dir, ignoreVerificationErrors }: {
|
|
15
|
+
file: any;
|
|
16
|
+
dir: any;
|
|
17
|
+
ignoreVerificationErrors: any;
|
|
18
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = exports.builder = exports.desc = exports.command = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const ajv_1 = require("ajv");
|
|
7
|
+
const chalk = require("chalk");
|
|
8
|
+
const error_1 = require("../../../helpers/error");
|
|
9
|
+
const util_1 = require("../../../helpers/util");
|
|
10
|
+
const listFilesInDir_1 = require("./util/listFilesInDir");
|
|
11
|
+
const metaschema = require("./util/metaschema.json");
|
|
12
|
+
const schemaverify_1 = require("./util/schemaverify");
|
|
13
|
+
exports.command = 'verify';
|
|
14
|
+
exports.desc = 'Syntactically verify a local schema';
|
|
15
|
+
const builder = (yargs) => (0, util_1.epilogue)(yargs).options({
|
|
16
|
+
file: {
|
|
17
|
+
describe: 'schema json file which needs to be verified',
|
|
18
|
+
type: 'string',
|
|
19
|
+
},
|
|
20
|
+
dir: {
|
|
21
|
+
describe: 'directory containing schemas to be verified',
|
|
22
|
+
type: 'string',
|
|
23
|
+
},
|
|
24
|
+
}).check(({ file, dir }) => {
|
|
25
|
+
if (file && (!fs.existsSync(path.resolve(file)) || !fs.lstatSync(path.resolve(file)).isFile())) {
|
|
26
|
+
throw new Error('Schema file does not exist, please make sure you provided the correct file path');
|
|
27
|
+
}
|
|
28
|
+
if (dir === undefined && file === undefined) {
|
|
29
|
+
throw new Error('Must either specify --file or --dir');
|
|
30
|
+
}
|
|
31
|
+
else if (dir !== undefined && file !== undefined) {
|
|
32
|
+
throw new Error('Cannot specify both --file and --dir');
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
exports.builder = builder;
|
|
37
|
+
const handler = async ({ file, dir, ignoreVerificationErrors }) => {
|
|
38
|
+
let files = [];
|
|
39
|
+
if (dir) {
|
|
40
|
+
files = await (0, listFilesInDir_1.flatListFiles)(dir, '.json');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
files = [file];
|
|
44
|
+
}
|
|
45
|
+
const checkFile = (schemaPath) => {
|
|
46
|
+
let schema = {};
|
|
47
|
+
console.log(chalk.bold('Checking', schemaPath));
|
|
48
|
+
try {
|
|
49
|
+
schema = require(path.resolve(schemaPath));
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.log(chalk.red(`Failed to load schema file ${file}. Possibly not a valid JSON file`));
|
|
53
|
+
console.log(err);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const ajv = new ajv_1.default();
|
|
57
|
+
for (const result of (new schemaverify_1.SchemaVerify(ajv, schema, metaschema)).RunChecks()) {
|
|
58
|
+
if (result.ok) {
|
|
59
|
+
console.log(chalk.green(`${result.test}... ✓`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk.red(`${result.test}... x`));
|
|
63
|
+
for (const error of result.errors) {
|
|
64
|
+
if (typeof error === 'object') {
|
|
65
|
+
console.log('\t', chalk.red(JSON.stringify(error, null, 4)));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log('\t', chalk.red(error));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!ignoreVerificationErrors) {
|
|
72
|
+
throw new error_1.CommandError(`Schema ${schemaPath} contains error, please fix`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
};
|
|
78
|
+
for (const f of files) {
|
|
79
|
+
checkFile(f);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.handler = handler;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = exports.builder = exports.desc = exports.command = void 0;
|
|
4
|
+
const util_1 = require("../../helpers/util");
|
|
5
|
+
exports.command = 'schemas <command>';
|
|
6
|
+
exports.desc = 'Manage data schemas';
|
|
7
|
+
const builder = (yargs) => (0, util_1.epilogue)(yargs).commandDir('schemas');
|
|
8
|
+
exports.builder = builder;
|
|
9
|
+
const handler = () => { };
|
|
10
|
+
exports.handler = handler;
|