codify-plugin-lib 1.0.106 → 1.0.107
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/resource/parsed-resource-settings.d.ts +2 -2
- package/dist/resource/parsed-resource-settings.js +1 -1
- package/dist/resource/resource-controller.js +1 -1
- package/dist/resource/resource-settings.d.ts +1 -1
- package/dist/resource/resource-settings.js +6 -0
- package/dist/utils/utils.js +3 -0
- package/package.json +1 -1
- package/src/resource/parsed-resource-settings.ts +2 -2
- package/src/resource/resource-controller.test.ts +22 -7
- package/src/resource/resource-controller.ts +1 -1
- package/src/resource/resource-settings.test.ts +118 -0
- package/src/resource/resource-settings.ts +8 -2
- package/src/utils/utils.ts +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
3
|
-
import { ArrayParameterSetting, DefaultParameterSetting, ResourceSettings } from './resource-settings.js';
|
|
3
|
+
import { ArrayParameterSetting, DefaultParameterSetting, ParameterSetting, ResourceSettings } from './resource-settings.js';
|
|
4
4
|
export interface ParsedStatefulParameterSetting extends DefaultParameterSetting {
|
|
5
5
|
type: 'stateful';
|
|
6
6
|
controller: StatefulParameterController<any, unknown>;
|
|
@@ -30,7 +30,7 @@ export declare class ParsedResourceSettings<T extends StringIndexedObject> imple
|
|
|
30
30
|
get statefulParameters(): Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
|
|
31
31
|
get parameterSettings(): Record<keyof T, ParsedParameterSetting>;
|
|
32
32
|
get defaultValues(): Partial<Record<keyof T, unknown>>;
|
|
33
|
-
get inputTransformations(): Partial<Record<keyof T, (a: unknown) => unknown>>;
|
|
33
|
+
get inputTransformations(): Partial<Record<keyof T, (a: unknown, parameter: ParameterSetting) => unknown>>;
|
|
34
34
|
get statefulParameterOrder(): Map<keyof T, number>;
|
|
35
35
|
private validateSettings;
|
|
36
36
|
private validateParameterEqualsFn;
|
|
@@ -83,7 +83,7 @@ export class ParsedResourceSettings {
|
|
|
83
83
|
return {};
|
|
84
84
|
}
|
|
85
85
|
return Object.fromEntries(Object.entries(this.settings.parameterSettings)
|
|
86
|
-
.filter(([, v]) => resolveParameterTransformFn(v) !== undefined)
|
|
86
|
+
.filter(([_, v]) => resolveParameterTransformFn(v) !== undefined)
|
|
87
87
|
.map(([k, v]) => [k, resolveParameterTransformFn(v)]));
|
|
88
88
|
});
|
|
89
89
|
}
|
|
@@ -230,7 +230,7 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
230
230
|
if (config[key] === undefined || !inputTransformation) {
|
|
231
231
|
continue;
|
|
232
232
|
}
|
|
233
|
-
config[key] = await inputTransformation(config[key]);
|
|
233
|
+
config[key] = await inputTransformation(config[key], this.settings.parameterSettings[key]);
|
|
234
234
|
}
|
|
235
235
|
if (this.settings.inputTransformation) {
|
|
236
236
|
const { parameters, coreParameters } = splitUserConfig(config);
|
|
@@ -216,4 +216,4 @@ export interface StatefulParameterSetting extends DefaultParameterSetting {
|
|
|
216
216
|
}
|
|
217
217
|
export declare function resolveEqualsFn(parameter: ParameterSetting): (desired: unknown, current: unknown) => boolean;
|
|
218
218
|
export declare function resolveFnFromEqualsFnOrString(fnOrString: ((a: unknown, b: unknown) => boolean) | ParameterSettingType | undefined): ((a: unknown, b: unknown) => boolean) | undefined;
|
|
219
|
-
export declare function resolveParameterTransformFn(parameter: ParameterSetting): ((input: any) => Promise<any> | any) | undefined;
|
|
219
|
+
export declare function resolveParameterTransformFn(parameter: ParameterSetting): ((input: any, parameter: ParameterSetting) => Promise<any> | any) | undefined;
|
|
@@ -35,6 +35,12 @@ export function resolveFnFromEqualsFnOrString(fnOrString) {
|
|
|
35
35
|
}
|
|
36
36
|
const ParameterTransformationDefaults = {
|
|
37
37
|
'directory': (a) => path.resolve(untildify(String(a))),
|
|
38
|
+
'stateful': (a, b) => {
|
|
39
|
+
const sp = b;
|
|
40
|
+
return (sp.definition?.getSettings()?.inputTransformation)
|
|
41
|
+
? (sp.definition.getSettings().inputTransformation(a))
|
|
42
|
+
: a;
|
|
43
|
+
},
|
|
38
44
|
'string': String,
|
|
39
45
|
};
|
|
40
46
|
export function resolveParameterTransformFn(parameter) {
|
package/dist/utils/utils.js
CHANGED
|
@@ -73,6 +73,9 @@ export function untildify(pathWithTilde) {
|
|
|
73
73
|
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
74
74
|
}
|
|
75
75
|
export function areArraysEqual(isElementEqual, desired, current) {
|
|
76
|
+
if (!desired || !current) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
76
79
|
if (!Array.isArray(desired) || !Array.isArray(current)) {
|
|
77
80
|
throw new Error(`A non-array value:
|
|
78
81
|
|
package/package.json
CHANGED
|
@@ -133,7 +133,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
get inputTransformations(): Partial<Record<keyof T, (a: unknown) => unknown>> {
|
|
136
|
+
get inputTransformations(): Partial<Record<keyof T, (a: unknown, parameter: ParameterSetting) => unknown>> {
|
|
137
137
|
return this.getFromCacheOrCreate('inputTransformations', () => {
|
|
138
138
|
if (!this.settings.parameterSettings) {
|
|
139
139
|
return {};
|
|
@@ -141,7 +141,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
141
141
|
|
|
142
142
|
return Object.fromEntries(
|
|
143
143
|
Object.entries(this.settings.parameterSettings)
|
|
144
|
-
.filter(([, v]) => resolveParameterTransformFn(v!) !== undefined)
|
|
144
|
+
.filter(([_, v]) => resolveParameterTransformFn(v!) !== undefined)
|
|
145
145
|
.map(([k, v]) => [k, resolveParameterTransformFn(v!)] as const)
|
|
146
146
|
) as Record<keyof T, (a: unknown) => unknown>;
|
|
147
147
|
});
|
|
@@ -401,12 +401,19 @@ describe('Resource tests', () => {
|
|
|
401
401
|
type: 'version'
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
|
+
|
|
404
405
|
override async refresh(desired: any, config: Partial<any>): Promise<any> {
|
|
405
406
|
return null;
|
|
406
407
|
}
|
|
407
|
-
|
|
408
|
-
override async
|
|
409
|
-
|
|
408
|
+
|
|
409
|
+
override async add(valueToAdd: any, plan: Plan<any>): Promise<void> {
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
override async modify(newValue: any, previousValue: any, plan: Plan<any>): Promise<void> {
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
override async remove(valueToRemove: any, plan: Plan<any>): Promise<void> {
|
|
416
|
+
}
|
|
410
417
|
}
|
|
411
418
|
|
|
412
419
|
const parameter2 = new class extends ArrayStatefulParameter<any, any> {
|
|
@@ -420,8 +427,12 @@ describe('Resource tests', () => {
|
|
|
420
427
|
override async refresh(desired: any[] | null, config: Partial<any>): Promise<any[] | null> {
|
|
421
428
|
return null;
|
|
422
429
|
}
|
|
423
|
-
|
|
424
|
-
override async
|
|
430
|
+
|
|
431
|
+
override async addItem(item: any, plan: Plan<any>): Promise<void> {
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
override async removeItem(item: any, plan: Plan<any>): Promise<void> {
|
|
435
|
+
}
|
|
425
436
|
}
|
|
426
437
|
|
|
427
438
|
const p1Spy = spy(parameter1);
|
|
@@ -471,8 +482,12 @@ describe('Resource tests', () => {
|
|
|
471
482
|
async refresh(desired: any[] | null, config: Partial<any>): Promise<any[] | null> {
|
|
472
483
|
return ['20']
|
|
473
484
|
}
|
|
474
|
-
|
|
475
|
-
async
|
|
485
|
+
|
|
486
|
+
async addItem(item: any, plan: Plan<any>): Promise<void> {
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async removeItem(item: any, plan: Plan<any>): Promise<void> {
|
|
490
|
+
}
|
|
476
491
|
})
|
|
477
492
|
|
|
478
493
|
const resource = new class extends TestResource {
|
|
@@ -318,7 +318,7 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
318
318
|
continue;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
(config as Record<string, unknown>)[key] = await inputTransformation(config[key]);
|
|
321
|
+
(config as Record<string, unknown>)[key] = await inputTransformation(config[key], this.settings.parameterSettings![key]!);
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
if (this.settings.inputTransformation) {
|
|
@@ -818,4 +818,122 @@ describe('Resource parameter tests', () => {
|
|
|
818
818
|
operation: ResourceOperation.RECREATE,
|
|
819
819
|
})
|
|
820
820
|
});
|
|
821
|
+
|
|
822
|
+
it('Transforms input parameters', async () => {
|
|
823
|
+
const resource = new class extends TestResource {
|
|
824
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
825
|
+
return {
|
|
826
|
+
id: 'resourceType',
|
|
827
|
+
parameterSettings: {
|
|
828
|
+
propD: {
|
|
829
|
+
type: 'array',
|
|
830
|
+
inputTransformation: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
831
|
+
Object.entries(h)
|
|
832
|
+
.map(([k, v]) => [
|
|
833
|
+
k,
|
|
834
|
+
typeof v === 'boolean'
|
|
835
|
+
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
836
|
+
: v,
|
|
837
|
+
])
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
846
|
+
expect(parameters.propD[0].AddKeysToAgent).to.eq('yes')
|
|
847
|
+
expect(parameters.propD[1].AddKeysToAgent).to.eq('yes')
|
|
848
|
+
expect(parameters.propD[1].UseKeychain).to.eq('yes')
|
|
849
|
+
expect(parameters.propD[2].PasswordAuthentication).to.eq('yes')
|
|
850
|
+
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const controller = new ResourceController(resource);
|
|
856
|
+
await controller.plan({
|
|
857
|
+
type: 'resourceType',
|
|
858
|
+
propD: [
|
|
859
|
+
{
|
|
860
|
+
Host: 'new.com',
|
|
861
|
+
AddKeysToAgent: true,
|
|
862
|
+
IdentityFile: 'id_ed25519'
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
Host: 'github.com',
|
|
866
|
+
AddKeysToAgent: true,
|
|
867
|
+
UseKeychain: true,
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
Match: 'User bob,joe,phil',
|
|
871
|
+
PasswordAuthentication: true,
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
it('Transforms input parameters for stateful parameters', async () => {
|
|
879
|
+
const sp = new class extends TestStatefulParameter {
|
|
880
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
881
|
+
return {
|
|
882
|
+
type: 'array',
|
|
883
|
+
inputTransformation: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
|
|
884
|
+
Object.entries(h)
|
|
885
|
+
.map(([k, v]) => [
|
|
886
|
+
k,
|
|
887
|
+
typeof v === 'boolean'
|
|
888
|
+
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
|
|
889
|
+
: v,
|
|
890
|
+
])
|
|
891
|
+
)
|
|
892
|
+
)
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
async refresh(desired: any): Promise<Partial<TestConfig> | null> {
|
|
897
|
+
expect(desired[0].AddKeysToAgent).to.eq('yes')
|
|
898
|
+
expect(desired[1].AddKeysToAgent).to.eq('yes')
|
|
899
|
+
expect(desired[1].UseKeychain).to.eq('yes')
|
|
900
|
+
expect(desired[2].PasswordAuthentication).to.eq('yes')
|
|
901
|
+
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
const resource = new class extends TestResource {
|
|
907
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
908
|
+
return {
|
|
909
|
+
id: 'resourceType',
|
|
910
|
+
parameterSettings: {
|
|
911
|
+
propD: { type: 'stateful', definition: sp }
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const controller = new ResourceController(resource);
|
|
918
|
+
await controller.plan({
|
|
919
|
+
type: 'resourceType',
|
|
920
|
+
propD: [
|
|
921
|
+
{
|
|
922
|
+
Host: 'new.com',
|
|
923
|
+
AddKeysToAgent: true,
|
|
924
|
+
IdentityFile: 'id_ed25519'
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
Host: 'github.com',
|
|
928
|
+
AddKeysToAgent: true,
|
|
929
|
+
UseKeychain: true,
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
Match: 'User bob,joe,phil',
|
|
933
|
+
PasswordAuthentication: true,
|
|
934
|
+
}
|
|
935
|
+
]
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
})
|
|
821
939
|
})
|
|
@@ -303,13 +303,19 @@ export function resolveFnFromEqualsFnOrString(
|
|
|
303
303
|
return fnOrString as ((a: unknown, b: unknown) => boolean) | undefined;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, (input: any) => Promise<any> | any>> = {
|
|
306
|
+
const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, (input: any, parameter: ParameterSetting) => Promise<any> | any>> = {
|
|
307
307
|
'directory': (a: unknown) => path.resolve(untildify(String(a))),
|
|
308
|
+
'stateful': (a: unknown, b: ParameterSetting) => {
|
|
309
|
+
const sp = b as StatefulParameterSetting;
|
|
310
|
+
return (sp.definition?.getSettings()?.inputTransformation)
|
|
311
|
+
? (sp.definition.getSettings().inputTransformation!(a))
|
|
312
|
+
: a;
|
|
313
|
+
},
|
|
308
314
|
'string': String,
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
export function resolveParameterTransformFn(
|
|
312
318
|
parameter: ParameterSetting
|
|
313
|
-
): ((input: any) => Promise<any> | any) | undefined {
|
|
319
|
+
): ((input: any, parameter: ParameterSetting) => Promise<any> | any) | undefined {
|
|
314
320
|
return parameter.inputTransformation ?? ParameterTransformationDefaults[parameter.type as ParameterSettingType] ?? undefined;
|
|
315
321
|
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -114,6 +114,10 @@ export function areArraysEqual(
|
|
|
114
114
|
desired: unknown,
|
|
115
115
|
current: unknown
|
|
116
116
|
): boolean {
|
|
117
|
+
if (!desired || !current) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
117
121
|
if (!Array.isArray(desired) || !Array.isArray(current)) {
|
|
118
122
|
throw new Error(`A non-array value:
|
|
119
123
|
|