codify-plugin-lib 1.0.52 → 1.0.54

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.
@@ -58,39 +58,40 @@ export class Plan {
58
58
  Object.entries(defaultValues)
59
59
  .forEach(([key, defaultValue]) => {
60
60
  const configValueExists = data
61
- ?.parameters
62
- .find((p) => p.name === key) !== undefined;
63
- if (!configValueExists) {
64
- switch (data?.operation) {
65
- case ResourceOperation.CREATE: {
66
- data?.parameters.push({
67
- name: key,
68
- operation: ParameterOperation.ADD,
69
- previousValue: null,
70
- newValue: defaultValue,
71
- });
72
- break;
73
- }
74
- case ResourceOperation.DESTROY: {
75
- data?.parameters.push({
76
- name: key,
77
- operation: ParameterOperation.REMOVE,
78
- previousValue: defaultValue,
79
- newValue: null,
80
- });
81
- break;
82
- }
83
- case ResourceOperation.MODIFY:
84
- case ResourceOperation.RECREATE:
85
- case ResourceOperation.NOOP: {
86
- data?.parameters.push({
87
- name: key,
88
- operation: ParameterOperation.NOOP,
89
- previousValue: defaultValue,
90
- newValue: defaultValue,
91
- });
92
- break;
93
- }
61
+ .parameters
62
+ .some((p) => p.name === key);
63
+ if (configValueExists) {
64
+ return;
65
+ }
66
+ switch (data.operation) {
67
+ case ResourceOperation.CREATE: {
68
+ data.parameters.push({
69
+ name: key,
70
+ operation: ParameterOperation.ADD,
71
+ previousValue: null,
72
+ newValue: defaultValue,
73
+ });
74
+ break;
75
+ }
76
+ case ResourceOperation.DESTROY: {
77
+ data.parameters.push({
78
+ name: key,
79
+ operation: ParameterOperation.REMOVE,
80
+ previousValue: defaultValue,
81
+ newValue: null,
82
+ });
83
+ break;
84
+ }
85
+ case ResourceOperation.MODIFY:
86
+ case ResourceOperation.RECREATE:
87
+ case ResourceOperation.NOOP: {
88
+ data.parameters.push({
89
+ name: key,
90
+ operation: ParameterOperation.NOOP,
91
+ previousValue: defaultValue,
92
+ newValue: defaultValue,
93
+ });
94
+ break;
94
95
  }
95
96
  }
96
97
  });
@@ -66,10 +66,10 @@ export class Plugin {
66
66
  return this.planStorage.get(planId);
67
67
  }
68
68
  if (!planRequest?.resourceType || !this.resources.has(planRequest.resourceType)) {
69
- throw new Error('Malformed plan. Resource type must be supplied');
69
+ throw new Error('Malformed plan. Resource type must be supplied or resource type was not found');
70
70
  }
71
71
  const resource = this.resources.get(planRequest.resourceType);
72
- return Plan.fromResponse(data.plan, resource?.defaultValues);
72
+ return Plan.fromResponse(data.plan, resource.defaultValues);
73
73
  }
74
74
  async crossValidateResources(configs) { }
75
75
  }
@@ -46,8 +46,8 @@ export class ResourceOptionsParser {
46
46
  return {};
47
47
  }
48
48
  return Object.fromEntries([...this.resourceParameters.entries()]
49
- .filter(([, rp]) => rp.defaultValue !== undefined)
50
- .map(([name, rp]) => [name, rp.defaultValue]));
49
+ .filter(([, rp]) => rp.default !== undefined)
50
+ .map(([name, rp]) => [name, rp.default]));
51
51
  }
52
52
  get statefulParameterOrder() {
53
53
  const entries = Object.entries(this.options.parameterOptions ?? {})
@@ -3,7 +3,7 @@ export type ErrorMessage = string;
3
3
  export interface ResourceParameterOptions {
4
4
  planOperation?: ResourceOperation.MODIFY | ResourceOperation.RECREATE;
5
5
  isEqual?: (desired: any, current: any) => boolean;
6
- defaultValue?: unknown;
6
+ default?: unknown;
7
7
  }
8
8
  export interface ResourceDefinition {
9
9
  [x: string]: {
@@ -29,6 +29,7 @@ export declare abstract class Resource<T extends StringIndexedObject> {
29
29
  private _applyModify;
30
30
  private _applyDestroy;
31
31
  private validateRefreshResults;
32
+ private applyDefaultValueToRefreshResults;
32
33
  private applyTransformParameters;
33
34
  private addDefaultValues;
34
35
  private refreshResourceParameters;
@@ -142,8 +142,10 @@ export class Resource {
142
142
  if (!refresh) {
143
143
  return;
144
144
  }
145
- const desiredKeys = new Set(desiredMap.keys());
146
- const refreshKeys = new Set(Object.keys(refresh));
145
+ const desiredKeys = new Set([...desiredMap.keys()]
146
+ .filter((key) => this.defaultValues[key] === undefined));
147
+ const refreshKeys = new Set([...Object.keys(refresh)]
148
+ .filter((key) => this.defaultValues[key] === undefined));
147
149
  if (!setsEqual(desiredKeys, refreshKeys)) {
148
150
  throw new Error(`Resource ${this.typeId}
149
151
  refresh() must return back exactly the keys that were provided
@@ -151,12 +153,22 @@ Missing: ${[...desiredKeys].filter((k) => !refreshKeys.has(k))};
151
153
  Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
152
154
  }
153
155
  }
156
+ applyDefaultValueToRefreshResults(refresh) {
157
+ if (!refresh) {
158
+ return;
159
+ }
160
+ for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
161
+ if (refresh[key] === undefined) {
162
+ refresh[key] = defaultValue;
163
+ }
164
+ }
165
+ }
154
166
  async applyTransformParameters(transformParameters, desired) {
155
167
  const orderedEntries = [...Object.entries(transformParameters)]
156
168
  .sort(([keyA], [keyB]) => this.transformParameterOrder.get(keyA) - this.transformParameterOrder.get(keyB));
157
- for (const [key, tp] of orderedEntries) {
169
+ for (const [key] of orderedEntries) {
158
170
  if (desired[key] !== null) {
159
- const transformedValue = await tp.transform(desired[key]);
171
+ const transformedValue = await this.transformParameters.get(key).transform(desired[key]);
160
172
  if (Object.keys(transformedValue).some((k) => desired[k] !== undefined)) {
161
173
  throw new Error(`Transform parameter ${key} is attempting to override existing value ${desired[key]}`);
162
174
  }
@@ -179,6 +191,7 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
179
191
  const entriesToRefresh = new Map(Object.entries(resourceParameters));
180
192
  const currentParameters = await this.refresh(entriesToRefresh);
181
193
  this.validateRefreshResults(currentParameters, entriesToRefresh);
194
+ this.applyDefaultValueToRefreshResults(currentParameters);
182
195
  return currentParameters;
183
196
  }
184
197
  async refreshStatefulParameters(statefulParametersConfig, isStatefulMode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -141,9 +141,7 @@ function createResource(): Resource<any> {
141
141
  super({
142
142
  type: 'type',
143
143
  parameterOptions: {
144
- propA: {
145
- defaultValue: 'defaultA'
146
- }
144
+ propA: { default: 'defaultA' }
147
145
  }
148
146
  });
149
147
  }
@@ -97,43 +97,46 @@ export class Plan<T extends StringIndexedObject> {
97
97
  function addDefaultValues(): void {
98
98
  Object.entries(defaultValues)
99
99
  .forEach(([key, defaultValue]) => {
100
- const configValueExists = data
101
- ?.parameters
102
- .find((p) => p.name === key) !== undefined;
103
-
104
- if (!configValueExists) {
105
- switch (data?.operation) {
106
- case ResourceOperation.CREATE: {
107
- data?.parameters.push({
108
- name: key,
109
- operation: ParameterOperation.ADD,
110
- previousValue: null,
111
- newValue: defaultValue,
112
- });
113
- break;
114
- }
115
-
116
- case ResourceOperation.DESTROY: {
117
- data?.parameters.push({
118
- name: key,
119
- operation: ParameterOperation.REMOVE,
120
- previousValue: defaultValue,
121
- newValue: null,
122
- });
123
- break;
124
- }
125
-
126
- case ResourceOperation.MODIFY:
127
- case ResourceOperation.RECREATE:
128
- case ResourceOperation.NOOP: {
129
- data?.parameters.push({
130
- name: key,
131
- operation: ParameterOperation.NOOP,
132
- previousValue: defaultValue,
133
- newValue: defaultValue,
134
- });
135
- break;
136
- }
100
+ const configValueExists = data!
101
+ .parameters
102
+ .some((p) => p.name === key);
103
+
104
+ // Only set default values if the value does not exist in the config
105
+ if (configValueExists) {
106
+ return;
107
+ }
108
+
109
+ switch (data!.operation) {
110
+ case ResourceOperation.CREATE: {
111
+ data!.parameters.push({
112
+ name: key,
113
+ operation: ParameterOperation.ADD,
114
+ previousValue: null,
115
+ newValue: defaultValue,
116
+ });
117
+ break;
118
+ }
119
+
120
+ case ResourceOperation.DESTROY: {
121
+ data!.parameters.push({
122
+ name: key,
123
+ operation: ParameterOperation.REMOVE,
124
+ previousValue: defaultValue,
125
+ newValue: null,
126
+ });
127
+ break;
128
+ }
129
+
130
+ case ResourceOperation.MODIFY:
131
+ case ResourceOperation.RECREATE:
132
+ case ResourceOperation.NOOP: {
133
+ data!.parameters.push({
134
+ name: key,
135
+ operation: ParameterOperation.NOOP,
136
+ previousValue: defaultValue,
137
+ newValue: defaultValue,
138
+ });
139
+ break;
137
140
  }
138
141
  }
139
142
  });
@@ -95,11 +95,11 @@ export class Plugin {
95
95
  }
96
96
 
97
97
  if (!planRequest?.resourceType || !this.resources.has(planRequest.resourceType)) {
98
- throw new Error('Malformed plan. Resource type must be supplied');
98
+ throw new Error('Malformed plan. Resource type must be supplied or resource type was not found');
99
99
  }
100
100
 
101
- const resource = this.resources.get(planRequest.resourceType);
102
- return Plan.fromResponse(data.plan, resource?.defaultValues!);
101
+ const resource = this.resources.get(planRequest.resourceType)!;
102
+ return Plan.fromResponse(data.plan, resource.defaultValues);
103
103
  }
104
104
 
105
105
  protected async crossValidateResources(configs: ResourceConfig[]): Promise<void> {}
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ResourceOptions, ResourceOptionsParser } from './resource-options.js';
3
+ import { TestConfig } from './resource.test.js';
4
+
5
+ describe('Resource options parser tests', () => {
6
+ it('Parses default values from options', () => {
7
+ const option: ResourceOptions<TestConfig> = {
8
+ type: 'typeId',
9
+ parameterOptions: {
10
+ propA: { default: 'propA' },
11
+ propB: { default: 'propB' },
12
+ propC: { isEqual: () => true },
13
+ propD: { },
14
+ }
15
+ }
16
+
17
+ const result = new ResourceOptionsParser(option);
18
+ expect(result.defaultValues).to.deep.eq({
19
+ propA: 'propA',
20
+ propB: 'propB'
21
+ })
22
+ })
23
+ })
@@ -113,8 +113,8 @@ export class ResourceOptionsParser<T extends StringIndexedObject> {
113
113
 
114
114
  return Object.fromEntries(
115
115
  [...this.resourceParameters.entries()]
116
- .filter(([, rp]) => rp.defaultValue !== undefined)
117
- .map(([name, rp]) => [name, rp.defaultValue])
116
+ .filter(([, rp]) => rp.default !== undefined)
117
+ .map(([name, rp]) => [name, rp.default])
118
118
  ) as Partial<Record<keyof T, unknown>>;
119
119
  }
120
120
 
@@ -19,7 +19,7 @@ export interface ResourceParameterOptions {
19
19
  /**
20
20
  * Default value for the parameter. If a value is not provided in the config, the library will use this value.
21
21
  */
22
- defaultValue?: unknown,
22
+ default?: unknown,
23
23
  }
24
24
 
25
25
  /**
@@ -296,7 +296,7 @@ describe('Resource tests', () => {
296
296
  super({
297
297
  type: 'type',
298
298
  parameterOptions: {
299
- propA: { defaultValue: 'propADefault' }
299
+ propA: { default: 'propADefault' }
300
300
  }
301
301
  });
302
302
  }
@@ -316,7 +316,28 @@ describe('Resource tests', () => {
316
316
  expect(plan.currentConfig.propA).to.eq('propAAfter');
317
317
  expect(plan.desiredConfig.propA).to.eq('propADefault');
318
318
  expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
319
+ })
320
+
321
+ it('Allows default values to be added to both desired and current', async () => {
322
+ const resource = new class extends TestResource {
323
+ constructor() {
324
+ super({
325
+ type: 'type',
326
+ parameterOptions: {
327
+ propE: { default: 'propEDefault' }
328
+ }
329
+ });
330
+ }
331
+
332
+ async refresh(): Promise<Partial<TestConfig> | null> {
333
+ return {};
334
+ }
335
+ }
319
336
 
337
+ const plan = await resource.plan({ type: 'resource'})
338
+ expect(plan.currentConfig.propE).to.eq('propEDefault');
339
+ expect(plan.desiredConfig.propE).to.eq('propEDefault');
340
+ expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
320
341
  })
321
342
 
322
343
  it('Allows default values to be added (ignore default value if already present)', async () => {
@@ -325,7 +346,7 @@ describe('Resource tests', () => {
325
346
  super({
326
347
  type: 'type',
327
348
  parameterOptions: {
328
- propA: { defaultValue: 'propADefault' }
349
+ propA: { default: 'propADefault' }
329
350
  }
330
351
  });
331
352
  }
@@ -346,4 +367,21 @@ describe('Resource tests', () => {
346
367
  expect(plan.desiredConfig.propA).to.eq('propA');
347
368
  expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
348
369
  });
370
+
371
+ it('Sets the default value properly on the resource', () => {
372
+ const resource = new class extends TestResource {
373
+ constructor() {
374
+ super({
375
+ type: 'type',
376
+ parameterOptions: {
377
+ propA: { default: 'propADefault' }
378
+ }
379
+ });
380
+ }
381
+ }
382
+
383
+ expect(resource.defaultValues).to.deep.eq({
384
+ propA: 'propADefault',
385
+ })
386
+ })
349
387
  });
@@ -211,8 +211,15 @@ export abstract class Resource<T extends StringIndexedObject> {
211
211
  return;
212
212
  }
213
213
 
214
- const desiredKeys = new Set<keyof T>(desiredMap.keys());
215
- const refreshKeys = new Set(Object.keys(refresh)) as Set<keyof T>;
214
+ const desiredKeys = new Set<keyof T>(
215
+ [...desiredMap.keys()]
216
+ .filter((key) => this.defaultValues[key] === undefined)
217
+ );
218
+
219
+ const refreshKeys = new Set(
220
+ [...Object.keys(refresh)]
221
+ .filter((key) => this.defaultValues[key] === undefined)
222
+ ) as Set<keyof T>;
216
223
 
217
224
  if (!setsEqual(desiredKeys, refreshKeys)) {
218
225
  throw new Error(
@@ -224,13 +231,26 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
224
231
  }
225
232
  }
226
233
 
234
+ private applyDefaultValueToRefreshResults(refresh: Partial<T> | null) {
235
+ if (!refresh) {
236
+ return;
237
+ }
238
+
239
+ for (const [key, defaultValue] of Object.entries(this.defaultValues)) {
240
+ if (refresh[key] === undefined) {
241
+ // @ts-ignore
242
+ refresh[key] = defaultValue;
243
+ }
244
+ }
245
+ }
246
+
227
247
  private async applyTransformParameters(transformParameters: Partial<T>, desired: Partial<T>): Promise<void> {
228
248
  const orderedEntries = [...Object.entries(transformParameters)]
229
249
  .sort(([keyA], [keyB]) => this.transformParameterOrder.get(keyA)! - this.transformParameterOrder.get(keyB)!)
230
250
 
231
- for (const [key, tp] of orderedEntries) {
251
+ for (const [key] of orderedEntries) {
232
252
  if (desired[key] !== null) {
233
- const transformedValue = await tp.transform(desired[key]);
253
+ const transformedValue = await this.transformParameters.get(key)!.transform(desired[key]);
234
254
 
235
255
  if (Object.keys(transformedValue).some((k) => desired[k] !== undefined)) {
236
256
  throw new Error(`Transform parameter ${key as string} is attempting to override existing value ${desired[key]}`);
@@ -261,6 +281,8 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
261
281
  const currentParameters = await this.refresh(entriesToRefresh);
262
282
 
263
283
  this.validateRefreshResults(currentParameters, entriesToRefresh);
284
+ this.applyDefaultValueToRefreshResults(currentParameters);
285
+
264
286
  return currentParameters;
265
287
  }
266
288