codify-plugin-lib 1.0.69 → 1.0.71

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,13 +1,13 @@
1
+ import Ajv from 'ajv';
2
+ import { ValidateFunction } from 'ajv/dist/2020.js';
1
3
  import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
2
4
  import { ParameterChange } from './change-set.js';
3
5
  import { Plan } from './plan.js';
4
- import { StatefulParameter } from './stateful-parameter.js';
5
- import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
6
6
  import { CreatePlan, DestroyPlan, ModifyPlan, ParameterOptions } from './plan-types.js';
7
- import { TransformParameter } from './transform-parameter.js';
8
7
  import { ResourceOptions } from './resource-options.js';
9
- import Ajv from 'ajv';
10
- import { ValidateFunction } from 'ajv/dist/2020.js';
8
+ import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
9
+ import { StatefulParameter } from './stateful-parameter.js';
10
+ import { TransformParameter } from './transform-parameter.js';
11
11
  export declare abstract class Resource<T extends StringIndexedObject> {
12
12
  readonly typeId: string;
13
13
  readonly statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
@@ -23,7 +23,7 @@ export declare abstract class Resource<T extends StringIndexedObject> {
23
23
  protected schemaValidator?: ValidateFunction;
24
24
  protected constructor(options: ResourceOptions<T>);
25
25
  onInitialize(): Promise<void>;
26
- validateResource(parameters: unknown): Promise<ValidationResult>;
26
+ validateResource(parameters: Partial<T>): Promise<ValidationResult>;
27
27
  plan(desiredConfig: Partial<T> & ResourceConfig | null, currentConfig?: Partial<T> & ResourceConfig | null, statefulMode?: boolean): Promise<Plan<T>>;
28
28
  apply(plan: Plan<T>): Promise<void>;
29
29
  private _applyCreate;
@@ -35,8 +35,8 @@ export declare abstract class Resource<T extends StringIndexedObject> {
35
35
  private refreshNonStatefulParameters;
36
36
  private refreshStatefulParameters;
37
37
  private validatePlanInputs;
38
- validate(parameters: unknown): Promise<ValidationResult>;
39
- abstract refresh(values: Map<keyof T, T[keyof T]>): Promise<Partial<T> | null>;
38
+ validate(parameters: Partial<T>): Promise<ValidationResult>;
39
+ abstract refresh(parameters: Partial<T>): Promise<Partial<T> | null>;
40
40
  abstract applyCreate(plan: CreatePlan<T>): Promise<void>;
41
41
  applyModify(pc: ParameterChange<T>, plan: ModifyPlan<T>): Promise<void>;
42
42
  abstract applyDestroy(plan: DestroyPlan<T>): Promise<void>;
@@ -1,8 +1,8 @@
1
+ import Ajv2020 from 'ajv/dist/2020.js';
1
2
  import { ParameterOperation, ResourceOperation, } from 'codify-schemas';
2
- import { Plan } from './plan.js';
3
3
  import { setsEqual, splitUserConfig } from '../utils/utils.js';
4
+ import { Plan } from './plan.js';
4
5
  import { ResourceOptionsParser } from './resource-options.js';
5
- import Ajv2020 from 'ajv/dist/2020.js';
6
6
  export class Resource {
7
7
  typeId;
8
8
  statefulParameters;
@@ -42,8 +42,8 @@ export class Resource {
42
42
  const isValid = this.schemaValidator(parameters);
43
43
  if (!isValid) {
44
44
  return {
45
- isValid: false,
46
45
  errors: this.schemaValidator?.errors ?? [],
46
+ isValid: false,
47
47
  };
48
48
  }
49
49
  }
@@ -52,13 +52,13 @@ export class Resource {
52
52
  async plan(desiredConfig, currentConfig = null, statefulMode = false) {
53
53
  this.validatePlanInputs(desiredConfig, currentConfig, statefulMode);
54
54
  const planOptions = {
55
- statefulMode,
56
55
  parameterOptions: this.parameterOptions,
56
+ statefulMode,
57
57
  };
58
58
  this.addDefaultValues(desiredConfig);
59
59
  await this.applyTransformParameters(desiredConfig);
60
60
  const parsedConfig = new ConfigParser(desiredConfig, currentConfig, this.statefulParameters, this.transformParameters);
61
- const { desiredParameters, resourceMetadata, nonStatefulParameters, statefulParameters, } = parsedConfig;
61
+ const { desiredParameters, nonStatefulParameters, resourceMetadata, statefulParameters, } = parsedConfig;
62
62
  const currentParameters = await this.refreshNonStatefulParameters(nonStatefulParameters);
63
63
  if (currentParameters == null) {
64
64
  return Plan.create(desiredParameters, null, resourceMetadata, planOptions);
@@ -139,11 +139,11 @@ export class Resource {
139
139
  }
140
140
  await this.applyDestroy(plan);
141
141
  }
142
- validateRefreshResults(refresh, desiredMap) {
142
+ validateRefreshResults(refresh, desired) {
143
143
  if (!refresh) {
144
144
  return;
145
145
  }
146
- const desiredKeys = new Set(desiredMap.keys());
146
+ const desiredKeys = new Set(Object.keys(refresh));
147
147
  const refreshKeys = new Set(Object.keys(refresh));
148
148
  if (!setsEqual(desiredKeys, refreshKeys)) {
149
149
  throw new Error(`Resource ${this.typeId}
@@ -167,26 +167,24 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
167
167
  throw new Error(`Transform parameter ${key} is attempting to override existing values ${JSON.stringify(transformedValue, null, 2)}`);
168
168
  }
169
169
  delete desired[key];
170
- Object.entries(transformedValue).forEach(([tvKey, tvValue]) => {
170
+ for (const [tvKey, tvValue] of Object.entries(transformedValue)) {
171
171
  desired[tvKey] = tvValue;
172
- });
172
+ }
173
173
  }
174
174
  }
175
175
  addDefaultValues(desired) {
176
176
  if (!desired) {
177
177
  return;
178
178
  }
179
- Object.entries(this.defaultValues)
180
- .forEach(([key, defaultValue]) => {
179
+ for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
181
180
  if (defaultValue !== undefined && desired[key] === undefined) {
182
181
  desired[key] = defaultValue;
183
182
  }
184
- });
183
+ }
185
184
  }
186
185
  async refreshNonStatefulParameters(resourceParameters) {
187
- const entriesToRefresh = new Map(Object.entries(resourceParameters));
188
- const currentParameters = await this.refresh(entriesToRefresh);
189
- this.validateRefreshResults(currentParameters, entriesToRefresh);
186
+ const currentParameters = await this.refresh(resourceParameters);
187
+ this.validateRefreshResults(currentParameters, resourceParameters);
190
188
  return currentParameters;
191
189
  }
192
190
  async refreshStatefulParameters(statefulParametersConfig, isStatefulMode) {
@@ -270,18 +268,14 @@ ${JSON.stringify(currentMetadata, null, 2)}`);
270
268
  get parameters() {
271
269
  const desiredParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).parameters : undefined;
272
270
  const currentParameters = this.currentConfig ? splitUserConfig(this.currentConfig).parameters : undefined;
273
- return { ...(desiredParameters ?? {}), ...(currentParameters ?? {}) };
271
+ return { ...desiredParameters, ...currentParameters };
274
272
  }
275
273
  get nonStatefulParameters() {
276
- const parameters = this.parameters;
277
- return Object.fromEntries([
278
- ...Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))),
279
- ]);
274
+ const { parameters } = this;
275
+ return Object.fromEntries(Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))));
280
276
  }
281
277
  get statefulParameters() {
282
- const parameters = this.parameters;
283
- return Object.fromEntries([
284
- ...Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)),
285
- ]);
278
+ const { parameters } = this;
279
+ return Object.fromEntries(Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)));
286
280
  }
287
281
  }
@@ -39,8 +39,8 @@ export function isDebug() {
39
39
  export function splitUserConfig(config) {
40
40
  const resourceMetadata = {
41
41
  type: config.type,
42
- ...(config.name && { name: config.name }),
43
- ...(config.dependsOn && { dependsOn: config.dependsOn }),
42
+ ...(config.name ? { name: config.name } : {}),
43
+ ...(config.dependsOn ? { dependsOn: config.dependsOn } : {}),
44
44
  };
45
45
  const { type, name, dependsOn, ...parameters } = config;
46
46
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.69",
3
+ "version": "1.0.71",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "dependencies": {
15
15
  "ajv": "^8.12.0",
16
16
  "ajv-formats": "^2.1.1",
17
- "codify-schemas": "1.0.38",
17
+ "codify-schemas": "1.0.39",
18
18
  "@npmcli/promise-spawn": "^7.0.1"
19
19
  },
20
20
  "devDependencies": {
@@ -1,6 +1,3 @@
1
- import { Plan } from './plan.js';
2
- import { StringIndexedObject } from 'codify-schemas';
3
-
4
1
  export class SudoError extends Error {
5
2
  command: string;
6
3
 
@@ -121,6 +121,28 @@ describe('Plan entity tests', () => {
121
121
  .every((pc) => pc.operation === ParameterOperation.ADD)
122
122
  ).to.be.true;
123
123
  })
124
+
125
+ it('Returns the original resource names', () => {
126
+ const resource = createResource();
127
+
128
+ const plan = Plan.create(
129
+ {
130
+ propA: 'propA',
131
+ },
132
+ {
133
+ propA: 'propA2',
134
+ },
135
+ {
136
+ type: 'type',
137
+ name: 'name1'
138
+ }, { statefulMode: false });
139
+
140
+ expect(plan.toResponse()).toMatchObject({
141
+ resourceType: 'type',
142
+ resourceName: 'name1',
143
+ operation: ResourceOperation.RECREATE
144
+ })
145
+ })
124
146
  })
125
147
 
126
148
  function createResource(): Resource<any> {
@@ -5,7 +5,6 @@ import {
5
5
  PlanRequestData,
6
6
  PlanResponseData,
7
7
  ResourceConfig,
8
- ResourceOperation,
9
8
  ValidateRequestData,
10
9
  ValidateResponseData
11
10
  } from 'codify-schemas';
@@ -1,14 +1,15 @@
1
+ import Ajv from 'ajv';
2
+ import Ajv2020, { ValidateFunction } from 'ajv/dist/2020.js';
1
3
  import { ParameterOperation, ResourceConfig, ResourceOperation, StringIndexedObject, } from 'codify-schemas';
4
+
5
+ import { setsEqual, splitUserConfig } from '../utils/utils.js';
2
6
  import { ParameterChange } from './change-set.js';
3
7
  import { Plan } from './plan.js';
4
- import { StatefulParameter } from './stateful-parameter.js';
5
- import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
6
- import { setsEqual, splitUserConfig } from '../utils/utils.js';
7
8
  import { CreatePlan, DestroyPlan, ModifyPlan, ParameterOptions, PlanOptions } from './plan-types.js';
8
- import { TransformParameter } from './transform-parameter.js';
9
9
  import { ResourceOptions, ResourceOptionsParser } from './resource-options.js';
10
- import Ajv from 'ajv';
11
- import Ajv2020, { ValidateFunction } from 'ajv/dist/2020.js';
10
+ import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
11
+ import { StatefulParameter } from './stateful-parameter.js';
12
+ import { TransformParameter } from './transform-parameter.js';
12
13
 
13
14
  /**
14
15
  * Description of resource here
@@ -59,14 +60,14 @@ export abstract class Resource<T extends StringIndexedObject> {
59
60
 
60
61
  async onInitialize(): Promise<void> {}
61
62
 
62
- async validateResource(parameters: unknown): Promise<ValidationResult> {
63
+ async validateResource(parameters: Partial<T>): Promise<ValidationResult> {
63
64
  if (this.schemaValidator) {
64
65
  const isValid = this.schemaValidator(parameters);
65
66
 
66
67
  if (!isValid) {
67
68
  return {
68
- isValid: false,
69
69
  errors: this.schemaValidator?.errors ?? [],
70
+ isValid: false,
70
71
  }
71
72
  }
72
73
  }
@@ -84,8 +85,8 @@ export abstract class Resource<T extends StringIndexedObject> {
84
85
  this.validatePlanInputs(desiredConfig, currentConfig, statefulMode);
85
86
 
86
87
  const planOptions: PlanOptions<T> = {
87
- statefulMode,
88
88
  parameterOptions: this.parameterOptions,
89
+ statefulMode,
89
90
  }
90
91
 
91
92
  this.addDefaultValues(desiredConfig);
@@ -95,8 +96,8 @@ export abstract class Resource<T extends StringIndexedObject> {
95
96
  const parsedConfig = new ConfigParser(desiredConfig, currentConfig, this.statefulParameters, this.transformParameters)
96
97
  const {
97
98
  desiredParameters,
98
- resourceMetadata,
99
99
  nonStatefulParameters,
100
+ resourceMetadata,
100
101
  statefulParameters,
101
102
  } = parsedConfig;
102
103
 
@@ -133,13 +134,16 @@ export abstract class Resource<T extends StringIndexedObject> {
133
134
  case ResourceOperation.CREATE: {
134
135
  return this._applyCreate(plan); // TODO: Add new parameters value so that apply
135
136
  }
137
+
136
138
  case ResourceOperation.MODIFY: {
137
139
  return this._applyModify(plan);
138
140
  }
141
+
139
142
  case ResourceOperation.RECREATE: {
140
143
  await this._applyDestroy(plan);
141
144
  return this._applyCreate(plan);
142
145
  }
146
+
143
147
  case ResourceOperation.DESTROY: {
144
148
  return this._applyDestroy(plan);
145
149
  }
@@ -185,11 +189,13 @@ export abstract class Resource<T extends StringIndexedObject> {
185
189
  await statefulParameter.applyAdd(parameterChange.newValue, plan);
186
190
  break;
187
191
  }
192
+
188
193
  case ParameterOperation.MODIFY: {
189
194
  // TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
190
195
  await statefulParameter.applyModify(parameterChange.newValue, parameterChange.previousValue, false, plan);
191
196
  break;
192
197
  }
198
+
193
199
  case ParameterOperation.REMOVE: {
194
200
  await statefulParameter.applyRemove(parameterChange.previousValue, plan);
195
201
  break;
@@ -215,12 +221,12 @@ export abstract class Resource<T extends StringIndexedObject> {
215
221
  await this.applyDestroy(plan as DestroyPlan<T>);
216
222
  }
217
223
 
218
- private validateRefreshResults(refresh: Partial<T> | null, desiredMap: Map<keyof T, T[keyof T]>) {
224
+ private validateRefreshResults(refresh: Partial<T> | null, desired: Partial<T>) {
219
225
  if (!refresh) {
220
226
  return;
221
227
  }
222
228
 
223
- const desiredKeys = new Set<keyof T>(desiredMap.keys());
229
+ const desiredKeys = new Set(Object.keys(refresh)) as Set<keyof T>;
224
230
  const refreshKeys = new Set(Object.keys(refresh)) as Set<keyof T>;
225
231
 
226
232
  if (!setsEqual(desiredKeys, refreshKeys)) {
@@ -256,10 +262,10 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
256
262
  delete desired[key];
257
263
 
258
264
  // Add the new transformed values
259
- Object.entries(transformedValue).forEach(([tvKey, tvValue]) => {
265
+ for (const [tvKey, tvValue] of Object.entries(transformedValue)) {
260
266
  // @ts-ignore
261
267
  desired[tvKey] = tvValue;
262
- })
268
+ }
263
269
  }
264
270
  }
265
271
 
@@ -268,21 +274,17 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
268
274
  return;
269
275
  }
270
276
 
271
- Object.entries(this.defaultValues)
272
- .forEach(([key, defaultValue]) => {
277
+ for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
273
278
  if (defaultValue !== undefined && desired[key as any] === undefined) {
274
279
  // @ts-ignore
275
280
  desired[key] = defaultValue;
276
281
  }
277
- });
282
+ }
278
283
  }
279
284
 
280
285
  private async refreshNonStatefulParameters(resourceParameters: Partial<T>): Promise<Partial<T> | null> {
281
- const entriesToRefresh = new Map<keyof T, T[keyof T]>(
282
- Object.entries(resourceParameters)
283
- )
284
- const currentParameters = await this.refresh(entriesToRefresh);
285
- this.validateRefreshResults(currentParameters, entriesToRefresh);
286
+ const currentParameters = await this.refresh(resourceParameters);
287
+ this.validateRefreshResults(currentParameters, resourceParameters);
286
288
  return currentParameters;
287
289
  }
288
290
 
@@ -340,13 +342,13 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
340
342
  }
341
343
  }
342
344
 
343
- async validate(parameters: unknown): Promise<ValidationResult> {
345
+ async validate(parameters: Partial<T>): Promise<ValidationResult> {
344
346
  return {
345
347
  isValid: true,
346
348
  }
347
349
  };
348
350
 
349
- abstract refresh(values: Map<keyof T, T[keyof T]>): Promise<Partial<T> | null>;
351
+ abstract refresh(parameters: Partial<T>): Promise<Partial<T> | null>;
350
352
 
351
353
  abstract applyCreate(plan: CreatePlan<T>): Promise<void>;
352
354
 
@@ -410,22 +412,22 @@ ${JSON.stringify(currentMetadata, null, 2)}`);
410
412
  const desiredParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).parameters : undefined;
411
413
  const currentParameters = this.currentConfig ? splitUserConfig(this.currentConfig).parameters : undefined;
412
414
 
413
- return { ...(desiredParameters ?? {}), ...(currentParameters ?? {}) } as Partial<T>;
415
+ return { ...desiredParameters, ...currentParameters } as Partial<T>;
414
416
  }
415
417
 
416
418
  get nonStatefulParameters(): Partial<T> {
417
- const parameters = this.parameters;
419
+ const { parameters } = this;
418
420
 
419
- return Object.fromEntries([
420
- ...Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))),
421
- ]) as Partial<T>;
421
+ return Object.fromEntries(
422
+ Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key)))
423
+ ) as Partial<T>;
422
424
  }
423
425
 
424
426
  get statefulParameters(): Partial<T> {
425
- const parameters = this.parameters;
427
+ const { parameters } = this;
426
428
 
427
- return Object.fromEntries([
428
- ...Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)),
429
- ]) as Partial<T>;
429
+ return Object.fromEntries(
430
+ Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key))
431
+ ) as Partial<T>;
430
432
  }
431
433
  }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { splitUserConfig } from './utils.js';
3
+
4
+ describe('Utils tests', () => {
5
+ it('Can split a config correctly', () => {
6
+ const { parameters, resourceMetadata } = splitUserConfig({
7
+ type: 'type',
8
+ name: 'name',
9
+ dependsOn: ['a', 'b', 'c'],
10
+ propA: 'propA',
11
+ propB: 'propB',
12
+ propC: 'propC',
13
+ propD: 'propD',
14
+ })
15
+
16
+ expect(resourceMetadata).toMatchObject({
17
+ type: 'type',
18
+ name: 'name',
19
+ dependsOn: ['a', 'b', 'c'],
20
+ })
21
+
22
+ expect(parameters).toMatchObject({
23
+ propA: 'propA',
24
+ propB: 'propB',
25
+ propC: 'propC',
26
+ propD: 'propD',
27
+ })
28
+ })
29
+ })
@@ -85,8 +85,8 @@ export function splitUserConfig<T extends StringIndexedObject>(
85
85
  ): { parameters: T; resourceMetadata: ResourceConfig} {
86
86
  const resourceMetadata = {
87
87
  type: config.type,
88
- ...(config.name && { name: config.name }),
89
- ...(config.dependsOn && { dependsOn: config.dependsOn }),
88
+ ...(config.name ? { name: config.name } : {}),
89
+ ...(config.dependsOn ? { dependsOn: config.dependsOn } : {}),
90
90
  };
91
91
 
92
92
  const { type, name, dependsOn, ...parameters } = config;
@@ -1 +0,0 @@
1
- export {};
@@ -1,22 +0,0 @@
1
- import { Resource } from './resource.js';
2
- class Test extends Resource {
3
- validate(config) {
4
- throw new Error('Method not implemented.');
5
- }
6
- async refresh(keys) {
7
- const result = {};
8
- if (keys.has('propA')) {
9
- result['propA'] = 'abc';
10
- }
11
- return result;
12
- }
13
- applyCreate(plan) {
14
- throw new Error('Method not implemented.');
15
- }
16
- applyModify(parameterName, newValue, previousValue, plan) {
17
- throw new Error('Method not implemented.');
18
- }
19
- applyDestroy(plan) {
20
- throw new Error('Method not implemented.');
21
- }
22
- }
@@ -1,3 +0,0 @@
1
- export interface StringIndexedObject {
2
- [x: string]: unknown;
3
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,5 +0,0 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import { ChildProcess } from 'child_process';
3
- export declare class CodifyTestUtils {
4
- static sendMessageToProcessAwaitResponse(process: ChildProcess, message: any): Promise<any>;
5
- }
@@ -1,17 +0,0 @@
1
- export class CodifyTestUtils {
2
- static sendMessageToProcessAwaitResponse(process, message) {
3
- return new Promise((resolve, reject) => {
4
- process.on('message', (response) => {
5
- resolve(response);
6
- });
7
- process.on('error', (err) => reject(err));
8
- process.on('exit', (code) => {
9
- if (code != 0) {
10
- reject('Exit code is not 0');
11
- }
12
- resolve(code);
13
- });
14
- process.send(message);
15
- });
16
- }
17
- }