codify-plugin-lib 1.0.182-beta70 → 1.0.182-beta71

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
@@ -199,16 +199,22 @@ export class Plan {
199
199
  console.log('Before exit', isStateful, desired);
200
200
  // For stateful mode, we're done after filtering by the keys of desired + state. Stateless mode
201
201
  // 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
202
  if (isStateful && desired) {
205
203
  return filteredCurrent;
206
204
  }
205
+ // We also want to filter parameters when in delete mode. We don't want to delete parameters that
206
+ // are not specified in the original config.
207
+ if (isStateful && !desired) {
208
+ const arrayStatefulParameters = Object.fromEntries(Object.entries(filteredCurrent)
209
+ .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
210
+ .map(([k, v]) => [k, filterArrayParameterForDeletes(k, v)]));
211
+ return { ...filteredCurrent, ...arrayStatefulParameters };
212
+ }
207
213
  console.log('Post exit', isStateful, desired);
208
214
  // TODO: Add object handling here in addition to arrays in the future
209
215
  const arrayStatefulParameters = Object.fromEntries(Object.entries(filteredCurrent)
210
216
  .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
211
- .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)]));
217
+ .map(([k, v]) => [k, filterArrayParameterForStatelessMode(k, v)]));
212
218
  console.log('Result', { ...filteredCurrent, ...arrayStatefulParameters });
213
219
  return { ...filteredCurrent, ...arrayStatefulParameters };
214
220
  function filterCurrent() {
@@ -250,7 +256,8 @@ export class Plan {
250
256
  && Array.isArray(v);
251
257
  }
252
258
  // For stateless mode, we must filter the current array so that the diff algorithm will not detect any deletes
253
- function filterArrayStatefulParameter(k, v) {
259
+ function filterArrayParameterForStatelessMode(k, v) {
260
+ console.log('Attempting to filter (key, value)', k, v);
254
261
  const desiredArray = desired[k];
255
262
  const matcher = settings.parameterSettings[k].type === 'stateful'
256
263
  ? settings.parameterSettings[k]
@@ -278,6 +285,35 @@ export class Plan {
278
285
  ? filterParameter(desiredCopy, currentCopy)
279
286
  : defaultFilterMethod(desiredCopy, currentCopy);
280
287
  }
288
+ function filterArrayParameterForDeletes(k, v) {
289
+ console.log('Attempting to filter (key, value)', k, v);
290
+ const stateArray = state[k];
291
+ const matcher = settings.parameterSettings[k].type === 'stateful'
292
+ ? settings.parameterSettings[k]
293
+ .nestedSettings
294
+ .isElementEqual
295
+ : settings.parameterSettings[k]
296
+ .isElementEqual;
297
+ const stateCopy = [...stateArray];
298
+ const currentCopy = [...v];
299
+ const defaultFilterMethod = ((state, current) => {
300
+ const result = [];
301
+ for (let counter = state.length - 1; counter >= 0; counter--) {
302
+ const idx = currentCopy.findIndex((e2) => matcher(state[counter], e2));
303
+ if (idx === -1) {
304
+ continue;
305
+ }
306
+ state.splice(counter, 1);
307
+ const [element] = current.splice(idx, 1);
308
+ result.push(element);
309
+ }
310
+ return result;
311
+ });
312
+ const filterParameter = getFilterParameter(k);
313
+ return typeof filterParameter === 'function'
314
+ ? filterParameter(stateCopy, currentCopy)
315
+ : defaultFilterMethod(stateCopy, currentCopy);
316
+ }
281
317
  }
282
318
  // TODO: This needs to be revisited. I don't think this is valid anymore.
283
319
  // 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-beta71",
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
@@ -330,19 +330,29 @@ export class Plan<T extends StringIndexedObject> {
330
330
 
331
331
  // For stateful mode, we're done after filtering by the keys of desired + state. Stateless mode
332
332
  // 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
333
  if (isStateful && desired) {
336
334
  return filteredCurrent;
337
335
  }
338
336
 
337
+ // We also want to filter parameters when in delete mode. We don't want to delete parameters that
338
+ // are not specified in the original config.
339
+ if (isStateful && !desired) {
340
+ const arrayStatefulParameters = Object.fromEntries(
341
+ Object.entries(filteredCurrent)
342
+ .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
343
+ .map(([k, v]) => [k, filterArrayParameterForDeletes(k, v)])
344
+ )
345
+
346
+ return { ...filteredCurrent, ...arrayStatefulParameters }
347
+ }
348
+
339
349
  console.log('Post exit', isStateful, desired);
340
350
 
341
351
  // TODO: Add object handling here in addition to arrays in the future
342
352
  const arrayStatefulParameters = Object.fromEntries(
343
353
  Object.entries(filteredCurrent)
344
354
  .filter(([k, v]) => isArrayParameterWithFiltering(k, v))
345
- .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)])
355
+ .map(([k, v]) => [k, filterArrayParameterForStatelessMode(k, v)])
346
356
  )
347
357
 
348
358
  console.log('Result', { ...filteredCurrent, ...arrayStatefulParameters });
@@ -388,7 +398,7 @@ export class Plan<T extends StringIndexedObject> {
388
398
 
389
399
  function isArrayParameterWithFiltering(k: string, v: T[keyof T]): boolean {
390
400
  const filterParameter = getFilterParameter(k);
391
-
401
+
392
402
  if (settings.parameterSettings?.[k]?.type === 'stateful') {
393
403
  const statefulSetting = settings.parameterSettings[k] as ParsedStatefulParameterSetting;
394
404
  return statefulSetting.nestedSettings.type === 'array' &&
@@ -402,7 +412,9 @@ export class Plan<T extends StringIndexedObject> {
402
412
  }
403
413
 
404
414
  // 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[] {
415
+ function filterArrayParameterForStatelessMode(k: string, v: unknown[]): unknown[] {
416
+ console.log('Attempting to filter (key, value)', k, v);
417
+
406
418
  const desiredArray = desired![k] as unknown[];
407
419
  const matcher = settings.parameterSettings![k]!.type === 'stateful'
408
420
  ? ((settings.parameterSettings![k] as ParsedStatefulParameterSetting)
@@ -437,6 +449,45 @@ export class Plan<T extends StringIndexedObject> {
437
449
  ? filterParameter(desiredCopy, currentCopy)
438
450
  : defaultFilterMethod(desiredCopy, currentCopy);
439
451
  }
452
+
453
+ function filterArrayParameterForDeletes(k: string, v: unknown[]): unknown[] {
454
+ console.log('Attempting to filter (key, value)', k, v);
455
+
456
+ const stateArray = state![k] as unknown[];
457
+ const matcher = settings.parameterSettings![k]!.type === 'stateful'
458
+ ? ((settings.parameterSettings![k] as ParsedStatefulParameterSetting)
459
+ .nestedSettings as ParsedArrayParameterSetting)
460
+ .isElementEqual
461
+ : (settings.parameterSettings![k] as ParsedArrayParameterSetting)
462
+ .isElementEqual
463
+
464
+ const stateCopy = [...stateArray];
465
+ const currentCopy = [...v];
466
+
467
+ const defaultFilterMethod = ((state: any[], current: any[]) => {
468
+ const result = [];
469
+
470
+ for (let counter = state.length - 1; counter >= 0; counter--) {
471
+ const idx = currentCopy.findIndex((e2) => matcher(state[counter], e2))
472
+
473
+ if (idx === -1) {
474
+ continue;
475
+ }
476
+
477
+ state.splice(counter, 1)
478
+ const [element] = current.splice(idx, 1)
479
+ result.push(element)
480
+ }
481
+
482
+ return result;
483
+ })
484
+
485
+
486
+ const filterParameter = getFilterParameter(k);
487
+ return typeof filterParameter === 'function'
488
+ ? filterParameter(stateCopy, currentCopy)
489
+ : defaultFilterMethod(stateCopy, currentCopy);
490
+ }
440
491
  }
441
492
 
442
493
  // TODO: This needs to be revisited. I don't think this is valid anymore.