codify-plugin-lib 1.0.70 → 1.0.72
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/dist/entities/plugin.js +6 -8
- package/dist/entities/resource-types.d.ts +0 -4
- package/dist/entities/resource.d.ts +9 -9
- package/dist/entities/resource.js +42 -26
- package/dist/utils/utils.js +2 -2
- package/package.json +2 -2
- package/src/entities/plan.test.ts +22 -0
- package/src/entities/plugin.test.ts +22 -112
- package/src/entities/plugin.ts +7 -9
- package/src/entities/resource-parameters.test.ts +5 -5
- package/src/entities/resource-stateful-mode.test.ts +4 -4
- package/src/entities/resource-types.ts +0 -5
- package/src/entities/resource.test.ts +9 -18
- package/src/entities/resource.ts +74 -32
- package/src/entities/stateful-parameter.test.ts +4 -5
- package/src/utils/utils.test.ts +29 -0
- package/src/utils/utils.ts +2 -2
- package/dist/entities/test.d.ts +0 -1
- package/dist/entities/test.js +0 -22
- package/dist/utils/common-types.d.ts +0 -3
- package/dist/utils/common-types.js +0 -1
- package/dist/utils/test-utils.d.ts +0 -5
- package/dist/utils/test-utils.js +0 -17
package/dist/entities/plugin.js
CHANGED
|
@@ -31,17 +31,15 @@ export class Plugin {
|
|
|
31
31
|
if (!this.resources.has(config.type)) {
|
|
32
32
|
throw new Error(`Resource type not found: ${config.type}`);
|
|
33
33
|
}
|
|
34
|
-
const { parameters } = splitUserConfig(config);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
resourceName: config.name,
|
|
40
|
-
});
|
|
34
|
+
const { parameters, resourceMetadata } = splitUserConfig(config);
|
|
35
|
+
const validation = await this.resources
|
|
36
|
+
.get(config.type)
|
|
37
|
+
.validate(parameters, resourceMetadata);
|
|
38
|
+
validationResults.push(validation);
|
|
41
39
|
}
|
|
42
40
|
await this.crossValidateResources(data.configs);
|
|
43
41
|
return {
|
|
44
|
-
validationResults
|
|
42
|
+
resourceValidations: validationResults
|
|
45
43
|
};
|
|
46
44
|
}
|
|
47
45
|
async plan(data) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import { ValidateFunction } from 'ajv/dist/2020.js';
|
|
3
|
+
import { ResourceConfig, StringIndexedObject, ValidateResponseData } from 'codify-schemas';
|
|
2
4
|
import { ParameterChange } from './change-set.js';
|
|
3
5
|
import { Plan } from './plan.js';
|
|
4
|
-
import { StatefulParameter } from './stateful-parameter.js';
|
|
5
|
-
import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
|
|
6
6
|
import { CreatePlan, DestroyPlan, ModifyPlan, ParameterOptions } from './plan-types.js';
|
|
7
|
-
import { TransformParameter } from './transform-parameter.js';
|
|
8
7
|
import { ResourceOptions } from './resource-options.js';
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
8
|
+
import { ResourceParameterOptions } from './resource-types.js';
|
|
9
|
+
import { StatefulParameter } from './stateful-parameter.js';
|
|
10
|
+
import { TransformParameter } from './transform-parameter.js';
|
|
11
11
|
export declare abstract class Resource<T extends StringIndexedObject> {
|
|
12
12
|
readonly typeId: string;
|
|
13
13
|
readonly statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
|
|
@@ -23,7 +23,7 @@ export declare abstract class Resource<T extends StringIndexedObject> {
|
|
|
23
23
|
protected schemaValidator?: ValidateFunction;
|
|
24
24
|
protected constructor(options: ResourceOptions<T>);
|
|
25
25
|
onInitialize(): Promise<void>;
|
|
26
|
-
|
|
26
|
+
validate(parameters: Partial<T>, resourceMetaData: ResourceConfig): Promise<ValidateResponseData['resourceValidations'][0]>;
|
|
27
27
|
plan(desiredConfig: Partial<T> & ResourceConfig | null, currentConfig?: Partial<T> & ResourceConfig | null, statefulMode?: boolean): Promise<Plan<T>>;
|
|
28
28
|
apply(plan: Plan<T>): Promise<void>;
|
|
29
29
|
private _applyCreate;
|
|
@@ -35,8 +35,8 @@ export declare abstract class Resource<T extends StringIndexedObject> {
|
|
|
35
35
|
private refreshNonStatefulParameters;
|
|
36
36
|
private refreshStatefulParameters;
|
|
37
37
|
private validatePlanInputs;
|
|
38
|
-
|
|
39
|
-
abstract refresh(
|
|
38
|
+
customValidation(parameters: Partial<T>): Promise<void>;
|
|
39
|
+
abstract refresh(parameters: Partial<T>): Promise<Partial<T> | null>;
|
|
40
40
|
abstract applyCreate(plan: CreatePlan<T>): Promise<void>;
|
|
41
41
|
applyModify(pc: ParameterChange<T>, plan: ModifyPlan<T>): Promise<void>;
|
|
42
42
|
abstract applyDestroy(plan: DestroyPlan<T>): Promise<void>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
1
2
|
import { ParameterOperation, ResourceOperation, } from 'codify-schemas';
|
|
2
|
-
import { Plan } from './plan.js';
|
|
3
3
|
import { setsEqual, splitUserConfig } from '../utils/utils.js';
|
|
4
|
+
import { Plan } from './plan.js';
|
|
4
5
|
import { ResourceOptionsParser } from './resource-options.js';
|
|
5
|
-
import Ajv2020 from 'ajv/dist/2020.js';
|
|
6
6
|
export class Resource {
|
|
7
7
|
typeId;
|
|
8
8
|
statefulParameters;
|
|
@@ -37,28 +37,53 @@ export class Resource {
|
|
|
37
37
|
this.transformParameterOrder = parser.transformParameterOrder;
|
|
38
38
|
}
|
|
39
39
|
async onInitialize() { }
|
|
40
|
-
async
|
|
40
|
+
async validate(parameters, resourceMetaData) {
|
|
41
41
|
if (this.schemaValidator) {
|
|
42
42
|
const isValid = this.schemaValidator(parameters);
|
|
43
43
|
if (!isValid) {
|
|
44
44
|
return {
|
|
45
|
+
resourceType: resourceMetaData.type,
|
|
46
|
+
resourceName: resourceMetaData.name,
|
|
47
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
45
48
|
isValid: false,
|
|
46
|
-
errors: this.schemaValidator?.errors ?? [],
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
|
|
52
|
+
let isValid = true;
|
|
53
|
+
let customValidationErrorMessage = undefined;
|
|
54
|
+
try {
|
|
55
|
+
await this.customValidation(parameters);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
isValid = false;
|
|
59
|
+
customValidationErrorMessage = err.message;
|
|
60
|
+
}
|
|
61
|
+
if (!isValid) {
|
|
62
|
+
return {
|
|
63
|
+
resourceType: resourceMetaData.type,
|
|
64
|
+
resourceName: resourceMetaData.name,
|
|
65
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
66
|
+
customValidationErrorMessage,
|
|
67
|
+
isValid: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
resourceType: resourceMetaData.type,
|
|
72
|
+
resourceName: resourceMetaData.name,
|
|
73
|
+
schemaValidationErrors: [],
|
|
74
|
+
isValid: true,
|
|
75
|
+
};
|
|
51
76
|
}
|
|
52
77
|
async plan(desiredConfig, currentConfig = null, statefulMode = false) {
|
|
53
78
|
this.validatePlanInputs(desiredConfig, currentConfig, statefulMode);
|
|
54
79
|
const planOptions = {
|
|
55
|
-
statefulMode,
|
|
56
80
|
parameterOptions: this.parameterOptions,
|
|
81
|
+
statefulMode,
|
|
57
82
|
};
|
|
58
83
|
this.addDefaultValues(desiredConfig);
|
|
59
84
|
await this.applyTransformParameters(desiredConfig);
|
|
60
85
|
const parsedConfig = new ConfigParser(desiredConfig, currentConfig, this.statefulParameters, this.transformParameters);
|
|
61
|
-
const { desiredParameters,
|
|
86
|
+
const { desiredParameters, nonStatefulParameters, resourceMetadata, statefulParameters, } = parsedConfig;
|
|
62
87
|
const currentParameters = await this.refreshNonStatefulParameters(nonStatefulParameters);
|
|
63
88
|
if (currentParameters == null) {
|
|
64
89
|
return Plan.create(desiredParameters, null, resourceMetadata, planOptions);
|
|
@@ -167,21 +192,20 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
|
|
|
167
192
|
throw new Error(`Transform parameter ${key} is attempting to override existing values ${JSON.stringify(transformedValue, null, 2)}`);
|
|
168
193
|
}
|
|
169
194
|
delete desired[key];
|
|
170
|
-
|
|
195
|
+
for (const [tvKey, tvValue] of Object.entries(transformedValue)) {
|
|
171
196
|
desired[tvKey] = tvValue;
|
|
172
|
-
}
|
|
197
|
+
}
|
|
173
198
|
}
|
|
174
199
|
}
|
|
175
200
|
addDefaultValues(desired) {
|
|
176
201
|
if (!desired) {
|
|
177
202
|
return;
|
|
178
203
|
}
|
|
179
|
-
Object.entries(this.defaultValues)
|
|
180
|
-
.forEach(([key, defaultValue]) => {
|
|
204
|
+
for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
|
|
181
205
|
if (defaultValue !== undefined && desired[key] === undefined) {
|
|
182
206
|
desired[key] = defaultValue;
|
|
183
207
|
}
|
|
184
|
-
}
|
|
208
|
+
}
|
|
185
209
|
}
|
|
186
210
|
async refreshNonStatefulParameters(resourceParameters) {
|
|
187
211
|
const currentParameters = await this.refresh(resourceParameters);
|
|
@@ -222,11 +246,7 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
|
|
|
222
246
|
throw new Error('Desired config must be provided in non-stateful mode');
|
|
223
247
|
}
|
|
224
248
|
}
|
|
225
|
-
async
|
|
226
|
-
return {
|
|
227
|
-
isValid: true,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
249
|
+
async customValidation(parameters) { }
|
|
230
250
|
;
|
|
231
251
|
async applyModify(pc, plan) { }
|
|
232
252
|
;
|
|
@@ -269,18 +289,14 @@ ${JSON.stringify(currentMetadata, null, 2)}`);
|
|
|
269
289
|
get parameters() {
|
|
270
290
|
const desiredParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).parameters : undefined;
|
|
271
291
|
const currentParameters = this.currentConfig ? splitUserConfig(this.currentConfig).parameters : undefined;
|
|
272
|
-
return { ...
|
|
292
|
+
return { ...desiredParameters, ...currentParameters };
|
|
273
293
|
}
|
|
274
294
|
get nonStatefulParameters() {
|
|
275
|
-
const parameters = this
|
|
276
|
-
return Object.fromEntries([
|
|
277
|
-
...Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))),
|
|
278
|
-
]);
|
|
295
|
+
const { parameters } = this;
|
|
296
|
+
return Object.fromEntries(Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))));
|
|
279
297
|
}
|
|
280
298
|
get statefulParameters() {
|
|
281
|
-
const parameters = this
|
|
282
|
-
return Object.fromEntries([
|
|
283
|
-
...Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)),
|
|
284
|
-
]);
|
|
299
|
+
const { parameters } = this;
|
|
300
|
+
return Object.fromEntries(Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)));
|
|
285
301
|
}
|
|
286
302
|
}
|
package/dist/utils/utils.js
CHANGED
|
@@ -39,8 +39,8 @@ export function isDebug() {
|
|
|
39
39
|
export function splitUserConfig(config) {
|
|
40
40
|
const resourceMetadata = {
|
|
41
41
|
type: config.type,
|
|
42
|
-
...(config.name
|
|
43
|
-
...(config.dependsOn
|
|
42
|
+
...(config.name ? { name: config.name } : {}),
|
|
43
|
+
...(config.dependsOn ? { dependsOn: config.dependsOn } : {}),
|
|
44
44
|
};
|
|
45
45
|
const { type, name, dependsOn, ...parameters } = config;
|
|
46
46
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.72",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"ajv": "^8.12.0",
|
|
16
16
|
"ajv-formats": "^2.1.1",
|
|
17
|
-
"codify-schemas": "1.0.
|
|
17
|
+
"codify-schemas": "1.0.42",
|
|
18
18
|
"@npmcli/promise-spawn": "^7.0.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -121,6 +121,28 @@ describe('Plan entity tests', () => {
|
|
|
121
121
|
.every((pc) => pc.operation === ParameterOperation.ADD)
|
|
122
122
|
).to.be.true;
|
|
123
123
|
})
|
|
124
|
+
|
|
125
|
+
it('Returns the original resource names', () => {
|
|
126
|
+
const resource = createResource();
|
|
127
|
+
|
|
128
|
+
const plan = Plan.create(
|
|
129
|
+
{
|
|
130
|
+
propA: 'propA',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
propA: 'propA2',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
type: 'type',
|
|
137
|
+
name: 'name1'
|
|
138
|
+
}, { statefulMode: false });
|
|
139
|
+
|
|
140
|
+
expect(plan.toResponse()).toMatchObject({
|
|
141
|
+
resourceType: 'type',
|
|
142
|
+
resourceName: 'name1',
|
|
143
|
+
operation: ResourceOperation.RECREATE
|
|
144
|
+
})
|
|
145
|
+
})
|
|
124
146
|
})
|
|
125
147
|
|
|
126
148
|
function createResource(): Resource<any> {
|
|
@@ -3,8 +3,7 @@ import { Plugin } from './plugin.js';
|
|
|
3
3
|
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
4
4
|
import { Resource } from './resource.js';
|
|
5
5
|
import { Plan } from './plan.js';
|
|
6
|
-
import {
|
|
7
|
-
import { ApplyValidationError } from './errors.js';
|
|
6
|
+
import { spy } from 'sinon';
|
|
8
7
|
|
|
9
8
|
interface TestConfig extends StringIndexedObject {
|
|
10
9
|
propA: string;
|
|
@@ -27,36 +26,19 @@ class TestResource extends Resource<TestConfig> {
|
|
|
27
26
|
return Promise.resolve(undefined);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
async refresh(
|
|
29
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
31
30
|
return {
|
|
32
31
|
propA: 'a',
|
|
33
32
|
propB: 10,
|
|
34
33
|
propC: 'c',
|
|
35
34
|
};
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
async validateResource(config: unknown): Promise<ValidationResult> {
|
|
39
|
-
return {
|
|
40
|
-
isValid: true
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
describe('Plugin tests', () => {
|
|
46
|
-
it('
|
|
47
|
-
const resource= new
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Refresh has to line up with desired for the apply to go through
|
|
52
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
53
|
-
return {
|
|
54
|
-
propA: 'abc'
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const plugin = Plugin.create('testPlugin', [resource])
|
|
39
|
+
it('Can apply resource', async () => {
|
|
40
|
+
const resource= spy(new TestResource())
|
|
41
|
+
const plugin = Plugin.create('testPlugin', [resource as any])
|
|
60
42
|
|
|
61
43
|
const plan = {
|
|
62
44
|
operation: ResourceOperation.CREATE,
|
|
@@ -66,45 +48,13 @@ describe('Plugin tests', () => {
|
|
|
66
48
|
]
|
|
67
49
|
};
|
|
68
50
|
|
|
69
|
-
// If this doesn't throw then it passes the test
|
|
70
51
|
await plugin.apply({ plan });
|
|
52
|
+
expect(resource.applyCreate.calledOnce).to.be.true;
|
|
71
53
|
});
|
|
72
54
|
|
|
73
|
-
it('
|
|
74
|
-
const resource = new
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Return null to indicate that the resource was not created
|
|
79
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const plugin = Plugin.create('testPlugin', [resource])
|
|
84
|
-
|
|
85
|
-
const plan = {
|
|
86
|
-
operation: ResourceOperation.CREATE,
|
|
87
|
-
resourceType: 'testResource',
|
|
88
|
-
parameters: [
|
|
89
|
-
{ name: 'propA', operation: ParameterOperation.ADD, newValue: 'abc', previousValue: null },
|
|
90
|
-
]
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
await expect(async () => plugin.apply({ plan })).rejects.toThrowError(expect.any(ApplyValidationError));
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('Validates that deletes were successfully applied', async () => {
|
|
97
|
-
const resource = new class extends TestResource {
|
|
98
|
-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Return null to indicate that the resource was deleted
|
|
102
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
55
|
+
it('Can destroy resource', async () => {
|
|
56
|
+
const resource = spy(new TestResource());
|
|
57
|
+
const testPlugin = Plugin.create('testPlugin', [resource as any])
|
|
108
58
|
|
|
109
59
|
const plan = {
|
|
110
60
|
operation: ResourceOperation.DESTROY,
|
|
@@ -114,46 +64,13 @@ describe('Plugin tests', () => {
|
|
|
114
64
|
]
|
|
115
65
|
};
|
|
116
66
|
|
|
117
|
-
// If this doesn't throw then it passes the test
|
|
118
67
|
await testPlugin.apply({ plan })
|
|
68
|
+
expect(resource.applyDestroy.calledOnce).to.be.true;
|
|
119
69
|
});
|
|
120
70
|
|
|
121
|
-
it('
|
|
122
|
-
const resource = new
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Return a value to indicate that the resource still exists
|
|
127
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
128
|
-
return { propA: 'abc' };
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
133
|
-
|
|
134
|
-
const plan = {
|
|
135
|
-
operation: ResourceOperation.DESTROY,
|
|
136
|
-
resourceType: 'testResource',
|
|
137
|
-
parameters: [
|
|
138
|
-
{ name: 'propA', operation: ParameterOperation.REMOVE, newValue: null, previousValue: 'abc' },
|
|
139
|
-
]
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// If this doesn't throw then it passes the test
|
|
143
|
-
expect(async () => await testPlugin.apply({ plan })).rejects.toThrowError(expect.any(ApplyValidationError));
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('Validates that re-create was successfully applied', async () => {
|
|
147
|
-
const resource = new class extends TestResource {
|
|
148
|
-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
152
|
-
return { propA: 'def'};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
71
|
+
it('Can re-create resource', async () => {
|
|
72
|
+
const resource = spy(new TestResource())
|
|
73
|
+
const testPlugin = Plugin.create('testPlugin', [resource as any])
|
|
157
74
|
|
|
158
75
|
const plan = {
|
|
159
76
|
operation: ResourceOperation.RECREATE,
|
|
@@ -163,31 +80,24 @@ describe('Plugin tests', () => {
|
|
|
163
80
|
]
|
|
164
81
|
};
|
|
165
82
|
|
|
166
|
-
// If this doesn't throw then it passes the test
|
|
167
83
|
await testPlugin.apply({ plan })
|
|
84
|
+
expect(resource.applyDestroy.calledOnce).to.be.true;
|
|
85
|
+
expect(resource.applyCreate.calledOnce).to.be.true;
|
|
168
86
|
});
|
|
169
87
|
|
|
170
|
-
it('
|
|
171
|
-
const resource = new
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
176
|
-
return { propA: 'abc' };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
88
|
+
it('Can modify resource', async () => {
|
|
89
|
+
const resource = spy(new TestResource())
|
|
90
|
+
const testPlugin = Plugin.create('testPlugin', [resource as any])
|
|
181
91
|
|
|
182
92
|
const plan = {
|
|
183
|
-
operation: ResourceOperation.
|
|
93
|
+
operation: ResourceOperation.MODIFY,
|
|
184
94
|
resourceType: 'testResource',
|
|
185
95
|
parameters: [
|
|
186
|
-
{ name: 'propA', operation: ParameterOperation.
|
|
96
|
+
{ name: 'propA', operation: ParameterOperation.MODIFY, newValue: 'def', previousValue: 'abc' },
|
|
187
97
|
]
|
|
188
98
|
};
|
|
189
99
|
|
|
190
|
-
|
|
191
|
-
expect(
|
|
100
|
+
await testPlugin.apply({ plan })
|
|
101
|
+
expect(resource.applyModify.calledOnce).to.be.true;
|
|
192
102
|
});
|
|
193
103
|
});
|
package/src/entities/plugin.ts
CHANGED
|
@@ -50,19 +50,17 @@ export class Plugin {
|
|
|
50
50
|
throw new Error(`Resource type not found: ${config.type}`);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const { parameters } = splitUserConfig(config);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
resourceName: config.name,
|
|
60
|
-
});
|
|
53
|
+
const { parameters, resourceMetadata } = splitUserConfig(config);
|
|
54
|
+
const validation = await this.resources
|
|
55
|
+
.get(config.type)!
|
|
56
|
+
.validate(parameters, resourceMetadata);
|
|
57
|
+
|
|
58
|
+
validationResults.push(validation);
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
await this.crossValidateResources(data.configs);
|
|
64
62
|
return {
|
|
65
|
-
validationResults
|
|
63
|
+
resourceValidations: validationResults
|
|
66
64
|
};
|
|
67
65
|
}
|
|
68
66
|
|
|
@@ -471,9 +471,9 @@ describe('Resource parameter tests', () => {
|
|
|
471
471
|
const plan = await resource.plan({ type: 'resourceType', propC: 'abc' } as any);
|
|
472
472
|
|
|
473
473
|
expect(resource.refresh.called).to.be.true;
|
|
474
|
-
expect(resource.refresh.getCall(0).firstArg
|
|
475
|
-
expect(resource.refresh.getCall(0).firstArg
|
|
476
|
-
expect(resource.refresh.getCall(0).firstArg
|
|
474
|
+
expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
|
|
475
|
+
expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
|
|
476
|
+
expect(resource.refresh.getCall(0).firstArg['propC']).to.not.exist;
|
|
477
477
|
|
|
478
478
|
expect(plan.desiredConfig?.propA).to.eq('propA');
|
|
479
479
|
expect(plan.desiredConfig?.propB).to.eq(10);
|
|
@@ -513,8 +513,8 @@ describe('Resource parameter tests', () => {
|
|
|
513
513
|
const plan = await resource.plan({ type: 'resourceType', propA: 'propA', propB: 10 } as any);
|
|
514
514
|
|
|
515
515
|
expect(transformParameter.transform.called).to.be.false;
|
|
516
|
-
expect(resource.refresh.getCall(0).firstArg
|
|
517
|
-
expect(resource.refresh.getCall(0).firstArg
|
|
516
|
+
expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
|
|
517
|
+
expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
|
|
518
518
|
|
|
519
519
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
520
520
|
})
|
|
@@ -12,7 +12,7 @@ describe('Resource tests for stateful plans', () => {
|
|
|
12
12
|
super({ type: 'resource' });
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
async refresh(
|
|
15
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
16
16
|
return {
|
|
17
17
|
propA: 'propADifferent',
|
|
18
18
|
propB: undefined,
|
|
@@ -61,7 +61,7 @@ describe('Resource tests for stateful plans', () => {
|
|
|
61
61
|
super({ type: 'resource' });
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
async refresh(
|
|
64
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -113,7 +113,7 @@ describe('Resource tests for stateful plans', () => {
|
|
|
113
113
|
super({ type: 'resource' });
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async refresh(
|
|
116
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
117
117
|
return {
|
|
118
118
|
propA: 'propA',
|
|
119
119
|
propC: 'propC',
|
|
@@ -184,7 +184,7 @@ describe('Resource tests for stateful plans', () => {
|
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
async refresh(
|
|
187
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
188
188
|
return {
|
|
189
189
|
propA: 'propA',
|
|
190
190
|
propC: 'propC',
|
|
@@ -3,7 +3,6 @@ import { ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
|
3
3
|
import { spy } from 'sinon';
|
|
4
4
|
import { Plan } from './plan.js';
|
|
5
5
|
import { describe, expect, it } from 'vitest'
|
|
6
|
-
import { ValidationResult } from './resource-types.js';
|
|
7
6
|
import { StatefulParameter } from './stateful-parameter.js';
|
|
8
7
|
import { ResourceOptions } from './resource-options.js';
|
|
9
8
|
import { CreatePlan, DestroyPlan, ModifyPlan } from './plan-types.js';
|
|
@@ -28,19 +27,13 @@ export class TestResource extends Resource<TestConfig> {
|
|
|
28
27
|
return Promise.resolve(undefined);
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
async refresh(
|
|
30
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
32
31
|
return {
|
|
33
32
|
propA: 'a',
|
|
34
33
|
propB: 10,
|
|
35
34
|
propC: 'c',
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
async validateResource(config: unknown): Promise<ValidationResult> {
|
|
40
|
-
return {
|
|
41
|
-
isValid: true
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
37
|
}
|
|
45
38
|
|
|
46
39
|
describe('Resource tests', () => {
|
|
@@ -298,9 +291,8 @@ describe('Resource tests', () => {
|
|
|
298
291
|
}
|
|
299
292
|
|
|
300
293
|
// @ts-ignore
|
|
301
|
-
async refresh(desired:
|
|
302
|
-
expect(desired
|
|
303
|
-
expect(desired.get('propA')).to.be.eq('propADefault');
|
|
294
|
+
async refresh(desired: Partial<TestConfig>): Promise<Partial<TestConfig>> {
|
|
295
|
+
expect(desired['propA']).to.be.eq('propADefault');
|
|
304
296
|
|
|
305
297
|
return {
|
|
306
298
|
propA: 'propAAfter'
|
|
@@ -325,11 +317,11 @@ describe('Resource tests', () => {
|
|
|
325
317
|
});
|
|
326
318
|
}
|
|
327
319
|
|
|
328
|
-
async refresh(
|
|
329
|
-
expect(
|
|
320
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
321
|
+
expect(parameters['propE']).to.exist;
|
|
330
322
|
|
|
331
323
|
return {
|
|
332
|
-
propE:
|
|
324
|
+
propE: parameters['propE'],
|
|
333
325
|
};
|
|
334
326
|
}
|
|
335
327
|
}
|
|
@@ -374,9 +366,8 @@ describe('Resource tests', () => {
|
|
|
374
366
|
}
|
|
375
367
|
|
|
376
368
|
// @ts-ignore
|
|
377
|
-
async refresh(
|
|
378
|
-
expect(
|
|
379
|
-
expect(desired.get('propA')).to.be.eq('propA');
|
|
369
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig>> {
|
|
370
|
+
expect(parameters['propA']).to.be.eq('propA');
|
|
380
371
|
|
|
381
372
|
return {
|
|
382
373
|
propA: 'propAAfter'
|
|
@@ -413,7 +404,7 @@ describe('Resource tests', () => {
|
|
|
413
404
|
super({ type: 'type' });
|
|
414
405
|
}
|
|
415
406
|
|
|
416
|
-
async refresh(
|
|
407
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
417
408
|
return null;
|
|
418
409
|
}
|
|
419
410
|
|
package/src/entities/resource.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import Ajv2020, { ValidateFunction } from 'ajv/dist/2020.js';
|
|
3
|
+
import {
|
|
4
|
+
ParameterOperation,
|
|
5
|
+
ResourceConfig,
|
|
6
|
+
ResourceOperation,
|
|
7
|
+
StringIndexedObject,
|
|
8
|
+
ValidateResponseData,
|
|
9
|
+
} from 'codify-schemas';
|
|
10
|
+
|
|
11
|
+
import { setsEqual, splitUserConfig } from '../utils/utils.js';
|
|
2
12
|
import { ParameterChange } from './change-set.js';
|
|
3
13
|
import { Plan } from './plan.js';
|
|
4
|
-
import { StatefulParameter } from './stateful-parameter.js';
|
|
5
|
-
import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
|
|
6
|
-
import { setsEqual, splitUserConfig } from '../utils/utils.js';
|
|
7
14
|
import { CreatePlan, DestroyPlan, ModifyPlan, ParameterOptions, PlanOptions } from './plan-types.js';
|
|
8
|
-
import { TransformParameter } from './transform-parameter.js';
|
|
9
15
|
import { ResourceOptions, ResourceOptionsParser } from './resource-options.js';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
16
|
+
import { ResourceParameterOptions } from './resource-types.js';
|
|
17
|
+
import { StatefulParameter } from './stateful-parameter.js';
|
|
18
|
+
import { TransformParameter } from './transform-parameter.js';
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
21
|
* Description of resource here
|
|
@@ -59,19 +66,48 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
59
66
|
|
|
60
67
|
async onInitialize(): Promise<void> {}
|
|
61
68
|
|
|
62
|
-
async
|
|
69
|
+
async validate(
|
|
70
|
+
parameters: Partial<T>,
|
|
71
|
+
resourceMetaData: ResourceConfig
|
|
72
|
+
): Promise<ValidateResponseData['resourceValidations'][0]> {
|
|
63
73
|
if (this.schemaValidator) {
|
|
64
74
|
const isValid = this.schemaValidator(parameters);
|
|
65
75
|
|
|
66
76
|
if (!isValid) {
|
|
67
77
|
return {
|
|
78
|
+
resourceType: resourceMetaData.type,
|
|
79
|
+
resourceName: resourceMetaData.name,
|
|
80
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
68
81
|
isValid: false,
|
|
69
|
-
errors: this.schemaValidator?.errors ?? [],
|
|
70
82
|
}
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
let isValid = true;
|
|
87
|
+
let customValidationErrorMessage = undefined;
|
|
88
|
+
try {
|
|
89
|
+
await this.customValidation(parameters);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
isValid = false;
|
|
92
|
+
customValidationErrorMessage = (err as Error).message;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!isValid) {
|
|
96
|
+
return {
|
|
97
|
+
resourceType: resourceMetaData.type,
|
|
98
|
+
resourceName: resourceMetaData.name,
|
|
99
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
100
|
+
customValidationErrorMessage,
|
|
101
|
+
isValid: false,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
resourceType: resourceMetaData.type,
|
|
107
|
+
resourceName: resourceMetaData.name,
|
|
108
|
+
schemaValidationErrors: [],
|
|
109
|
+
isValid: true,
|
|
110
|
+
}
|
|
75
111
|
}
|
|
76
112
|
|
|
77
113
|
// TODO: Currently stateful mode expects that the currentConfig does not need any additional transformations (default and transform parameters)
|
|
@@ -84,8 +120,8 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
84
120
|
this.validatePlanInputs(desiredConfig, currentConfig, statefulMode);
|
|
85
121
|
|
|
86
122
|
const planOptions: PlanOptions<T> = {
|
|
87
|
-
statefulMode,
|
|
88
123
|
parameterOptions: this.parameterOptions,
|
|
124
|
+
statefulMode,
|
|
89
125
|
}
|
|
90
126
|
|
|
91
127
|
this.addDefaultValues(desiredConfig);
|
|
@@ -95,8 +131,8 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
95
131
|
const parsedConfig = new ConfigParser(desiredConfig, currentConfig, this.statefulParameters, this.transformParameters)
|
|
96
132
|
const {
|
|
97
133
|
desiredParameters,
|
|
98
|
-
resourceMetadata,
|
|
99
134
|
nonStatefulParameters,
|
|
135
|
+
resourceMetadata,
|
|
100
136
|
statefulParameters,
|
|
101
137
|
} = parsedConfig;
|
|
102
138
|
|
|
@@ -133,13 +169,16 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
133
169
|
case ResourceOperation.CREATE: {
|
|
134
170
|
return this._applyCreate(plan); // TODO: Add new parameters value so that apply
|
|
135
171
|
}
|
|
172
|
+
|
|
136
173
|
case ResourceOperation.MODIFY: {
|
|
137
174
|
return this._applyModify(plan);
|
|
138
175
|
}
|
|
176
|
+
|
|
139
177
|
case ResourceOperation.RECREATE: {
|
|
140
178
|
await this._applyDestroy(plan);
|
|
141
179
|
return this._applyCreate(plan);
|
|
142
180
|
}
|
|
181
|
+
|
|
143
182
|
case ResourceOperation.DESTROY: {
|
|
144
183
|
return this._applyDestroy(plan);
|
|
145
184
|
}
|
|
@@ -185,11 +224,13 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
185
224
|
await statefulParameter.applyAdd(parameterChange.newValue, plan);
|
|
186
225
|
break;
|
|
187
226
|
}
|
|
227
|
+
|
|
188
228
|
case ParameterOperation.MODIFY: {
|
|
189
229
|
// TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
|
|
190
230
|
await statefulParameter.applyModify(parameterChange.newValue, parameterChange.previousValue, false, plan);
|
|
191
231
|
break;
|
|
192
232
|
}
|
|
233
|
+
|
|
193
234
|
case ParameterOperation.REMOVE: {
|
|
194
235
|
await statefulParameter.applyRemove(parameterChange.previousValue, plan);
|
|
195
236
|
break;
|
|
@@ -256,10 +297,10 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
|
|
|
256
297
|
delete desired[key];
|
|
257
298
|
|
|
258
299
|
// Add the new transformed values
|
|
259
|
-
|
|
300
|
+
for (const [tvKey, tvValue] of Object.entries(transformedValue)) {
|
|
260
301
|
// @ts-ignore
|
|
261
302
|
desired[tvKey] = tvValue;
|
|
262
|
-
}
|
|
303
|
+
}
|
|
263
304
|
}
|
|
264
305
|
}
|
|
265
306
|
|
|
@@ -268,13 +309,12 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
|
|
|
268
309
|
return;
|
|
269
310
|
}
|
|
270
311
|
|
|
271
|
-
Object.entries(this.defaultValues)
|
|
272
|
-
.forEach(([key, defaultValue]) => {
|
|
312
|
+
for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
|
|
273
313
|
if (defaultValue !== undefined && desired[key as any] === undefined) {
|
|
274
314
|
// @ts-ignore
|
|
275
315
|
desired[key] = defaultValue;
|
|
276
316
|
}
|
|
277
|
-
}
|
|
317
|
+
}
|
|
278
318
|
}
|
|
279
319
|
|
|
280
320
|
private async refreshNonStatefulParameters(resourceParameters: Partial<T>): Promise<Partial<T> | null> {
|
|
@@ -337,13 +377,15 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
|
|
|
337
377
|
}
|
|
338
378
|
}
|
|
339
379
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Add custom validation logic in-addition to the default schema validation.
|
|
382
|
+
* In this method throw an error if the object did not validate. The message of the
|
|
383
|
+
* error will be shown to the user.
|
|
384
|
+
* @param parameters
|
|
385
|
+
*/
|
|
386
|
+
async customValidation(parameters: Partial<T>): Promise<void> {};
|
|
345
387
|
|
|
346
|
-
abstract refresh(
|
|
388
|
+
abstract refresh(parameters: Partial<T>): Promise<Partial<T> | null>;
|
|
347
389
|
|
|
348
390
|
abstract applyCreate(plan: CreatePlan<T>): Promise<void>;
|
|
349
391
|
|
|
@@ -407,22 +449,22 @@ ${JSON.stringify(currentMetadata, null, 2)}`);
|
|
|
407
449
|
const desiredParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).parameters : undefined;
|
|
408
450
|
const currentParameters = this.currentConfig ? splitUserConfig(this.currentConfig).parameters : undefined;
|
|
409
451
|
|
|
410
|
-
return { ...
|
|
452
|
+
return { ...desiredParameters, ...currentParameters } as Partial<T>;
|
|
411
453
|
}
|
|
412
454
|
|
|
413
455
|
get nonStatefulParameters(): Partial<T> {
|
|
414
|
-
const parameters = this
|
|
456
|
+
const { parameters } = this;
|
|
415
457
|
|
|
416
|
-
return Object.fromEntries(
|
|
417
|
-
|
|
418
|
-
|
|
458
|
+
return Object.fromEntries(
|
|
459
|
+
Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key)))
|
|
460
|
+
) as Partial<T>;
|
|
419
461
|
}
|
|
420
462
|
|
|
421
463
|
get statefulParameters(): Partial<T> {
|
|
422
|
-
const parameters = this
|
|
464
|
+
const { parameters } = this;
|
|
423
465
|
|
|
424
|
-
return Object.fromEntries(
|
|
425
|
-
|
|
426
|
-
|
|
466
|
+
return Object.fromEntries(
|
|
467
|
+
Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key))
|
|
468
|
+
) as Partial<T>;
|
|
427
469
|
}
|
|
428
470
|
}
|
|
@@ -22,7 +22,6 @@ class TestArrayParameter extends ArrayStatefulParameter<TestConfig, string> {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
describe('Stateful parameter tests', () => {
|
|
27
26
|
it('applyAddItem is called the correct number of times', async () => {
|
|
28
27
|
const plan = Plan.create<TestConfig>(
|
|
@@ -36,7 +35,7 @@ describe('Stateful parameter tests', () => {
|
|
|
36
35
|
expect(plan.changeSet.parameterChanges.length).to.eq(1);
|
|
37
36
|
|
|
38
37
|
const testParameter = spy(new TestArrayParameter());
|
|
39
|
-
await testParameter.applyAdd(plan.desiredConfig
|
|
38
|
+
await testParameter.applyAdd(plan.desiredConfig!.propA, plan);
|
|
40
39
|
|
|
41
40
|
expect(testParameter.applyAddItem.callCount).to.eq(3);
|
|
42
41
|
expect(testParameter.applyRemoveItem.called).to.be.false;
|
|
@@ -54,7 +53,7 @@ describe('Stateful parameter tests', () => {
|
|
|
54
53
|
expect(plan.changeSet.parameterChanges.length).to.eq(1);
|
|
55
54
|
|
|
56
55
|
const testParameter = spy(new TestArrayParameter());
|
|
57
|
-
await testParameter.applyRemove(plan.currentConfig
|
|
56
|
+
await testParameter.applyRemove(plan.currentConfig!.propA, plan);
|
|
58
57
|
|
|
59
58
|
expect(testParameter.applyAddItem.called).to.be.false;
|
|
60
59
|
expect(testParameter.applyRemoveItem.callCount).to.eq(3);
|
|
@@ -77,7 +76,7 @@ describe('Stateful parameter tests', () => {
|
|
|
77
76
|
})
|
|
78
77
|
|
|
79
78
|
const testParameter = spy(new TestArrayParameter());
|
|
80
|
-
await testParameter.applyModify(plan.desiredConfig
|
|
79
|
+
await testParameter.applyModify(plan.desiredConfig!.propA, plan.currentConfig!.propA, false, plan);
|
|
81
80
|
|
|
82
81
|
expect(testParameter.applyAddItem.calledThrice).to.be.true;
|
|
83
82
|
expect(testParameter.applyRemoveItem.called).to.be.false;
|
|
@@ -107,7 +106,7 @@ describe('Stateful parameter tests', () => {
|
|
|
107
106
|
}
|
|
108
107
|
});
|
|
109
108
|
|
|
110
|
-
await testParameter.applyModify(plan.desiredConfig
|
|
109
|
+
await testParameter.applyModify(plan.desiredConfig!.propA, plan.currentConfig!.propA, false, plan);
|
|
111
110
|
|
|
112
111
|
expect(testParameter.applyAddItem.calledOnce).to.be.true;
|
|
113
112
|
expect(testParameter.applyRemoveItem.called).to.be.false;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { splitUserConfig } from './utils.js';
|
|
3
|
+
|
|
4
|
+
describe('Utils tests', () => {
|
|
5
|
+
it('Can split a config correctly', () => {
|
|
6
|
+
const { parameters, resourceMetadata } = splitUserConfig({
|
|
7
|
+
type: 'type',
|
|
8
|
+
name: 'name',
|
|
9
|
+
dependsOn: ['a', 'b', 'c'],
|
|
10
|
+
propA: 'propA',
|
|
11
|
+
propB: 'propB',
|
|
12
|
+
propC: 'propC',
|
|
13
|
+
propD: 'propD',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
expect(resourceMetadata).toMatchObject({
|
|
17
|
+
type: 'type',
|
|
18
|
+
name: 'name',
|
|
19
|
+
dependsOn: ['a', 'b', 'c'],
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expect(parameters).toMatchObject({
|
|
23
|
+
propA: 'propA',
|
|
24
|
+
propB: 'propB',
|
|
25
|
+
propC: 'propC',
|
|
26
|
+
propD: 'propD',
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
})
|
package/src/utils/utils.ts
CHANGED
|
@@ -85,8 +85,8 @@ export function splitUserConfig<T extends StringIndexedObject>(
|
|
|
85
85
|
): { parameters: T; resourceMetadata: ResourceConfig} {
|
|
86
86
|
const resourceMetadata = {
|
|
87
87
|
type: config.type,
|
|
88
|
-
...(config.name
|
|
89
|
-
...(config.dependsOn
|
|
88
|
+
...(config.name ? { name: config.name } : {}),
|
|
89
|
+
...(config.dependsOn ? { dependsOn: config.dependsOn } : {}),
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
const { type, name, dependsOn, ...parameters } = config;
|
package/dist/entities/test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/entities/test.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Resource } from './resource.js';
|
|
2
|
-
class Test extends Resource {
|
|
3
|
-
validate(config) {
|
|
4
|
-
throw new Error('Method not implemented.');
|
|
5
|
-
}
|
|
6
|
-
async refresh(keys) {
|
|
7
|
-
const result = {};
|
|
8
|
-
if (keys.has('propA')) {
|
|
9
|
-
result['propA'] = 'abc';
|
|
10
|
-
}
|
|
11
|
-
return result;
|
|
12
|
-
}
|
|
13
|
-
applyCreate(plan) {
|
|
14
|
-
throw new Error('Method not implemented.');
|
|
15
|
-
}
|
|
16
|
-
applyModify(parameterName, newValue, previousValue, plan) {
|
|
17
|
-
throw new Error('Method not implemented.');
|
|
18
|
-
}
|
|
19
|
-
applyDestroy(plan) {
|
|
20
|
-
throw new Error('Method not implemented.');
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/test-utils.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export class CodifyTestUtils {
|
|
2
|
-
static sendMessageToProcessAwaitResponse(process, message) {
|
|
3
|
-
return new Promise((resolve, reject) => {
|
|
4
|
-
process.on('message', (response) => {
|
|
5
|
-
resolve(response);
|
|
6
|
-
});
|
|
7
|
-
process.on('error', (err) => reject(err));
|
|
8
|
-
process.on('exit', (code) => {
|
|
9
|
-
if (code != 0) {
|
|
10
|
-
reject('Exit code is not 0');
|
|
11
|
-
}
|
|
12
|
-
resolve(code);
|
|
13
|
-
});
|
|
14
|
-
process.send(message);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|