codify-plugin-lib 1.0.131 → 1.0.133
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/.github/workflows/unit-test-ci.yaml +1 -2
- package/dist/plan/plan.d.ts +1 -3
- package/dist/plan/plan.js +31 -9
- package/dist/plugin/plugin.js +1 -1
- package/dist/resource/parsed-resource-settings.d.ts +5 -3
- package/dist/resource/parsed-resource-settings.js +6 -7
- package/dist/resource/resource-controller.d.ts +1 -0
- package/dist/resource/resource-controller.js +23 -5
- package/dist/resource/resource-settings.d.ts +36 -14
- package/dist/resource/resource-settings.js +43 -11
- package/dist/stateful-parameter/stateful-parameter-controller.js +2 -3
- package/dist/utils/utils.d.ts +1 -0
- package/dist/utils/utils.js +3 -0
- package/package.json +3 -2
- package/src/plan/plan.test.ts +114 -0
- package/src/plan/plan.ts +38 -9
- package/src/plugin/plugin.test.ts +47 -3
- package/src/plugin/plugin.ts +1 -1
- package/src/pty/background-pty.test.ts +16 -15
- package/src/pty/index.test.ts +6 -9
- package/src/resource/parsed-resource-settings.test.ts +4 -4
- package/src/resource/parsed-resource-settings.ts +12 -9
- package/src/resource/resource-controller.ts +33 -5
- package/src/resource/resource-settings.test.ts +87 -14
- package/src/resource/resource-settings.ts +91 -27
- package/src/stateful-parameter/stateful-parameter-controller.ts +2 -3
- package/src/utils/test-utils.test.ts +6 -0
- package/src/utils/utils.ts +4 -0
- package/vitest.config.ts +2 -3
- package/src/pty/vitest.config.ts +0 -11
package/src/plan/plan.ts
CHANGED
|
@@ -260,13 +260,44 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
260
260
|
return null;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
const matcher = typeof settings.allowMultiple === 'boolean' || !settings.allowMultiple.matcher
|
|
264
|
+
? ((desired: Partial<T>, currentArr: Array<Partial<T>>) => {
|
|
265
|
+
const requiredParameters = typeof settings.allowMultiple === 'object'
|
|
266
|
+
? settings.allowMultiple?.requiredParameters ?? (settings.schema?.required as string[]) ?? []
|
|
267
|
+
: (settings.schema?.required as string[]) ?? []
|
|
268
|
+
|
|
269
|
+
const matched = currentArr.filter((c) => requiredParameters.every((key) => {
|
|
270
|
+
const currentParameter = c[key];
|
|
271
|
+
const desiredParameter = desired[key];
|
|
272
|
+
|
|
273
|
+
if (!currentParameter) {
|
|
274
|
+
console.warn(`Unable to find required parameter for current ${currentParameter}`)
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!desiredParameter) {
|
|
279
|
+
console.warn(`Unable to find required parameter for current ${currentParameter}`)
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return currentParameter === desiredParameter;
|
|
284
|
+
}))
|
|
285
|
+
|
|
286
|
+
if (matched.length > 1) {
|
|
287
|
+
console.warn(`Required parameters did not uniquely identify a resource: ${currentArray}. Defaulting to the first one`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return matched[0];
|
|
291
|
+
})
|
|
292
|
+
: settings.allowMultiple.matcher
|
|
293
|
+
|
|
263
294
|
if (isStateful) {
|
|
264
295
|
return state
|
|
265
|
-
?
|
|
296
|
+
? matcher(state, currentArray) ?? null
|
|
266
297
|
: null
|
|
267
298
|
}
|
|
268
299
|
|
|
269
|
-
return
|
|
300
|
+
return matcher(desired!, currentArray) ?? null;
|
|
270
301
|
}
|
|
271
302
|
|
|
272
303
|
/**
|
|
@@ -384,15 +415,15 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
384
415
|
const defaultFilterMethod = ((desired: any[], current: any[]) => {
|
|
385
416
|
const result = [];
|
|
386
417
|
|
|
387
|
-
for (let counter =
|
|
388
|
-
const idx = currentCopy.findIndex((e2) => matcher(
|
|
418
|
+
for (let counter = desired.length - 1; counter >= 0; counter--) {
|
|
419
|
+
const idx = currentCopy.findIndex((e2) => matcher(desired[counter], e2))
|
|
389
420
|
|
|
390
421
|
if (idx === -1) {
|
|
391
422
|
continue;
|
|
392
423
|
}
|
|
393
424
|
|
|
394
|
-
|
|
395
|
-
const [element] =
|
|
425
|
+
desired.splice(counter, 1)
|
|
426
|
+
const [element] = current.splice(idx, 1)
|
|
396
427
|
result.push(element)
|
|
397
428
|
}
|
|
398
429
|
|
|
@@ -413,9 +444,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
413
444
|
return this.changeSet.operation !== ResourceOperation.NOOP;
|
|
414
445
|
}
|
|
415
446
|
|
|
416
|
-
/**
|
|
417
|
-
* Convert the plan to a JSON response object
|
|
418
|
-
*/
|
|
447
|
+
/** Convert the plan to a JSON response object */
|
|
419
448
|
toResponse(): PlanResponseData {
|
|
420
449
|
return {
|
|
421
450
|
planId: this.id,
|
|
@@ -6,7 +6,6 @@ import { Plan } from '../plan/plan.js';
|
|
|
6
6
|
import { spy } from 'sinon';
|
|
7
7
|
import { ResourceSettings } from '../resource/resource-settings.js';
|
|
8
8
|
import { TestConfig } from '../utils/test-utils.test.js';
|
|
9
|
-
import { ApplyValidationError } from '../common/errors.js';
|
|
10
9
|
import { getPty } from '../pty/index.js';
|
|
11
10
|
|
|
12
11
|
interface TestConfig extends StringIndexedObject {
|
|
@@ -194,7 +193,7 @@ describe('Plugin tests', () => {
|
|
|
194
193
|
return {
|
|
195
194
|
id: 'typeId',
|
|
196
195
|
schema,
|
|
197
|
-
|
|
196
|
+
importAndDestroy: {
|
|
198
197
|
requiredParameters: []
|
|
199
198
|
}
|
|
200
199
|
}
|
|
@@ -229,7 +228,7 @@ describe('Plugin tests', () => {
|
|
|
229
228
|
|
|
230
229
|
await expect(() => testPlugin.apply({ plan }))
|
|
231
230
|
.rejects
|
|
232
|
-
.toThrowError(
|
|
231
|
+
.toThrowError();
|
|
233
232
|
expect(resource.modify.calledOnce).to.be.true;
|
|
234
233
|
})
|
|
235
234
|
|
|
@@ -280,4 +279,49 @@ describe('Plugin tests', () => {
|
|
|
280
279
|
await testPlugin.apply({ plan })
|
|
281
280
|
expect(resource.refresh.calledOnce).to.be.true;
|
|
282
281
|
})
|
|
282
|
+
|
|
283
|
+
it('Maintains types for validate', async () => {
|
|
284
|
+
const resource = new class extends TestResource {
|
|
285
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
286
|
+
return {
|
|
287
|
+
id: 'type',
|
|
288
|
+
schema: {
|
|
289
|
+
'$schema': 'http://json-schema.org/draft-07/schema',
|
|
290
|
+
'$id': 'https://www.codifycli.com/ssh-config.json',
|
|
291
|
+
'type': 'object',
|
|
292
|
+
'properties': {
|
|
293
|
+
'hosts': {
|
|
294
|
+
'description': 'The host blocks inside of the ~/.ssh/config file. See http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5 ',
|
|
295
|
+
'type': 'array',
|
|
296
|
+
'items': {
|
|
297
|
+
'type': 'object',
|
|
298
|
+
'description': 'The individual host blocks inside of the ~/.ssh/config file',
|
|
299
|
+
'properties': {
|
|
300
|
+
'UseKeychain': {
|
|
301
|
+
'type': 'boolean',
|
|
302
|
+
'description': 'A UseKeychain option was introduced in macOS Sierra allowing users to specify whether they would like for the passphrase to be stored in the keychain'
|
|
303
|
+
},
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const plugin = Plugin.create('testPlugin', [resource as any]);
|
|
314
|
+
const result = await plugin.validate({
|
|
315
|
+
configs: [{
|
|
316
|
+
core: { type: 'type' },
|
|
317
|
+
parameters: {
|
|
318
|
+
hosts: [{
|
|
319
|
+
UseKeychain: true,
|
|
320
|
+
}]
|
|
321
|
+
}
|
|
322
|
+
}]
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
console.log(result);
|
|
326
|
+
})
|
|
283
327
|
});
|
package/src/plugin/plugin.ts
CHANGED
|
@@ -67,7 +67,7 @@ export class Plugin {
|
|
|
67
67
|
|
|
68
68
|
const schema = resource.settings.schema as JSONSchemaType<any> | undefined;
|
|
69
69
|
const requiredPropertyNames = (
|
|
70
|
-
resource.settings.
|
|
70
|
+
resource.settings.importAndDestroy?.requiredParameters
|
|
71
71
|
?? schema?.required
|
|
72
72
|
?? null
|
|
73
73
|
) as null | string[];
|
|
@@ -18,21 +18,22 @@ describe('BackgroundPty tests', () => {
|
|
|
18
18
|
});
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
// This test takes forever so going to disable for now.
|
|
22
|
+
// it('Can launch 100 commands in parallel', { timeout: 15000 }, async () => {
|
|
23
|
+
// const pty = new BackgroundPty();
|
|
24
|
+
//
|
|
25
|
+
// const fn = async () => pty.spawnSafe('ls');
|
|
26
|
+
//
|
|
27
|
+
// const results = await Promise.all(
|
|
28
|
+
// Array.from({ length: 100 }, (_, i) => i + 1)
|
|
29
|
+
// .map(() => fn())
|
|
30
|
+
// )
|
|
31
|
+
//
|
|
32
|
+
// expect(results.length).to.eq(100);
|
|
33
|
+
// expect(results.every((r) => r.exitCode === 0))
|
|
34
|
+
//
|
|
35
|
+
// await pty.kill();
|
|
36
|
+
// })
|
|
36
37
|
|
|
37
38
|
it('Reports back the correct exit code and status', async () => {
|
|
38
39
|
const pty = new BackgroundPty();
|
package/src/pty/index.test.ts
CHANGED
|
@@ -25,9 +25,8 @@ describe('General tests for PTYs', () => {
|
|
|
25
25
|
|
|
26
26
|
const plugin = Plugin.create('test plugin', [testResource])
|
|
27
27
|
const plan = await plugin.plan({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
28
|
+
core: { type: 'type' },
|
|
29
|
+
desired: {},
|
|
31
30
|
state: undefined,
|
|
32
31
|
isStateful: false,
|
|
33
32
|
})
|
|
@@ -84,17 +83,15 @@ describe('General tests for PTYs', () => {
|
|
|
84
83
|
|
|
85
84
|
const plugin = Plugin.create('test plugin', [testResource1, testResource2]);
|
|
86
85
|
await plugin.plan({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
86
|
+
core: { type: 'type1' },
|
|
87
|
+
desired: {},
|
|
90
88
|
state: undefined,
|
|
91
89
|
isStateful: false,
|
|
92
90
|
})
|
|
93
91
|
|
|
94
92
|
await plugin.plan({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
},
|
|
93
|
+
core: { type: 'type2' },
|
|
94
|
+
desired: {},
|
|
98
95
|
state: undefined,
|
|
99
96
|
isStateful: false,
|
|
100
97
|
})
|
|
@@ -81,7 +81,7 @@ describe('Resource options parser tests', () => {
|
|
|
81
81
|
const option: ResourceSettings<TestConfig> = {
|
|
82
82
|
id: 'typeId',
|
|
83
83
|
schema,
|
|
84
|
-
|
|
84
|
+
importAndDestroy: {
|
|
85
85
|
requiredParameters: ['import-error']
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -104,7 +104,7 @@ describe('Resource options parser tests', () => {
|
|
|
104
104
|
const option: ResourceSettings<TestConfig> = {
|
|
105
105
|
id: 'typeId',
|
|
106
106
|
schema,
|
|
107
|
-
|
|
107
|
+
importAndDestroy: {
|
|
108
108
|
refreshKeys: ['import-error']
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -127,7 +127,7 @@ describe('Resource options parser tests', () => {
|
|
|
127
127
|
const option: ResourceSettings<TestConfig> = {
|
|
128
128
|
id: 'typeId',
|
|
129
129
|
schema,
|
|
130
|
-
|
|
130
|
+
importAndDestroy: {
|
|
131
131
|
refreshKeys: ['remote'],
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -150,7 +150,7 @@ describe('Resource options parser tests', () => {
|
|
|
150
150
|
const option: ResourceSettings<TestConfig> = {
|
|
151
151
|
id: 'typeId',
|
|
152
152
|
schema,
|
|
153
|
-
|
|
153
|
+
importAndDestroy: {
|
|
154
154
|
defaultRefreshValues: {
|
|
155
155
|
repository: 'abc'
|
|
156
156
|
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
ArrayParameterSetting,
|
|
7
7
|
DefaultParameterSetting,
|
|
8
8
|
ParameterSetting,
|
|
9
|
+
resolveElementEqualsFn,
|
|
9
10
|
resolveEqualsFn,
|
|
10
|
-
resolveFnFromEqualsFnOrString,
|
|
11
11
|
resolveParameterTransformFn,
|
|
12
12
|
ResourceSettings,
|
|
13
13
|
StatefulParameterSetting
|
|
@@ -35,8 +35,12 @@ export type ParsedParameterSetting =
|
|
|
35
35
|
export class ParsedResourceSettings<T extends StringIndexedObject> implements ResourceSettings<T> {
|
|
36
36
|
private cache = new Map<string, unknown>();
|
|
37
37
|
id!: string;
|
|
38
|
-
schema?:
|
|
39
|
-
allowMultiple?: {
|
|
38
|
+
schema?: JSONSchemaType<T | any>;
|
|
39
|
+
allowMultiple?: {
|
|
40
|
+
matcher?: (desired: Partial<T>, current: Partial<T>[]) => Partial<T>;
|
|
41
|
+
requiredParameters?: string[]
|
|
42
|
+
} | boolean;
|
|
43
|
+
|
|
40
44
|
removeStatefulParametersBeforeDestroy?: boolean | undefined;
|
|
41
45
|
dependencies?: string[] | undefined;
|
|
42
46
|
inputTransformation?: ((desired: Partial<T>) => unknown) | undefined;
|
|
@@ -95,8 +99,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
95
99
|
if (v.type === 'array') {
|
|
96
100
|
const parsed = {
|
|
97
101
|
...v,
|
|
98
|
-
isElementEqual:
|
|
99
|
-
?? ((a: unknown, b: unknown) => a === b),
|
|
102
|
+
isElementEqual: resolveElementEqualsFn(v as ArrayParameterSetting)
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
return [k, parsed as ParsedArrayParameterSetting];
|
|
@@ -142,7 +145,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
142
145
|
return Object.fromEntries(
|
|
143
146
|
Object.entries(this.settings.parameterSettings)
|
|
144
147
|
.filter(([_, v]) => resolveParameterTransformFn(v!) !== undefined)
|
|
145
|
-
.map(([k, v]) => [k, resolveParameterTransformFn(v!)] as const)
|
|
148
|
+
.map(([k, v]) => [k, resolveParameterTransformFn(v!)!.to] as const)
|
|
146
149
|
) as Record<keyof T, (a: unknown) => unknown>;
|
|
147
150
|
});
|
|
148
151
|
}
|
|
@@ -186,7 +189,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
const schema = this.settings.schema as JSONSchemaType<any>;
|
|
189
|
-
if (!this.settings.
|
|
192
|
+
if (!this.settings.importAndDestroy && (schema?.oneOf
|
|
190
193
|
&& Array.isArray(schema.oneOf)
|
|
191
194
|
&& schema.oneOf.some((s) => s.required)
|
|
192
195
|
)
|
|
@@ -214,8 +217,8 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
214
217
|
)
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
if (this.settings.
|
|
218
|
-
const { requiredParameters, refreshKeys, defaultRefreshValues } = this.settings.
|
|
220
|
+
if (this.settings.importAndDestroy) {
|
|
221
|
+
const { requiredParameters, refreshKeys, defaultRefreshValues } = this.settings.importAndDestroy;
|
|
219
222
|
|
|
220
223
|
const requiredParametersNotInSchema = requiredParameters
|
|
221
224
|
?.filter(
|
|
@@ -57,12 +57,13 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
57
57
|
core: ResourceConfig,
|
|
58
58
|
parameters: Partial<T>,
|
|
59
59
|
): Promise<ValidateResponseData['resourceValidations'][0]> {
|
|
60
|
+
const originalParameters = structuredClone(parameters);
|
|
60
61
|
await this.applyTransformParameters(parameters);
|
|
61
62
|
this.addDefaultValues(parameters);
|
|
62
63
|
|
|
63
64
|
if (this.schemaValidator) {
|
|
64
65
|
// Schema validator uses pre transformation parameters
|
|
65
|
-
const isValid = this.schemaValidator(
|
|
66
|
+
const isValid = this.schemaValidator(originalParameters);
|
|
66
67
|
|
|
67
68
|
if (!isValid) {
|
|
68
69
|
return {
|
|
@@ -157,6 +158,33 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
157
158
|
})
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
async planDestroy(
|
|
162
|
+
core: ResourceConfig,
|
|
163
|
+
parameters: Partial<T>
|
|
164
|
+
): Promise<Plan<T>> {
|
|
165
|
+
this.addDefaultValues(parameters);
|
|
166
|
+
await this.applyTransformParameters(parameters);
|
|
167
|
+
|
|
168
|
+
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
169
|
+
const parametersToRefresh = this.settings.importAndDestroy?.refreshKeys
|
|
170
|
+
? {
|
|
171
|
+
...Object.fromEntries(
|
|
172
|
+
this.settings.importAndDestroy?.refreshKeys.map((k) => [k, null])
|
|
173
|
+
),
|
|
174
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
175
|
+
...parameters,
|
|
176
|
+
}
|
|
177
|
+
: {
|
|
178
|
+
...Object.fromEntries(
|
|
179
|
+
this.getAllParameterKeys().map((k) => [k, null])
|
|
180
|
+
),
|
|
181
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
182
|
+
...parameters,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return this.plan(core, null, parametersToRefresh, true);
|
|
186
|
+
}
|
|
187
|
+
|
|
160
188
|
async apply(plan: Plan<T>): Promise<void> {
|
|
161
189
|
if (plan.getResourceType() !== this.typeId) {
|
|
162
190
|
throw new Error(`Internal error: Plan set to wrong resource during apply. Expected ${this.typeId} but got: ${plan.getResourceType()}`);
|
|
@@ -190,19 +218,19 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
190
218
|
await this.applyTransformParameters(parameters);
|
|
191
219
|
|
|
192
220
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
193
|
-
const parametersToRefresh = this.settings.
|
|
221
|
+
const parametersToRefresh = this.settings.importAndDestroy?.refreshKeys
|
|
194
222
|
? {
|
|
195
223
|
...Object.fromEntries(
|
|
196
|
-
this.settings.
|
|
224
|
+
this.settings.importAndDestroy?.refreshKeys.map((k) => [k, null])
|
|
197
225
|
),
|
|
198
|
-
...this.settings.
|
|
226
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
199
227
|
...parameters,
|
|
200
228
|
}
|
|
201
229
|
: {
|
|
202
230
|
...Object.fromEntries(
|
|
203
231
|
this.getAllParameterKeys().map((k) => [k, null])
|
|
204
232
|
),
|
|
205
|
-
...this.settings.
|
|
233
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
206
234
|
...parameters,
|
|
207
235
|
};
|
|
208
236
|
|
|
@@ -637,7 +637,7 @@ describe('Resource parameter tests', () => {
|
|
|
637
637
|
getSettings(): ResourceSettings<TestConfig> {
|
|
638
638
|
return {
|
|
639
639
|
id: 'resourceType',
|
|
640
|
-
|
|
640
|
+
importAndDestroy: {
|
|
641
641
|
requiredParameters: [
|
|
642
642
|
'propA',
|
|
643
643
|
'propB',
|
|
@@ -723,7 +723,7 @@ describe('Resource parameter tests', () => {
|
|
|
723
723
|
getSettings(): ResourceSettings<TestConfig> {
|
|
724
724
|
return {
|
|
725
725
|
id: 'resourceType',
|
|
726
|
-
|
|
726
|
+
importAndDestroy: {
|
|
727
727
|
requiredParameters: ['propA'],
|
|
728
728
|
refreshKeys: ['propB', 'propA'],
|
|
729
729
|
defaultRefreshValues: {
|
|
@@ -852,16 +852,25 @@ describe('Resource parameter tests', () => {
|
|
|
852
852
|
parameterSettings: {
|
|
853
853
|
propD: {
|
|
854
854
|
type: 'array',
|
|
855
|
-
inputTransformation:
|
|
855
|
+
inputTransformation: {
|
|
856
|
+
to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
857
|
+
Object.entries(h)
|
|
858
|
+
.map(([k, v]) => [
|
|
859
|
+
k,
|
|
860
|
+
typeof v === 'boolean'
|
|
861
|
+
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
862
|
+
: v,
|
|
863
|
+
])
|
|
864
|
+
)
|
|
865
|
+
),
|
|
866
|
+
from: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
856
867
|
Object.entries(h)
|
|
857
868
|
.map(([k, v]) => [
|
|
858
869
|
k,
|
|
859
|
-
|
|
860
|
-
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
861
|
-
: v,
|
|
870
|
+
v === 'yes',
|
|
862
871
|
])
|
|
863
|
-
)
|
|
864
|
-
|
|
872
|
+
))
|
|
873
|
+
}
|
|
865
874
|
}
|
|
866
875
|
}
|
|
867
876
|
}
|
|
@@ -909,16 +918,25 @@ describe('Resource parameter tests', () => {
|
|
|
909
918
|
getSettings(): any {
|
|
910
919
|
return {
|
|
911
920
|
type: 'array',
|
|
912
|
-
inputTransformation:
|
|
921
|
+
inputTransformation: {
|
|
922
|
+
to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
923
|
+
Object.entries(h)
|
|
924
|
+
.map(([k, v]) => [
|
|
925
|
+
k,
|
|
926
|
+
typeof v === 'boolean'
|
|
927
|
+
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
928
|
+
: v,
|
|
929
|
+
])
|
|
930
|
+
)
|
|
931
|
+
),
|
|
932
|
+
from: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
913
933
|
Object.entries(h)
|
|
914
934
|
.map(([k, v]) => [
|
|
915
935
|
k,
|
|
916
|
-
|
|
917
|
-
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
918
|
-
: v,
|
|
936
|
+
v === 'yes',
|
|
919
937
|
])
|
|
920
|
-
)
|
|
921
|
-
|
|
938
|
+
))
|
|
939
|
+
}
|
|
922
940
|
}
|
|
923
941
|
}
|
|
924
942
|
|
|
@@ -969,4 +987,59 @@ describe('Resource parameter tests', () => {
|
|
|
969
987
|
);
|
|
970
988
|
|
|
971
989
|
})
|
|
990
|
+
|
|
991
|
+
it('Supports equality check for itemType', async () => {
|
|
992
|
+
const resource = new class extends TestResource {
|
|
993
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
994
|
+
return {
|
|
995
|
+
id: 'resourceType',
|
|
996
|
+
parameterSettings: {
|
|
997
|
+
propA: { type: 'array', itemType: 'version' }
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
1003
|
+
return {
|
|
1004
|
+
propA: ['10.0.0']
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
const controller = new ResourceController(resource);
|
|
1010
|
+
|
|
1011
|
+
const result = await controller.plan({ type: 'resourceType' }, { propA: ['10.0'] }, null, false);
|
|
1012
|
+
expect(result.changeSet).toMatchObject({
|
|
1013
|
+
operation: ResourceOperation.NOOP,
|
|
1014
|
+
})
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
it('Supports transformations for itemType', async () => {
|
|
1018
|
+
const home = os.homedir()
|
|
1019
|
+
const testPath = path.join(home, 'test/folder');
|
|
1020
|
+
|
|
1021
|
+
const resource = new class extends TestResource {
|
|
1022
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
1023
|
+
return {
|
|
1024
|
+
id: 'resourceType',
|
|
1025
|
+
parameterSettings: {
|
|
1026
|
+
propA: { type: 'array', itemType: 'directory' }
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
1032
|
+
return {
|
|
1033
|
+
propA: [testPath]
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
const controller = new ResourceController(resource);
|
|
1039
|
+
|
|
1040
|
+
const result = await controller.plan({ type: 'resourceType' }, { propA: ['~/test/folder'] }, null, false);
|
|
1041
|
+
expect(result.changeSet).toMatchObject({
|
|
1042
|
+
operation: ResourceOperation.NOOP,
|
|
1043
|
+
})
|
|
1044
|
+
})
|
|
972
1045
|
})
|