codify-plugin-lib 1.0.134 → 1.0.135

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,7 +1,7 @@
1
1
  import { JSONSchemaType } from 'ajv';
2
2
  import { StringIndexedObject } from 'codify-schemas';
3
3
  import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
4
- import { ArrayParameterSetting, DefaultParameterSetting, ParameterSetting, ResourceSettings } from './resource-settings.js';
4
+ import { ArrayParameterSetting, DefaultParameterSetting, InputTransformation, ResourceSettings } from './resource-settings.js';
5
5
  export interface ParsedStatefulParameterSetting extends DefaultParameterSetting {
6
6
  type: 'stateful';
7
7
  controller: StatefulParameterController<any, unknown>;
@@ -25,14 +25,13 @@ export declare class ParsedResourceSettings<T extends StringIndexedObject> imple
25
25
  } | boolean;
26
26
  removeStatefulParametersBeforeDestroy?: boolean | undefined;
27
27
  dependencies?: string[] | undefined;
28
- inputTransformation?: ((desired: Partial<T>) => unknown) | undefined;
29
28
  private settings;
30
29
  constructor(settings: ResourceSettings<T>);
31
30
  get typeId(): string;
32
31
  get statefulParameters(): Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
33
32
  get parameterSettings(): Record<keyof T, ParsedParameterSetting>;
34
33
  get defaultValues(): Partial<Record<keyof T, unknown>>;
35
- get inputTransformations(): Partial<Record<keyof T, (a: unknown, parameter: ParameterSetting) => unknown>>;
34
+ get inputTransformations(): Partial<Record<keyof T, InputTransformation>>;
36
35
  get statefulParameterOrder(): Map<keyof T, number>;
37
36
  private validateSettings;
38
37
  private validateParameterEqualsFn;
@@ -7,7 +7,6 @@ export class ParsedResourceSettings {
7
7
  allowMultiple;
8
8
  removeStatefulParametersBeforeDestroy;
9
9
  dependencies;
10
- inputTransformation;
11
10
  settings;
12
11
  constructor(settings) {
13
12
  this.settings = settings;
@@ -16,7 +15,6 @@ export class ParsedResourceSettings {
16
15
  this.allowMultiple = settings.allowMultiple;
17
16
  this.removeStatefulParametersBeforeDestroy = settings.removeStatefulParametersBeforeDestroy;
18
17
  this.dependencies = settings.dependencies;
19
- this.inputTransformation = settings.inputTransformation;
20
18
  this.validateSettings();
21
19
  }
22
20
  get typeId() {
@@ -83,7 +81,7 @@ export class ParsedResourceSettings {
83
81
  }
84
82
  return Object.fromEntries(Object.entries(this.settings.parameterSettings)
85
83
  .filter(([_, v]) => resolveParameterTransformFn(v) !== undefined)
86
- .map(([k, v]) => [k, resolveParameterTransformFn(v).to]));
84
+ .map(([k, v]) => [k, resolveParameterTransformFn(v)]));
87
85
  });
88
86
  }
89
87
  get statefulParameterOrder() {
@@ -174,7 +174,9 @@ export class ResourceController {
174
174
  ?? null;
175
175
  }
176
176
  const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, parametersToRefresh);
177
- return [{ core, parameters: { ...currentParametersArray[0], ...statefulCurrentParameters } }];
177
+ const resultParameters = { ...currentParametersArray[0], ...statefulCurrentParameters };
178
+ await this.applyTransformParameters(resultParameters, true);
179
+ return [{ core, parameters: resultParameters }];
178
180
  }
179
181
  async applyCreate(plan) {
180
182
  await this.resource.create(plan);
@@ -236,7 +238,7 @@ ${JSON.stringify(refresh, null, 2)}
236
238
  `);
237
239
  }
238
240
  }
239
- async applyTransformParameters(config) {
241
+ async applyTransformParameters(config, reverse = false) {
240
242
  if (!config) {
241
243
  return;
242
244
  }
@@ -244,12 +246,9 @@ ${JSON.stringify(refresh, null, 2)}
244
246
  if (config[key] === undefined || !inputTransformation) {
245
247
  continue;
246
248
  }
247
- config[key] = await inputTransformation(config[key], this.settings.parameterSettings[key]);
248
- }
249
- if (this.settings.inputTransformation) {
250
- const transformed = await this.settings.inputTransformation({ ...config });
251
- Object.keys(config).forEach((k) => delete config[k]);
252
- Object.assign(config, transformed);
249
+ config[key] = reverse
250
+ ? await inputTransformation.from(config[key])
251
+ : await inputTransformation.to(config[key]);
253
252
  }
254
253
  }
255
254
  addDefaultValues(config) {
@@ -60,13 +60,6 @@ export interface ResourceSettings<T extends StringIndexedObject> {
60
60
  * and applying any input transformations. Use parameter settings to define stateful parameters as well.
61
61
  */
62
62
  parameterSettings?: Partial<Record<keyof T, ParameterSetting>>;
63
- /**
64
- * A config level transformation that is only applied to the user supplied desired config. This transformation is allowed
65
- * to add, remove or modify keys as well as values. Changing this transformation for existing libraries will mess up existing states.
66
- *
67
- * @param desired
68
- */
69
- inputTransformation?: (desired: Partial<T>) => Promise<unknown> | unknown;
70
63
  /**
71
64
  * Customize the import and destory behavior of the resource. By default, <code>codify import</code> and <code>codify destroy</code> will call
72
65
  * `refresh()` with every parameter set to null and return the result of the refresh as the imported config. It looks for required parameters
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.134",
3
+ "version": "1.0.135",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -5,6 +5,7 @@ import { StatefulParameterController } from '../stateful-parameter/stateful-para
5
5
  import {
6
6
  ArrayParameterSetting,
7
7
  DefaultParameterSetting,
8
+ InputTransformation,
8
9
  ParameterSetting,
9
10
  resolveElementEqualsFn,
10
11
  resolveEqualsFn,
@@ -43,7 +44,6 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
43
44
 
44
45
  removeStatefulParametersBeforeDestroy?: boolean | undefined;
45
46
  dependencies?: string[] | undefined;
46
- inputTransformation?: ((desired: Partial<T>) => unknown) | undefined;
47
47
  private settings: ResourceSettings<T>;
48
48
 
49
49
  constructor(settings: ResourceSettings<T>) {
@@ -53,7 +53,6 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
53
53
  this.allowMultiple = settings.allowMultiple;
54
54
  this.removeStatefulParametersBeforeDestroy = settings.removeStatefulParametersBeforeDestroy;
55
55
  this.dependencies = settings.dependencies;
56
- this.inputTransformation = settings.inputTransformation;
57
56
 
58
57
  this.validateSettings();
59
58
  }
@@ -136,7 +135,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
136
135
  });
137
136
  }
138
137
 
139
- get inputTransformations(): Partial<Record<keyof T, (a: unknown, parameter: ParameterSetting) => unknown>> {
138
+ get inputTransformations(): Partial<Record<keyof T, InputTransformation>> {
140
139
  return this.getFromCacheOrCreate('inputTransformations', () => {
141
140
  if (!this.settings.parameterSettings) {
142
141
  return {};
@@ -145,8 +144,8 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
145
144
  return Object.fromEntries(
146
145
  Object.entries(this.settings.parameterSettings)
147
146
  .filter(([_, v]) => resolveParameterTransformFn(v!) !== undefined)
148
- .map(([k, v]) => [k, resolveParameterTransformFn(v!)!.to] as const)
149
- ) as Record<keyof T, (a: unknown) => unknown>;
147
+ .map(([k, v]) => [k, resolveParameterTransformFn(v!)] as const)
148
+ ) as Record<keyof T, InputTransformation>;
150
149
  });
151
150
  }
152
151
 
@@ -7,7 +7,7 @@ import { CreatePlan, DestroyPlan, ModifyPlan } from '../plan/plan-types.js';
7
7
  import { ParameterChange } from '../plan/change-set.js';
8
8
  import { ResourceController } from './resource-controller.js';
9
9
  import { TestConfig, testPlan, TestResource, TestStatefulParameter } from '../utils/test-utils.test.js';
10
- import { untildify } from '../utils/utils.js';
10
+ import { tildify, untildify } from '../utils/utils.js';
11
11
  import { ArrayStatefulParameter, StatefulParameter } from '../stateful-parameter/stateful-parameter.js';
12
12
  import { Plan } from '../plan/plan.js';
13
13
 
@@ -20,9 +20,8 @@ describe('Resource tests', () => {
20
20
  id: 'type',
21
21
  dependencies: ['homebrew', 'python'],
22
22
  parameterSettings: {
23
- propA: { canModify: true, inputTransformation: (input) => untildify(input) },
23
+ propA: { canModify: true, inputTransformation: { to: (input) => untildify(input), from: (input) => tildify(input) } },
24
24
  },
25
- inputTransformation: (config) => ({ propA: config.propA, propC: config.propB }),
26
25
  }
27
26
  }
28
27
 
@@ -535,4 +534,88 @@ describe('Resource tests', () => {
535
534
  expect(parameter1.modify.calledOnce).to.be.true;
536
535
  expect(parameter2.addItem.calledOnce).to.be.true;
537
536
  });
537
+
538
+ it('Applies reverse input transformations for imports', async () => {
539
+ const resource = new class extends TestResource {
540
+ getSettings(): ResourceSettings<TestConfig> {
541
+ return {
542
+ id: 'resourceType',
543
+ parameterSettings: {
544
+ propD: {
545
+ type: 'array',
546
+ inputTransformation: {
547
+ to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
548
+ Object.entries(h)
549
+ .map(([k, v]) => [
550
+ k,
551
+ typeof v === 'boolean'
552
+ ? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
553
+ : v,
554
+ ])
555
+ )
556
+ ),
557
+ from: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
558
+ Object.entries(h)
559
+ .map(([k, v]) => [
560
+ k,
561
+ v === 'yes' || v === 'no'
562
+ ? (v === 'yes')
563
+ : v,
564
+ ])
565
+ ))
566
+ }
567
+ }
568
+ }
569
+ }
570
+ }
571
+
572
+ async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
573
+ return {
574
+ propD: [
575
+ {
576
+ Host: 'new.com',
577
+ AddKeysToAgent: true,
578
+ IdentityFile: 'id_ed25519'
579
+ },
580
+ {
581
+ Host: 'github.com',
582
+ AddKeysToAgent: true,
583
+ UseKeychain: true,
584
+ },
585
+ {
586
+ Match: 'User bob,joe,phil',
587
+ PasswordAuthentication: true,
588
+ }
589
+ ],
590
+ }
591
+ }
592
+ }
593
+
594
+ const controller = new ResourceController(resource);
595
+ const plan = await controller.import({ type: 'resourceType' }, {});
596
+
597
+ expect(plan![0]).toMatchObject({
598
+ 'core': {
599
+ 'type': 'resourceType'
600
+ },
601
+ 'parameters': {
602
+ 'propD': [
603
+ {
604
+ 'Host': 'new.com',
605
+ 'AddKeysToAgent': true,
606
+ 'IdentityFile': 'id_ed25519'
607
+ },
608
+ {
609
+ 'Host': 'github.com',
610
+ 'AddKeysToAgent': true,
611
+ 'UseKeychain': true
612
+ },
613
+ {
614
+ 'Match': 'User bob,joe,phil',
615
+ 'PasswordAuthentication': true
616
+ }
617
+ ]
618
+ }
619
+ })
620
+ })
538
621
  });
@@ -254,7 +254,10 @@ export class ResourceController<T extends StringIndexedObject> {
254
254
  }
255
255
 
256
256
  const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, parametersToRefresh);
257
- return [{ core, parameters: { ...currentParametersArray[0], ...statefulCurrentParameters } }];
257
+ const resultParameters = { ...currentParametersArray[0], ...statefulCurrentParameters };
258
+
259
+ await this.applyTransformParameters(resultParameters, true);
260
+ return [{ core, parameters: resultParameters }];
258
261
  }
259
262
 
260
263
  private async applyCreate(plan: Plan<T>): Promise<void> {
@@ -333,7 +336,7 @@ ${JSON.stringify(refresh, null, 2)}
333
336
  }
334
337
  }
335
338
 
336
- private async applyTransformParameters(config: Partial<T> | null): Promise<void> {
339
+ private async applyTransformParameters(config: Partial<T> | null, reverse = false): Promise<void> {
337
340
  if (!config) {
338
341
  return;
339
342
  }
@@ -343,13 +346,9 @@ ${JSON.stringify(refresh, null, 2)}
343
346
  continue;
344
347
  }
345
348
 
346
- (config as Record<string, unknown>)[key] = await inputTransformation(config[key], this.settings.parameterSettings![key]!);
347
- }
348
-
349
- if (this.settings.inputTransformation) {
350
- const transformed = await this.settings.inputTransformation({ ...config })
351
- Object.keys(config).forEach((k) => delete config[k])
352
- Object.assign(config, transformed);
349
+ (config as Record<string, unknown>)[key] = reverse
350
+ ? await inputTransformation.from(config[key])
351
+ : await inputTransformation.to(config[key]);
353
352
  }
354
353
  }
355
354
 
@@ -564,74 +564,6 @@ describe('Resource parameter tests', () => {
564
564
  expect(timestampC).to.be.lessThan(timestampA as any);
565
565
  })
566
566
 
567
- it('Supports transform parameters', async () => {
568
- const resource = spy(new class extends TestResource {
569
- getSettings(): ResourceSettings<TestConfig> {
570
- return {
571
- id: 'resourceType',
572
- inputTransformation: (desired) => ({
573
- propA: 'propA',
574
- propB: 10,
575
- })
576
- }
577
- }
578
-
579
- async refresh(): Promise<Partial<TestConfig> | null> {
580
- return {
581
- propA: 'propA',
582
- propB: 10,
583
- }
584
- }
585
- });
586
-
587
- const controller = new ResourceController(resource);
588
- const plan = await controller.plan({ type: 'resourceType' }, { propC: 'abc' } as any, null, false);
589
-
590
- expect(resource.refresh.called).to.be.true;
591
- expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
592
- expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
593
- expect(resource.refresh.getCall(0).firstArg['propC']).to.not.exist;
594
-
595
- expect(plan.desiredConfig?.propA).to.eq('propA');
596
- expect(plan.desiredConfig?.propB).to.eq(10);
597
- expect(plan.desiredConfig?.propC).to.be.undefined;
598
-
599
- expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
600
- })
601
-
602
- it('Supports transform parameters for state parameters', async () => {
603
- const resource = spy(new class extends TestResource {
604
- getSettings(): ResourceSettings<TestConfig> {
605
- return {
606
- id: 'resourceType',
607
- inputTransformation: (desired) => ({
608
- propA: 'propA',
609
- propB: 10,
610
- })
611
- }
612
- }
613
-
614
- async refresh(): Promise<Partial<TestConfig> | null> {
615
- return {
616
- propA: 'propA',
617
- propB: 10,
618
- }
619
- }
620
- });
621
-
622
- const controller = new ResourceController(resource);
623
- const plan = await controller.plan({ type: 'resourceType' }, null, { propC: 'abc' }, true);
624
-
625
- expect(resource.refresh.called).to.be.true;
626
- expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
627
- expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
628
- expect(resource.refresh.getCall(0).firstArg['propC']).to.not.exist;
629
-
630
- expect(plan.currentConfig?.propA).to.eq('propA');
631
- expect(plan.currentConfig?.propB).to.eq(10);
632
- expect(plan.currentConfig?.propC).to.be.undefined;
633
- })
634
-
635
567
  it('Allows import required parameters customization', () => {
636
568
  const resource = new class extends TestResource {
637
569
  getSettings(): ResourceSettings<TestConfig> {
@@ -75,14 +75,6 @@ export interface ResourceSettings<T extends StringIndexedObject> {
75
75
  */
76
76
  parameterSettings?: Partial<Record<keyof T, ParameterSetting>>;
77
77
 
78
- /**
79
- * A config level transformation that is only applied to the user supplied desired config. This transformation is allowed
80
- * to add, remove or modify keys as well as values. Changing this transformation for existing libraries will mess up existing states.
81
- *
82
- * @param desired
83
- */
84
- inputTransformation?: (desired: Partial<T>) => Promise<unknown> | unknown;
85
-
86
78
  /**
87
79
  * Customize the import and destory behavior of the resource. By default, <code>codify import</code> and <code>codify destroy</code> will call
88
80
  * `refresh()` with every parameter set to null and return the result of the refresh as the imported config. It looks for required parameters