codify-plugin-lib 1.0.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/.eslintignore +1 -0
- package/.eslintrc.json +23 -0
- package/.mocharc.json +11 -0
- package/.prettierrc.json +1 -0
- package/dist/entities/change-set.d.ts +15 -0
- package/dist/entities/change-set.js +88 -0
- package/dist/entities/plan.d.ts +11 -0
- package/dist/entities/plan.js +30 -0
- package/dist/entities/plugin.d.ts +12 -0
- package/dist/entities/plugin.js +35 -0
- package/dist/entities/resource.d.ts +18 -0
- package/dist/entities/resource.js +41 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +30 -0
- package/dist/messages/handlers.d.ts +11 -0
- package/dist/messages/handlers.js +81 -0
- package/dist/utils/test-utils.d.ts +5 -0
- package/dist/utils/test-utils.js +21 -0
- package/dist/utils/utils.d.ts +16 -0
- package/dist/utils/utils.js +16 -0
- package/package.json +49 -0
- package/src/entities/change-set.test.ts +100 -0
- package/src/entities/change-set.ts +117 -0
- package/src/entities/plan.ts +40 -0
- package/src/entities/plugin.ts +53 -0
- package/src/entities/resource.test.ts +146 -0
- package/src/entities/resource.ts +72 -0
- package/src/index.test.ts +6 -0
- package/src/index.ts +17 -0
- package/src/messages/handlers.test.ts +124 -0
- package/src/messages/handlers.ts +100 -0
- package/src/utils/test-utils.test.ts +52 -0
- package/src/utils/test-utils.ts +20 -0
- package/src/utils/utils.ts +33 -0
- package/tsconfig.json +25 -0
- package/tsconfig.test.json +9 -0
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/dist
|
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"oclif",
|
|
4
|
+
"oclif-typescript",
|
|
5
|
+
"prettier"
|
|
6
|
+
],
|
|
7
|
+
"rules": {
|
|
8
|
+
"object-curly-spacing": [
|
|
9
|
+
"warn",
|
|
10
|
+
"always"
|
|
11
|
+
],
|
|
12
|
+
"perfectionist/sort-classes": [
|
|
13
|
+
"off"
|
|
14
|
+
],
|
|
15
|
+
"quotes": [
|
|
16
|
+
"error",
|
|
17
|
+
"single"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"ignorePatterns": [
|
|
21
|
+
"*.test.ts"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/.mocharc.json
ADDED
package/.prettierrc.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"@oclif/prettier-config"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ParameterOperation, ResourceConfig, ResourceOperation } from 'codify-schemas';
|
|
2
|
+
export interface ParameterChange {
|
|
3
|
+
name: string;
|
|
4
|
+
operation: ParameterOperation;
|
|
5
|
+
previousValue: string | null;
|
|
6
|
+
newValue: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare class ChangeSet {
|
|
9
|
+
operation: ResourceOperation;
|
|
10
|
+
parameterChanges: Array<ParameterChange>;
|
|
11
|
+
constructor(operation: ResourceOperation, parameterChanges: Array<ParameterChange>);
|
|
12
|
+
static createForNullCurrentConfig(desiredConfig: ResourceConfig): ChangeSet;
|
|
13
|
+
static calculateParameterChangeSet(prev: ResourceConfig, next: ResourceConfig): ParameterChange[];
|
|
14
|
+
static combineResourceOperations(prev: ResourceOperation, next: ResourceOperation): ResourceOperation;
|
|
15
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChangeSet = void 0;
|
|
4
|
+
const codify_schemas_1 = require("codify-schemas");
|
|
5
|
+
class ChangeSet {
|
|
6
|
+
operation;
|
|
7
|
+
parameterChanges;
|
|
8
|
+
constructor(operation, parameterChanges) {
|
|
9
|
+
this.operation = operation;
|
|
10
|
+
this.parameterChanges = parameterChanges;
|
|
11
|
+
}
|
|
12
|
+
static createForNullCurrentConfig(desiredConfig) {
|
|
13
|
+
const parameterChangeSet = Object.entries(desiredConfig)
|
|
14
|
+
.filter(([k,]) => k !== 'type' && k !== 'name')
|
|
15
|
+
.map(([k, v]) => {
|
|
16
|
+
return {
|
|
17
|
+
name: k,
|
|
18
|
+
operation: codify_schemas_1.ParameterOperation.ADD,
|
|
19
|
+
previousValue: null,
|
|
20
|
+
newValue: v,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return new ChangeSet(codify_schemas_1.ResourceOperation.CREATE, parameterChangeSet);
|
|
24
|
+
}
|
|
25
|
+
static calculateParameterChangeSet(prev, next) {
|
|
26
|
+
const parameterChangeSet = new Array();
|
|
27
|
+
const filteredPrev = Object.fromEntries(Object.entries(prev)
|
|
28
|
+
.filter(([k,]) => k !== 'type' && k !== 'name'));
|
|
29
|
+
const filteredNext = Object.fromEntries(Object.entries(next)
|
|
30
|
+
.filter(([k,]) => k !== 'type' && k !== 'name'));
|
|
31
|
+
for (const [k, v] of Object.entries(filteredPrev)) {
|
|
32
|
+
if (!filteredNext[k]) {
|
|
33
|
+
parameterChangeSet.push({
|
|
34
|
+
name: k,
|
|
35
|
+
previousValue: v,
|
|
36
|
+
newValue: null,
|
|
37
|
+
operation: codify_schemas_1.ParameterOperation.REMOVE,
|
|
38
|
+
});
|
|
39
|
+
delete filteredPrev[k];
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (filteredPrev[k] !== filteredNext[k]) {
|
|
43
|
+
parameterChangeSet.push({
|
|
44
|
+
name: k,
|
|
45
|
+
previousValue: v,
|
|
46
|
+
newValue: filteredNext[k],
|
|
47
|
+
operation: codify_schemas_1.ParameterOperation.MODIFY,
|
|
48
|
+
});
|
|
49
|
+
delete filteredPrev[k];
|
|
50
|
+
delete filteredNext[k];
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
parameterChangeSet.push({
|
|
54
|
+
name: k,
|
|
55
|
+
previousValue: v,
|
|
56
|
+
newValue: filteredNext[k],
|
|
57
|
+
operation: codify_schemas_1.ParameterOperation.NOOP,
|
|
58
|
+
});
|
|
59
|
+
delete filteredPrev[k];
|
|
60
|
+
delete filteredNext[k];
|
|
61
|
+
}
|
|
62
|
+
if (Object.keys(filteredPrev).length !== 0) {
|
|
63
|
+
throw Error('Diff algorithm error');
|
|
64
|
+
}
|
|
65
|
+
for (const [k, v] of Object.entries(filteredNext)) {
|
|
66
|
+
parameterChangeSet.push({
|
|
67
|
+
name: k,
|
|
68
|
+
previousValue: null,
|
|
69
|
+
newValue: v,
|
|
70
|
+
operation: codify_schemas_1.ParameterOperation.ADD,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return parameterChangeSet;
|
|
74
|
+
}
|
|
75
|
+
static combineResourceOperations(prev, next) {
|
|
76
|
+
const orderOfOperations = [
|
|
77
|
+
codify_schemas_1.ResourceOperation.NOOP,
|
|
78
|
+
codify_schemas_1.ResourceOperation.MODIFY,
|
|
79
|
+
codify_schemas_1.ResourceOperation.RECREATE,
|
|
80
|
+
codify_schemas_1.ResourceOperation.CREATE,
|
|
81
|
+
codify_schemas_1.ResourceOperation.DESTROY,
|
|
82
|
+
];
|
|
83
|
+
const indexPrev = orderOfOperations.indexOf(prev);
|
|
84
|
+
const indexNext = orderOfOperations.indexOf(next);
|
|
85
|
+
return orderOfOperations[Math.max(indexPrev, indexNext)];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.ChangeSet = ChangeSet;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ChangeSet } from './change-set';
|
|
2
|
+
import { PlanResponseData, ResourceConfig } from 'codify-schemas';
|
|
3
|
+
export declare class Plan {
|
|
4
|
+
id: string;
|
|
5
|
+
changeSet: ChangeSet;
|
|
6
|
+
resourceConfig: ResourceConfig;
|
|
7
|
+
constructor(id: string, changeSet: ChangeSet, resourceConfig: ResourceConfig);
|
|
8
|
+
static create(changeSet: ChangeSet, resourceConfig: ResourceConfig): Plan;
|
|
9
|
+
getResourceType(): string;
|
|
10
|
+
toResponse(): PlanResponseData;
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Plan = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
class Plan {
|
|
6
|
+
id;
|
|
7
|
+
changeSet;
|
|
8
|
+
resourceConfig;
|
|
9
|
+
constructor(id, changeSet, resourceConfig) {
|
|
10
|
+
this.id = id;
|
|
11
|
+
this.changeSet = changeSet;
|
|
12
|
+
this.resourceConfig = resourceConfig;
|
|
13
|
+
}
|
|
14
|
+
static create(changeSet, resourceConfig) {
|
|
15
|
+
return new Plan((0, crypto_1.randomUUID)(), changeSet, resourceConfig);
|
|
16
|
+
}
|
|
17
|
+
getResourceType() {
|
|
18
|
+
return this.resourceConfig.type;
|
|
19
|
+
}
|
|
20
|
+
toResponse() {
|
|
21
|
+
return {
|
|
22
|
+
planId: this.id,
|
|
23
|
+
operation: this.changeSet.operation,
|
|
24
|
+
resourceName: this.resourceConfig.name,
|
|
25
|
+
resourceType: this.resourceConfig.type,
|
|
26
|
+
parameters: this.changeSet.parameterChanges,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.Plan = Plan;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Resource } from './resource';
|
|
2
|
+
import { ApplyRequestData, PlanRequestData, PlanResponseData, ResourceConfig, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
3
|
+
export declare class Plugin {
|
|
4
|
+
resources: Map<string, Resource<ResourceConfig>>;
|
|
5
|
+
planStorage: Map<string, any>;
|
|
6
|
+
constructor(resources: Map<string, Resource<ResourceConfig>>);
|
|
7
|
+
onInitialize(): Promise<void>;
|
|
8
|
+
validate(data: ValidateRequestData): Promise<ValidateResponseData>;
|
|
9
|
+
plan(data: PlanRequestData): Promise<PlanResponseData>;
|
|
10
|
+
apply(data: ApplyRequestData): Promise<void>;
|
|
11
|
+
protected crossValidateResources(configs: ResourceConfig[]): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Plugin = void 0;
|
|
4
|
+
class Plugin {
|
|
5
|
+
resources;
|
|
6
|
+
planStorage;
|
|
7
|
+
constructor(resources) {
|
|
8
|
+
this.resources = resources;
|
|
9
|
+
this.planStorage = new Map();
|
|
10
|
+
}
|
|
11
|
+
async onInitialize() {
|
|
12
|
+
}
|
|
13
|
+
async validate(data) {
|
|
14
|
+
for (const config of data.configs) {
|
|
15
|
+
if (!this.resources.has(config.type)) {
|
|
16
|
+
throw new Error(`Resource type not found: ${config.type}`);
|
|
17
|
+
}
|
|
18
|
+
await this.resources.get(config.type).validate(config);
|
|
19
|
+
}
|
|
20
|
+
await this.crossValidateResources(data.configs);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
async plan(data) {
|
|
24
|
+
if (!this.resources.has(data.type)) {
|
|
25
|
+
throw new Error(`Resource type not found: ${data.type}`);
|
|
26
|
+
}
|
|
27
|
+
const plan = await this.resources.get(data.type).plan(data);
|
|
28
|
+
this.planStorage.set(plan.id, plan);
|
|
29
|
+
return plan.toResponse();
|
|
30
|
+
}
|
|
31
|
+
async apply(data) {
|
|
32
|
+
}
|
|
33
|
+
async crossValidateResources(configs) { }
|
|
34
|
+
}
|
|
35
|
+
exports.Plugin = Plugin;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ResourceConfig, ResourceOperation } from 'codify-schemas';
|
|
2
|
+
import { ChangeSet, ParameterChange } from './change-set';
|
|
3
|
+
import { Plan } from './plan';
|
|
4
|
+
export declare abstract class Resource<T extends ResourceConfig> {
|
|
5
|
+
private dependencies;
|
|
6
|
+
constructor(dependencies?: Resource<any>[]);
|
|
7
|
+
abstract getTypeId(): string;
|
|
8
|
+
onInitialize(): Promise<void>;
|
|
9
|
+
plan(desiredConfig: T): Promise<Plan>;
|
|
10
|
+
apply(plan: Plan): Promise<any>;
|
|
11
|
+
abstract validate(config: ResourceConfig): Promise<boolean>;
|
|
12
|
+
abstract getCurrentConfig(): Promise<T | null>;
|
|
13
|
+
abstract calculateOperation(change: ParameterChange): ResourceOperation.MODIFY | ResourceOperation.RECREATE;
|
|
14
|
+
abstract applyCreate(changeSet: ChangeSet): Promise<void>;
|
|
15
|
+
abstract applyModify(changeSet: ChangeSet): Promise<void>;
|
|
16
|
+
abstract applyRecreate(changeSet: ChangeSet): Promise<void>;
|
|
17
|
+
abstract applyDestroy(changeSet: ChangeSet): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Resource = void 0;
|
|
4
|
+
const codify_schemas_1 = require("codify-schemas");
|
|
5
|
+
const change_set_1 = require("./change-set");
|
|
6
|
+
const plan_1 = require("./plan");
|
|
7
|
+
class Resource {
|
|
8
|
+
dependencies;
|
|
9
|
+
constructor(dependencies = []) {
|
|
10
|
+
this.dependencies = dependencies;
|
|
11
|
+
}
|
|
12
|
+
async onInitialize() { }
|
|
13
|
+
async plan(desiredConfig) {
|
|
14
|
+
await this.validate(desiredConfig);
|
|
15
|
+
const currentConfig = await this.getCurrentConfig();
|
|
16
|
+
if (!currentConfig) {
|
|
17
|
+
return plan_1.Plan.create(change_set_1.ChangeSet.createForNullCurrentConfig(desiredConfig), desiredConfig);
|
|
18
|
+
}
|
|
19
|
+
const parameterChangeSet = change_set_1.ChangeSet.calculateParameterChangeSet(currentConfig, desiredConfig);
|
|
20
|
+
const resourceOperation = parameterChangeSet
|
|
21
|
+
.filter((change) => change.operation !== codify_schemas_1.ParameterOperation.NOOP)
|
|
22
|
+
.reduce((operation, curr) => {
|
|
23
|
+
const newOperation = this.calculateOperation(curr);
|
|
24
|
+
return change_set_1.ChangeSet.combineResourceOperations(operation, newOperation);
|
|
25
|
+
}, codify_schemas_1.ResourceOperation.NOOP);
|
|
26
|
+
return plan_1.Plan.create(new change_set_1.ChangeSet(resourceOperation, parameterChangeSet), desiredConfig);
|
|
27
|
+
}
|
|
28
|
+
async apply(plan) {
|
|
29
|
+
if (plan.getResourceType()) {
|
|
30
|
+
throw new Error('Internal error: Plan set to wrong resource during apply');
|
|
31
|
+
}
|
|
32
|
+
const changeSet = plan.changeSet;
|
|
33
|
+
switch (plan.changeSet.operation) {
|
|
34
|
+
case codify_schemas_1.ResourceOperation.CREATE: return this.applyCreate(changeSet);
|
|
35
|
+
case codify_schemas_1.ResourceOperation.MODIFY: return this.applyModify(changeSet);
|
|
36
|
+
case codify_schemas_1.ResourceOperation.RECREATE: return this.applyRecreate(changeSet);
|
|
37
|
+
case codify_schemas_1.ResourceOperation.DESTROY: return this.applyDestroy(changeSet);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.Resource = Resource;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Plugin } from './entities/plugin';
|
|
2
|
+
export * from './entities/resource';
|
|
3
|
+
export * from './entities/plugin';
|
|
4
|
+
export * from './entities/change-set';
|
|
5
|
+
export * from './entities/plan';
|
|
6
|
+
export * from './utils/test-utils';
|
|
7
|
+
export * from './utils/utils';
|
|
8
|
+
export declare function runPlugin(plugin: Plugin): Promise<void>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.runPlugin = void 0;
|
|
18
|
+
const handlers_1 = require("./messages/handlers");
|
|
19
|
+
__exportStar(require("./entities/resource"), exports);
|
|
20
|
+
__exportStar(require("./entities/plugin"), exports);
|
|
21
|
+
__exportStar(require("./entities/change-set"), exports);
|
|
22
|
+
__exportStar(require("./entities/plan"), exports);
|
|
23
|
+
__exportStar(require("./utils/test-utils"), exports);
|
|
24
|
+
__exportStar(require("./utils/utils"), exports);
|
|
25
|
+
async function runPlugin(plugin) {
|
|
26
|
+
await plugin.onInitialize();
|
|
27
|
+
const messageHandler = new handlers_1.MessageHandler(plugin);
|
|
28
|
+
process.on('message', (message) => messageHandler.onMessage(message));
|
|
29
|
+
}
|
|
30
|
+
exports.runPlugin = runPlugin;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Plugin } from '../entities/plugin';
|
|
2
|
+
export declare class MessageHandler {
|
|
3
|
+
private ajv;
|
|
4
|
+
private readonly plugin;
|
|
5
|
+
private messageSchemaValidator;
|
|
6
|
+
private requestValidators;
|
|
7
|
+
private responseValidators;
|
|
8
|
+
constructor(plugin: Plugin);
|
|
9
|
+
onMessage(message: unknown): Promise<void>;
|
|
10
|
+
private validateMessage;
|
|
11
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MessageHandler = void 0;
|
|
7
|
+
const _2020_1 = __importDefault(require("ajv/dist/2020"));
|
|
8
|
+
const ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
9
|
+
const codify_schemas_1 = require("codify-schemas");
|
|
10
|
+
const SupportedRequests = {
|
|
11
|
+
'validate': {
|
|
12
|
+
requestValidator: codify_schemas_1.ValidateRequestDataSchema,
|
|
13
|
+
responseValidator: codify_schemas_1.ValidateResponseDataSchema,
|
|
14
|
+
handler: async (plugin, data) => plugin.validate(data)
|
|
15
|
+
},
|
|
16
|
+
'plan': {
|
|
17
|
+
requestValidator: codify_schemas_1.PlanRequestDataSchema,
|
|
18
|
+
responseValidator: codify_schemas_1.PlanResponseDataSchema,
|
|
19
|
+
handler: async (plugin, data) => plugin.plan(data)
|
|
20
|
+
},
|
|
21
|
+
'apply': {
|
|
22
|
+
requestValidator: codify_schemas_1.ApplyRequestDataSchema,
|
|
23
|
+
responseValidator: codify_schemas_1.ApplyRequestDataSchema,
|
|
24
|
+
handler: async (plugin, data) => plugin.apply(data)
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
class MessageHandler {
|
|
28
|
+
ajv;
|
|
29
|
+
plugin;
|
|
30
|
+
messageSchemaValidator;
|
|
31
|
+
requestValidators;
|
|
32
|
+
responseValidators;
|
|
33
|
+
constructor(plugin) {
|
|
34
|
+
this.ajv = new _2020_1.default({ strict: true });
|
|
35
|
+
(0, ajv_formats_1.default)(this.ajv);
|
|
36
|
+
this.ajv.addSchema(codify_schemas_1.ResourceSchema);
|
|
37
|
+
this.plugin = plugin;
|
|
38
|
+
this.messageSchemaValidator = this.ajv.compile(codify_schemas_1.IpcMessageSchema);
|
|
39
|
+
this.requestValidators = new Map(Object.entries(SupportedRequests)
|
|
40
|
+
.map(([k, v]) => [k, this.ajv.compile(v.requestValidator)]));
|
|
41
|
+
this.responseValidators = new Map(Object.entries(SupportedRequests)
|
|
42
|
+
.map(([k, v]) => [k, this.ajv.compile(v.responseValidator)]));
|
|
43
|
+
}
|
|
44
|
+
async onMessage(message) {
|
|
45
|
+
if (!this.validateMessage(message)) {
|
|
46
|
+
throw new Error(`Message is malformed: ${JSON.stringify(this.messageSchemaValidator.errors, null, 2)}`);
|
|
47
|
+
}
|
|
48
|
+
if (!this.requestValidators.has(message.cmd)) {
|
|
49
|
+
throw new Error(`Unsupported message: ${message.cmd}`);
|
|
50
|
+
}
|
|
51
|
+
const requestValidator = this.requestValidators.get(message.cmd);
|
|
52
|
+
if (!requestValidator(message.data)) {
|
|
53
|
+
throw new Error(`Malformed message data: ${JSON.stringify(requestValidator.errors, null, 2)}`);
|
|
54
|
+
}
|
|
55
|
+
let result;
|
|
56
|
+
try {
|
|
57
|
+
result = await SupportedRequests[message.cmd].handler(this.plugin, message.data);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
process.send({
|
|
61
|
+
cmd: message.cmd + '_Response',
|
|
62
|
+
status: codify_schemas_1.MessageStatus.ERROR,
|
|
63
|
+
data: e.message,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const responseValidator = this.responseValidators.get(message.cmd);
|
|
68
|
+
if (responseValidator && !responseValidator(result)) {
|
|
69
|
+
throw new Error(`Malformed response data: ${JSON.stringify(responseValidator.errors, null, 2)}`);
|
|
70
|
+
}
|
|
71
|
+
process.send({
|
|
72
|
+
cmd: message.cmd + '_Response',
|
|
73
|
+
status: codify_schemas_1.MessageStatus.SUCCESS,
|
|
74
|
+
data: result,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
validateMessage(message) {
|
|
78
|
+
return this.messageSchemaValidator(message);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.MessageHandler = MessageHandler;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodifyTestUtils = void 0;
|
|
4
|
+
class CodifyTestUtils {
|
|
5
|
+
static sendMessageToProcessAwaitResponse(process, message) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
process.on('message', (response) => {
|
|
8
|
+
resolve(response);
|
|
9
|
+
});
|
|
10
|
+
process.on('error', (err) => reject(err));
|
|
11
|
+
process.on('exit', (code) => {
|
|
12
|
+
if (code != 0) {
|
|
13
|
+
reject('Exit code is not 0');
|
|
14
|
+
}
|
|
15
|
+
resolve(code);
|
|
16
|
+
});
|
|
17
|
+
process.send(message);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.CodifyTestUtils = CodifyTestUtils;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { SpawnOptions } from 'child_process';
|
|
4
|
+
export interface SpawnResult {
|
|
5
|
+
code: number;
|
|
6
|
+
signal: NodeJS.Signals | null;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
type CodifySpawnOptions = {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
stdioString?: boolean;
|
|
13
|
+
} & SpawnOptions;
|
|
14
|
+
export declare function codifySpawn(cmd: string, args: string[], opts?: CodifySpawnOptions, extras?: Record<any, any>): Promise<SpawnResult>;
|
|
15
|
+
export declare function isDebug(): boolean;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isDebug = exports.codifySpawn = void 0;
|
|
7
|
+
const promise_spawn_1 = __importDefault(require("@npmcli/promise-spawn"));
|
|
8
|
+
async function codifySpawn(cmd, args, opts, extras) {
|
|
9
|
+
const stdio = isDebug() ? 'inherit' : 'pipe';
|
|
10
|
+
return (0, promise_spawn_1.default)(cmd, args, { ...opts, stdio, stdioString: true }, extras);
|
|
11
|
+
}
|
|
12
|
+
exports.codifySpawn = codifySpawn;
|
|
13
|
+
function isDebug() {
|
|
14
|
+
return process.env.DEBUG != null && process.env.DEBUG.includes('codify');
|
|
15
|
+
}
|
|
16
|
+
exports.isDebug = isDebug;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codify-plugin-lib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"typings": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"ajv": "^8.12.0",
|
|
15
|
+
"ajv-formats": "^2.1.1",
|
|
16
|
+
"codify-schemas": "../codify-schemas",
|
|
17
|
+
"@npmcli/promise-spawn": "^7.0.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@oclif/prettier-config": "^0.2.1",
|
|
21
|
+
"@oclif/test": "^3",
|
|
22
|
+
"@types/npmcli__promise-spawn": "^6.0.3",
|
|
23
|
+
"@types/chai": "^4",
|
|
24
|
+
"@types/chai-as-promised": "^7.1.7",
|
|
25
|
+
"@types/mocha": "^9.0.0",
|
|
26
|
+
"@types/node": "^18",
|
|
27
|
+
"@types/semver": "^7.5.4",
|
|
28
|
+
"@types/sinon": "^17.0.3",
|
|
29
|
+
"@types/sinon-chai": "^3.2.12",
|
|
30
|
+
"chai": "^4",
|
|
31
|
+
"chai-as-promised": "^7.1.1",
|
|
32
|
+
"eslint": "^8.51.0",
|
|
33
|
+
"eslint-config-oclif": "^5",
|
|
34
|
+
"eslint-config-oclif-typescript": "^3",
|
|
35
|
+
"mocha": "^10",
|
|
36
|
+
"mock-fs": "^5.2.0",
|
|
37
|
+
"sinon": "^17.0.1",
|
|
38
|
+
"sinon-chai": "^3.7.0",
|
|
39
|
+
"oclif": "^4.0.0",
|
|
40
|
+
"shx": "^0.3.3",
|
|
41
|
+
"ts-node": "^10.9.1",
|
|
42
|
+
"tsc-watch": "^6.0.4",
|
|
43
|
+
"typescript": "^5",
|
|
44
|
+
"eslint-config-prettier": "^9.0.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ChangeSet } from './change-set';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { ParameterOperation, ResourceOperation } from 'codify-schemas';
|
|
4
|
+
|
|
5
|
+
describe('Change set tests', () => {
|
|
6
|
+
it ('Correctly diffs two resource configs (modify)', () => {
|
|
7
|
+
const before = {
|
|
8
|
+
type: 'config',
|
|
9
|
+
propA: 'before',
|
|
10
|
+
propB: 'before'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const after = {
|
|
14
|
+
type: 'config',
|
|
15
|
+
propA: 'after',
|
|
16
|
+
propB: 'after'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cs = ChangeSet.calculateParameterChangeSet(before, after);
|
|
20
|
+
expect(cs.length).to.eq(2);
|
|
21
|
+
expect(cs[0].operation).to.eq(ParameterOperation.MODIFY);
|
|
22
|
+
expect(cs[1].operation).to.eq(ParameterOperation.MODIFY);
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it ('Correctly diffs two resource configs (add)', () => {
|
|
26
|
+
const before = {
|
|
27
|
+
type: 'config',
|
|
28
|
+
propA: 'before',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const after = {
|
|
32
|
+
type: 'config',
|
|
33
|
+
propA: 'after',
|
|
34
|
+
propB: 'after'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cs = ChangeSet.calculateParameterChangeSet(before, after);
|
|
38
|
+
expect(cs.length).to.eq(2);
|
|
39
|
+
expect(cs[0].operation).to.eq(ParameterOperation.MODIFY);
|
|
40
|
+
expect(cs[1].operation).to.eq(ParameterOperation.ADD);
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it ('Correctly diffs two resource configs (remove)', () => {
|
|
44
|
+
const before = {
|
|
45
|
+
type: 'config',
|
|
46
|
+
propA: 'before',
|
|
47
|
+
propB: 'before'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const after = {
|
|
51
|
+
type: 'config',
|
|
52
|
+
propA: 'after',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const cs = ChangeSet.calculateParameterChangeSet(before, after);
|
|
56
|
+
expect(cs.length).to.eq(2);
|
|
57
|
+
expect(cs[0].operation).to.eq(ParameterOperation.MODIFY);
|
|
58
|
+
expect(cs[1].operation).to.eq(ParameterOperation.REMOVE);
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it ('Correctly diffs two resource configs (no-op)', () => {
|
|
62
|
+
const before = {
|
|
63
|
+
type: 'config',
|
|
64
|
+
propA: 'prop',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const after = {
|
|
68
|
+
type: 'config',
|
|
69
|
+
propA: 'prop',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const cs = ChangeSet.calculateParameterChangeSet(before, after);
|
|
73
|
+
expect(cs.length).to.eq(1);
|
|
74
|
+
expect(cs[0].operation).to.eq(ParameterOperation.NOOP);
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it ('determines the order of operations 1', () => {
|
|
78
|
+
const op1 = ResourceOperation.MODIFY;
|
|
79
|
+
const op2 = ResourceOperation.CREATE
|
|
80
|
+
|
|
81
|
+
const opResult = ChangeSet.combineResourceOperations(op1, op2);
|
|
82
|
+
expect(opResult).to.eq(ResourceOperation.CREATE);
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it ('determines the order of operations 2', () => {
|
|
86
|
+
const op1 = ResourceOperation.NOOP;
|
|
87
|
+
const op2 = ResourceOperation.MODIFY
|
|
88
|
+
|
|
89
|
+
const opResult = ChangeSet.combineResourceOperations(op1, op2);
|
|
90
|
+
expect(opResult).to.eq(ResourceOperation.MODIFY);
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it ('determines the order of operations 3', () => {
|
|
94
|
+
const op1 = ResourceOperation.MODIFY;
|
|
95
|
+
const op2 = ResourceOperation.MODIFY
|
|
96
|
+
|
|
97
|
+
const opResult = ChangeSet.combineResourceOperations(op1, op2);
|
|
98
|
+
expect(opResult).to.eq(ResourceOperation.MODIFY);
|
|
99
|
+
})
|
|
100
|
+
})
|