codify-plugin-lib 1.0.105 → 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.
@@ -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) {
@@ -77,6 +77,6 @@ export class StatefulParameterController {
77
77
  }
78
78
  }
79
79
  calculateIsArrayStatefulParameter() {
80
- return Object.hasOwn(this.sp, 'addItem') && Object.hasOwn(this.sp, 'removeItem');
80
+ return 'addItem' in this.sp && 'removeItem' in this.sp;
81
81
  }
82
82
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.105",
3
+ "version": "1.0.107",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -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
- override async add(valueToAdd: any, plan: Plan<any>): Promise<void> {}
408
- override async modify(newValue: any, previousValue: any, plan: Plan<any>): Promise<void> {}
409
- override async remove(valueToRemove: any, plan: Plan<any>): Promise<void> {}
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
- override async addItem(item: any, plan: Plan<any>): Promise<void> {}
424
- override async removeItem(item: any, plan: Plan<any>): Promise<void> {}
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
- async addItem(item: any, plan: Plan<any>): Promise<void> {}
475
- async removeItem(item: any, plan: Plan<any>): Promise<void> {}
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
  }
@@ -107,6 +107,6 @@ export class StatefulParameterController<T extends StringIndexedObject, V extend
107
107
  }
108
108
 
109
109
  private calculateIsArrayStatefulParameter() {
110
- return Object.hasOwn(this.sp, 'addItem') && Object.hasOwn(this.sp, 'removeItem');
110
+ return 'addItem' in this.sp && 'removeItem' in this.sp;
111
111
  }
112
112
  }
@@ -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