codify-plugin-lib 1.0.49 → 1.0.51

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.
@@ -2,9 +2,13 @@ import { ParameterOperation, ResourceConfig, ResourceOperation, StringIndexedObj
2
2
  import { ParameterChange } from './change-set.js';
3
3
  import { Plan } from './plan.js';
4
4
  import { StatefulParameter } from './stateful-parameter.js';
5
- import { ResourceConfiguration, ValidationResult } from './resource-types.js';
5
+ import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
6
6
  import { setsEqual, splitUserConfig } from '../utils/utils.js';
7
- import { ParameterConfiguration, PlanConfiguration } from './plan-types.js';
7
+ import { ParameterOptions, PlanOptions } from './plan-types.js';
8
+ import { TransformParameter } from './transform-parameter.js';
9
+ import { ResourceOptions, ResourceOptionsParser } from './resource-options.js';
10
+ import Ajv from 'ajv';
11
+ import Ajv2020, { ValidateFunction } from 'ajv/dist/2020.js';
8
12
 
9
13
  /**
10
14
  * Description of resource here
@@ -14,92 +18,99 @@ import { ParameterConfiguration, PlanConfiguration } from './plan-types.js';
14
18
  *
15
19
  */
16
20
  export abstract class Resource<T extends StringIndexedObject> {
17
-
18
21
  readonly typeId: string;
19
22
  readonly statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
23
+ readonly transformParameters: Map<keyof T, TransformParameter<T>>
24
+ readonly resourceParameters: Map<keyof T, ResourceParameterOptions>;
25
+
26
+ readonly statefulParameterOrder: Map<keyof T, number>;
27
+ readonly transformParameterOrder: Map<keyof T, number>;
28
+
20
29
  readonly dependencies: string[]; // TODO: Change this to a string
21
- readonly parameterConfigurations: Record<keyof T, ParameterConfiguration>
22
- readonly configuration: ResourceConfiguration<T>;
30
+ readonly parameterOptions: Record<keyof T, ParameterOptions>
31
+ readonly options: ResourceOptions<T>;
23
32
  readonly defaultValues: Partial<Record<keyof T, unknown>>;
24
33
 
25
- protected constructor(configuration: ResourceConfiguration<T>) {
26
- this.validateResourceConfiguration(configuration);
34
+ protected ajv?: Ajv.default;
35
+ protected schemaValidator?: ValidateFunction;
36
+
37
+ protected constructor(options: ResourceOptions<T>) {
38
+ this.typeId = options.type;
39
+ this.dependencies = options.dependencies ?? [];
40
+ this.options = options;
27
41
 
28
- this.typeId = configuration.type;
29
- this.statefulParameters = new Map(configuration.statefulParameters?.map((sp) => [sp.name, sp]));
30
- this.parameterConfigurations = this.initializeParameterConfigurations(configuration);
31
- this.defaultValues = this.initializeDefaultValues(configuration);
42
+ if (this.options.schema) {
43
+ this.ajv = new Ajv2020.default({
44
+ strict: true,
45
+ strictRequired: false,
46
+ })
47
+ this.schemaValidator = this.ajv.compile(this.options.schema);
48
+ }
32
49
 
33
- this.dependencies = configuration.dependencies ?? [];
34
- this.configuration = configuration;
50
+ const parser = new ResourceOptionsParser<T>(options);
51
+ this.statefulParameters = parser.statefulParameters;
52
+ this.transformParameters = parser.transformParameters;
53
+ this.resourceParameters = parser.resourceParameters;
54
+ this.parameterOptions = parser.changeSetParameterOptions;
55
+ this.defaultValues = parser.defaultValues;
56
+ this.statefulParameterOrder = parser.statefulParameterOrder;
57
+ this.transformParameterOrder = parser.transformParameterOrder;
35
58
  }
36
59
 
37
60
  async onInitialize(): Promise<void> {}
38
61
 
62
+ async validateResource(parameters: unknown): Promise<ValidationResult> {
63
+ if (this.schemaValidator) {
64
+ const isValid = this.schemaValidator(parameters);
65
+
66
+ if (!isValid) {
67
+ return {
68
+ isValid: false,
69
+ errors: this.schemaValidator?.errors ?? [],
70
+ }
71
+ }
72
+ }
73
+
74
+ return this.validate(parameters);
75
+ }
76
+
39
77
  // TODO: Add state in later.
40
78
  // Currently only calculating how to add things to reach desired state. Can't delete resources.
41
79
  // Add previousConfig as a parameter for plan(desired, previous);
42
80
  async plan(desiredConfig: Partial<T> & ResourceConfig): Promise<Plan<T>> {
43
-
44
- // Explanation: these are settings for how the plan will be generated
45
- const planConfiguration: PlanConfiguration<T> = {
81
+ const planOptions: PlanOptions<T> = {
46
82
  statefulMode: false,
47
- parameterConfigurations: this.parameterConfigurations,
83
+ parameterOptions: this.parameterOptions,
48
84
  }
49
85
 
50
- const { resourceMetadata, parameters: desiredParameters } = splitUserConfig(desiredConfig);
51
-
52
- this.addDefaultValues(desiredParameters);
86
+ // Parse data from the user supplied config
87
+ const parsedConfig = new ConfigParser(desiredConfig, this.statefulParameters, this.transformParameters)
88
+ const {
89
+ parameters: desiredParameters,
90
+ resourceMetadata,
91
+ resourceParameters,
92
+ statefulParameters
93
+ } = parsedConfig;
53
94
 
54
- const resourceParameters = Object.fromEntries([
55
- ...Object.entries(desiredParameters).filter(([key]) => !this.statefulParameters.has(key)),
56
- ]) as Partial<T>;
95
+ this.addDefaultValues(resourceParameters);
96
+ await this.applyTransformParameters(resourceParameters);
57
97
 
58
- const statefulParameters = [...this.statefulParameters.values()]
59
- .filter((sp) => desiredParameters[sp.name] !== undefined) // Checking for undefined is fine here because JSONs can only have null.
98
+ // Refresh resource parameters. This refreshes the parameters that configure the resource itself
99
+ const currentParameters = await this.refreshResourceParameters(resourceParameters);
60
100
 
61
- // Refresh resource parameters
62
- // This refreshes the parameters that configure the resource itself
63
- const entriesToRefresh = new Map(Object.entries(resourceParameters));
64
- const currentParameters = await this.refresh(entriesToRefresh);
65
-
66
- // Short circuit here. If resource is non-existent, then there's no point checking stateful parameters
101
+ // Short circuit here. If the resource is non-existent, there's no point checking stateful parameters
67
102
  if (currentParameters == null) {
68
- return Plan.create(desiredParameters, null, resourceMetadata, planConfiguration);
103
+ return Plan.create(desiredParameters, null, resourceMetadata, planOptions);
69
104
  }
70
105
 
71
- this.validateRefreshResults(currentParameters, entriesToRefresh);
72
-
73
- // Refresh stateful parameters
74
- // This refreshes parameters that are stateful (they can be added, deleted separately from the resource)
75
- for(const statefulParameter of statefulParameters) {
76
- const desiredValue = desiredParameters[statefulParameter.name];
77
-
78
- let currentValue = await statefulParameter.refresh(desiredValue ?? null) ?? undefined;
79
-
80
- // In stateless mode, filter the refreshed parameters by the desired to ensure that no deletes happen
81
- if (Array.isArray(currentValue)
82
- && Array.isArray(desiredValue)
83
- && !planConfiguration.statefulMode
84
- && !statefulParameter.configuration.disableStatelessModeArrayFiltering
85
- ) {
86
- currentValue = currentValue.filter((c) => desiredValue?.some((d) => {
87
- const pc = planConfiguration?.parameterConfigurations?.[statefulParameter.name];
88
- if (pc && pc.isElementEqual) {
89
- return pc.isElementEqual(d, c);
90
- }
91
- return d === c;
92
- })) as any;
93
- }
94
-
95
- currentParameters[statefulParameter.name] = currentValue;
96
- }
106
+ // Refresh stateful parameters. These parameters have state external to the resource
107
+ const statefulCurrentParameters = await this.refreshStatefulParameters(statefulParameters, planOptions.statefulMode);
97
108
 
98
109
  return Plan.create(
99
- desiredParameters,
100
- currentParameters as Partial<T>,
110
+ { ...resourceParameters, ...statefulParameters },
111
+ { ...currentParameters, ...statefulCurrentParameters } as Partial<T>,
101
112
  resourceMetadata,
102
- planConfiguration,
113
+ planOptions,
103
114
  )
104
115
  }
105
116
 
@@ -130,6 +141,8 @@ export abstract class Resource<T extends StringIndexedObject> {
130
141
 
131
142
  const statefulParameterChanges = plan.changeSet.parameterChanges
132
143
  .filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
144
+ .sort((a, b) => this.statefulParameterOrder.get(a.name)! - this.statefulParameterOrder.get(b.name)!)
145
+
133
146
  for (const parameterChange of statefulParameterChanges) {
134
147
  const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
135
148
  await statefulParameter.applyAdd(parameterChange.newValue, plan);
@@ -144,6 +157,7 @@ export abstract class Resource<T extends StringIndexedObject> {
144
157
 
145
158
  const statelessParameterChanges = parameterChanges
146
159
  .filter((pc: ParameterChange<T>) => !this.statefulParameters.has(pc.name))
160
+
147
161
  for (const pc of statelessParameterChanges) {
148
162
  // TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
149
163
  await this.applyModify(pc.name, pc.newValue, pc.previousValue, false, plan);
@@ -151,6 +165,8 @@ export abstract class Resource<T extends StringIndexedObject> {
151
165
 
152
166
  const statefulParameterChanges = parameterChanges
153
167
  .filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
168
+ .sort((a, b) => this.statefulParameterOrder.get(a.name)! - this.statefulParameterOrder.get(b.name)!)
169
+
154
170
  for (const parameterChange of statefulParameterChanges) {
155
171
  const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
156
172
 
@@ -175,9 +191,11 @@ export abstract class Resource<T extends StringIndexedObject> {
175
191
  private async _applyDestroy(plan: Plan<T>): Promise<void> {
176
192
  // If this option is set (defaults to false), then stateful parameters need to be destroyed
177
193
  // as well. This means that the stateful parameter wouldn't have been normally destroyed with applyDestroy()
178
- if (this.configuration.callStatefulParameterRemoveOnDestroy) {
194
+ if (this.options.callStatefulParameterRemoveOnDestroy) {
179
195
  const statefulParameterChanges = plan.changeSet.parameterChanges
180
196
  .filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
197
+ .sort((a, b) => this.statefulParameterOrder.get(a.name)! - this.statefulParameterOrder.get(b.name)!)
198
+
181
199
  for (const parameterChange of statefulParameterChanges) {
182
200
  const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
183
201
  await statefulParameter.applyRemove(parameterChange.previousValue, plan);
@@ -187,59 +205,6 @@ export abstract class Resource<T extends StringIndexedObject> {
187
205
  await this.applyDestroy(plan);
188
206
  }
189
207
 
190
- private initializeParameterConfigurations(
191
- resourceConfiguration: ResourceConfiguration<T>
192
- ): Record<keyof T, ParameterConfiguration> {
193
- const resourceParameters = Object.fromEntries(
194
- Object.entries(resourceConfiguration.parameterConfigurations ?? {})
195
- ?.map(([name, value]) => ([name, { ...value, isStatefulParameter: false }]))
196
- ) as Record<keyof T, ParameterConfiguration>
197
-
198
- const statefulParameters = resourceConfiguration.statefulParameters
199
- ?.reduce((obj, sp) => {
200
- return {
201
- ...obj,
202
- [sp.name]: {
203
- ...sp.configuration,
204
- isStatefulParameter: true,
205
- }
206
- }
207
- }, {}) ?? {}
208
-
209
- return {
210
- ...resourceParameters,
211
- ...statefulParameters,
212
- }
213
-
214
- }
215
-
216
- private initializeDefaultValues(
217
- resourceConfiguration: ResourceConfiguration<T>
218
- ): Partial<Record<keyof T, unknown>> {
219
- if (!resourceConfiguration.parameterConfigurations) {
220
- return {};
221
- }
222
-
223
- return Object.fromEntries(
224
- Object.entries(resourceConfiguration.parameterConfigurations)
225
- .filter((p) => p[1]?.defaultValue !== undefined)
226
- .map((config) => [config[0], config[1]!.defaultValue])
227
- ) as Partial<Record<keyof T, unknown>>;
228
- }
229
-
230
- private validateResourceConfiguration(data: ResourceConfiguration<T>) {
231
- // Stateful parameters are configured within the object not in the resource.
232
- if (data.parameterConfigurations && data.statefulParameters) {
233
- const parameters = [...Object.keys(data.parameterConfigurations)];
234
- const statefulParameterSet = new Set(data.statefulParameters.map((sp) => sp.name));
235
-
236
- const intersection = parameters.some((p) => statefulParameterSet.has(p));
237
- if (intersection) {
238
- throw new Error(`Resource ${this.typeId} cannot declare a parameter as both stateful and non-stateful`);
239
- }
240
- }
241
- }
242
-
243
208
  private validateRefreshResults(refresh: Partial<T> | null, desiredMap: Map<keyof T, T[keyof T]>) {
244
209
  if (!refresh) {
245
210
  return;
@@ -250,7 +215,7 @@ export abstract class Resource<T extends StringIndexedObject> {
250
215
 
251
216
  if (!setsEqual(desiredKeys, refreshKeys)) {
252
217
  throw new Error(
253
- `Resource ${this.configuration.type}
218
+ `Resource ${this.typeId}
254
219
  refresh() must return back exactly the keys that were provided
255
220
  Missing: ${[...desiredKeys].filter((k) => !refreshKeys.has(k))};
256
221
  Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
@@ -258,6 +223,28 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
258
223
  }
259
224
  }
260
225
 
226
+ private async applyTransformParameters(desired: Partial<T>): Promise<void> {
227
+ const orderedEntries = [...this.transformParameters.entries()]
228
+ .sort(([keyA], [keyB]) => this.transformParameterOrder.get(keyA)! - this.transformParameterOrder.get(keyB)!)
229
+
230
+ for (const [key, tp] of orderedEntries) {
231
+ if (desired[key] !== null) {
232
+ const transformedValue = await tp.transform(desired[key]);
233
+
234
+ if (Object.keys(transformedValue).some((k) => desired[k] !== undefined)) {
235
+ throw new Error(`Transform parameter ${key as string} is attempting to override existing value ${desired[key]}`);
236
+ }
237
+
238
+ Object.entries(transformedValue).forEach(([tvKey, tvValue]) => {
239
+ // @ts-ignore
240
+ desired[tvKey] = tvValue;
241
+ })
242
+
243
+ delete desired[key];
244
+ }
245
+ }
246
+ }
247
+
261
248
  private addDefaultValues(desired: Partial<T>): void {
262
249
  Object.entries(this.defaultValues)
263
250
  .forEach(([key, defaultValue]) => {
@@ -268,7 +255,59 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
268
255
  });
269
256
  }
270
257
 
271
- abstract validate(parameters: unknown): Promise<ValidationResult>;
258
+ private async refreshResourceParameters(resourceParameters: Partial<T>): Promise<Partial<T> | null> {
259
+ const entriesToRefresh = new Map(Object.entries(resourceParameters));
260
+ const currentParameters = await this.refresh(entriesToRefresh);
261
+
262
+ this.validateRefreshResults(currentParameters, entriesToRefresh);
263
+ return currentParameters;
264
+ }
265
+
266
+ // Refresh stateful parameters
267
+ // This refreshes parameters that are stateful (they can be added, deleted separately from the resource)
268
+ private async refreshStatefulParameters(statefulParametersConfig: Partial<T>, isStatefulMode: boolean): Promise<Partial<T>> {
269
+ const currentParameters: Partial<T> = {}
270
+ const sortedEntries = Object.entries(statefulParametersConfig)
271
+ .sort(([key1], [key2]) => this.statefulParameterOrder.get(key1)! - this.statefulParameterOrder.get(key2)!)
272
+
273
+ for(const [key, desiredValue] of sortedEntries) {
274
+ const statefulParameter = this.statefulParameters.get(key);
275
+ if (!statefulParameter) {
276
+ throw new Error(`Stateful parameter ${key} was not found`);
277
+ }
278
+
279
+ let currentValue = await statefulParameter.refresh(desiredValue ?? null);
280
+
281
+ // In stateless mode, filter the refreshed parameters by the desired to ensure that no deletes happen
282
+ // Otherwise the change set will pick up the extra keys from the current and try to delete them
283
+ // This allows arrays within stateful parameters to be first class objects
284
+ if (Array.isArray(currentValue)
285
+ && Array.isArray(desiredValue)
286
+ && !isStatefulMode
287
+ && !statefulParameter.options.disableStatelessModeArrayFiltering
288
+ ) {
289
+ currentValue = currentValue.filter((c) => desiredValue?.some((d) => {
290
+ const parameterOptions = statefulParameter.options as any;
291
+ if (parameterOptions && parameterOptions.isElementEqual) {
292
+ return parameterOptions.isElementEqual(d, c);
293
+ }
294
+
295
+ return d === c;
296
+ })) as any;
297
+ }
298
+
299
+ // @ts-ignore
300
+ currentParameters[key] = currentValue;
301
+ }
302
+
303
+ return currentParameters;
304
+ }
305
+
306
+ async validate(parameters: unknown): Promise<ValidationResult> {
307
+ return {
308
+ isValid: true,
309
+ }
310
+ };
272
311
 
273
312
  abstract refresh(keys: Map<keyof T, T[keyof T]>): Promise<Partial<T> | null>;
274
313
 
@@ -278,3 +317,53 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
278
317
 
279
318
  abstract applyDestroy(plan: Plan<T>): Promise<void>;
280
319
  }
320
+
321
+ class ConfigParser<T extends StringIndexedObject> {
322
+ private config: Partial<T> & ResourceConfig;
323
+ private statefulParametersMap: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
324
+ private transformParametersMap: Map<keyof T, TransformParameter<T>>;
325
+
326
+ constructor(
327
+ config: Partial<T> & ResourceConfig,
328
+ statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>,
329
+ transformParameters: Map<keyof T, TransformParameter<T>>,
330
+ ) {
331
+ this.config = config;
332
+ this.statefulParametersMap = statefulParameters;
333
+ this.transformParametersMap = transformParameters;
334
+ }
335
+
336
+ get resourceMetadata(): ResourceConfig {
337
+ const { resourceMetadata } = splitUserConfig(this.config);
338
+ return resourceMetadata;
339
+ }
340
+
341
+ get parameters(): Partial<T> {
342
+ const { parameters } = splitUserConfig(this.config);
343
+ return parameters;
344
+ }
345
+
346
+ get resourceParameters(): Partial<T> {
347
+ const parameters = this.parameters;
348
+
349
+ return Object.fromEntries([
350
+ ...Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))),
351
+ ]) as Partial<T>;
352
+ }
353
+
354
+ get statefulParameters(): Partial<T> {
355
+ const parameters = this.parameters;
356
+
357
+ return Object.fromEntries([
358
+ ...Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)),
359
+ ]) as Partial<T>;
360
+ }
361
+
362
+ get transformParameters(): Partial<T> {
363
+ const parameters = this.parameters;
364
+
365
+ return Object.fromEntries([
366
+ ...Object.entries(parameters).filter(([key]) => this.transformParametersMap.has(key)),
367
+ ]) as Partial<T>;
368
+ }
369
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { ArrayStatefulParameter, ArrayStatefulParameterConfiguration, } from './stateful-parameter.js';
2
+ import { ArrayStatefulParameter, ArrayStatefulParameterOptions, } from './stateful-parameter.js';
3
3
  import { Plan } from './plan.js';
4
4
  import { spy } from 'sinon';
5
5
  import { ParameterOperation, ResourceOperation } from 'codify-schemas';
@@ -10,8 +10,8 @@ interface TestConfig {
10
10
  }
11
11
 
12
12
  class TestArrayParameter extends ArrayStatefulParameter<TestConfig, string> {
13
- constructor(configuration?: ArrayStatefulParameterConfiguration<TestConfig>) {
14
- super(configuration ?? {
13
+ constructor(options?: ArrayStatefulParameterOptions<TestConfig>) {
14
+ super(options ?? {
15
15
  name: 'propA'
16
16
  })
17
17
  }
@@ -67,7 +67,7 @@ describe('Stateful parameter tests', () => {
67
67
  { propA: ['a', 'c', 'd', 'e', 'f'] }, // b to remove, d, e, f to add
68
68
  { propA: ['a', 'b', 'c'] },
69
69
  { type: 'typeA' },
70
- { statefulMode: true, parameterConfigurations: { propA: { isStatefulParameter: true }} }
70
+ { statefulMode: true, parameterOptions: { propA: { isStatefulParameter: true }} }
71
71
  );
72
72
 
73
73
  expect(plan.changeSet.operation).to.eq(ResourceOperation.MODIFY);
@@ -90,7 +90,7 @@ describe('Stateful parameter tests', () => {
90
90
  { propA: ['9.12', '9.13'] }, // b to remove, d, e, f to add
91
91
  { propA: ['9.12.9'] },
92
92
  { type: 'typeA' },
93
- { statefulMode: false, parameterConfigurations: { propA: { isStatefulParameter: true }} }
93
+ { statefulMode: false, parameterOptions: { propA: { isStatefulParameter: true }} }
94
94
  );
95
95
 
96
96
  expect(plan.changeSet.operation).to.eq(ResourceOperation.MODIFY);
@@ -1,7 +1,7 @@
1
1
  import { Plan } from './plan.js';
2
2
  import { StringIndexedObject } from 'codify-schemas';
3
3
 
4
- export interface StatefulParameterConfiguration<T> {
4
+ export interface StatefulParameterOptions<T> {
5
5
  name: keyof T;
6
6
  isEqual?: (desired: any, current: any) => boolean;
7
7
 
@@ -17,7 +17,7 @@ export interface StatefulParameterConfiguration<T> {
17
17
  disableStatelessModeArrayFiltering?: boolean;
18
18
  }
19
19
 
20
- export interface ArrayStatefulParameterConfiguration<T> extends StatefulParameterConfiguration<T> {
20
+ export interface ArrayStatefulParameterOptions<T> extends StatefulParameterOptions<T> {
21
21
  isEqual?: (desired: any[], current: any[]) => boolean;
22
22
  isElementEqual?: (desired: any, current: any) => boolean;
23
23
  }
@@ -25,11 +25,11 @@ export interface ArrayStatefulParameterConfiguration<T> extends StatefulParamete
25
25
 
26
26
  export abstract class StatefulParameter<T extends StringIndexedObject, V extends T[keyof T]> {
27
27
  readonly name: keyof T;
28
- readonly configuration: StatefulParameterConfiguration<T>;
28
+ readonly options: StatefulParameterOptions<T>;
29
29
 
30
- protected constructor(configuration: StatefulParameterConfiguration<T>) {
31
- this.name = configuration.name;
32
- this.configuration = configuration
30
+ protected constructor(options: StatefulParameterOptions<T>) {
31
+ this.name = options.name;
32
+ this.options = options
33
33
  }
34
34
 
35
35
  abstract refresh(desired: V | null): Promise<V | null>;
@@ -41,11 +41,11 @@ export abstract class StatefulParameter<T extends StringIndexedObject, V extends
41
41
  }
42
42
 
43
43
  export abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> extends StatefulParameter<T, any>{
44
- configuration: ArrayStatefulParameterConfiguration<T>;
44
+ options: ArrayStatefulParameterOptions<T>;
45
45
 
46
- constructor(configuration: ArrayStatefulParameterConfiguration<T>) {
47
- super(configuration);
48
- this.configuration = configuration;
46
+ constructor(options: ArrayStatefulParameterOptions<T>) {
47
+ super(options);
48
+ this.options = options;
49
49
  }
50
50
 
51
51
  async applyAdd(valuesToAdd: V[], plan: Plan<T>): Promise<void> {
@@ -55,18 +55,18 @@ export abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> e
55
55
  }
56
56
 
57
57
  async applyModify(newValues: V[], previousValues: V[], allowDeletes: boolean, plan: Plan<T>): Promise<void> {
58
- const configuration = this.configuration as ArrayStatefulParameterConfiguration<T>;
58
+ const options = this.options as ArrayStatefulParameterOptions<T>;
59
59
 
60
60
  const valuesToAdd = newValues.filter((n) => !previousValues.some((p) => {
61
- if ((configuration).isElementEqual) {
62
- return configuration.isElementEqual(n, p);
61
+ if (options.isElementEqual) {
62
+ return options.isElementEqual(n, p);
63
63
  }
64
64
  return n === p;
65
65
  }));
66
66
 
67
67
  const valuesToRemove = previousValues.filter((p) => !newValues.some((n) => {
68
- if ((configuration).isElementEqual) {
69
- return configuration.isElementEqual(n, p);
68
+ if (options.isElementEqual) {
69
+ return options.isElementEqual(n, p);
70
70
  }
71
71
  return n === p;
72
72
  }));
@@ -0,0 +1,7 @@
1
+ import { StringIndexedObject } from 'codify-schemas';
2
+
3
+ export abstract class TransformParameter<T extends StringIndexedObject> {
4
+
5
+ abstract transform(value: any): Promise<Partial<T>>
6
+
7
+ }