codify-plugin-lib 1.0.100 → 1.0.102
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plan/change-set.d.ts +2 -2
- package/dist/plan/plan.js +11 -8
- package/dist/resource/config-parser.d.ts +2 -2
- package/dist/resource/parsed-resource-settings.d.ts +17 -4
- package/dist/resource/parsed-resource-settings.js +25 -3
- package/dist/resource/resource-controller.js +2 -1
- package/dist/resource/resource-settings.d.ts +7 -6
- package/dist/resource/resource-settings.js +21 -5
- package/dist/stateful-parameter/stateful-parameter-controller.d.ts +21 -0
- package/dist/stateful-parameter/stateful-parameter-controller.js +82 -0
- package/dist/stateful-parameter/stateful-parameter.d.ts +144 -0
- package/dist/stateful-parameter/stateful-parameter.js +43 -0
- package/dist/utils/utils.d.ts +1 -2
- package/dist/utils/utils.js +3 -2
- package/package.json +4 -2
- package/src/index.ts +1 -1
- package/src/plan/change-set.ts +5 -5
- package/src/plan/plan.test.ts +6 -1
- package/src/plan/plan.ts +21 -16
- package/src/resource/config-parser.ts +3 -3
- package/src/resource/parsed-resource-settings.ts +53 -6
- package/src/resource/resource-controller.ts +2 -1
- package/src/resource/resource-settings.test.ts +101 -0
- package/src/resource/resource-settings.ts +34 -9
- package/src/{resource/stateful-parameter.test.ts → stateful-parameter/stateful-parameter-controller.test.ts} +91 -12
- package/src/stateful-parameter/stateful-parameter-controller.ts +112 -0
- package/src/{resource → stateful-parameter}/stateful-parameter.ts +9 -67
- package/src/utils/test-utils.test.ts +1 -1
- package/src/utils/utils.ts +9 -4
package/src/plan/change-set.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ParsedParameterSetting } from '../resource/parsed-resource-settings.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A parameter change describes a parameter level change to a resource.
|
|
@@ -87,7 +87,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
87
87
|
static calculateModification<T extends StringIndexedObject>(
|
|
88
88
|
desired: Partial<T>,
|
|
89
89
|
current: Partial<T>,
|
|
90
|
-
parameterSettings: Partial<Record<keyof T,
|
|
90
|
+
parameterSettings: Partial<Record<keyof T, ParsedParameterSetting>> = {},
|
|
91
91
|
): ChangeSet<T> {
|
|
92
92
|
const pc = ChangeSet.calculateParameterChanges(desired, current, parameterSettings);
|
|
93
93
|
|
|
@@ -128,7 +128,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
128
128
|
private static calculateParameterChanges<T extends StringIndexedObject>(
|
|
129
129
|
desiredParameters: Partial<T>,
|
|
130
130
|
currentParameters: Partial<T>,
|
|
131
|
-
parameterOptions?: Partial<Record<keyof T,
|
|
131
|
+
parameterOptions?: Partial<Record<keyof T, ParsedParameterSetting>>,
|
|
132
132
|
): ParameterChange<T>[] {
|
|
133
133
|
const parameterChangeSet = new Array<ParameterChange<T>>();
|
|
134
134
|
|
|
@@ -204,8 +204,8 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
204
204
|
private static isSame(
|
|
205
205
|
desired: unknown,
|
|
206
206
|
current: unknown,
|
|
207
|
-
setting?:
|
|
207
|
+
setting?: ParsedParameterSetting,
|
|
208
208
|
): boolean {
|
|
209
|
-
return (setting?.isEqual ?? ((a, b) => a === b))(desired, current)
|
|
209
|
+
return (setting?.isEqual ?? ((a: unknown, b: unknown) => a === b))(desired, current)
|
|
210
210
|
}
|
|
211
211
|
}
|
package/src/plan/plan.test.ts
CHANGED
|
@@ -183,7 +183,12 @@ describe('Plan entity tests', () => {
|
|
|
183
183
|
return {
|
|
184
184
|
id: 'type',
|
|
185
185
|
parameterSettings: {
|
|
186
|
-
propZ: {
|
|
186
|
+
propZ: {
|
|
187
|
+
type: 'array',
|
|
188
|
+
canModify: true,
|
|
189
|
+
isElementEqual: (a, b) => b.includes(a),
|
|
190
|
+
filterInStatelessMode: false
|
|
191
|
+
}
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
}
|
package/src/plan/plan.ts
CHANGED
|
@@ -8,8 +8,12 @@ import {
|
|
|
8
8
|
} from 'codify-schemas';
|
|
9
9
|
import { v4 as uuidV4 } from 'uuid';
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
ParsedArrayParameterSetting,
|
|
13
|
+
ParsedResourceSettings,
|
|
14
|
+
ParsedStatefulParameterSetting
|
|
15
|
+
} from '../resource/parsed-resource-settings.js';
|
|
16
|
+
import { ArrayParameterSetting, ResourceSettings } from '../resource/resource-settings.js';
|
|
13
17
|
import { ChangeSet } from './change-set.js';
|
|
14
18
|
|
|
15
19
|
/**
|
|
@@ -248,26 +252,27 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
248
252
|
}
|
|
249
253
|
|
|
250
254
|
function isArrayParameterWithFiltering(k: string, v: T[keyof T]): boolean {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
if (settings.parameterSettings?.[k]?.type === 'stateful') {
|
|
256
|
+
const statefulSetting = settings.parameterSettings[k] as ParsedStatefulParameterSetting;
|
|
257
|
+
return statefulSetting.nestedSettings.type === 'array' &&
|
|
258
|
+
((statefulSetting.nestedSettings as ArrayParameterSetting).filterInStatelessMode ?? true)
|
|
259
|
+
&& Array.isArray(v);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return settings.parameterSettings?.[k]?.type === 'array'
|
|
263
|
+
&& ((settings.parameterSettings?.[k] as ArrayParameterSetting).filterInStatelessMode ?? true)
|
|
264
|
+
&& Array.isArray(v);
|
|
259
265
|
}
|
|
260
266
|
|
|
261
267
|
// For stateless mode, we must filter the current array so that the diff algorithm will not detect any deletes
|
|
262
268
|
function filterArrayStatefulParameter(k: string, v: unknown[]): unknown[] {
|
|
263
269
|
const desiredArray = desired![k] as unknown[];
|
|
264
270
|
const matcher = settings.parameterSettings![k]!.type === 'stateful'
|
|
265
|
-
? ((settings.parameterSettings![k] as
|
|
266
|
-
.
|
|
267
|
-
.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
.isElementEqual ?? ((a, b) => a === b)
|
|
271
|
+
? ((settings.parameterSettings![k] as ParsedStatefulParameterSetting)
|
|
272
|
+
.nestedSettings as ParsedArrayParameterSetting)
|
|
273
|
+
.isElementEqual
|
|
274
|
+
: (settings.parameterSettings![k] as ParsedArrayParameterSetting)
|
|
275
|
+
.isElementEqual
|
|
271
276
|
|
|
272
277
|
const desiredCopy = [...desiredArray];
|
|
273
278
|
const currentCopy = [...v];
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
|
|
3
|
+
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
3
4
|
import { splitUserConfig } from '../utils/utils.js';
|
|
4
|
-
import { StatefulParameter } from './stateful-parameter.js';
|
|
5
5
|
|
|
6
6
|
export class ConfigParser<T extends StringIndexedObject> {
|
|
7
7
|
private readonly desiredConfig: Partial<T> & ResourceConfig | null;
|
|
8
8
|
private readonly stateConfig: Partial<T> & ResourceConfig | null;
|
|
9
|
-
private statefulParametersMap: Map<keyof T,
|
|
9
|
+
private statefulParametersMap: Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
|
|
10
10
|
|
|
11
11
|
constructor(
|
|
12
12
|
desiredConfig: Partial<T> & ResourceConfig | null,
|
|
13
13
|
stateConfig: Partial<T> & ResourceConfig | null,
|
|
14
|
-
statefulParameters: Map<keyof T,
|
|
14
|
+
statefulParameters: Map<keyof T, StatefulParameterController<T, T[keyof T]>>,
|
|
15
15
|
) {
|
|
16
16
|
this.desiredConfig = desiredConfig;
|
|
17
17
|
this.stateConfig = stateConfig
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import { JSONSchemaType } from 'ajv';
|
|
2
2
|
import { StringIndexedObject } from 'codify-schemas';
|
|
3
3
|
|
|
4
|
+
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
4
5
|
import {
|
|
6
|
+
ArrayParameterSetting,
|
|
7
|
+
DefaultParameterSetting,
|
|
5
8
|
ParameterSetting,
|
|
6
9
|
resolveEqualsFn,
|
|
10
|
+
resolveFnFromEqualsFnOrString,
|
|
7
11
|
resolveParameterTransformFn,
|
|
8
12
|
ResourceSettings,
|
|
9
13
|
StatefulParameterSetting
|
|
10
14
|
} from './resource-settings.js';
|
|
11
|
-
|
|
15
|
+
|
|
16
|
+
export interface ParsedStatefulParameterSetting extends DefaultParameterSetting {
|
|
17
|
+
type: 'stateful',
|
|
18
|
+
controller: StatefulParameterController<any, unknown>
|
|
19
|
+
order?: number,
|
|
20
|
+
nestedSettings: ParsedParameterSetting;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ParsedArrayParameterSetting = {
|
|
24
|
+
isElementEqual: (a: unknown, b: unknown) => boolean;
|
|
25
|
+
isEqual: (a: unknown, b: unknown) => boolean;
|
|
26
|
+
} & ArrayParameterSetting
|
|
27
|
+
|
|
28
|
+
export type ParsedParameterSetting =
|
|
29
|
+
{
|
|
30
|
+
isEqual: (desired: unknown, current: unknown) => boolean;
|
|
31
|
+
} & (DefaultParameterSetting
|
|
32
|
+
| ParsedArrayParameterSetting
|
|
33
|
+
| ParsedStatefulParameterSetting)
|
|
12
34
|
|
|
13
35
|
export class ParsedResourceSettings<T extends StringIndexedObject> implements ResourceSettings<T> {
|
|
14
36
|
private cache = new Map<string, unknown>();
|
|
@@ -36,24 +58,49 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
36
58
|
return this.id;
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
get statefulParameters(): Map<keyof T,
|
|
61
|
+
get statefulParameters(): Map<keyof T, StatefulParameterController<T, T[keyof T]>> {
|
|
40
62
|
return this.getFromCacheOrCreate('statefulParameters', () => {
|
|
41
63
|
|
|
42
64
|
const statefulParameters = Object.entries(this.settings.parameterSettings ?? {})
|
|
43
65
|
.filter(([, p]) => p?.type === 'stateful')
|
|
44
|
-
.map(([k, v]) => [
|
|
66
|
+
.map(([k, v]) => [
|
|
67
|
+
k,
|
|
68
|
+
new StatefulParameterController((v as StatefulParameterSetting).definition)
|
|
69
|
+
] as const)
|
|
45
70
|
|
|
46
|
-
return new Map(statefulParameters) as Map<keyof T,
|
|
71
|
+
return new Map(statefulParameters) as Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
|
|
47
72
|
})
|
|
48
73
|
}
|
|
49
74
|
|
|
50
|
-
get parameterSettings(): Record<keyof T,
|
|
75
|
+
get parameterSettings(): Record<keyof T, ParsedParameterSetting> {
|
|
51
76
|
return this.getFromCacheOrCreate('parameterSetting', () => {
|
|
52
77
|
|
|
53
78
|
const settings = Object.entries(this.settings.parameterSettings ?? {})
|
|
54
79
|
.map(([k, v]) => [k, v!] as const)
|
|
55
80
|
.map(([k, v]) => {
|
|
56
|
-
v.isEqual = resolveEqualsFn(v
|
|
81
|
+
v.isEqual = resolveEqualsFn(v);
|
|
82
|
+
|
|
83
|
+
if (v.type === 'stateful') {
|
|
84
|
+
const spController = this.statefulParameters.get(k);
|
|
85
|
+
const parsed = {
|
|
86
|
+
...v,
|
|
87
|
+
controller: spController,
|
|
88
|
+
nestedSettings: spController?.parsedSettings,
|
|
89
|
+
definition: undefined,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return [k, parsed as ParsedStatefulParameterSetting];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (v.type === 'array') {
|
|
96
|
+
const parsed = {
|
|
97
|
+
...v,
|
|
98
|
+
isElementEqual: resolveFnFromEqualsFnOrString((v as ArrayParameterSetting).isElementEqual)
|
|
99
|
+
?? ((a: unknown, b: unknown) => a === b),
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [k, parsed as ParsedArrayParameterSetting];
|
|
103
|
+
}
|
|
57
104
|
|
|
58
105
|
return [k, v];
|
|
59
106
|
})
|
|
@@ -63,7 +63,8 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
63
63
|
this.addDefaultValues(parameters);
|
|
64
64
|
|
|
65
65
|
if (this.schemaValidator) {
|
|
66
|
-
|
|
66
|
+
// Schema validator uses pre transformation parameters
|
|
67
|
+
const isValid = this.schemaValidator(desiredConfig);
|
|
67
68
|
|
|
68
69
|
if (!isValid) {
|
|
69
70
|
return {
|
|
@@ -629,4 +629,105 @@ describe('Resource parameter tests', () => {
|
|
|
629
629
|
}
|
|
630
630
|
};
|
|
631
631
|
})
|
|
632
|
+
|
|
633
|
+
it('Accepts a string isEqual method which selects from one of the defaults', async () => {
|
|
634
|
+
const resource = new class extends TestResource {
|
|
635
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
636
|
+
return {
|
|
637
|
+
id: 'resourceType',
|
|
638
|
+
parameterSettings: {
|
|
639
|
+
propA: { type: 'string', isEqual: 'version' }
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
645
|
+
return {
|
|
646
|
+
propA: '10.0.0'
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
const controller = new ResourceController(resource);
|
|
652
|
+
|
|
653
|
+
const result = await controller.plan({ type: 'resourceType', propA: '10.0' });
|
|
654
|
+
expect(result.changeSet).toMatchObject({
|
|
655
|
+
operation: ResourceOperation.NOOP,
|
|
656
|
+
})
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('Object equals method (works when equal)', async () => {
|
|
660
|
+
const resource = new class extends TestResource {
|
|
661
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
662
|
+
return {
|
|
663
|
+
id: 'resourceType',
|
|
664
|
+
parameterSettings: {
|
|
665
|
+
propD: { type: 'object' }
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
671
|
+
return {
|
|
672
|
+
propD: {
|
|
673
|
+
testA: 'a',
|
|
674
|
+
testB: 'b',
|
|
675
|
+
testC: 10,
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
const controller = new ResourceController(resource);
|
|
682
|
+
|
|
683
|
+
const result = await controller.plan({
|
|
684
|
+
type: 'resourceType',
|
|
685
|
+
propD: {
|
|
686
|
+
testC: 10,
|
|
687
|
+
testA: 'a',
|
|
688
|
+
testB: 'b',
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
expect(result.changeSet).toMatchObject({
|
|
693
|
+
operation: ResourceOperation.NOOP,
|
|
694
|
+
})
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('Object equals method (works when not equal)', async () => {
|
|
698
|
+
const resource = new class extends TestResource {
|
|
699
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
700
|
+
return {
|
|
701
|
+
id: 'resourceType',
|
|
702
|
+
parameterSettings: {
|
|
703
|
+
propD: { type: 'object' }
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
709
|
+
return {
|
|
710
|
+
propD: {
|
|
711
|
+
testA: 'a',
|
|
712
|
+
testB: 'b',
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
const controller = new ResourceController(resource);
|
|
719
|
+
|
|
720
|
+
const result = await controller.plan({
|
|
721
|
+
type: 'resourceType',
|
|
722
|
+
propD: {
|
|
723
|
+
testC: 10,
|
|
724
|
+
testA: 'a',
|
|
725
|
+
testB: 'b',
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
expect(result.changeSet).toMatchObject({
|
|
730
|
+
operation: ResourceOperation.RECREATE,
|
|
731
|
+
})
|
|
732
|
+
});
|
|
632
733
|
})
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { StringIndexedObject } from 'codify-schemas';
|
|
2
|
+
import isObjectsEqual from 'lodash.isequal'
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
|
|
5
|
+
import { ArrayStatefulParameter, StatefulParameter } from '../stateful-parameter/stateful-parameter.js';
|
|
4
6
|
import { areArraysEqual, untildify } from '../utils/utils.js';
|
|
5
|
-
import { StatefulParameter } from './stateful-parameter.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The configuration and settings for a resource.
|
|
@@ -130,6 +131,7 @@ export type ParameterSettingType =
|
|
|
130
131
|
| 'boolean'
|
|
131
132
|
| 'directory'
|
|
132
133
|
| 'number'
|
|
134
|
+
| 'object'
|
|
133
135
|
| 'setting'
|
|
134
136
|
| 'stateful'
|
|
135
137
|
| 'string'
|
|
@@ -181,7 +183,7 @@ export interface DefaultParameterSetting {
|
|
|
181
183
|
*
|
|
182
184
|
* @return Return true if equal
|
|
183
185
|
*/
|
|
184
|
-
isEqual?: (desired: any, current: any) => boolean;
|
|
186
|
+
isEqual?: ((desired: any, current: any) => boolean) | ParameterSettingType;
|
|
185
187
|
|
|
186
188
|
/**
|
|
187
189
|
* Chose if the resource can be modified instead of re-created when there is a change to this parameter.
|
|
@@ -210,7 +212,7 @@ export interface ArrayParameterSetting extends DefaultParameterSetting {
|
|
|
210
212
|
*
|
|
211
213
|
* @return Return true if desired is equivalent to current.
|
|
212
214
|
*/
|
|
213
|
-
isElementEqual?: (desired: any, current: any) => boolean
|
|
215
|
+
isElementEqual?: ((desired: any, current: any) => boolean) | ParameterSettingType;
|
|
214
216
|
|
|
215
217
|
/**
|
|
216
218
|
* Filter the contents of the refreshed array by the desired. This way items currently on the system but not
|
|
@@ -249,7 +251,7 @@ export interface StatefulParameterSetting extends DefaultParameterSetting {
|
|
|
249
251
|
* as a resource and taps, formulas and casks are represented as a stateful parameter. A formula can be installed,
|
|
250
252
|
* modified and removed (has state) but it is still tied to the overall lifecycle of homebrew.
|
|
251
253
|
*/
|
|
252
|
-
definition: StatefulParameter<any, unknown>,
|
|
254
|
+
definition: ArrayStatefulParameter<any, unknown> | StatefulParameter<any, unknown>,
|
|
253
255
|
|
|
254
256
|
/**
|
|
255
257
|
* The order multiple stateful parameters should be applied in. The order is applied in ascending order (1, 2, 3...).
|
|
@@ -263,19 +265,42 @@ const ParameterEqualsDefaults: Partial<Record<ParameterSettingType, (a: unknown,
|
|
|
263
265
|
'number': (a: unknown, b: unknown) => Number(a) === Number(b),
|
|
264
266
|
'string': (a: unknown, b: unknown) => String(a) === String(b),
|
|
265
267
|
'version': (desired: unknown, current: unknown) => String(current).includes(String(desired)),
|
|
266
|
-
'setting': (
|
|
268
|
+
'setting': () => true,
|
|
269
|
+
'object': isObjectsEqual,
|
|
267
270
|
}
|
|
268
271
|
|
|
269
|
-
export function resolveEqualsFn(parameter: ParameterSetting
|
|
272
|
+
export function resolveEqualsFn(parameter: ParameterSetting): (desired: unknown, current: unknown) => boolean {
|
|
273
|
+
const isEqual = resolveFnFromEqualsFnOrString(parameter.isEqual);
|
|
274
|
+
|
|
270
275
|
if (parameter.type === 'array') {
|
|
271
|
-
|
|
276
|
+
const arrayParameter = parameter as ArrayParameterSetting;
|
|
277
|
+
const isElementEqual = resolveFnFromEqualsFnOrString(arrayParameter.isElementEqual);
|
|
278
|
+
|
|
279
|
+
return isEqual ?? areArraysEqual.bind(areArraysEqual, isElementEqual)
|
|
272
280
|
}
|
|
273
281
|
|
|
274
282
|
if (parameter.type === 'stateful') {
|
|
275
|
-
return resolveEqualsFn((parameter as StatefulParameterSetting).definition.getSettings()
|
|
283
|
+
return resolveEqualsFn((parameter as StatefulParameterSetting).definition.getSettings())
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return isEqual ?? ParameterEqualsDefaults[parameter.type as ParameterSettingType] ?? (((a, b) => a === b));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// This resolves the fn if it is a string.
|
|
290
|
+
// A string can be specified to use a default equals method
|
|
291
|
+
export function resolveFnFromEqualsFnOrString(
|
|
292
|
+
fnOrString: ((a: unknown, b: unknown) => boolean) | ParameterSettingType | undefined,
|
|
293
|
+
): ((a: unknown, b: unknown) => boolean) | undefined {
|
|
294
|
+
|
|
295
|
+
if (fnOrString && typeof fnOrString === 'string') {
|
|
296
|
+
if (!ParameterEqualsDefaults[fnOrString]) {
|
|
297
|
+
throw new Error(`isEqual of type ${fnOrString} was not found`)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return ParameterEqualsDefaults[fnOrString]!
|
|
276
301
|
}
|
|
277
302
|
|
|
278
|
-
return
|
|
303
|
+
return fnOrString as ((a: unknown, b: unknown) => boolean) | undefined;
|
|
279
304
|
}
|
|
280
305
|
|
|
281
306
|
const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, (input: any) => Promise<any> | any>> = {
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
TestResource,
|
|
9
9
|
TestStatefulParameter
|
|
10
10
|
} from '../utils/test-utils.test.js';
|
|
11
|
-
import { ArrayParameterSetting, ParameterSetting, ResourceSettings } from '
|
|
12
|
-
import { ResourceController } from '
|
|
11
|
+
import { ArrayParameterSetting, ParameterSetting, ResourceSettings } from '../resource/resource-settings.js';
|
|
12
|
+
import { ResourceController } from '../resource/resource-controller.js';
|
|
13
|
+
import { StatefulParameterController } from './stateful-parameter-controller.js';
|
|
13
14
|
|
|
14
15
|
describe('Stateful parameter tests', () => {
|
|
15
16
|
it('addItem is called the correct number of times', async () => {
|
|
@@ -20,11 +21,12 @@ describe('Stateful parameter tests', () => {
|
|
|
20
21
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.CREATE);
|
|
21
22
|
expect(plan.changeSet.parameterChanges.length).to.eq(1);
|
|
22
23
|
|
|
23
|
-
const
|
|
24
|
-
|
|
24
|
+
const parameter = spy(new TestArrayStatefulParameter());
|
|
25
|
+
const controller = new StatefulParameterController(parameter);
|
|
26
|
+
await controller.add((plan.desiredConfig! as any).propZ, plan);
|
|
25
27
|
|
|
26
|
-
expect(
|
|
27
|
-
expect(
|
|
28
|
+
expect(parameter.addItem.callCount).to.eq(3);
|
|
29
|
+
expect(parameter.removeItem.called).to.be.false;
|
|
28
30
|
})
|
|
29
31
|
|
|
30
32
|
it('applyRemoveItem is called the correct number of times', async () => {
|
|
@@ -38,11 +40,12 @@ describe('Stateful parameter tests', () => {
|
|
|
38
40
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.DESTROY);
|
|
39
41
|
expect(plan.changeSet.parameterChanges.length).to.eq(1);
|
|
40
42
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
+
const parameter = spy(new TestArrayStatefulParameter());
|
|
44
|
+
const controller = new StatefulParameterController(parameter);
|
|
45
|
+
await controller.remove((plan.currentConfig as any).propZ, plan);
|
|
43
46
|
|
|
44
|
-
expect(
|
|
45
|
-
expect(
|
|
47
|
+
expect(parameter.addItem.called).to.be.false;
|
|
48
|
+
expect(parameter.removeItem.callCount).to.eq(3);
|
|
46
49
|
})
|
|
47
50
|
|
|
48
51
|
it('In stateless mode only applyAddItem is called only for modifies', async () => {
|
|
@@ -61,8 +64,10 @@ describe('Stateful parameter tests', () => {
|
|
|
61
64
|
operation: ParameterOperation.MODIFY,
|
|
62
65
|
})
|
|
63
66
|
|
|
67
|
+
|
|
64
68
|
const testParameter = spy(parameter);
|
|
65
|
-
|
|
69
|
+
const controller = new StatefulParameterController(testParameter);
|
|
70
|
+
await controller.modify((plan.desiredConfig as any).propZ, (plan.currentConfig as any).propZ, plan);
|
|
66
71
|
|
|
67
72
|
expect(testParameter.addItem.calledThrice).to.be.true;
|
|
68
73
|
expect(testParameter.removeItem.called).to.be.false;
|
|
@@ -92,7 +97,8 @@ describe('Stateful parameter tests', () => {
|
|
|
92
97
|
operation: ParameterOperation.MODIFY,
|
|
93
98
|
})
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
const controller = new StatefulParameterController(testParameter);
|
|
101
|
+
await controller.modify((plan.desiredConfig as any).propZ, (plan.currentConfig as any).propZ, plan);
|
|
96
102
|
|
|
97
103
|
expect(testParameter.addItem.calledOnce).to.be.true;
|
|
98
104
|
expect(testParameter.removeItem.called).to.be.false;
|
|
@@ -153,4 +159,77 @@ describe('Stateful parameter tests', () => {
|
|
|
153
159
|
|
|
154
160
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
155
161
|
})
|
|
162
|
+
|
|
163
|
+
it('Accepts a string equals value', async () => {
|
|
164
|
+
const testParameter = spy(new class extends TestStatefulParameter {
|
|
165
|
+
getSettings(): ParameterSetting {
|
|
166
|
+
return {
|
|
167
|
+
type: 'string',
|
|
168
|
+
isEqual: 'version'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async refresh(): Promise<any> {
|
|
173
|
+
return '20.15.0';
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const resource = new class extends TestResource {
|
|
178
|
+
getSettings(): ResourceSettings<any> {
|
|
179
|
+
return {
|
|
180
|
+
id: 'type',
|
|
181
|
+
parameterSettings: { propA: { type: 'stateful', definition: testParameter } }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async refresh(): Promise<Partial<any> | null> {
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const controller = new ResourceController(resource);
|
|
191
|
+
const plan = await controller.plan({
|
|
192
|
+
propA: '20.15',
|
|
193
|
+
} as any)
|
|
194
|
+
|
|
195
|
+
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('Accepts a string isElementEquals value', async () => {
|
|
199
|
+
const testParameter = spy(new class extends TestStatefulParameter {
|
|
200
|
+
getSettings(): ParameterSetting {
|
|
201
|
+
return {
|
|
202
|
+
type: 'array',
|
|
203
|
+
isElementEqual: 'version'
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async refresh(): Promise<any> {
|
|
208
|
+
return [
|
|
209
|
+
'20.15.0',
|
|
210
|
+
'20.18.0'
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const resource = new class extends TestResource {
|
|
216
|
+
getSettings(): ResourceSettings<any> {
|
|
217
|
+
return {
|
|
218
|
+
id: 'type',
|
|
219
|
+
parameterSettings: { propA: { type: 'stateful', definition: testParameter } }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async refresh(): Promise<Partial<any> | null> {
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const controller = new ResourceController(resource);
|
|
229
|
+
const plan = await controller.plan({
|
|
230
|
+
propA: ['20.15', '20.18'],
|
|
231
|
+
} as any)
|
|
232
|
+
|
|
233
|
+
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
234
|
+
})
|
|
156
235
|
})
|