codify-plugin-lib 1.0.182-beta70 → 1.0.182-beta72

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.
package/dist/plan/plan.js CHANGED
@@ -187,7 +187,6 @@ export class Plan {
187
187
  * or wants to set. If a parameter is not specified then it's not managed by Codify.
188
188
  */
189
189
  static filterCurrentParams(params) {
190
- console.log('Filter current params', params.desired, params.current, params.state, params.settings, params.isStateful);
191
190
  const { desired, current, state, settings, isStateful } = params;
192
191
  if (!current) {
193
192
  return null;
@@ -196,19 +195,23 @@ export class Plan {
196
195
  if (!filteredCurrent) {
197
196
  return null;
198
197
  }
199
- console.log('Before exit', isStateful, desired);
200
198
  // For stateful mode, we're done after filtering by the keys of desired + state. Stateless mode
201
199
  // requires additional filtering for stateful parameter arrays and objects.
202
- // We also want to filter parameters when in delete mode. We don't want to delete parameters that
203
- // are not specified in the original config.
204
200
  if (isStateful && desired) {
205
201
  return filteredCurrent;
206
202
  }
207
- console.log('Post exit', isStateful, desired);
203
+ // We also want to filter parameters when in delete mode. We don't want to delete parameters that
204
+ // are not specified in the original config.
205
+ if (isStateful && !desired) {
206
+ const arrayStatefulParameters = Object.fromEntries(Object.entries(filteredCurrent)
207
+ .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
208
+ .map(([k, v]) => [k, filterArrayParameterForDeletes(k, v)]));
209
+ return { ...filteredCurrent, ...arrayStatefulParameters };
210
+ }
208
211
  // TODO: Add object handling here in addition to arrays in the future
209
212
  const arrayStatefulParameters = Object.fromEntries(Object.entries(filteredCurrent)
210
213
  .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
211
- .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)]));
214
+ .map(([k, v]) => [k, filterArrayParameterForStatelessMode(k, v)]));
212
215
  console.log('Result', { ...filteredCurrent, ...arrayStatefulParameters });
213
216
  return { ...filteredCurrent, ...arrayStatefulParameters };
214
217
  function filterCurrent() {
@@ -250,7 +253,8 @@ export class Plan {
250
253
  && Array.isArray(v);
251
254
  }
252
255
  // For stateless mode, we must filter the current array so that the diff algorithm will not detect any deletes
253
- function filterArrayStatefulParameter(k, v) {
256
+ function filterArrayParameterForStatelessMode(k, v) {
257
+ console.log('Attempting to filter stateless (key, value)', k, v);
254
258
  const desiredArray = desired[k];
255
259
  const matcher = settings.parameterSettings[k].type === 'stateful'
256
260
  ? settings.parameterSettings[k]
@@ -278,6 +282,35 @@ export class Plan {
278
282
  ? filterParameter(desiredCopy, currentCopy)
279
283
  : defaultFilterMethod(desiredCopy, currentCopy);
280
284
  }
285
+ function filterArrayParameterForDeletes(k, v) {
286
+ console.log('Attempting to filter deletes (key, value)', k, v);
287
+ const stateArray = state[k];
288
+ const matcher = settings.parameterSettings[k].type === 'stateful'
289
+ ? settings.parameterSettings[k]
290
+ .nestedSettings
291
+ .isElementEqual
292
+ : settings.parameterSettings[k]
293
+ .isElementEqual;
294
+ const stateCopy = [...stateArray];
295
+ const currentCopy = [...v];
296
+ const defaultFilterMethod = ((state, current) => {
297
+ const result = [];
298
+ for (let counter = state.length - 1; counter >= 0; counter--) {
299
+ const idx = currentCopy.findIndex((e2) => matcher(state[counter], e2));
300
+ if (idx === -1) {
301
+ continue;
302
+ }
303
+ state.splice(counter, 1);
304
+ const [element] = current.splice(idx, 1);
305
+ result.push(element);
306
+ }
307
+ return result;
308
+ });
309
+ const filterParameter = getFilterParameter(k);
310
+ return typeof filterParameter === 'function'
311
+ ? filterParameter(stateCopy, currentCopy)
312
+ : defaultFilterMethod(stateCopy, currentCopy);
313
+ }
281
314
  }
282
315
  // TODO: This needs to be revisited. I don't think this is valid anymore.
283
316
  // 1. For all scenarios, there shouldn't be an apply without a plan beforehand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.182-beta70",
3
+ "version": "1.0.182-beta72",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -180,6 +180,52 @@ describe('Plan entity tests', () => {
180
180
  expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
181
181
  })
182
182
 
183
+ it('Filters array parameters in delete mode (when desired is null)', async () => {
184
+ const resource = new class extends TestResource {
185
+ getSettings(): ResourceSettings<any> {
186
+ return {
187
+ id: 'type',
188
+ operatingSystems: [OS.Darwin],
189
+ parameterSettings: {
190
+ propZ: { type: 'array', isElementEqual: (a, b) => b.includes(a) }
191
+ }
192
+ }
193
+ }
194
+
195
+ async refresh(): Promise<Partial<any> | null> {
196
+ return {
197
+ propZ: [
198
+ '20.15.0',
199
+ '20.15.1'
200
+ ]
201
+ }
202
+ }
203
+ }
204
+
205
+ const controller = new ResourceController(resource);
206
+ const plan = await controller.plan(
207
+ { type: 'type' },
208
+ null,
209
+ { propZ: ['20.15.0'], } as any,
210
+ true
211
+ )
212
+
213
+ console.log(JSON.stringify(plan, null, 2));
214
+ expect(plan).toMatchObject({
215
+ id: expect.any(String),
216
+ changeSet: expect.objectContaining({
217
+ operation: ResourceOperation.DESTROY,
218
+ parameterChanges: [
219
+ expect.objectContaining({ operation: 'remove', name: 'propZ', previousValue: ['20.15.0'], newValue: null }),
220
+ ],
221
+ }),
222
+ coreParameters: expect.objectContaining({
223
+ type: 'type',
224
+ }),
225
+ isStateful: true,
226
+ })
227
+ })
228
+
183
229
  it('Doesn\'t filters array parameters if filtering is disabled', async () => {
184
230
  const resource = new class extends TestResource {
185
231
  getSettings(): ResourceSettings<any> {
package/src/plan/plan.ts CHANGED
@@ -307,8 +307,6 @@ export class Plan<T extends StringIndexedObject> {
307
307
  settings: ResourceSettings<T>,
308
308
  isStateful: boolean,
309
309
  }): Partial<T> | null {
310
- console.log('Filter current params', params.desired, params.current, params.state, params.settings, params.isStateful)
311
-
312
310
  const {
313
311
  desired,
314
312
  current,
@@ -326,23 +324,29 @@ export class Plan<T extends StringIndexedObject> {
326
324
  return null
327
325
  }
328
326
 
329
- console.log('Before exit', isStateful, desired);
330
-
331
327
  // For stateful mode, we're done after filtering by the keys of desired + state. Stateless mode
332
328
  // requires additional filtering for stateful parameter arrays and objects.
333
- // We also want to filter parameters when in delete mode. We don't want to delete parameters that
334
- // are not specified in the original config.
335
329
  if (isStateful && desired) {
336
330
  return filteredCurrent;
337
331
  }
338
332
 
339
- console.log('Post exit', isStateful, desired);
333
+ // We also want to filter parameters when in delete mode. We don't want to delete parameters that
334
+ // are not specified in the original config.
335
+ if (isStateful && !desired) {
336
+ const arrayStatefulParameters = Object.fromEntries(
337
+ Object.entries(filteredCurrent)
338
+ .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
339
+ .map(([k, v]) => [k, filterArrayParameterForDeletes(k, v)])
340
+ )
341
+
342
+ return { ...filteredCurrent, ...arrayStatefulParameters }
343
+ }
340
344
 
341
345
  // TODO: Add object handling here in addition to arrays in the future
342
346
  const arrayStatefulParameters = Object.fromEntries(
343
347
  Object.entries(filteredCurrent)
344
348
  .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
345
- .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)])
349
+ .map(([k, v]) => [k, filterArrayParameterForStatelessMode(k, v)])
346
350
  )
347
351
 
348
352
  console.log('Result', { ...filteredCurrent, ...arrayStatefulParameters });
@@ -388,7 +392,7 @@ export class Plan<T extends StringIndexedObject> {
388
392
 
389
393
  function isArrayParameterWithFiltering(k: string, v: T[keyof T]): boolean {
390
394
  const filterParameter = getFilterParameter(k);
391
-
395
+
392
396
  if (settings.parameterSettings?.[k]?.type === 'stateful') {
393
397
  const statefulSetting = settings.parameterSettings[k] as ParsedStatefulParameterSetting;
394
398
  return statefulSetting.nestedSettings.type === 'array' &&
@@ -402,7 +406,9 @@ export class Plan<T extends StringIndexedObject> {
402
406
  }
403
407
 
404
408
  // For stateless mode, we must filter the current array so that the diff algorithm will not detect any deletes
405
- function filterArrayStatefulParameter(k: string, v: unknown[]): unknown[] {
409
+ function filterArrayParameterForStatelessMode(k: string, v: unknown[]): unknown[] {
410
+ console.log('Attempting to filter stateless (key, value)', k, v);
411
+
406
412
  const desiredArray = desired![k] as unknown[];
407
413
  const matcher = settings.parameterSettings![k]!.type === 'stateful'
408
414
  ? ((settings.parameterSettings![k] as ParsedStatefulParameterSetting)
@@ -437,6 +443,45 @@ export class Plan<T extends StringIndexedObject> {
437
443
  ? filterParameter(desiredCopy, currentCopy)
438
444
  : defaultFilterMethod(desiredCopy, currentCopy);
439
445
  }
446
+
447
+ function filterArrayParameterForDeletes(k: string, v: unknown[]): unknown[] {
448
+ console.log('Attempting to filter deletes (key, value)', k, v);
449
+
450
+ const stateArray = state![k] as unknown[];
451
+ const matcher = settings.parameterSettings![k]!.type === 'stateful'
452
+ ? ((settings.parameterSettings![k] as ParsedStatefulParameterSetting)
453
+ .nestedSettings as ParsedArrayParameterSetting)
454
+ .isElementEqual
455
+ : (settings.parameterSettings![k] as ParsedArrayParameterSetting)
456
+ .isElementEqual
457
+
458
+ const stateCopy = [...stateArray];
459
+ const currentCopy = [...v];
460
+
461
+ const defaultFilterMethod = ((state: any[], current: any[]) => {
462
+ const result = [];
463
+
464
+ for (let counter = state.length - 1; counter >= 0; counter--) {
465
+ const idx = currentCopy.findIndex((e2) => matcher(state[counter], e2))
466
+
467
+ if (idx === -1) {
468
+ continue;
469
+ }
470
+
471
+ state.splice(counter, 1)
472
+ const [element] = current.splice(idx, 1)
473
+ result.push(element)
474
+ }
475
+
476
+ return result;
477
+ })
478
+
479
+
480
+ const filterParameter = getFilterParameter(k);
481
+ return typeof filterParameter === 'function'
482
+ ? filterParameter(stateCopy, currentCopy)
483
+ : defaultFilterMethod(stateCopy, currentCopy);
484
+ }
440
485
  }
441
486
 
442
487
  // TODO: This needs to be revisited. I don't think this is valid anymore.