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 +40 -7
- package/package.json +1 -1
- package/src/plan/plan.test.ts +46 -0
- package/src/plan/plan.ts +55 -10
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
|
-
|
|
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,
|
|
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
|
|
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
package/src/plan/plan.test.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
|
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.
|