codify-plugin-lib 1.0.128 → 1.0.130

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,16 +1,15 @@
1
- import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
1
+ import { StringIndexedObject } from 'codify-schemas';
2
2
 
3
3
  import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
4
- import { splitUserConfig } from '../utils/utils.js';
5
4
 
6
5
  export class ConfigParser<T extends StringIndexedObject> {
7
- private readonly desiredConfig: Partial<T> & ResourceConfig | null;
8
- private readonly stateConfig: Partial<T> & ResourceConfig | null;
6
+ private readonly desiredConfig: Partial<T> | null;
7
+ private readonly stateConfig: Partial<T> | null;
9
8
  private statefulParametersMap: Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
10
9
 
11
10
  constructor(
12
- desiredConfig: Partial<T> & ResourceConfig | null,
13
- stateConfig: Partial<T> & ResourceConfig | null,
11
+ desiredConfig: Partial<T> | null,
12
+ stateConfig: Partial<T> | null,
14
13
  statefulParameters: Map<keyof T, StatefulParameterController<T, T[keyof T]>>,
15
14
  ) {
16
15
  this.desiredConfig = desiredConfig;
@@ -18,42 +17,8 @@ export class ConfigParser<T extends StringIndexedObject> {
18
17
  this.statefulParametersMap = statefulParameters;
19
18
  }
20
19
 
21
- get coreParameters(): ResourceConfig {
22
- const desiredCoreParameters = this.desiredConfig ? splitUserConfig(this.desiredConfig).coreParameters : undefined;
23
- const currentCoreParameters = this.stateConfig ? splitUserConfig(this.stateConfig).coreParameters : undefined;
24
-
25
- if (!desiredCoreParameters && !currentCoreParameters) {
26
- throw new Error(`Unable to parse resource core parameters from:
27
-
28
- Desired: ${JSON.stringify(this.desiredConfig, null, 2)}
29
-
30
- Current: ${JSON.stringify(this.stateConfig, null, 2)}`)
31
- }
32
-
33
- return desiredCoreParameters ?? currentCoreParameters!;
34
- }
35
-
36
- get desiredParameters(): Partial<T> | null {
37
- if (!this.desiredConfig) {
38
- return null;
39
- }
40
-
41
- const { parameters } = splitUserConfig(this.desiredConfig);
42
- return parameters;
43
- }
44
-
45
- get stateParameters(): Partial<T> | null {
46
- if (!this.stateConfig) {
47
- return null;
48
- }
49
-
50
- const { parameters } = splitUserConfig(this.stateConfig);
51
- return parameters;
52
- }
53
-
54
-
55
20
  get allParameters(): Partial<T> {
56
- return { ...this.desiredParameters, ...this.stateParameters } as Partial<T>;
21
+ return { ...this.desiredConfig, ...this.stateConfig } as Partial<T>;
57
22
  }
58
23
 
59
24
  get allNonStatefulParameters(): Partial<T> {
@@ -19,9 +19,9 @@ describe('Resource tests for stateful plans', () => {
19
19
 
20
20
  const controller = new ResourceController(resource);
21
21
  const plan = await controller.plan(
22
+ { type: 'type' },
22
23
  null,
23
24
  {
24
- type: 'type',
25
25
  propA: 'propA',
26
26
  propB: 10,
27
27
  propC: 'propC',
@@ -67,8 +67,8 @@ describe('Resource tests for stateful plans', () => {
67
67
 
68
68
  const controller = new ResourceController(resource);
69
69
  const plan = await controller.plan(
70
+ { type: 'resource' },
70
71
  {
71
- type: 'resource',
72
72
  propA: 'propA',
73
73
  propB: 10,
74
74
  propC: 'propC',
@@ -119,6 +119,7 @@ describe('Resource tests for stateful plans', () => {
119
119
 
120
120
  const controller = new ResourceController(resource)
121
121
  const plan = await controller.plan(
122
+ { type: 'type' },
122
123
  {
123
124
  type: 'type',
124
125
  propA: 'propA',
@@ -191,15 +192,14 @@ describe('Resource tests for stateful plans', () => {
191
192
 
192
193
  const controller = new ResourceController(resource);
193
194
  const plan = await controller.plan(
195
+ { type: 'type' },
194
196
  {
195
- type: 'type',
196
197
  propA: 'propA',
197
198
  propB: 10,
198
199
  propC: 'propC',
199
200
  propD: 'propD'
200
201
  },
201
202
  {
202
- type: 'type',
203
203
  propA: 'propA',
204
204
  propC: 'propC'
205
205
  },
@@ -34,11 +34,14 @@ describe('Resource tests', () => {
34
34
  }
35
35
 
36
36
  const controller = new ResourceController(resource);
37
- await controller.validate({
38
- type: 'type',
39
- propA: '~/.tool_versions',
40
- propB: 10,
41
- })
37
+ await controller.validate(
38
+ { type: 'type' },
39
+ {
40
+ type: 'type',
41
+ propA: '~/.tool_versions',
42
+ propB: 10,
43
+ }
44
+ )
42
45
  })
43
46
 
44
47
  it('Plans successfully', async () => {
@@ -55,16 +58,17 @@ describe('Resource tests', () => {
55
58
  const controller = new ResourceController(resource)
56
59
 
57
60
  const resourceSpy = spy(controller);
58
- const result = await resourceSpy.plan({
59
- type: 'type',
60
- name: 'name',
61
- propA: 'propA',
62
- propB: 10,
63
- })
61
+ const result = await resourceSpy.plan(
62
+ { type: 'type', name: 'name' },
63
+ {
64
+ propA: 'propA',
65
+ propB: 10,
66
+ },
67
+ null,
68
+ false,
69
+ )
64
70
 
65
71
  expect(result.desiredConfig).to.deep.eq({
66
- type: 'type',
67
- name: 'name',
68
72
  propA: 'propA',
69
73
  propB: 10,
70
74
  });
@@ -92,15 +96,16 @@ describe('Resource tests', () => {
92
96
  const controller = new ResourceController(resource);
93
97
 
94
98
  const resourceSpy = spy(controller);
95
- const result = await resourceSpy.plan({
96
- type: 'type',
97
- name: 'name',
98
- propA: 'propA',
99
- propB: 10,
100
- propC: 'somethingAfter'
101
- })
102
-
103
- console.log(result.changeSet.parameterChanges)
99
+ const result = await resourceSpy.plan(
100
+ { type: 'type', name: 'name' },
101
+ {
102
+ propA: 'propA',
103
+ propB: 10,
104
+ propC: 'somethingAfter'
105
+ },
106
+ null,
107
+ false,
108
+ )
104
109
 
105
110
  expect(result.changeSet.operation).to.eq(ResourceOperation.CREATE);
106
111
  expect(result.changeSet.parameterChanges.length).to.eq(3);
@@ -115,7 +120,12 @@ describe('Resource tests', () => {
115
120
  const controller = new ResourceController(resource);
116
121
 
117
122
  const resourceSpy = spy(controller);
118
- const result = await resourceSpy.plan({ type: 'type' })
123
+ const result = await resourceSpy.plan(
124
+ { type: 'type' },
125
+ {},
126
+ null,
127
+ false
128
+ )
119
129
 
120
130
  expect(result.changeSet.operation).to.eq(ResourceOperation.CREATE);
121
131
  expect(result.changeSet.parameterChanges.length).to.eq(0);
@@ -150,7 +160,7 @@ describe('Resource tests', () => {
150
160
  testPlan({
151
161
  current: [{ propA: 'a', propB: 0 }],
152
162
  state: { propA: 'a', propB: 0 },
153
- statefulMode: true,
163
+ isStateful: true,
154
164
  })
155
165
  )
156
166
 
@@ -169,7 +179,7 @@ describe('Resource tests', () => {
169
179
  testPlan({
170
180
  desired: { propA: 'a', propB: 0 },
171
181
  current: [{ propA: 'b', propB: -1 }],
172
- statefulMode: true
182
+ isStateful: true
173
183
  })
174
184
  );
175
185
 
@@ -195,7 +205,12 @@ describe('Resource tests', () => {
195
205
  }
196
206
  const controller = new ResourceController(resource);
197
207
 
198
- const plan = await controller.plan({ type: 'resource', propA: 'a', propB: 0 })
208
+ const plan = await controller.plan(
209
+ { type: 'resource' },
210
+ { propA: 'a', propB: 0 },
211
+ null,
212
+ false,
213
+ )
199
214
 
200
215
  const resourceSpy = spy(resource);
201
216
  await controller.apply(
@@ -267,7 +282,7 @@ describe('Resource tests', () => {
267
282
  }
268
283
  const controller = new ResourceController(resource);
269
284
 
270
- const plan = await controller.plan({ type: 'resource' })
285
+ const plan = await controller.plan({ type: 'resource' }, {}, null, false)
271
286
  expect(plan.currentConfig?.propA).to.eq('propAAfter');
272
287
  expect(plan.desiredConfig?.propA).to.eq('propADefault');
273
288
  expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
@@ -294,7 +309,7 @@ describe('Resource tests', () => {
294
309
  }
295
310
  const controller = new ResourceController(resource);
296
311
 
297
- const plan = await controller.plan({ type: 'resource' })
312
+ const plan = await controller.plan({ type: 'resource' }, {}, null, false)
298
313
  expect(plan.currentConfig?.propE).to.eq('propEDefault');
299
314
  expect(plan.desiredConfig?.propE).to.eq('propEDefault');
300
315
  expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
@@ -317,7 +332,7 @@ describe('Resource tests', () => {
317
332
  }
318
333
  const controller = new ResourceController(resource);
319
334
 
320
- const plan = await controller.plan({ type: 'resource' })
335
+ const plan = await controller.plan({ type: 'resource' }, {}, null, false)
321
336
  expect(plan.currentConfig).to.be.null
322
337
  expect(plan.desiredConfig!.propE).to.eq('propEDefault');
323
338
  expect(plan.changeSet.operation).to.eq(ResourceOperation.CREATE);
@@ -345,7 +360,7 @@ describe('Resource tests', () => {
345
360
  }
346
361
  const controller = new ResourceController(resource);
347
362
 
348
- const plan = await controller.plan({ type: 'resource', propA: 'propA' })
363
+ const plan = await controller.plan({ type: 'resource' }, { propA: 'propA' }, null, false)
349
364
  expect(plan.currentConfig?.propA).to.eq('propAAfter');
350
365
  expect(plan.desiredConfig?.propA).to.eq('propA');
351
366
  expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
@@ -455,7 +470,7 @@ describe('Resource tests', () => {
455
470
  }
456
471
 
457
472
  const controller = new ResourceController(resource);
458
- const plan = await controller.plan({ type: 'nvm', global: '20.12', nodeVersions: ['18', '20'] })
473
+ const plan = await controller.plan({ type: 'nvm' }, { global: '20.12', nodeVersions: ['18', '20'] }, null, false)
459
474
 
460
475
  expect(plan).toMatchObject({
461
476
  changeSet: {
@@ -507,7 +522,7 @@ describe('Resource tests', () => {
507
522
  }
508
523
 
509
524
  const controller = new ResourceController(resource);
510
- const plan = await controller.plan({ type: 'nvm', global: '20.12', nodeVersions: ['18', '20'] })
525
+ const plan = await controller.plan({ type: 'nvm' }, { global: '20.12', nodeVersions: ['18', '20'] }, null, false)
511
526
 
512
527
  expect(plan).toMatchObject({
513
528
  changeSet: {
@@ -2,6 +2,7 @@ import { Ajv, ValidateFunction } from 'ajv';
2
2
  import {
3
3
  ParameterOperation,
4
4
  ResourceConfig,
5
+ ResourceJson,
5
6
  ResourceOperation,
6
7
  StringIndexedObject,
7
8
  ValidateResponseData
@@ -10,7 +11,6 @@ import {
10
11
  import { ParameterChange } from '../plan/change-set.js';
11
12
  import { Plan } from '../plan/plan.js';
12
13
  import { CreatePlan, DestroyPlan, ModifyPlan } from '../plan/plan-types.js';
13
- import { splitUserConfig } from '../utils/utils.js';
14
14
  import { ConfigParser } from './config-parser.js';
15
15
  import { ParsedResourceSettings } from './parsed-resource-settings.js';
16
16
  import { Resource } from './resource.js';
@@ -54,23 +54,21 @@ export class ResourceController<T extends StringIndexedObject> {
54
54
  }
55
55
 
56
56
  async validate(
57
- desiredConfig: Partial<T> & ResourceConfig,
57
+ core: ResourceConfig,
58
+ parameters: Partial<T>,
58
59
  ): Promise<ValidateResponseData['resourceValidations'][0]> {
59
- const configToValidate = { ...desiredConfig };
60
- await this.applyTransformParameters(configToValidate);
61
-
62
- const { parameters, coreParameters } = splitUserConfig(configToValidate);
60
+ await this.applyTransformParameters(parameters);
63
61
  this.addDefaultValues(parameters);
64
62
 
65
63
  if (this.schemaValidator) {
66
64
  // Schema validator uses pre transformation parameters
67
- const isValid = this.schemaValidator(splitUserConfig(desiredConfig).parameters);
65
+ const isValid = this.schemaValidator(parameters);
68
66
 
69
67
  if (!isValid) {
70
68
  return {
71
69
  isValid: false,
72
- resourceName: coreParameters.name,
73
- resourceType: coreParameters.type,
70
+ resourceName: core.name,
71
+ resourceType: core.type,
74
72
  schemaValidationErrors: this.schemaValidator?.errors ?? [],
75
73
  }
76
74
  }
@@ -89,61 +87,59 @@ export class ResourceController<T extends StringIndexedObject> {
89
87
  return {
90
88
  customValidationErrorMessage,
91
89
  isValid: false,
92
- resourceName: coreParameters.name,
93
- resourceType: coreParameters.type,
90
+ resourceName: core.name,
91
+ resourceType: core.type,
94
92
  schemaValidationErrors: this.schemaValidator?.errors ?? [],
95
93
  }
96
94
  }
97
95
 
98
96
  return {
99
97
  isValid: true,
100
- resourceName: coreParameters.name,
101
- resourceType: coreParameters.type,
98
+ resourceName: core.name,
99
+ resourceType: core.type,
102
100
  schemaValidationErrors: [],
103
101
  }
104
102
  }
105
103
 
106
104
  async plan(
107
- desiredConfig: Partial<T> & ResourceConfig | null,
108
- stateConfig: Partial<T> & ResourceConfig | null = null,
109
- statefulMode = false,
105
+ core: ResourceConfig,
106
+ desired: Partial<T> | null,
107
+ state: Partial<T> | null,
108
+ isStateful = false,
110
109
  ): Promise<Plan<T>> {
111
- this.validatePlanInputs(desiredConfig, stateConfig, statefulMode);
110
+ this.validatePlanInputs(core, desired, state, isStateful);
112
111
 
113
- this.addDefaultValues(desiredConfig);
114
- await this.applyTransformParameters(desiredConfig);
112
+ this.addDefaultValues(desired);
113
+ await this.applyTransformParameters(desired);
115
114
 
116
- this.addDefaultValues(stateConfig);
117
- await this.applyTransformParameters(stateConfig);
115
+ this.addDefaultValues(state);
116
+ await this.applyTransformParameters(state);
118
117
 
119
118
  // Parse data from the user supplied config
120
- const parsedConfig = new ConfigParser(desiredConfig, stateConfig, this.parsedSettings.statefulParameters)
119
+ const parsedConfig = new ConfigParser(desired, state, this.parsedSettings.statefulParameters)
121
120
  const {
122
- coreParameters,
123
- desiredParameters,
124
- stateParameters,
125
121
  allParameters,
126
122
  allNonStatefulParameters,
127
123
  allStatefulParameters,
128
124
  } = parsedConfig;
129
125
 
130
126
  // Refresh resource parameters. This refreshes the parameters that configure the resource itself
131
- const currentParametersArray = await this.refreshNonStatefulParameters(allNonStatefulParameters);
127
+ const currentArray = await this.refreshNonStatefulParameters(allNonStatefulParameters);
132
128
 
133
129
  // Short circuit here. If the resource is non-existent, there's no point checking stateful parameters
134
- if (currentParametersArray === null
135
- || currentParametersArray === undefined
130
+ if (currentArray === null
131
+ || currentArray === undefined
136
132
  || this.settings.allowMultiple // Stateful parameters are not supported currently if allowMultiple is true
137
- || currentParametersArray.length === 0
138
- || currentParametersArray.filter(Boolean).length === 0
133
+ || currentArray.length === 0
134
+ || currentArray.filter(Boolean).length === 0
139
135
  ) {
140
136
  return Plan.calculate({
141
- desiredParameters,
142
- currentParametersArray,
143
- stateParameters,
144
- coreParameters,
137
+ desired,
138
+ currentArray,
139
+ state,
140
+ core,
145
141
  settings: this.parsedSettings,
146
- statefulMode,
142
+ isStateful,
147
143
  });
148
144
  }
149
145
 
@@ -152,12 +148,12 @@ export class ResourceController<T extends StringIndexedObject> {
152
148
  const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, allParameters);
153
149
 
154
150
  return Plan.calculate({
155
- desiredParameters,
156
- currentParametersArray: [{ ...currentParametersArray[0], ...statefulCurrentParameters }] as Partial<T>[],
157
- stateParameters,
158
- coreParameters,
151
+ desired,
152
+ currentArray: [{ ...currentArray[0], ...statefulCurrentParameters }] as Partial<T>[],
153
+ state,
154
+ core,
159
155
  settings: this.parsedSettings,
160
- statefulMode
156
+ isStateful
161
157
  })
162
158
  }
163
159
 
@@ -186,9 +182,12 @@ export class ResourceController<T extends StringIndexedObject> {
186
182
  }
187
183
  }
188
184
 
189
- async import(config: Partial<T> & ResourceConfig): Promise<(Partial<T> & ResourceConfig)[] | null> {
190
- this.addDefaultValues(config);
191
- await this.applyTransformParameters(config);
185
+ async import(
186
+ core: ResourceConfig,
187
+ parameters: Partial<T>
188
+ ): Promise<Array<ResourceJson> | null> {
189
+ this.addDefaultValues(parameters);
190
+ await this.applyTransformParameters(parameters);
192
191
 
193
192
  // Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
194
193
  const parametersToRefresh = this.settings.import?.refreshKeys
@@ -197,14 +196,14 @@ export class ResourceController<T extends StringIndexedObject> {
197
196
  this.settings.import?.refreshKeys.map((k) => [k, null])
198
197
  ),
199
198
  ...this.settings.import?.defaultRefreshValues,
200
- ...config,
199
+ ...parameters,
201
200
  }
202
201
  : {
203
202
  ...Object.fromEntries(
204
203
  this.getAllParameterKeys().map((k) => [k, null])
205
204
  ),
206
205
  ...this.settings.import?.defaultRefreshValues,
207
- ...config,
206
+ ...parameters,
208
207
  };
209
208
 
210
209
  // Parse data from the user supplied config
@@ -212,7 +211,6 @@ export class ResourceController<T extends StringIndexedObject> {
212
211
  const {
213
212
  allNonStatefulParameters,
214
213
  allStatefulParameters,
215
- coreParameters,
216
214
  } = parsedConfig;
217
215
 
218
216
  const currentParametersArray = await this.refreshNonStatefulParameters(allNonStatefulParameters);
@@ -220,16 +218,15 @@ export class ResourceController<T extends StringIndexedObject> {
220
218
  if (currentParametersArray === null
221
219
  || currentParametersArray === undefined
222
220
  || this.settings.allowMultiple // Stateful parameters are not supported currently if allowMultiple is true
223
- || currentParametersArray.length === 0
224
221
  || currentParametersArray.filter(Boolean).length === 0
225
222
  ) {
226
223
  return currentParametersArray
227
- ?.map((r) => ({ ...coreParameters, ...r }))
224
+ ?.map((r) => ({ core, parameters: r }))
228
225
  ?? null;
229
226
  }
230
227
 
231
228
  const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, parametersToRefresh);
232
- return [{ ...coreParameters, ...currentParametersArray[0], ...statefulCurrentParameters }];
229
+ return [{ core, parameters: { ...currentParametersArray[0], ...statefulCurrentParameters } }];
233
230
  }
234
231
 
235
232
  private async applyCreate(plan: Plan<T>): Promise<void> {
@@ -308,7 +305,7 @@ ${JSON.stringify(refresh, null, 2)}
308
305
  }
309
306
  }
310
307
 
311
- private async applyTransformParameters(config: Partial<T> & ResourceConfig | null): Promise<void> {
308
+ private async applyTransformParameters(config: Partial<T> | null): Promise<void> {
312
309
  if (!config) {
313
310
  return;
314
311
  }
@@ -322,11 +319,9 @@ ${JSON.stringify(refresh, null, 2)}
322
319
  }
323
320
 
324
321
  if (this.settings.inputTransformation) {
325
- const { parameters, coreParameters } = splitUserConfig(config);
326
-
327
- const transformed = await this.settings.inputTransformation(parameters)
322
+ const transformed = await this.settings.inputTransformation({ ...config })
328
323
  Object.keys(config).forEach((k) => delete config[k])
329
- Object.assign(config, transformed, coreParameters);
324
+ Object.assign(config, transformed);
330
325
  }
331
326
  }
332
327
 
@@ -375,15 +370,20 @@ ${JSON.stringify(refresh, null, 2)}
375
370
  }
376
371
 
377
372
  private validatePlanInputs(
378
- desired: Partial<T> & ResourceConfig | null,
379
- current: Partial<T> & ResourceConfig | null,
380
- statefulMode: boolean,
373
+ core: ResourceConfig,
374
+ desired: Partial<T> | null,
375
+ current: Partial<T> | null,
376
+ isStateful: boolean,
381
377
  ) {
378
+ if (!core || !core.type) {
379
+ throw new Error('Core parameters type must be defined');
380
+ }
381
+
382
382
  if (!desired && !current) {
383
383
  throw new Error('Desired config and current config cannot both be missing')
384
384
  }
385
385
 
386
- if (!statefulMode && !desired) {
386
+ if (!isStateful && !desired) {
387
387
  throw new Error('Desired config must be provided in non-stateful mode')
388
388
  }
389
389
  }