codify-plugin-lib 1.0.128 → 1.0.130
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/plan/plan.d.ts +14 -14
- package/dist/plan/plan.js +60 -66
- package/dist/plugin/plugin.d.ts +2 -2
- package/dist/plugin/plugin.js +17 -14
- package/dist/resource/config-parser.d.ts +2 -5
- package/dist/resource/config-parser.js +1 -28
- package/dist/resource/resource-controller.d.ts +4 -4
- package/dist/resource/resource-controller.js +47 -49
- package/package.json +2 -2
- package/src/common/errors.test.ts +1 -1
- package/src/messages/handlers.test.ts +9 -3
- package/src/plan/plan.test.ts +21 -20
- package/src/plan/plan.ts +99 -105
- package/src/plugin/plugin.test.ts +8 -9
- package/src/plugin/plugin.ts +24 -16
- package/src/resource/config-parser.ts +6 -41
- package/src/resource/resource-controller-stateful-mode.test.ts +4 -4
- package/src/resource/resource-controller.test.ts +47 -32
- package/src/resource/resource-controller.ts +58 -58
- package/src/resource/resource-settings.test.ts +113 -83
- package/src/stateful-parameter/stateful-parameter-controller.test.ts +19 -10
- package/src/utils/test-utils.test.ts +6 -6
package/dist/plan/plan.d.ts
CHANGED
|
@@ -16,8 +16,8 @@ export declare class Plan<T extends StringIndexedObject> {
|
|
|
16
16
|
* Ex: name, type, dependsOn etc. Metadata parameters
|
|
17
17
|
*/
|
|
18
18
|
coreParameters: ResourceConfig;
|
|
19
|
-
|
|
20
|
-
constructor(id: string, changeSet: ChangeSet<T>,
|
|
19
|
+
isStateful: boolean;
|
|
20
|
+
constructor(id: string, changeSet: ChangeSet<T>, coreParameters: ResourceConfig, isStateful: boolean);
|
|
21
21
|
/**
|
|
22
22
|
* The desired config that a plan will achieve after executing all the actions.
|
|
23
23
|
*/
|
|
@@ -28,13 +28,20 @@ export declare class Plan<T extends StringIndexedObject> {
|
|
|
28
28
|
get currentConfig(): T | null;
|
|
29
29
|
get resourceId(): string;
|
|
30
30
|
static calculate<T extends StringIndexedObject>(params: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
desired: Partial<T> | null;
|
|
32
|
+
currentArray: Partial<T>[] | null;
|
|
33
|
+
state: Partial<T> | null;
|
|
34
|
+
core: ResourceConfig;
|
|
35
35
|
settings: ParsedResourceSettings<T>;
|
|
36
|
-
|
|
36
|
+
isStateful: boolean;
|
|
37
37
|
}): Plan<T>;
|
|
38
|
+
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues?: Partial<Record<keyof T, unknown>>): Plan<T>;
|
|
39
|
+
/**
|
|
40
|
+
* The type (id) of the resource
|
|
41
|
+
*
|
|
42
|
+
* @return string
|
|
43
|
+
*/
|
|
44
|
+
getResourceType(): string;
|
|
38
45
|
/**
|
|
39
46
|
* When multiples of the same resource are allowed, this matching function will match a given config with one of the
|
|
40
47
|
* existing configs on the system. For example if there are multiple versions of Android Studios installed, we can use
|
|
@@ -44,13 +51,6 @@ export declare class Plan<T extends StringIndexedObject> {
|
|
|
44
51
|
* @private
|
|
45
52
|
*/
|
|
46
53
|
private static matchCurrentParameters;
|
|
47
|
-
/**
|
|
48
|
-
* The type (id) of the resource
|
|
49
|
-
*
|
|
50
|
-
* @return string
|
|
51
|
-
*/
|
|
52
|
-
getResourceType(): string;
|
|
53
|
-
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues?: Partial<Record<keyof T, unknown>>): Plan<T>;
|
|
54
54
|
/**
|
|
55
55
|
* Only keep relevant params for the plan. We don't want to change settings that were not already
|
|
56
56
|
* defined.
|
package/dist/plan/plan.js
CHANGED
|
@@ -16,12 +16,12 @@ export class Plan {
|
|
|
16
16
|
* Ex: name, type, dependsOn etc. Metadata parameters
|
|
17
17
|
*/
|
|
18
18
|
coreParameters;
|
|
19
|
-
|
|
20
|
-
constructor(id, changeSet,
|
|
19
|
+
isStateful;
|
|
20
|
+
constructor(id, changeSet, coreParameters, isStateful) {
|
|
21
21
|
this.id = id;
|
|
22
22
|
this.changeSet = changeSet;
|
|
23
|
-
this.coreParameters =
|
|
24
|
-
this.
|
|
23
|
+
this.coreParameters = coreParameters;
|
|
24
|
+
this.isStateful = isStateful;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* The desired config that a plan will achieve after executing all the actions.
|
|
@@ -30,10 +30,7 @@ export class Plan {
|
|
|
30
30
|
if (this.changeSet.operation === ResourceOperation.DESTROY) {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
-
return
|
|
34
|
-
...this.coreParameters,
|
|
35
|
-
...this.changeSet.desiredParameters,
|
|
36
|
-
};
|
|
33
|
+
return this.changeSet.desiredParameters;
|
|
37
34
|
}
|
|
38
35
|
/**
|
|
39
36
|
* The current config that the plan is changing.
|
|
@@ -42,10 +39,7 @@ export class Plan {
|
|
|
42
39
|
if (this.changeSet.operation === ResourceOperation.CREATE) {
|
|
43
40
|
return null;
|
|
44
41
|
}
|
|
45
|
-
return
|
|
46
|
-
...this.coreParameters,
|
|
47
|
-
...this.changeSet.currentParameters,
|
|
48
|
-
};
|
|
42
|
+
return this.changeSet.currentParameters;
|
|
49
43
|
}
|
|
50
44
|
get resourceId() {
|
|
51
45
|
return this.coreParameters.name
|
|
@@ -53,67 +47,36 @@ export class Plan {
|
|
|
53
47
|
: this.coreParameters.type;
|
|
54
48
|
}
|
|
55
49
|
static calculate(params) {
|
|
56
|
-
const {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const { desired, currentArray, state, core, settings, isStateful } = params;
|
|
51
|
+
const current = Plan.matchCurrentParameters({
|
|
52
|
+
desired,
|
|
53
|
+
currentArray,
|
|
54
|
+
state,
|
|
61
55
|
settings,
|
|
62
|
-
|
|
56
|
+
isStateful
|
|
63
57
|
});
|
|
64
58
|
const filteredCurrentParameters = Plan.filterCurrentParams({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
desired,
|
|
60
|
+
current,
|
|
61
|
+
state,
|
|
68
62
|
settings,
|
|
69
|
-
|
|
63
|
+
isStateful
|
|
70
64
|
});
|
|
71
65
|
// Empty
|
|
72
|
-
if (!filteredCurrentParameters && !
|
|
73
|
-
return new Plan(uuidV4(), ChangeSet.empty(),
|
|
66
|
+
if (!filteredCurrentParameters && !desired) {
|
|
67
|
+
return new Plan(uuidV4(), ChangeSet.empty(), core, isStateful);
|
|
74
68
|
}
|
|
75
69
|
// CREATE
|
|
76
|
-
if (!filteredCurrentParameters &&
|
|
77
|
-
return new Plan(uuidV4(), ChangeSet.create(
|
|
70
|
+
if (!filteredCurrentParameters && desired) {
|
|
71
|
+
return new Plan(uuidV4(), ChangeSet.create(desired), core, isStateful);
|
|
78
72
|
}
|
|
79
73
|
// DESTROY
|
|
80
|
-
if (filteredCurrentParameters && !
|
|
81
|
-
return new Plan(uuidV4(), ChangeSet.destroy(filteredCurrentParameters),
|
|
74
|
+
if (filteredCurrentParameters && !desired) {
|
|
75
|
+
return new Plan(uuidV4(), ChangeSet.destroy(filteredCurrentParameters), core, isStateful);
|
|
82
76
|
}
|
|
83
77
|
// NO-OP, MODIFY or RE-CREATE
|
|
84
|
-
const changeSet = ChangeSet.calculateModification(
|
|
85
|
-
return new Plan(uuidV4(), changeSet,
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* When multiples of the same resource are allowed, this matching function will match a given config with one of the
|
|
89
|
-
* existing configs on the system. For example if there are multiple versions of Android Studios installed, we can use
|
|
90
|
-
* the application name and location to match it to our desired configs name and location.
|
|
91
|
-
*
|
|
92
|
-
* @param params
|
|
93
|
-
* @private
|
|
94
|
-
*/
|
|
95
|
-
static matchCurrentParameters(params) {
|
|
96
|
-
const { desiredParameters, currentParametersArray, stateParameters, settings, statefulMode } = params;
|
|
97
|
-
if (!settings.allowMultiple) {
|
|
98
|
-
return currentParametersArray?.[0] ?? null;
|
|
99
|
-
}
|
|
100
|
-
if (!currentParametersArray) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
if (statefulMode) {
|
|
104
|
-
return stateParameters
|
|
105
|
-
? settings.allowMultiple.matcher(stateParameters, currentParametersArray)
|
|
106
|
-
: null;
|
|
107
|
-
}
|
|
108
|
-
return settings.allowMultiple.matcher(desiredParameters, currentParametersArray);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* The type (id) of the resource
|
|
112
|
-
*
|
|
113
|
-
* @return string
|
|
114
|
-
*/
|
|
115
|
-
getResourceType() {
|
|
116
|
-
return this.coreParameters.type;
|
|
78
|
+
const changeSet = ChangeSet.calculateModification(desired, filteredCurrentParameters, settings.parameterSettings);
|
|
79
|
+
return new Plan(uuidV4(), changeSet, core, isStateful);
|
|
117
80
|
}
|
|
118
81
|
// 2. Even if there was (maybe for testing reasons), the plan values should not be adjusted
|
|
119
82
|
static fromResponse(data, defaultValues) {
|
|
@@ -124,7 +87,7 @@ export class Plan {
|
|
|
124
87
|
return new Plan(uuidV4(), new ChangeSet(data.operation, data.parameters), {
|
|
125
88
|
type: data.resourceType,
|
|
126
89
|
name: data.resourceName,
|
|
127
|
-
}, data.
|
|
90
|
+
}, data.isStateful);
|
|
128
91
|
function addDefaultValues() {
|
|
129
92
|
Object.entries(defaultValues ?? {})
|
|
130
93
|
.forEach(([key, defaultValue]) => {
|
|
@@ -169,6 +132,37 @@ export class Plan {
|
|
|
169
132
|
});
|
|
170
133
|
}
|
|
171
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The type (id) of the resource
|
|
137
|
+
*
|
|
138
|
+
* @return string
|
|
139
|
+
*/
|
|
140
|
+
getResourceType() {
|
|
141
|
+
return this.coreParameters.type;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* When multiples of the same resource are allowed, this matching function will match a given config with one of the
|
|
145
|
+
* existing configs on the system. For example if there are multiple versions of Android Studios installed, we can use
|
|
146
|
+
* the application name and location to match it to our desired configs name and location.
|
|
147
|
+
*
|
|
148
|
+
* @param params
|
|
149
|
+
* @private
|
|
150
|
+
*/
|
|
151
|
+
static matchCurrentParameters(params) {
|
|
152
|
+
const { desired, currentArray, state, settings, isStateful } = params;
|
|
153
|
+
if (!settings.allowMultiple) {
|
|
154
|
+
return currentArray?.[0] ?? null;
|
|
155
|
+
}
|
|
156
|
+
if (!currentArray) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
if (isStateful) {
|
|
160
|
+
return state
|
|
161
|
+
? settings.allowMultiple.matcher(state, currentArray)
|
|
162
|
+
: null;
|
|
163
|
+
}
|
|
164
|
+
return settings.allowMultiple.matcher(desired, currentArray);
|
|
165
|
+
}
|
|
172
166
|
/**
|
|
173
167
|
* Only keep relevant params for the plan. We don't want to change settings that were not already
|
|
174
168
|
* defined.
|
|
@@ -178,7 +172,7 @@ export class Plan {
|
|
|
178
172
|
* or wants to set. If a parameter is not specified then it's not managed by Codify.
|
|
179
173
|
*/
|
|
180
174
|
static filterCurrentParams(params) {
|
|
181
|
-
const {
|
|
175
|
+
const { desired, current, state, settings, isStateful } = params;
|
|
182
176
|
if (!current) {
|
|
183
177
|
return null;
|
|
184
178
|
}
|
|
@@ -188,7 +182,7 @@ export class Plan {
|
|
|
188
182
|
}
|
|
189
183
|
// For stateful mode, we're done after filtering by the keys of desired + state. Stateless mode
|
|
190
184
|
// requires additional filtering for stateful parameter arrays and objects.
|
|
191
|
-
if (
|
|
185
|
+
if (isStateful) {
|
|
192
186
|
return filteredCurrent;
|
|
193
187
|
}
|
|
194
188
|
// TODO: Add object handling here in addition to arrays in the future
|
|
@@ -200,7 +194,7 @@ export class Plan {
|
|
|
200
194
|
if (!current) {
|
|
201
195
|
return null;
|
|
202
196
|
}
|
|
203
|
-
if (
|
|
197
|
+
if (isStateful) {
|
|
204
198
|
const keys = new Set([...Object.keys(state ?? {}), ...Object.keys(desired ?? {})]);
|
|
205
199
|
return Object.fromEntries(Object.entries(current)
|
|
206
200
|
.filter(([k]) => keys.has(k)));
|
|
@@ -276,7 +270,7 @@ export class Plan {
|
|
|
276
270
|
return {
|
|
277
271
|
planId: this.id,
|
|
278
272
|
operation: this.changeSet.operation,
|
|
279
|
-
|
|
273
|
+
isStateful: this.isStateful,
|
|
280
274
|
resourceName: this.coreParameters.name,
|
|
281
275
|
resourceType: this.coreParameters.type,
|
|
282
276
|
parameters: this.changeSet.parameterChanges,
|
package/dist/plugin/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApplyRequestData, GetResourceInfoRequestData, GetResourceInfoResponseData, ImportRequestData, ImportResponseData, InitializeResponseData, PlanRequestData, PlanResponseData, ResourceConfig, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
1
|
+
import { ApplyRequestData, GetResourceInfoRequestData, GetResourceInfoResponseData, ImportRequestData, ImportResponseData, InitializeResponseData, PlanRequestData, PlanResponseData, ResourceConfig, ResourceJson, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
2
2
|
import { Plan } from '../plan/plan.js';
|
|
3
3
|
import { BackgroundPty } from '../pty/background-pty.js';
|
|
4
4
|
import { Resource } from '../resource/resource.js';
|
|
@@ -18,5 +18,5 @@ export declare class Plugin {
|
|
|
18
18
|
apply(data: ApplyRequestData): Promise<void>;
|
|
19
19
|
kill(): Promise<void>;
|
|
20
20
|
private resolvePlan;
|
|
21
|
-
protected crossValidateResources(
|
|
21
|
+
protected crossValidateResources(resources: ResourceJson[]): Promise<void>;
|
|
22
22
|
}
|
package/dist/plugin/plugin.js
CHANGED
|
@@ -52,26 +52,28 @@ export class Plugin {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
async import(data) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const { core, parameters } = data;
|
|
56
|
+
if (!this.resourceControllers.has(core.type)) {
|
|
57
|
+
throw new Error(`Cannot get info for resource ${core.type}, resource doesn't exist`);
|
|
57
58
|
}
|
|
58
59
|
const result = await ptyLocalStorage.run(this.planPty, () => this.resourceControllers
|
|
59
|
-
.get(
|
|
60
|
-
?.import(
|
|
60
|
+
.get(core.type)
|
|
61
|
+
?.import(core, parameters));
|
|
61
62
|
return {
|
|
62
|
-
request: data
|
|
63
|
+
request: data,
|
|
63
64
|
result: result ?? [],
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
async validate(data) {
|
|
67
68
|
const validationResults = [];
|
|
68
69
|
for (const config of data.configs) {
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const { core, parameters } = config;
|
|
71
|
+
if (!this.resourceControllers.has(core.type)) {
|
|
72
|
+
throw new Error(`Resource type not found: ${core.type}`);
|
|
71
73
|
}
|
|
72
74
|
const validation = await this.resourceControllers
|
|
73
|
-
.get(
|
|
74
|
-
.validate(
|
|
75
|
+
.get(core.type)
|
|
76
|
+
.validate(core, parameters);
|
|
75
77
|
validationResults.push(validation);
|
|
76
78
|
}
|
|
77
79
|
await this.crossValidateResources(data.configs);
|
|
@@ -80,11 +82,11 @@ export class Plugin {
|
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
async plan(data) {
|
|
83
|
-
const type = data.
|
|
84
|
-
if (!
|
|
85
|
+
const { type } = data.core;
|
|
86
|
+
if (!this.resourceControllers.has(type)) {
|
|
85
87
|
throw new Error(`Resource type not found: ${type}`);
|
|
86
88
|
}
|
|
87
|
-
const plan = await ptyLocalStorage.run(this.planPty, async () => this.resourceControllers.get(type).plan(data.desired ?? null, data.state ?? null, data.isStateful));
|
|
89
|
+
const plan = await ptyLocalStorage.run(this.planPty, async () => this.resourceControllers.get(type).plan(data.core, data.desired ?? null, data.state ?? null, data.isStateful));
|
|
88
90
|
this.planStorage.set(plan.id, plan);
|
|
89
91
|
return plan.toResponse();
|
|
90
92
|
}
|
|
@@ -101,7 +103,7 @@ export class Plugin {
|
|
|
101
103
|
// Validate using desired/desired. If the apply was successful, no changes should be reported back.
|
|
102
104
|
// Default back desired back to current if it is not defined (for destroys only)
|
|
103
105
|
const validationPlan = await ptyLocalStorage.run(new BackgroundPty(), async () => {
|
|
104
|
-
const result = await resource.plan(plan.desiredConfig, plan.desiredConfig ?? plan.currentConfig, plan.
|
|
106
|
+
const result = await resource.plan(plan.coreParameters, plan.desiredConfig, plan.desiredConfig ?? plan.currentConfig, plan.isStateful);
|
|
105
107
|
await getPty().kill();
|
|
106
108
|
return result;
|
|
107
109
|
});
|
|
@@ -126,5 +128,6 @@ export class Plugin {
|
|
|
126
128
|
const resource = this.resourceControllers.get(planRequest.resourceType);
|
|
127
129
|
return Plan.fromResponse(planRequest, resource.parsedSettings.defaultValues);
|
|
128
130
|
}
|
|
129
|
-
async crossValidateResources(
|
|
131
|
+
async crossValidateResources(resources) {
|
|
132
|
+
}
|
|
130
133
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
3
3
|
export declare class ConfigParser<T extends StringIndexedObject> {
|
|
4
4
|
private readonly desiredConfig;
|
|
5
5
|
private readonly stateConfig;
|
|
6
6
|
private statefulParametersMap;
|
|
7
|
-
constructor(desiredConfig: Partial<T>
|
|
8
|
-
get coreParameters(): ResourceConfig;
|
|
9
|
-
get desiredParameters(): Partial<T> | null;
|
|
10
|
-
get stateParameters(): Partial<T> | null;
|
|
7
|
+
constructor(desiredConfig: Partial<T> | null, stateConfig: Partial<T> | null, statefulParameters: Map<keyof T, StatefulParameterController<T, T[keyof T]>>);
|
|
11
8
|
get allParameters(): Partial<T>;
|
|
12
9
|
get allNonStatefulParameters(): Partial<T>;
|
|
13
10
|
get allStatefulParameters(): Partial<T>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { splitUserConfig } from '../utils/utils.js';
|
|
2
1
|
export class ConfigParser {
|
|
3
2
|
desiredConfig;
|
|
4
3
|
stateConfig;
|
|
@@ -8,34 +7,8 @@ export class ConfigParser {
|
|
|
8
7
|
this.stateConfig = stateConfig;
|
|
9
8
|
this.statefulParametersMap = statefulParameters;
|
|
10
9
|
}
|
|
11
|
-
get coreParameters() {
|
|
12
|
-
const desiredCoreParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).coreParameters : undefined;
|
|
13
|
-
const currentCoreParameters = this.stateConfig ? splitUserConfig(this.stateConfig).coreParameters : undefined;
|
|
14
|
-
if (!desiredCoreParameters && !currentCoreParameters) {
|
|
15
|
-
throw new Error(`Unable to parse resource core parameters from:
|
|
16
|
-
|
|
17
|
-
Desired: ${JSON.stringify(this.desiredConfig, null, 2)}
|
|
18
|
-
|
|
19
|
-
Current: ${JSON.stringify(this.stateConfig, null, 2)}`);
|
|
20
|
-
}
|
|
21
|
-
return desiredCoreParameters ?? currentCoreParameters;
|
|
22
|
-
}
|
|
23
|
-
get desiredParameters() {
|
|
24
|
-
if (!this.desiredConfig) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
const { parameters } = splitUserConfig(this.desiredConfig);
|
|
28
|
-
return parameters;
|
|
29
|
-
}
|
|
30
|
-
get stateParameters() {
|
|
31
|
-
if (!this.stateConfig) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
const { parameters } = splitUserConfig(this.stateConfig);
|
|
35
|
-
return parameters;
|
|
36
|
-
}
|
|
37
10
|
get allParameters() {
|
|
38
|
-
return { ...this.
|
|
11
|
+
return { ...this.desiredConfig, ...this.stateConfig };
|
|
39
12
|
}
|
|
40
13
|
get allNonStatefulParameters() {
|
|
41
14
|
const { allParameters, statefulParametersMap, } = this;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Ajv, ValidateFunction } from 'ajv';
|
|
2
|
-
import { ResourceConfig, StringIndexedObject, ValidateResponseData } from 'codify-schemas';
|
|
2
|
+
import { ResourceConfig, ResourceJson, StringIndexedObject, ValidateResponseData } from 'codify-schemas';
|
|
3
3
|
import { Plan } from '../plan/plan.js';
|
|
4
4
|
import { ParsedResourceSettings } from './parsed-resource-settings.js';
|
|
5
5
|
import { Resource } from './resource.js';
|
|
@@ -14,10 +14,10 @@ export declare class ResourceController<T extends StringIndexedObject> {
|
|
|
14
14
|
protected schemaValidator?: ValidateFunction;
|
|
15
15
|
constructor(resource: Resource<T>);
|
|
16
16
|
initialize(): Promise<void>;
|
|
17
|
-
validate(
|
|
18
|
-
plan(
|
|
17
|
+
validate(core: ResourceConfig, parameters: Partial<T>): Promise<ValidateResponseData['resourceValidations'][0]>;
|
|
18
|
+
plan(core: ResourceConfig, desired: Partial<T> | null, state: Partial<T> | null, isStateful?: boolean): Promise<Plan<T>>;
|
|
19
19
|
apply(plan: Plan<T>): Promise<void>;
|
|
20
|
-
import(
|
|
20
|
+
import(core: ResourceConfig, parameters: Partial<T>): Promise<Array<ResourceJson> | null>;
|
|
21
21
|
private applyCreate;
|
|
22
22
|
private applyModify;
|
|
23
23
|
private applyDestroy;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Ajv } from 'ajv';
|
|
2
2
|
import { ParameterOperation, ResourceOperation } from 'codify-schemas';
|
|
3
3
|
import { Plan } from '../plan/plan.js';
|
|
4
|
-
import { splitUserConfig } from '../utils/utils.js';
|
|
5
4
|
import { ConfigParser } from './config-parser.js';
|
|
6
5
|
import { ParsedResourceSettings } from './parsed-resource-settings.js';
|
|
7
6
|
export class ResourceController {
|
|
@@ -31,19 +30,17 @@ export class ResourceController {
|
|
|
31
30
|
async initialize() {
|
|
32
31
|
return this.resource.initialize();
|
|
33
32
|
}
|
|
34
|
-
async validate(
|
|
35
|
-
|
|
36
|
-
await this.applyTransformParameters(configToValidate);
|
|
37
|
-
const { parameters, coreParameters } = splitUserConfig(configToValidate);
|
|
33
|
+
async validate(core, parameters) {
|
|
34
|
+
await this.applyTransformParameters(parameters);
|
|
38
35
|
this.addDefaultValues(parameters);
|
|
39
36
|
if (this.schemaValidator) {
|
|
40
37
|
// Schema validator uses pre transformation parameters
|
|
41
|
-
const isValid = this.schemaValidator(
|
|
38
|
+
const isValid = this.schemaValidator(parameters);
|
|
42
39
|
if (!isValid) {
|
|
43
40
|
return {
|
|
44
41
|
isValid: false,
|
|
45
|
-
resourceName:
|
|
46
|
-
resourceType:
|
|
42
|
+
resourceName: core.name,
|
|
43
|
+
resourceType: core.type,
|
|
47
44
|
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
48
45
|
};
|
|
49
46
|
}
|
|
@@ -61,54 +58,54 @@ export class ResourceController {
|
|
|
61
58
|
return {
|
|
62
59
|
customValidationErrorMessage,
|
|
63
60
|
isValid: false,
|
|
64
|
-
resourceName:
|
|
65
|
-
resourceType:
|
|
61
|
+
resourceName: core.name,
|
|
62
|
+
resourceType: core.type,
|
|
66
63
|
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
67
64
|
};
|
|
68
65
|
}
|
|
69
66
|
return {
|
|
70
67
|
isValid: true,
|
|
71
|
-
resourceName:
|
|
72
|
-
resourceType:
|
|
68
|
+
resourceName: core.name,
|
|
69
|
+
resourceType: core.type,
|
|
73
70
|
schemaValidationErrors: [],
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
|
-
async plan(
|
|
77
|
-
this.validatePlanInputs(
|
|
78
|
-
this.addDefaultValues(
|
|
79
|
-
await this.applyTransformParameters(
|
|
80
|
-
this.addDefaultValues(
|
|
81
|
-
await this.applyTransformParameters(
|
|
73
|
+
async plan(core, desired, state, isStateful = false) {
|
|
74
|
+
this.validatePlanInputs(core, desired, state, isStateful);
|
|
75
|
+
this.addDefaultValues(desired);
|
|
76
|
+
await this.applyTransformParameters(desired);
|
|
77
|
+
this.addDefaultValues(state);
|
|
78
|
+
await this.applyTransformParameters(state);
|
|
82
79
|
// Parse data from the user supplied config
|
|
83
|
-
const parsedConfig = new ConfigParser(
|
|
84
|
-
const {
|
|
80
|
+
const parsedConfig = new ConfigParser(desired, state, this.parsedSettings.statefulParameters);
|
|
81
|
+
const { allParameters, allNonStatefulParameters, allStatefulParameters, } = parsedConfig;
|
|
85
82
|
// Refresh resource parameters. This refreshes the parameters that configure the resource itself
|
|
86
|
-
const
|
|
83
|
+
const currentArray = await this.refreshNonStatefulParameters(allNonStatefulParameters);
|
|
87
84
|
// Short circuit here. If the resource is non-existent, there's no point checking stateful parameters
|
|
88
|
-
if (
|
|
89
|
-
||
|
|
85
|
+
if (currentArray === null
|
|
86
|
+
|| currentArray === undefined
|
|
90
87
|
|| this.settings.allowMultiple // Stateful parameters are not supported currently if allowMultiple is true
|
|
91
|
-
||
|
|
92
|
-
||
|
|
88
|
+
|| currentArray.length === 0
|
|
89
|
+
|| currentArray.filter(Boolean).length === 0) {
|
|
93
90
|
return Plan.calculate({
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
desired,
|
|
92
|
+
currentArray,
|
|
93
|
+
state,
|
|
94
|
+
core,
|
|
98
95
|
settings: this.parsedSettings,
|
|
99
|
-
|
|
96
|
+
isStateful,
|
|
100
97
|
});
|
|
101
98
|
}
|
|
102
99
|
// Refresh stateful parameters. These parameters have state external to the resource. allowMultiple
|
|
103
100
|
// does not work together with stateful parameters
|
|
104
101
|
const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, allParameters);
|
|
105
102
|
return Plan.calculate({
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
desired,
|
|
104
|
+
currentArray: [{ ...currentArray[0], ...statefulCurrentParameters }],
|
|
105
|
+
state,
|
|
106
|
+
core,
|
|
110
107
|
settings: this.parsedSettings,
|
|
111
|
-
|
|
108
|
+
isStateful
|
|
112
109
|
});
|
|
113
110
|
}
|
|
114
111
|
async apply(plan) {
|
|
@@ -131,36 +128,35 @@ export class ResourceController {
|
|
|
131
128
|
}
|
|
132
129
|
}
|
|
133
130
|
}
|
|
134
|
-
async import(
|
|
135
|
-
this.addDefaultValues(
|
|
136
|
-
await this.applyTransformParameters(
|
|
131
|
+
async import(core, parameters) {
|
|
132
|
+
this.addDefaultValues(parameters);
|
|
133
|
+
await this.applyTransformParameters(parameters);
|
|
137
134
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
138
135
|
const parametersToRefresh = this.settings.import?.refreshKeys
|
|
139
136
|
? {
|
|
140
137
|
...Object.fromEntries(this.settings.import?.refreshKeys.map((k) => [k, null])),
|
|
141
138
|
...this.settings.import?.defaultRefreshValues,
|
|
142
|
-
...
|
|
139
|
+
...parameters,
|
|
143
140
|
}
|
|
144
141
|
: {
|
|
145
142
|
...Object.fromEntries(this.getAllParameterKeys().map((k) => [k, null])),
|
|
146
143
|
...this.settings.import?.defaultRefreshValues,
|
|
147
|
-
...
|
|
144
|
+
...parameters,
|
|
148
145
|
};
|
|
149
146
|
// Parse data from the user supplied config
|
|
150
147
|
const parsedConfig = new ConfigParser(parametersToRefresh, null, this.parsedSettings.statefulParameters);
|
|
151
|
-
const { allNonStatefulParameters, allStatefulParameters,
|
|
148
|
+
const { allNonStatefulParameters, allStatefulParameters, } = parsedConfig;
|
|
152
149
|
const currentParametersArray = await this.refreshNonStatefulParameters(allNonStatefulParameters);
|
|
153
150
|
if (currentParametersArray === null
|
|
154
151
|
|| currentParametersArray === undefined
|
|
155
152
|
|| this.settings.allowMultiple // Stateful parameters are not supported currently if allowMultiple is true
|
|
156
|
-
|| currentParametersArray.length === 0
|
|
157
153
|
|| currentParametersArray.filter(Boolean).length === 0) {
|
|
158
154
|
return currentParametersArray
|
|
159
|
-
?.map((r) => ({
|
|
155
|
+
?.map((r) => ({ core, parameters: r }))
|
|
160
156
|
?? null;
|
|
161
157
|
}
|
|
162
158
|
const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, parametersToRefresh);
|
|
163
|
-
return [{
|
|
159
|
+
return [{ core, parameters: { ...currentParametersArray[0], ...statefulCurrentParameters } }];
|
|
164
160
|
}
|
|
165
161
|
async applyCreate(plan) {
|
|
166
162
|
await this.resource.create(plan);
|
|
@@ -233,10 +229,9 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
233
229
|
config[key] = await inputTransformation(config[key], this.settings.parameterSettings[key]);
|
|
234
230
|
}
|
|
235
231
|
if (this.settings.inputTransformation) {
|
|
236
|
-
const
|
|
237
|
-
const transformed = await this.settings.inputTransformation(parameters);
|
|
232
|
+
const transformed = await this.settings.inputTransformation({ ...config });
|
|
238
233
|
Object.keys(config).forEach((k) => delete config[k]);
|
|
239
|
-
Object.assign(config, transformed
|
|
234
|
+
Object.assign(config, transformed);
|
|
240
235
|
}
|
|
241
236
|
}
|
|
242
237
|
addDefaultValues(config) {
|
|
@@ -272,11 +267,14 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
272
267
|
}));
|
|
273
268
|
return result;
|
|
274
269
|
}
|
|
275
|
-
validatePlanInputs(desired, current,
|
|
270
|
+
validatePlanInputs(core, desired, current, isStateful) {
|
|
271
|
+
if (!core || !core.type) {
|
|
272
|
+
throw new Error('Core parameters type must be defined');
|
|
273
|
+
}
|
|
276
274
|
if (!desired && !current) {
|
|
277
275
|
throw new Error('Desired config and current config cannot both be missing');
|
|
278
276
|
}
|
|
279
|
-
if (!
|
|
277
|
+
if (!isStateful && !desired) {
|
|
280
278
|
throw new Error('Desired config must be provided in non-stateful mode');
|
|
281
279
|
}
|
|
282
280
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.130",
|
|
4
4
|
"description": "Library plugin library",
|
|
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.61",
|
|
18
18
|
"@npmcli/promise-spawn": "^7.0.1",
|
|
19
19
|
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
|
|
20
20
|
"uuid": "^10.0.0",
|