codify-plugin-lib 1.0.80 → 1.0.81

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.
@@ -30,7 +30,7 @@ export declare class ChangeSet<T extends StringIndexedObject> {
30
30
  static empty<T extends StringIndexedObject>(): ChangeSet<T>;
31
31
  static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T>;
32
32
  static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T>;
33
- static calculateModification<T extends StringIndexedObject>(desired: Partial<T>, current: Partial<T>, parameterSettings?: Partial<Record<keyof T, ParameterSetting>>): Promise<ChangeSet<T>>;
33
+ static calculateModification<T extends StringIndexedObject>(desired: Partial<T>, current: Partial<T>, parameterSettings?: Partial<Record<keyof T, ParameterSetting>>): ChangeSet<T>;
34
34
  /**
35
35
  * Calculates the differences between the desired and current parameters,
36
36
  * and returns a list of parameter changes that describe what needs to be added,
@@ -45,8 +45,8 @@ export class ChangeSet {
45
45
  }));
46
46
  return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
47
47
  }
48
- static async calculateModification(desired, current, parameterSettings = {}) {
49
- const pc = await ChangeSet.calculateParameterChanges(desired, current, parameterSettings);
48
+ static calculateModification(desired, current, parameterSettings = {}) {
49
+ const pc = ChangeSet.calculateParameterChanges(desired, current, parameterSettings);
50
50
  const statefulParameterKeys = new Set(Object.entries(parameterSettings)
51
51
  .filter(([, v]) => v?.type === 'stateful')
52
52
  .map(([k]) => k));
@@ -77,7 +77,7 @@ export class ChangeSet {
77
77
  * @param {Partial<Record<keyof T, ParameterSetting>>} [parameterOptions] - Optional settings used when comparing parameters.
78
78
  * @return {ParameterChange<T>[]} A list of changes required to transition from the current state to the desired state.
79
79
  */
80
- static async calculateParameterChanges(desiredParameters, currentParameters, parameterOptions) {
80
+ static calculateParameterChanges(desiredParameters, currentParameters, parameterOptions) {
81
81
  const parameterChangeSet = new Array();
82
82
  // Filter out null and undefined values or else the diff below will not work
83
83
  const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => v !== null && v !== undefined));
@@ -93,7 +93,7 @@ export class ChangeSet {
93
93
  delete current[k];
94
94
  continue;
95
95
  }
96
- if (!await ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
96
+ if (!ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
97
97
  parameterChangeSet.push({
98
98
  name: k,
99
99
  previousValue: v ?? null,
@@ -138,12 +138,8 @@ export class ChangeSet {
138
138
  const indexNext = orderOfOperations.indexOf(next);
139
139
  return orderOfOperations[Math.max(indexPrev, indexNext)];
140
140
  }
141
- static async isSame(desired, current, setting) {
141
+ static isSame(desired, current, setting) {
142
142
  switch (setting?.type) {
143
- case 'stateful': {
144
- const statefulSetting = setting.definition.getSettings();
145
- return ChangeSet.isSame(desired, current, statefulSetting);
146
- }
147
143
  case 'array': {
148
144
  const arrayParameter = setting;
149
145
  return areArraysEqual(arrayParameter, desired, current);
@@ -41,7 +41,7 @@ export declare class Plan<T extends StringIndexedObject> {
41
41
  coreParameters: ResourceConfig;
42
42
  settings: ParsedResourceSettings<T>;
43
43
  statefulMode: boolean;
44
- }): Promise<Plan<T>>;
44
+ }): Plan<T>;
45
45
  /**
46
46
  * Only keep relevant params for the plan. We don't want to change settings that were not already
47
47
  * defined.
package/dist/plan/plan.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { ParameterOperation, ResourceOperation, } from 'codify-schemas';
2
2
  import { v4 as uuidV4 } from 'uuid';
3
- import { asyncFilter, asyncIncludes, asyncMap } from '../utils/utils.js';
4
3
  import { ChangeSet } from './change-set.js';
5
4
  /**
6
5
  * A plan represents a set of actions that after taken will turn the current resource into the desired one.
@@ -71,7 +70,7 @@ export class Plan {
71
70
  getResourceType() {
72
71
  return this.coreParameters.type;
73
72
  }
74
- static async calculate(params) {
73
+ static calculate(params) {
75
74
  const { desiredParameters, currentParametersArray, stateParameters, coreParameters, settings, statefulMode } = params;
76
75
  const currentParameters = Plan.matchCurrentParameters({
77
76
  desiredParameters,
@@ -80,7 +79,7 @@ export class Plan {
80
79
  settings,
81
80
  statefulMode
82
81
  });
83
- const filteredCurrentParameters = await Plan.filterCurrentParams({
82
+ const filteredCurrentParameters = Plan.filterCurrentParams({
84
83
  desiredParameters,
85
84
  currentParameters,
86
85
  stateParameters,
@@ -100,7 +99,7 @@ export class Plan {
100
99
  return new Plan(uuidV4(), ChangeSet.destroy(filteredCurrentParameters), coreParameters);
101
100
  }
102
101
  // NO-OP, MODIFY or RE-CREATE
103
- const changeSet = await ChangeSet.calculateModification(desiredParameters, filteredCurrentParameters, settings.parameterSettings);
102
+ const changeSet = ChangeSet.calculateModification(desiredParameters, filteredCurrentParameters, settings.parameterSettings);
104
103
  return new Plan(uuidV4(), changeSet, coreParameters);
105
104
  }
106
105
  /**
@@ -111,7 +110,7 @@ export class Plan {
111
110
  * 2. In stateful mode, filter current by state and desired. We only know about the settings the user has previously set
112
111
  * or wants to set. If a parameter is not specified then it's not managed by Codify.
113
112
  */
114
- static async filterCurrentParams(params) {
113
+ static filterCurrentParams(params) {
115
114
  const { desiredParameters: desired, currentParameters: current, stateParameters: state, settings, statefulMode } = params;
116
115
  if (!current) {
117
116
  return null;
@@ -126,8 +125,9 @@ export class Plan {
126
125
  return filteredCurrent;
127
126
  }
128
127
  // TODO: Add object handling here in addition to arrays in the future
129
- const arrayStatefulParameters = Object.fromEntries(await asyncMap(Object.entries(filteredCurrent)
130
- .filter(([k, v]) => isArrayStatefulParameter(k, v)), async ([k, v]) => [k, await filterArrayStatefulParameter(k, v)]));
128
+ const arrayStatefulParameters = Object.fromEntries(Object.entries(filteredCurrent)
129
+ .filter(([k, v]) => isArrayStatefulParameter(k, v))
130
+ .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)]));
131
131
  return { ...filteredCurrent, ...arrayStatefulParameters };
132
132
  function filterCurrent() {
133
133
  if (!current) {
@@ -148,14 +148,13 @@ export class Plan {
148
148
  && settings.parameterSettings[k].definition.getSettings().type === 'array'
149
149
  && Array.isArray(v);
150
150
  }
151
- async function filterArrayStatefulParameter(k, v) {
151
+ function filterArrayStatefulParameter(k, v) {
152
152
  const desiredArray = desired[k];
153
153
  const matcher = settings.parameterSettings[k]
154
154
  .definition
155
155
  .getSettings()
156
156
  .isElementEqual;
157
- const eq = matcher ?? ((a, b) => a === b);
158
- return asyncFilter(v, async (cv) => asyncIncludes(desiredArray, async (dv) => eq(dv, cv)));
157
+ return v.filter((cv) => desiredArray.find((dv) => (matcher ?? ((a, b) => a === b))(dv, cv)));
159
158
  }
160
159
  }
161
160
  // TODO: This needs to be revisited. I don't think this is valid anymore.
@@ -97,7 +97,7 @@ export interface DefaultParameterSetting {
97
97
  *
98
98
  * @return Return true if equal
99
99
  */
100
- isEqual?: (desired: any, current: any) => Promise<boolean> | boolean;
100
+ isEqual?: (desired: any, current: any) => boolean;
101
101
  /**
102
102
  * Chose if the resource can be modified instead of re-created when there is a change to this parameter.
103
103
  * Defaults to false (re-create).
@@ -123,7 +123,7 @@ export interface ArrayParameterSetting extends DefaultParameterSetting {
123
123
  *
124
124
  * @return Return true if desired is equivalent to current.
125
125
  */
126
- isElementEqual?: (desired: any, current: any) => Promise<boolean> | boolean;
126
+ isElementEqual?: (desired: any, current: any) => boolean;
127
127
  }
128
128
  /**
129
129
  * Stateful parameter type specific settings. A stateful parameter is a sub-resource that can hold its own
@@ -37,10 +37,5 @@ export declare function splitUserConfig<T extends StringIndexedObject>(config: R
37
37
  };
38
38
  export declare function setsEqual(set1: Set<unknown>, set2: Set<unknown>): boolean;
39
39
  export declare function untildify(pathWithTilde: string): string;
40
- export declare function areArraysEqual(parameter: ArrayParameterSetting, desired: unknown, current: unknown): Promise<boolean>;
41
- export declare function asyncFilter<T>(arr: T[], filter: (a: T) => Promise<boolean> | boolean): Promise<T[]>;
42
- export declare function asyncMap<T, R>(arr: T[], map: (a: T) => Promise<R> | R): Promise<R[]>;
43
- export declare function asyncFindIndex<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<number>;
44
- export declare function asyncFind<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<T | undefined>;
45
- export declare function asyncIncludes<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<boolean>;
40
+ export declare function areArraysEqual(parameter: ArrayParameterSetting, desired: unknown, current: unknown): boolean;
46
41
  export {};
@@ -72,7 +72,7 @@ const homeDirectory = os.homedir();
72
72
  export function untildify(pathWithTilde) {
73
73
  return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
74
74
  }
75
- export async function areArraysEqual(parameter, desired, current) {
75
+ export function areArraysEqual(parameter, desired, current) {
76
76
  if (!Array.isArray(desired) || !Array.isArray(current)) {
77
77
  throw new Error(`A non-array value:
78
78
 
@@ -88,11 +88,10 @@ Was provided even though type array was specified.
88
88
  }
89
89
  const desiredCopy = [...desired];
90
90
  const currentCopy = [...current];
91
- const eq = parameter.isElementEqual ?? ((a, b) => a === b);
92
91
  // Algorithm for to check equality between two un-ordered; un-hashable arrays using
93
92
  // an isElementEqual method. Time: O(n^2)
94
93
  for (let counter = desiredCopy.length - 1; counter >= 0; counter--) {
95
- const idx = await asyncFindIndex(currentCopy, (e2) => eq(desiredCopy[counter], e2));
94
+ const idx = currentCopy.findIndex((e2) => (parameter.isElementEqual ?? ((a, b) => a === b))(desiredCopy[counter], e2));
96
95
  if (idx === -1) {
97
96
  return false;
98
97
  }
@@ -101,43 +100,3 @@ Was provided even though type array was specified.
101
100
  }
102
101
  return currentCopy.length === 0;
103
102
  }
104
- export async function asyncFilter(arr, filter) {
105
- const result = [];
106
- for (const element of arr) {
107
- if (await filter(element)) {
108
- result.push(element);
109
- }
110
- }
111
- return result;
112
- }
113
- export async function asyncMap(arr, map) {
114
- const result = [];
115
- for (const element of arr) {
116
- result.push(await map(element));
117
- }
118
- return result;
119
- }
120
- export async function asyncFindIndex(arr, eq) {
121
- for (const [counter, element] of arr.entries()) {
122
- if (await eq(element)) {
123
- return counter;
124
- }
125
- }
126
- return -1;
127
- }
128
- export async function asyncFind(arr, eq) {
129
- for (const element of arr) {
130
- if (await eq(element)) {
131
- return element;
132
- }
133
- }
134
- return undefined;
135
- }
136
- export async function asyncIncludes(arr, eq) {
137
- for (const element of arr) {
138
- if (await eq(element)) {
139
- return true;
140
- }
141
- }
142
- return false;
143
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.80",
3
+ "version": "1.0.81",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -2,8 +2,8 @@ import { ChangeSet } from './change-set.js';
2
2
  import { ParameterOperation, ResourceOperation } from 'codify-schemas';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
- describe('Change set tests', async () => {
6
- it ('Correctly diffs two resource configs (modify)', async () => {
5
+ describe('Change set tests', () => {
6
+ it ('Correctly diffs two resource configs (modify)', () => {
7
7
  const after = {
8
8
  propA: 'before',
9
9
  propB: 'before'
@@ -14,14 +14,14 @@ describe('Change set tests', async () => {
14
14
  propB: 'after'
15
15
  }
16
16
 
17
- const cs = await ChangeSet.calculateModification(after, before);
17
+ const cs = ChangeSet.calculateModification(after, before);
18
18
  expect(cs.parameterChanges.length).to.eq(2);
19
19
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
20
20
  expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.MODIFY);
21
21
  expect(cs.operation).to.eq(ResourceOperation.RECREATE)
22
22
  })
23
23
 
24
- it ('Correctly diffs two resource configs (add)', async () => {
24
+ it ('Correctly diffs two resource configs (add)', () => {
25
25
  const after = {
26
26
  propA: 'before',
27
27
  propB: 'after'
@@ -31,7 +31,7 @@ describe('Change set tests', async () => {
31
31
  propA: 'after',
32
32
  }
33
33
 
34
- const cs = await ChangeSet.calculateModification(after, before,);
34
+ const cs = ChangeSet.calculateModification(after, before,);
35
35
  expect(cs.parameterChanges.length).to.eq(2);
36
36
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
37
37
  expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.ADD);
@@ -39,7 +39,7 @@ describe('Change set tests', async () => {
39
39
 
40
40
  })
41
41
 
42
- it ('Correctly diffs two resource configs (remove)', async () => {
42
+ it ('Correctly diffs two resource configs (remove)', () => {
43
43
  const after = {
44
44
  propA: 'after',
45
45
  }
@@ -49,14 +49,14 @@ describe('Change set tests', async () => {
49
49
  propB: 'before'
50
50
  }
51
51
 
52
- const cs = await ChangeSet.calculateModification(after, before);
52
+ const cs = ChangeSet.calculateModification(after, before);
53
53
  expect(cs.parameterChanges.length).to.eq(2);
54
54
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
55
55
  expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
56
56
  expect(cs.operation).to.eq(ResourceOperation.RECREATE)
57
57
  })
58
58
 
59
- it ('Correctly diffs two resource configs (no-op)', async () => {
59
+ it ('Correctly diffs two resource configs (no-op)', () => {
60
60
  const after = {
61
61
  propA: 'prop',
62
62
  }
@@ -65,7 +65,7 @@ describe('Change set tests', async () => {
65
65
  propA: 'prop',
66
66
  }
67
67
 
68
- const cs = await ChangeSet.calculateModification(after, before);
68
+ const cs = ChangeSet.calculateModification(after, before);
69
69
  expect(cs.parameterChanges.length).to.eq(1);
70
70
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
71
71
  expect(cs.operation).to.eq(ResourceOperation.NOOP)
@@ -95,7 +95,7 @@ describe('Change set tests', async () => {
95
95
  expect(cs.operation).to.eq(ResourceOperation.DESTROY)
96
96
  })
97
97
 
98
- it ('handles simple arrays', async () => {
98
+ it ('handles simple arrays', () => {
99
99
  const before = {
100
100
  propA: ['a', 'b', 'c'],
101
101
  }
@@ -104,13 +104,13 @@ describe('Change set tests', async () => {
104
104
  propA: ['b', 'a', 'c'],
105
105
  }
106
106
 
107
- const cs = await ChangeSet.calculateModification(after, before, { propA: { type: 'array' } });
107
+ const cs = ChangeSet.calculateModification(after, before, { propA: { type: 'array' } });
108
108
  expect(cs.parameterChanges.length).to.eq(1);
109
109
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
110
110
  expect(cs.operation).to.eq(ResourceOperation.NOOP)
111
111
  })
112
112
 
113
- it('handles simple arrays 2', async () => {
113
+ it('handles simple arrays 2', () => {
114
114
  const after = {
115
115
  propA: ['a', 'b', 'c'],
116
116
  }
@@ -119,13 +119,13 @@ describe('Change set tests', async () => {
119
119
  propA: ['b', 'a'],
120
120
  }
121
121
 
122
- const cs = await ChangeSet.calculateModification(after, before, { propA: { type: 'array' } });
122
+ const cs = ChangeSet.calculateModification(after, before, { propA: { type: 'array' } });
123
123
  expect(cs.parameterChanges.length).to.eq(1);
124
124
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
125
125
  expect(cs.operation).to.eq(ResourceOperation.RECREATE)
126
126
  })
127
127
 
128
- it('determines the order of operations with canModify 1', async () => {
128
+ it('determines the order of operations with canModify 1', () => {
129
129
  const after = {
130
130
  propA: 'after',
131
131
  }
@@ -135,14 +135,14 @@ describe('Change set tests', async () => {
135
135
  propB: 'before'
136
136
  }
137
137
 
138
- const cs = await ChangeSet.calculateModification(after, before, { propA: { canModify: true } });
138
+ const cs = ChangeSet.calculateModification(after, before, { propA: { canModify: true } });
139
139
  expect(cs.parameterChanges.length).to.eq(2);
140
140
  expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
141
141
  expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
142
142
  expect(cs.operation).to.eq(ResourceOperation.RECREATE)
143
143
  })
144
144
 
145
- it('determines the order of operations with canModify 2', async () => {
145
+ it('determines the order of operations with canModify 2', () => {
146
146
  const after = {
147
147
  propA: 'after',
148
148
  }
@@ -152,7 +152,7 @@ describe('Change set tests', async () => {
152
152
  propB: 'before'
153
153
  }
154
154
 
155
- const cs = await ChangeSet.calculateModification<any>(after, before, {
155
+ const cs = ChangeSet.calculateModification<any>(after, before, {
156
156
  propA: { canModify: true },
157
157
  propB: { canModify: true }
158
158
  });
@@ -163,38 +163,38 @@ describe('Change set tests', async () => {
163
163
  })
164
164
 
165
165
 
166
- it('correctly determines array equality', async () => {
166
+ it('correctly determines array equality', () => {
167
167
  const arrA = ['a', 'b', 'd'];
168
168
  const arrB = ['a', 'b', 'd'];
169
169
 
170
- const result = await ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
170
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
171
171
 
172
172
  expect(result.operation).to.eq(ResourceOperation.NOOP);
173
173
  })
174
174
 
175
- it('correctly determines array equality 2', async () => {
175
+ it('correctly determines array equality 2', () => {
176
176
  const arrA = ['a', 'b'];
177
177
  const arrB = ['a', 'b', 'd'];
178
178
 
179
- const result = await ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
179
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
180
180
 
181
181
  expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
182
182
  })
183
183
 
184
- it('correctly determines array equality 3', async () => {
184
+ it('correctly determines array equality 3', () => {
185
185
  const arrA = ['b', 'a', 'd'];
186
186
  const arrB = ['a', 'b', 'd'];
187
187
 
188
- const result = await ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
188
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, { propA: { type: 'array' } })
189
189
 
190
190
  expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
191
191
  })
192
192
 
193
- it('correctly determines array equality 4', async () => {
193
+ it('correctly determines array equality 4', () => {
194
194
  const arrA = [{ key1: 'a' }, { key1: 'a' }, { key1: 'a' }];
195
195
  const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
196
196
 
197
- const result = await ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, {
197
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, {
198
198
  propA: {
199
199
  type: 'array',
200
200
  isElementEqual: (a, b) => a.key1 === b.key1
@@ -204,11 +204,11 @@ describe('Change set tests', async () => {
204
204
  expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
205
205
  })
206
206
 
207
- it('correctly determines array equality 5', async () => {
207
+ it('correctly determines array equality 5', () => {
208
208
  const arrA = [{ key1: 'b' }, { key1: 'a' }, { key1: 'a' }];
209
209
  const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
210
210
 
211
- const result = await ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, {
211
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, {
212
212
  propA: {
213
213
  type: 'array',
214
214
  isElementEqual: (a, b) => a.key1 === b.key1
@@ -85,12 +85,12 @@ export class ChangeSet<T extends StringIndexedObject> {
85
85
  return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
86
86
  }
87
87
 
88
- static async calculateModification<T extends StringIndexedObject>(
88
+ static calculateModification<T extends StringIndexedObject>(
89
89
  desired: Partial<T>,
90
90
  current: Partial<T>,
91
91
  parameterSettings: Partial<Record<keyof T, ParameterSetting>> = {},
92
- ): Promise<ChangeSet<T>> {
93
- const pc = await ChangeSet.calculateParameterChanges(desired, current, parameterSettings);
92
+ ): ChangeSet<T> {
93
+ const pc = ChangeSet.calculateParameterChanges(desired, current, parameterSettings);
94
94
 
95
95
  const statefulParameterKeys = new Set(
96
96
  Object.entries(parameterSettings)
@@ -126,11 +126,11 @@ export class ChangeSet<T extends StringIndexedObject> {
126
126
  * @param {Partial<Record<keyof T, ParameterSetting>>} [parameterOptions] - Optional settings used when comparing parameters.
127
127
  * @return {ParameterChange<T>[]} A list of changes required to transition from the current state to the desired state.
128
128
  */
129
- private static async calculateParameterChanges<T extends StringIndexedObject>(
129
+ private static calculateParameterChanges<T extends StringIndexedObject>(
130
130
  desiredParameters: Partial<T>,
131
131
  currentParameters: Partial<T>,
132
132
  parameterOptions?: Partial<Record<keyof T, ParameterSetting>>,
133
- ): Promise<ParameterChange<T>[]> {
133
+ ): ParameterChange<T>[] {
134
134
  const parameterChangeSet = new Array<ParameterChange<T>>();
135
135
 
136
136
  // Filter out null and undefined values or else the diff below will not work
@@ -155,7 +155,7 @@ export class ChangeSet<T extends StringIndexedObject> {
155
155
  continue;
156
156
  }
157
157
 
158
- if (!await ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
158
+ if (!ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
159
159
  parameterChangeSet.push({
160
160
  name: k,
161
161
  previousValue: v ?? null,
@@ -210,18 +210,12 @@ export class ChangeSet<T extends StringIndexedObject> {
210
210
  return orderOfOperations[Math.max(indexPrev, indexNext)];
211
211
  }
212
212
 
213
- private static async isSame(
213
+ private static isSame(
214
214
  desired: unknown,
215
215
  current: unknown,
216
216
  setting?: ParameterSetting,
217
- ): Promise<boolean> {
217
+ ): boolean {
218
218
  switch (setting?.type) {
219
- case 'stateful': {
220
- const statefulSetting = (setting as StatefulParameterSetting).definition.getSettings()
221
-
222
- return ChangeSet.isSame(desired, current, statefulSetting as ParameterSetting);
223
- }
224
-
225
219
  case 'array': {
226
220
  const arrayParameter = setting as ArrayParameterSetting;
227
221
  return areArraysEqual(arrayParameter, desired, current)
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { Plan } from './plan.js';
3
3
  import { ParameterOperation, ResourceOperation } from 'codify-schemas';
4
- import { TestConfig, testPlan, TestResource } from '../utils/test-utils.test.js';
4
+ import { TestConfig, TestResource } from '../utils/test-utils.test.js';
5
5
  import { ResourceController } from '../resource/resource-controller.js';
6
6
  import { ParsedResourceSettings } from '../resource/parsed-resource-settings.js';
7
7
  import { ResourceSettings } from '../resource/resource-settings.js';
@@ -128,8 +128,8 @@ describe('Plan entity tests', () => {
128
128
  ).to.be.true;
129
129
  })
130
130
 
131
- it('Returns the original resource names', async () => {
132
- const plan = await Plan.calculate<TestConfig>({
131
+ it('Returns the original resource names', () => {
132
+ const plan = Plan.calculate<TestConfig>({
133
133
  desiredParameters: { propA: 'propA' },
134
134
  currentParametersArray: [{ propA: 'propA2' }],
135
135
  stateParameters: null,
@@ -147,8 +147,7 @@ describe('Plan entity tests', () => {
147
147
  operation: ResourceOperation.RECREATE
148
148
  })
149
149
  })
150
- });
151
-
150
+ })
152
151
 
153
152
  function createTestResource() {
154
153
  return new class extends TestResource {
package/src/plan/plan.ts CHANGED
@@ -10,7 +10,6 @@ import { v4 as uuidV4 } from 'uuid';
10
10
 
11
11
  import { ParsedResourceSettings } from '../resource/parsed-resource-settings.js';
12
12
  import { ArrayParameterSetting, ResourceSettings, StatefulParameterSetting } from '../resource/resource-settings.js';
13
- import { asyncFilter, asyncIncludes, asyncMap } from '../utils/utils.js';
14
13
  import { ChangeSet } from './change-set.js';
15
14
 
16
15
  /**
@@ -106,14 +105,14 @@ export class Plan<T extends StringIndexedObject> {
106
105
  return this.coreParameters.type
107
106
  }
108
107
 
109
- static async calculate<T extends StringIndexedObject>(params: {
108
+ static calculate<T extends StringIndexedObject>(params: {
110
109
  desiredParameters: Partial<T> | null,
111
110
  currentParametersArray: Partial<T>[] | null,
112
111
  stateParameters: Partial<T> | null,
113
112
  coreParameters: ResourceConfig,
114
113
  settings: ParsedResourceSettings<T>,
115
114
  statefulMode: boolean,
116
- }): Promise<Plan<T>> {
115
+ }): Plan<T> {
117
116
  const {
118
117
  desiredParameters,
119
118
  currentParametersArray,
@@ -131,7 +130,7 @@ export class Plan<T extends StringIndexedObject> {
131
130
  statefulMode
132
131
  });
133
132
 
134
- const filteredCurrentParameters = await Plan.filterCurrentParams<T>({
133
+ const filteredCurrentParameters = Plan.filterCurrentParams<T>({
135
134
  desiredParameters,
136
135
  currentParameters,
137
136
  stateParameters,
@@ -167,7 +166,7 @@ export class Plan<T extends StringIndexedObject> {
167
166
  }
168
167
 
169
168
  // NO-OP, MODIFY or RE-CREATE
170
- const changeSet = await ChangeSet.calculateModification(
169
+ const changeSet = ChangeSet.calculateModification(
171
170
  desiredParameters!,
172
171
  filteredCurrentParameters!,
173
172
  settings.parameterSettings,
@@ -188,13 +187,13 @@ export class Plan<T extends StringIndexedObject> {
188
187
  * 2. In stateful mode, filter current by state and desired. We only know about the settings the user has previously set
189
188
  * or wants to set. If a parameter is not specified then it's not managed by Codify.
190
189
  */
191
- private static async filterCurrentParams<T extends StringIndexedObject>(params: {
190
+ private static filterCurrentParams<T extends StringIndexedObject>(params: {
192
191
  desiredParameters: Partial<T> | null,
193
192
  currentParameters: Partial<T> | null,
194
193
  stateParameters: Partial<T> | null,
195
194
  settings: ResourceSettings<T>,
196
195
  statefulMode: boolean,
197
- }): Promise<Partial<T> | null> {
196
+ }): Partial<T> | null {
198
197
  const {
199
198
  desiredParameters: desired,
200
199
  currentParameters: current,
@@ -220,12 +219,10 @@ export class Plan<T extends StringIndexedObject> {
220
219
 
221
220
  // TODO: Add object handling here in addition to arrays in the future
222
221
  const arrayStatefulParameters = Object.fromEntries(
223
- await asyncMap(
224
- Object.entries(filteredCurrent)
225
- .filter(([k, v]) => isArrayStatefulParameter(k, v)),
226
- async ([k, v]) => [k, await filterArrayStatefulParameter(k, v)],
227
- )
228
- );
222
+ Object.entries(filteredCurrent)
223
+ .filter(([k, v]) => isArrayStatefulParameter(k, v))
224
+ .map(([k, v]) => [k, filterArrayStatefulParameter(k, v)])
225
+ )
229
226
 
230
227
  return { ...filteredCurrent, ...arrayStatefulParameters }
231
228
 
@@ -256,17 +253,15 @@ export class Plan<T extends StringIndexedObject> {
256
253
  && Array.isArray(v)
257
254
  }
258
255
 
259
- async function filterArrayStatefulParameter(k: string, v: unknown[]): Promise<unknown[]> {
256
+ function filterArrayStatefulParameter(k: string, v: unknown[]): unknown[] {
260
257
  const desiredArray = desired![k] as unknown[];
261
258
  const matcher = ((settings.parameterSettings![k] as StatefulParameterSetting)
262
259
  .definition
263
260
  .getSettings() as ArrayParameterSetting)
264
261
  .isElementEqual;
265
262
 
266
- const eq = matcher ?? ((a: unknown, b: unknown) => a === b)
267
-
268
- return asyncFilter(v, async (cv) =>
269
- asyncIncludes(desiredArray, async (dv) => eq(dv, cv))
263
+ return v.filter((cv) =>
264
+ desiredArray.find((dv) => (matcher ?? ((a: any, b: any) => a === b))(dv, cv))
270
265
  )
271
266
  }
272
267
  }
@@ -154,7 +154,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
154
154
  // The rest of the types have defaults set already
155
155
  }
156
156
 
157
- private resolveEqualsFn(parameter: ParameterSetting, key: string): (desired: unknown, current: unknown) => Promise<boolean> | boolean {
157
+ private resolveEqualsFn(parameter: ParameterSetting, key: string): (desired: unknown, current: unknown) => boolean {
158
158
  if (parameter.type === 'array') {
159
159
  return parameter.isEqual ?? areArraysEqual.bind(areArraysEqual, parameter as ArrayParameterSetting)
160
160
  }
@@ -99,7 +99,7 @@ describe('Resource tests', () => {
99
99
  const resourceSpy = spy(resource);
100
100
 
101
101
  await controllerSpy.apply(
102
- await testPlan({
102
+ testPlan({
103
103
  desired: { propA: 'a', propB: 0 },
104
104
  })
105
105
  )
@@ -116,7 +116,7 @@ describe('Resource tests', () => {
116
116
  const resourceSpy = spy(resource);
117
117
 
118
118
  await controllerSpy.apply(
119
- await testPlan({
119
+ testPlan({
120
120
  current: [{ propA: 'a', propB: 0 }],
121
121
  state: { propA: 'a', propB: 0 },
122
122
  statefulMode: true,
@@ -135,7 +135,7 @@ describe('Resource tests', () => {
135
135
  const resourceSpy = spy(resource);
136
136
 
137
137
  await controllerSpy.apply(
138
- await testPlan({
138
+ testPlan({
139
139
  desired: { propA: 'a', propB: 0 },
140
140
  current: [{ propA: 'b', propB: -1 }],
141
141
  statefulMode: true
@@ -77,7 +77,7 @@ describe('Resource parameter tests', () => {
77
77
  const resourceSpy = spy(resource);
78
78
 
79
79
  await controller.apply(
80
- await testPlan<TestConfig>({
80
+ testPlan<TestConfig>({
81
81
  desired: { propA: 'a', propB: 0, propC: 'c' }
82
82
  })
83
83
  );
@@ -491,35 +491,4 @@ describe('Resource parameter tests', () => {
491
491
 
492
492
  expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
493
493
  })
494
-
495
- it('Works with async equals methods', async () => {
496
- const resource = new class extends TestResource {
497
- getSettings(): ResourceSettings<TestConfig> {
498
- return {
499
- id: 'type',
500
- parameterSettings: {
501
- propA: {
502
- isEqual: async (desired, current) => {
503
- console.log(desired, current)
504
- await sleep(500);
505
- return true;
506
- }
507
- }
508
- }
509
- }
510
- }
511
- };
512
- const controller = new ResourceController(resource);
513
-
514
- const plan = await controller.plan({ type: 'type', propA: 'abc' } as any);
515
-
516
- console.log(JSON.stringify(plan, null, 2));
517
-
518
- expect(plan.toResponse().operation).to.equal(ResourceOperation.NOOP);
519
- })
520
494
  })
521
-
522
- function sleep(ms: number) {
523
- return new Promise(resolve => setTimeout(resolve, ms));
524
- }
525
-
@@ -127,7 +127,7 @@ export interface DefaultParameterSetting {
127
127
  *
128
128
  * @return Return true if equal
129
129
  */
130
- isEqual?: (desired: any, current: any) => Promise<boolean> | boolean;
130
+ isEqual?: (desired: any, current: any) => boolean;
131
131
 
132
132
  /**
133
133
  * Chose if the resource can be modified instead of re-created when there is a change to this parameter.
@@ -156,7 +156,7 @@ export interface ArrayParameterSetting extends DefaultParameterSetting {
156
156
  *
157
157
  * @return Return true if desired is equivalent to current.
158
158
  */
159
- isElementEqual?: (desired: any, current: any) => Promise<boolean> | boolean;
159
+ isElementEqual?: (desired: any, current: any) => boolean
160
160
  }
161
161
 
162
162
  /**
@@ -1,13 +1,12 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { spy } from 'sinon';
3
3
  import { ParameterOperation, ResourceOperation } from 'codify-schemas';
4
- import { TestArrayStatefulParameter, TestConfig, testPlan, TestResource } from '../utils/test-utils.test.js';
5
- import { ArrayParameterSetting, ResourceSettings } from './resource-settings.js';
6
- import { ResourceController } from './resource-controller.js';
4
+ import { TestArrayStatefulParameter, TestConfig, testPlan, TestStatefulParameter } from '../utils/test-utils.test.js';
5
+ import { ArrayParameterSetting, ParameterSetting } from './resource-settings.js';
7
6
 
8
7
  describe('Stateful parameter tests', () => {
9
8
  it('addItem is called the correct number of times', async () => {
10
- const plan = await testPlan<TestConfig>({
9
+ const plan = testPlan<TestConfig>({
11
10
  desired: { propZ: ['a', 'b', 'c'] },
12
11
  });
13
12
 
@@ -22,7 +21,7 @@ describe('Stateful parameter tests', () => {
22
21
  })
23
22
 
24
23
  it('applyRemoveItem is called the correct number of times', async () => {
25
- const plan = await testPlan<TestConfig>({
24
+ const plan = testPlan<TestConfig>({
26
25
  desired: null,
27
26
  current: [{ propZ: ['a', 'b', 'c'] }],
28
27
  state: { propZ: ['a', 'b', 'c'] },
@@ -41,7 +40,7 @@ describe('Stateful parameter tests', () => {
41
40
 
42
41
  it('In stateless mode only applyAddItem is called only for modifies', async () => {
43
42
  const parameter = new TestArrayStatefulParameter()
44
- const plan = await testPlan<TestConfig>({
43
+ const plan = testPlan<TestConfig>({
45
44
  desired: { propZ: ['a', 'c', 'd', 'e', 'f'] }, // b to remove, d, e, f to add
46
45
  current: [{ propZ: ['a', 'b', 'c'] }],
47
46
  settings: { id: 'type', parameterSettings: { propZ: { type: 'stateful', definition: parameter } } },
@@ -72,7 +71,7 @@ describe('Stateful parameter tests', () => {
72
71
  }
73
72
  });
74
73
 
75
- const plan = await testPlan<TestConfig>({
74
+ const plan = testPlan<TestConfig>({
76
75
  desired: { propZ: ['9.12', '9.13'] }, // b to remove, d, e, f to add
77
76
  current: [{ propZ: ['9.12.9'] }],
78
77
  settings: { id: 'type', parameterSettings: { propZ: { type: 'stateful', definition: testParameter } } }
@@ -92,31 +91,21 @@ describe('Stateful parameter tests', () => {
92
91
  expect(testParameter.removeItem.called).to.be.false;
93
92
  })
94
93
 
95
- it('isElementEqual works being async', async () => {
96
- const testParameter = spy(new class extends TestArrayStatefulParameter {
97
- getSettings(): ArrayParameterSetting {
94
+ it('isEqual works with type defaults', () => {
95
+ const testParameter = spy(new class extends TestStatefulParameter {
96
+ getSettings(): ParameterSetting {
98
97
  return {
99
- type: 'array',
100
- isElementEqual: async (desired, current) => {
101
- console.log(desired, current)
102
- await sleep(50);
103
- return true;
104
- }
98
+ type: 'version',
105
99
  }
106
100
  }
107
101
  });
108
102
 
109
- const plan = await testPlan<TestConfig>({
110
- desired: { propZ: ['9.12'] }, // b to remove, d, e, f to add
111
- current: [{ propZ: ['23472934'] }], // purposely make these two values very different since isElementEqual always returns true
103
+ const plan = testPlan<TestConfig>({
104
+ desired: { propZ: '20' },
105
+ current: [{ propZ: '20.17.0' }],
112
106
  settings: { id: 'type', parameterSettings: { propZ: { type: 'stateful', definition: testParameter } } }
113
107
  });
114
108
 
115
- expect(plan.toResponse().operation).to.equal(ResourceOperation.NOOP);
109
+ expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
116
110
  })
117
111
  })
118
-
119
- function sleep(ms: number) {
120
- return new Promise(resolve => setTimeout(resolve, ms));
121
- }
122
-
@@ -111,7 +111,7 @@ export function untildify(pathWithTilde: string) {
111
111
  return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
112
112
  }
113
113
 
114
- export async function areArraysEqual(parameter: ArrayParameterSetting, desired: unknown, current: unknown) {
114
+ export function areArraysEqual(parameter: ArrayParameterSetting, desired: unknown, current: unknown) {
115
115
  if (!Array.isArray(desired) || !Array.isArray(current)) {
116
116
  throw new Error(`A non-array value:
117
117
 
@@ -130,12 +130,10 @@ Was provided even though type array was specified.
130
130
  const desiredCopy = [...desired];
131
131
  const currentCopy = [...current];
132
132
 
133
- const eq = parameter.isElementEqual ?? ((a, b) => a === b);
134
-
135
133
  // Algorithm for to check equality between two un-ordered; un-hashable arrays using
136
134
  // an isElementEqual method. Time: O(n^2)
137
135
  for (let counter = desiredCopy.length - 1; counter >= 0; counter--) {
138
- const idx = await asyncFindIndex(currentCopy, (e2) => eq(desiredCopy[counter], e2))
136
+ const idx = currentCopy.findIndex((e2) => (parameter.isElementEqual ?? ((a, b) => a === b))(desiredCopy[counter], e2))
139
137
 
140
138
  if (idx === -1) {
141
139
  return false;
@@ -147,56 +145,3 @@ Was provided even though type array was specified.
147
145
 
148
146
  return currentCopy.length === 0;
149
147
  }
150
-
151
- export async function asyncFilter<T>(arr: T[], filter: (a: T) => Promise<boolean> | boolean): Promise<T[]> {
152
- const result = [];
153
-
154
- for (const element of arr) {
155
- if (await filter(element)) {
156
- result.push(element);
157
- }
158
- }
159
-
160
- return result;
161
- }
162
-
163
- export async function asyncMap<T, R>(arr: T[], map: (a: T) => Promise<R> | R): Promise<R[]> {
164
- const result: R[] = [];
165
-
166
- for (const element of arr) {
167
- result.push(await map(element));
168
- }
169
-
170
- return result;
171
- }
172
-
173
-
174
- export async function asyncFindIndex<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<number> {
175
- for (const [counter, element] of arr.entries()) {
176
- if (await eq(element)) {
177
- return counter;
178
- }
179
- }
180
-
181
- return -1;
182
- }
183
-
184
- export async function asyncFind<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<T | undefined> {
185
- for (const element of arr) {
186
- if (await eq(element)) {
187
- return element;
188
- }
189
- }
190
-
191
- return undefined;
192
- }
193
-
194
- export async function asyncIncludes<T>(arr: T[], eq: (a: T) => Promise<boolean> | boolean): Promise<boolean> {
195
- for (const element of arr) {
196
- if (await eq(element)) {
197
- return true
198
- }
199
- }
200
-
201
- return false;
202
- }