codify-plugin-lib 1.0.50 → 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.
@@ -1,5 +1,5 @@
1
1
  import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
2
- import { ParameterConfiguration } from './plan-types.js';
2
+ import { ParameterOptions } from './plan-types.js';
3
3
  export interface ParameterChange<T extends StringIndexedObject> {
4
4
  name: keyof T & string;
5
5
  operation: ParameterOperation;
@@ -14,10 +14,10 @@ export declare class ChangeSet<T extends StringIndexedObject> {
14
14
  get currentParameters(): T;
15
15
  static calculateParameterChangeSet<T extends StringIndexedObject>(desired: T | null, current: T | null, options: {
16
16
  statefulMode: boolean;
17
- parameterConfigurations?: Record<keyof T, ParameterConfiguration>;
17
+ parameterOptions?: Record<keyof T, ParameterOptions>;
18
18
  }): ParameterChange<T>[];
19
19
  static combineResourceOperations(prev: ResourceOperation, next: ResourceOperation): ResourceOperation;
20
- static isSame(desired: unknown, current: unknown, configuration?: ParameterConfiguration): boolean;
20
+ static isSame(desired: unknown, current: unknown, options?: ParameterOptions): boolean;
21
21
  private static calculateStatefulModeChangeSet;
22
22
  private static calculateStatelessModeChangeSet;
23
23
  }
@@ -22,10 +22,10 @@ export class ChangeSet {
22
22
  }
23
23
  static calculateParameterChangeSet(desired, current, options) {
24
24
  if (options.statefulMode) {
25
- return ChangeSet.calculateStatefulModeChangeSet(desired, current, options.parameterConfigurations);
25
+ return ChangeSet.calculateStatefulModeChangeSet(desired, current, options.parameterOptions);
26
26
  }
27
27
  else {
28
- return ChangeSet.calculateStatelessModeChangeSet(desired, current, options.parameterConfigurations);
28
+ return ChangeSet.calculateStatelessModeChangeSet(desired, current, options.parameterOptions);
29
29
  }
30
30
  }
31
31
  static combineResourceOperations(prev, next) {
@@ -40,9 +40,9 @@ export class ChangeSet {
40
40
  const indexNext = orderOfOperations.indexOf(next);
41
41
  return orderOfOperations[Math.max(indexPrev, indexNext)];
42
42
  }
43
- static isSame(desired, current, configuration) {
44
- if (configuration?.isEqual) {
45
- return configuration.isEqual(desired, current);
43
+ static isSame(desired, current, options) {
44
+ if (options?.isEqual) {
45
+ return options.isEqual(desired, current);
46
46
  }
47
47
  if (Array.isArray(desired) && Array.isArray(current)) {
48
48
  const sortedDesired = desired.map((x) => x).sort();
@@ -50,14 +50,14 @@ export class ChangeSet {
50
50
  if (sortedDesired.length !== sortedCurrent.length) {
51
51
  return false;
52
52
  }
53
- if (configuration?.isElementEqual) {
54
- return sortedDesired.every((value, index) => configuration.isElementEqual(value, sortedCurrent[index]));
53
+ if (options?.isElementEqual) {
54
+ return sortedDesired.every((value, index) => options.isElementEqual(value, sortedCurrent[index]));
55
55
  }
56
56
  return JSON.stringify(sortedDesired) === JSON.stringify(sortedCurrent);
57
57
  }
58
58
  return desired === current;
59
59
  }
60
- static calculateStatefulModeChangeSet(desired, current, parameterConfigurations) {
60
+ static calculateStatefulModeChangeSet(desired, current, parameterOptions) {
61
61
  const parameterChangeSet = new Array();
62
62
  const _desired = { ...desired };
63
63
  const _current = { ...current };
@@ -72,7 +72,7 @@ export class ChangeSet {
72
72
  delete _current[k];
73
73
  continue;
74
74
  }
75
- if (!ChangeSet.isSame(_desired[k], _current[k], parameterConfigurations?.[k])) {
75
+ if (!ChangeSet.isSame(_desired[k], _current[k], parameterOptions?.[k])) {
76
76
  parameterChangeSet.push({
77
77
  name: k,
78
78
  previousValue: v,
@@ -105,7 +105,7 @@ export class ChangeSet {
105
105
  }
106
106
  return parameterChangeSet;
107
107
  }
108
- static calculateStatelessModeChangeSet(desired, current, parameterConfigurations) {
108
+ static calculateStatelessModeChangeSet(desired, current, parameterOptions) {
109
109
  const parameterChangeSet = new Array();
110
110
  const _desired = { ...desired };
111
111
  const _current = { ...current };
@@ -119,7 +119,7 @@ export class ChangeSet {
119
119
  });
120
120
  continue;
121
121
  }
122
- if (!ChangeSet.isSame(_desired[k], _current[k], parameterConfigurations?.[k])) {
122
+ if (!ChangeSet.isSame(_desired[k], _current[k], parameterOptions?.[k])) {
123
123
  parameterChangeSet.push({
124
124
  name: k,
125
125
  previousValue: _current[k],
@@ -1,11 +1,11 @@
1
1
  import { ResourceOperation } from 'codify-schemas';
2
- export interface ParameterConfiguration {
2
+ export interface ParameterOptions {
3
3
  planOperation?: ResourceOperation.MODIFY | ResourceOperation.RECREATE;
4
4
  isEqual?: (desired: any, current: any) => boolean;
5
5
  isElementEqual?: (desired: any, current: any) => boolean;
6
6
  isStatefulParameter?: boolean;
7
7
  }
8
- export interface PlanConfiguration<T> {
8
+ export interface PlanOptions<T> {
9
9
  statefulMode: boolean;
10
- parameterConfigurations?: Record<keyof T, ParameterConfiguration>;
10
+ parameterOptions?: Record<keyof T, ParameterOptions>;
11
11
  }
@@ -1,12 +1,12 @@
1
1
  import { ChangeSet } from './change-set.js';
2
2
  import { ApplyRequestData, PlanResponseData, ResourceConfig, StringIndexedObject } from 'codify-schemas';
3
- import { PlanConfiguration } from './plan-types.js';
3
+ import { PlanOptions } from './plan-types.js';
4
4
  export declare class Plan<T extends StringIndexedObject> {
5
5
  id: string;
6
6
  changeSet: ChangeSet<T>;
7
7
  resourceMetadata: ResourceConfig;
8
8
  constructor(id: string, changeSet: ChangeSet<T>, resourceMetadata: ResourceConfig);
9
- static create<T extends StringIndexedObject>(desiredParameters: Partial<T> | null, currentParameters: Partial<T> | null, resourceMetadata: ResourceConfig, configuration: PlanConfiguration<T>): Plan<T>;
9
+ static create<T extends StringIndexedObject>(desiredParameters: Partial<T> | null, currentParameters: Partial<T> | null, resourceMetadata: ResourceConfig, options: PlanOptions<T>): Plan<T>;
10
10
  getResourceType(): string;
11
11
  static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues: Partial<Record<keyof T, unknown>>): Plan<T>;
12
12
  get desiredConfig(): T;
@@ -10,12 +10,12 @@ export class Plan {
10
10
  this.changeSet = changeSet;
11
11
  this.resourceMetadata = resourceMetadata;
12
12
  }
13
- static create(desiredParameters, currentParameters, resourceMetadata, configuration) {
14
- const parameterConfigurations = configuration.parameterConfigurations ?? {};
15
- const statefulParameterNames = new Set([...Object.entries(parameterConfigurations)]
13
+ static create(desiredParameters, currentParameters, resourceMetadata, options) {
14
+ const parameterOptions = options.parameterOptions ?? {};
15
+ const statefulParameterNames = new Set([...Object.entries(parameterOptions)]
16
16
  .filter(([k, v]) => v.isStatefulParameter)
17
17
  .map(([k, v]) => k));
18
- const parameterChangeSet = ChangeSet.calculateParameterChangeSet(desiredParameters, currentParameters, { statefulMode: configuration.statefulMode, parameterConfigurations });
18
+ const parameterChangeSet = ChangeSet.calculateParameterChangeSet(desiredParameters, currentParameters, { statefulMode: options.statefulMode, parameterOptions });
19
19
  let resourceOperation;
20
20
  if (!currentParameters && desiredParameters) {
21
21
  resourceOperation = ResourceOperation.CREATE;
@@ -31,8 +31,8 @@ export class Plan {
31
31
  if (statefulParameterNames.has(curr.name)) {
32
32
  newOperation = ResourceOperation.MODIFY;
33
33
  }
34
- else if (parameterConfigurations[curr.name]?.planOperation) {
35
- newOperation = parameterConfigurations[curr.name].planOperation;
34
+ else if (parameterOptions[curr.name]?.planOperation) {
35
+ newOperation = parameterOptions[curr.name].planOperation;
36
36
  }
37
37
  else {
38
38
  newOperation = ResourceOperation.RECREATE;
@@ -26,7 +26,7 @@ export class Plugin {
26
26
  throw new Error(`Resource type not found: ${config.type}`);
27
27
  }
28
28
  const { parameters } = splitUserConfig(config);
29
- const validateResult = await this.resources.get(config.type).validate(parameters);
29
+ const validateResult = await this.resources.get(config.type).validateResource(parameters);
30
30
  validationResults.push({
31
31
  ...validateResult,
32
32
  resourceType: config.type,
@@ -0,0 +1,31 @@
1
+ import { StringIndexedObject } from 'codify-schemas';
2
+ import { StatefulParameter } from './stateful-parameter.js';
3
+ import { TransformParameter } from './transform-parameter.js';
4
+ import { ResourceParameterOptions } from './resource-types.js';
5
+ import { ParameterOptions } from './plan-types.js';
6
+ export interface ResourceOptions<T extends StringIndexedObject> {
7
+ type: string;
8
+ schema?: unknown;
9
+ callStatefulParameterRemoveOnDestroy?: boolean;
10
+ dependencies?: string[];
11
+ parameterOptions?: Partial<Record<keyof T, ResourceParameterOptions | ResourceStatefulParameterOptions<T> | ResourceTransformParameterOptions<T>>>;
12
+ }
13
+ export interface ResourceStatefulParameterOptions<T extends StringIndexedObject> {
14
+ statefulParameter: StatefulParameter<T, T[keyof T]>;
15
+ order?: number;
16
+ }
17
+ export interface ResourceTransformParameterOptions<T extends StringIndexedObject> {
18
+ transformParameter: TransformParameter<T>;
19
+ order?: number;
20
+ }
21
+ export declare class ResourceOptionsParser<T extends StringIndexedObject> {
22
+ private options;
23
+ constructor(options: ResourceOptions<T>);
24
+ get statefulParameters(): Map<keyof T, StatefulParameter<T, T[keyof T]>>;
25
+ get transformParameters(): Map<keyof T, TransformParameter<T>>;
26
+ get resourceParameters(): Map<keyof T, ResourceParameterOptions>;
27
+ get changeSetParameterOptions(): Record<keyof T, ParameterOptions>;
28
+ get defaultValues(): Partial<Record<keyof T, unknown>>;
29
+ get statefulParameterOrder(): Map<keyof T, number>;
30
+ get transformParameterOrder(): Map<keyof T, number>;
31
+ }
@@ -0,0 +1,78 @@
1
+ export class ResourceOptionsParser {
2
+ options;
3
+ constructor(options) {
4
+ this.options = options;
5
+ }
6
+ get statefulParameters() {
7
+ const statefulParameters = Object.entries(this.options.parameterOptions ?? {})
8
+ .filter(([, p]) => p?.hasOwnProperty('statefulParameter'))
9
+ .map(([k, v]) => [k, v])
10
+ .map(([k, v]) => [k, v.statefulParameter]);
11
+ return new Map(statefulParameters);
12
+ }
13
+ get transformParameters() {
14
+ const transformParameters = Object.entries(this.options.parameterOptions ?? {})
15
+ .filter(([, p]) => p?.hasOwnProperty('transformParameter'))
16
+ .map(([k, v]) => [k, v])
17
+ .map(([k, v]) => [k, v.transformParameter]);
18
+ return new Map(transformParameters);
19
+ }
20
+ get resourceParameters() {
21
+ const resourceParameters = Object.entries(this.options.parameterOptions ?? {})
22
+ .filter(([, p]) => !(p?.hasOwnProperty('statefulParameter') || p?.hasOwnProperty('transformParameter')))
23
+ .map(([k, v]) => [k, v]);
24
+ return new Map(resourceParameters);
25
+ }
26
+ get changeSetParameterOptions() {
27
+ const resourceParameters = Object.fromEntries([...this.resourceParameters.entries()]
28
+ .map(([name, value]) => ([name, { ...value, isStatefulParameter: false }])));
29
+ const statefulParameters = [...this.statefulParameters.entries()]
30
+ ?.reduce((obj, sp) => {
31
+ return {
32
+ ...obj,
33
+ [sp[0]]: {
34
+ ...sp[1].options,
35
+ isStatefulParameter: true,
36
+ }
37
+ };
38
+ }, {});
39
+ return {
40
+ ...resourceParameters,
41
+ ...statefulParameters,
42
+ };
43
+ }
44
+ get defaultValues() {
45
+ if (!this.options.parameterOptions) {
46
+ return {};
47
+ }
48
+ return Object.fromEntries([...this.resourceParameters.entries()]
49
+ .filter(([, rp]) => rp.defaultValue !== undefined)
50
+ .map(([name, rp]) => [name, rp.defaultValue]));
51
+ }
52
+ get statefulParameterOrder() {
53
+ const entries = Object.entries(this.options.parameterOptions ?? {})
54
+ .filter(([, v]) => v?.hasOwnProperty('statefulParameter'))
55
+ .map(([k, v]) => [k, v]);
56
+ const orderedEntries = entries.filter(([, v]) => v.order !== undefined);
57
+ const unorderedEntries = entries.filter(([, v]) => v.order === undefined);
58
+ orderedEntries.sort((a, b) => a[1].order - b[1].order);
59
+ const resultArray = [
60
+ ...orderedEntries.map(([k]) => k),
61
+ ...unorderedEntries.map(([k]) => k)
62
+ ];
63
+ return new Map(resultArray.map((key, idx) => [key, idx]));
64
+ }
65
+ get transformParameterOrder() {
66
+ const entries = Object.entries(this.options.parameterOptions ?? {})
67
+ .filter(([, v]) => v?.hasOwnProperty('transformParameter'))
68
+ .map(([k, v]) => [k, v]);
69
+ const orderedEntries = entries.filter(([, v]) => v.order !== undefined);
70
+ const unorderedEntries = entries.filter(([, v]) => v.order === undefined);
71
+ orderedEntries.sort((a, b) => a[1].order - b[1].order);
72
+ const resultArray = [
73
+ ...orderedEntries.map(([k]) => k),
74
+ ...unorderedEntries.map(([k]) => k)
75
+ ];
76
+ return new Map(resultArray.map((key, idx) => [key, idx]));
77
+ }
78
+ }
@@ -1,20 +1,10 @@
1
- import { StatefulParameter } from './stateful-parameter.js';
2
- import { ResourceOperation, StringIndexedObject } from 'codify-schemas';
3
- import { TransformParameter } from './transform-parameter.js';
1
+ import { ResourceOperation } from 'codify-schemas';
4
2
  export type ErrorMessage = string;
5
- export interface ResourceParameterConfiguration {
3
+ export interface ResourceParameterOptions {
6
4
  planOperation?: ResourceOperation.MODIFY | ResourceOperation.RECREATE;
7
5
  isEqual?: (desired: any, current: any) => boolean;
8
6
  defaultValue?: unknown;
9
7
  }
10
- export interface ResourceConfiguration<T extends StringIndexedObject> {
11
- type: string;
12
- callStatefulParameterRemoveOnDestroy?: boolean;
13
- dependencies?: string[];
14
- statefulParameters?: Array<StatefulParameter<T, T[keyof T]>>;
15
- transformParameters?: Partial<Record<keyof T, TransformParameter<T>>>;
16
- parameterConfigurations?: Partial<Record<keyof T, ResourceParameterConfiguration>>;
17
- }
18
8
  export interface ResourceDefinition {
19
9
  [x: string]: {
20
10
  type: string;
@@ -1,31 +1,39 @@
1
1
  import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
2
2
  import { Plan } from './plan.js';
3
3
  import { StatefulParameter } from './stateful-parameter.js';
4
- import { ResourceConfiguration, ValidationResult } from './resource-types.js';
5
- import { ParameterConfiguration } from './plan-types.js';
4
+ import { ResourceParameterOptions, ValidationResult } from './resource-types.js';
5
+ import { ParameterOptions } from './plan-types.js';
6
6
  import { TransformParameter } from './transform-parameter.js';
7
+ import { ResourceOptions } from './resource-options.js';
8
+ import Ajv from 'ajv';
9
+ import { ValidateFunction } from 'ajv/dist/2020.js';
7
10
  export declare abstract class Resource<T extends StringIndexedObject> {
8
11
  readonly typeId: string;
9
12
  readonly statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
10
13
  readonly transformParameters: Map<keyof T, TransformParameter<T>>;
14
+ readonly resourceParameters: Map<keyof T, ResourceParameterOptions>;
15
+ readonly statefulParameterOrder: Map<keyof T, number>;
16
+ readonly transformParameterOrder: Map<keyof T, number>;
11
17
  readonly dependencies: string[];
12
- readonly parameterConfigurations: Record<keyof T, ParameterConfiguration>;
13
- readonly configuration: ResourceConfiguration<T>;
18
+ readonly parameterOptions: Record<keyof T, ParameterOptions>;
19
+ readonly options: ResourceOptions<T>;
14
20
  readonly defaultValues: Partial<Record<keyof T, unknown>>;
15
- protected constructor(configuration: ResourceConfiguration<T>);
21
+ protected ajv?: Ajv.default;
22
+ protected schemaValidator?: ValidateFunction;
23
+ protected constructor(options: ResourceOptions<T>);
16
24
  onInitialize(): Promise<void>;
25
+ validateResource(parameters: unknown): Promise<ValidationResult>;
17
26
  plan(desiredConfig: Partial<T> & ResourceConfig): Promise<Plan<T>>;
18
27
  apply(plan: Plan<T>): Promise<void>;
19
28
  private _applyCreate;
20
29
  private _applyModify;
21
30
  private _applyDestroy;
22
- private initializeParameterConfigurations;
23
- private initializeDefaultValues;
24
- private validateResourceConfiguration;
25
31
  private validateRefreshResults;
26
32
  private applyTransformParameters;
27
33
  private addDefaultValues;
28
- abstract validate(parameters: unknown): Promise<ValidationResult>;
34
+ private refreshResourceParameters;
35
+ private refreshStatefulParameters;
36
+ validate(parameters: unknown): Promise<ValidationResult>;
29
37
  abstract refresh(keys: Map<keyof T, T[keyof T]>): Promise<Partial<T> | null>;
30
38
  abstract applyCreate(plan: Plan<T>): Promise<void>;
31
39
  applyModify(parameterName: keyof T, newValue: unknown, previousValue: unknown, allowDeletes: boolean, plan: Plan<T>): Promise<void>;
@@ -1,62 +1,69 @@
1
1
  import { ParameterOperation, ResourceOperation, } from 'codify-schemas';
2
2
  import { Plan } from './plan.js';
3
3
  import { setsEqual, splitUserConfig } from '../utils/utils.js';
4
+ import { ResourceOptionsParser } from './resource-options.js';
5
+ import Ajv2020 from 'ajv/dist/2020.js';
4
6
  export class Resource {
5
7
  typeId;
6
8
  statefulParameters;
7
9
  transformParameters;
10
+ resourceParameters;
11
+ statefulParameterOrder;
12
+ transformParameterOrder;
8
13
  dependencies;
9
- parameterConfigurations;
10
- configuration;
14
+ parameterOptions;
15
+ options;
11
16
  defaultValues;
12
- constructor(configuration) {
13
- this.validateResourceConfiguration(configuration);
14
- this.typeId = configuration.type;
15
- this.statefulParameters = new Map(configuration.statefulParameters?.map((sp) => [sp.name, sp]));
16
- this.transformParameters = new Map(Object.entries(configuration.transformParameters ?? {}));
17
- this.parameterConfigurations = this.initializeParameterConfigurations(configuration);
18
- this.defaultValues = this.initializeDefaultValues(configuration);
19
- this.dependencies = configuration.dependencies ?? [];
20
- this.configuration = configuration;
17
+ ajv;
18
+ schemaValidator;
19
+ constructor(options) {
20
+ this.typeId = options.type;
21
+ this.dependencies = options.dependencies ?? [];
22
+ this.options = options;
23
+ if (this.options.schema) {
24
+ this.ajv = new Ajv2020.default({
25
+ strict: true,
26
+ strictRequired: false,
27
+ });
28
+ this.schemaValidator = this.ajv.compile(this.options.schema);
29
+ }
30
+ const parser = new ResourceOptionsParser(options);
31
+ this.statefulParameters = parser.statefulParameters;
32
+ this.transformParameters = parser.transformParameters;
33
+ this.resourceParameters = parser.resourceParameters;
34
+ this.parameterOptions = parser.changeSetParameterOptions;
35
+ this.defaultValues = parser.defaultValues;
36
+ this.statefulParameterOrder = parser.statefulParameterOrder;
37
+ this.transformParameterOrder = parser.transformParameterOrder;
21
38
  }
22
39
  async onInitialize() { }
40
+ async validateResource(parameters) {
41
+ if (this.schemaValidator) {
42
+ const isValid = this.schemaValidator(parameters);
43
+ if (!isValid) {
44
+ return {
45
+ isValid: false,
46
+ errors: this.schemaValidator?.errors ?? [],
47
+ };
48
+ }
49
+ }
50
+ return this.validate(parameters);
51
+ }
23
52
  async plan(desiredConfig) {
24
- const planConfiguration = {
53
+ const planOptions = {
25
54
  statefulMode: false,
26
- parameterConfigurations: this.parameterConfigurations,
55
+ parameterOptions: this.parameterOptions,
27
56
  };
28
- const { resourceMetadata, parameters: desiredParameters } = splitUserConfig(desiredConfig);
29
- this.addDefaultValues(desiredParameters);
30
- await this.applyTransformParameters(desiredParameters);
31
- const resourceParameters = Object.fromEntries([
32
- ...Object.entries(desiredParameters).filter(([key]) => !this.statefulParameters.has(key)),
33
- ]);
34
- const statefulParameters = [...this.statefulParameters.values()]
35
- .filter((sp) => desiredParameters[sp.name] !== undefined);
36
- const entriesToRefresh = new Map(Object.entries(resourceParameters));
37
- const currentParameters = await this.refresh(entriesToRefresh);
57
+ const parsedConfig = new ConfigParser(desiredConfig, this.statefulParameters, this.transformParameters);
58
+ const { parameters: desiredParameters, resourceMetadata, resourceParameters, statefulParameters } = parsedConfig;
59
+ this.addDefaultValues(resourceParameters);
60
+ await this.applyTransformParameters(resourceParameters);
61
+ const currentParameters = await this.refreshResourceParameters(resourceParameters);
38
62
  if (currentParameters == null) {
39
- return Plan.create(desiredParameters, null, resourceMetadata, planConfiguration);
40
- }
41
- this.validateRefreshResults(currentParameters, entriesToRefresh);
42
- for (const statefulParameter of statefulParameters) {
43
- const desiredValue = desiredParameters[statefulParameter.name];
44
- let currentValue = await statefulParameter.refresh(desiredValue ?? null) ?? undefined;
45
- if (Array.isArray(currentValue)
46
- && Array.isArray(desiredValue)
47
- && !planConfiguration.statefulMode
48
- && !statefulParameter.configuration.disableStatelessModeArrayFiltering) {
49
- currentValue = currentValue.filter((c) => desiredValue?.some((d) => {
50
- const pc = planConfiguration?.parameterConfigurations?.[statefulParameter.name];
51
- if (pc && pc.isElementEqual) {
52
- return pc.isElementEqual(d, c);
53
- }
54
- return d === c;
55
- }));
56
- }
57
- currentParameters[statefulParameter.name] = currentValue;
63
+ return Plan.create(desiredParameters, null, resourceMetadata, planOptions);
58
64
  }
59
- return Plan.create(desiredParameters, currentParameters, resourceMetadata, planConfiguration);
65
+ const statefulCurrentParameters = await this.refreshStatefulParameters(statefulParameters, planOptions.statefulMode);
66
+ return Plan.create({ ...resourceParameters, ...statefulParameters }, { ...currentParameters, ...statefulCurrentParameters }, resourceMetadata, planOptions);
60
67
  }
61
68
  async apply(plan) {
62
69
  if (plan.getResourceType() !== this.typeId) {
@@ -81,7 +88,8 @@ export class Resource {
81
88
  async _applyCreate(plan) {
82
89
  await this.applyCreate(plan);
83
90
  const statefulParameterChanges = plan.changeSet.parameterChanges
84
- .filter((pc) => this.statefulParameters.has(pc.name));
91
+ .filter((pc) => this.statefulParameters.has(pc.name))
92
+ .sort((a, b) => this.statefulParameterOrder.get(a.name) - this.statefulParameterOrder.get(b.name));
85
93
  for (const parameterChange of statefulParameterChanges) {
86
94
  const statefulParameter = this.statefulParameters.get(parameterChange.name);
87
95
  await statefulParameter.applyAdd(parameterChange.newValue, plan);
@@ -98,7 +106,8 @@ export class Resource {
98
106
  await this.applyModify(pc.name, pc.newValue, pc.previousValue, false, plan);
99
107
  }
100
108
  const statefulParameterChanges = parameterChanges
101
- .filter((pc) => this.statefulParameters.has(pc.name));
109
+ .filter((pc) => this.statefulParameters.has(pc.name))
110
+ .sort((a, b) => this.statefulParameterOrder.get(a.name) - this.statefulParameterOrder.get(b.name));
102
111
  for (const parameterChange of statefulParameterChanges) {
103
112
  const statefulParameter = this.statefulParameters.get(parameterChange.name);
104
113
  switch (parameterChange.operation) {
@@ -118,9 +127,10 @@ export class Resource {
118
127
  }
119
128
  }
120
129
  async _applyDestroy(plan) {
121
- if (this.configuration.callStatefulParameterRemoveOnDestroy) {
130
+ if (this.options.callStatefulParameterRemoveOnDestroy) {
122
131
  const statefulParameterChanges = plan.changeSet.parameterChanges
123
- .filter((pc) => this.statefulParameters.has(pc.name));
132
+ .filter((pc) => this.statefulParameters.has(pc.name))
133
+ .sort((a, b) => this.statefulParameterOrder.get(a.name) - this.statefulParameterOrder.get(b.name));
124
134
  for (const parameterChange of statefulParameterChanges) {
125
135
  const statefulParameter = this.statefulParameters.get(parameterChange.name);
126
136
  await statefulParameter.applyRemove(parameterChange.previousValue, plan);
@@ -128,42 +138,6 @@ export class Resource {
128
138
  }
129
139
  await this.applyDestroy(plan);
130
140
  }
131
- initializeParameterConfigurations(resourceConfiguration) {
132
- const resourceParameters = Object.fromEntries(Object.entries(resourceConfiguration.parameterConfigurations ?? {})
133
- ?.map(([name, value]) => ([name, { ...value, isStatefulParameter: false }])));
134
- const statefulParameters = resourceConfiguration.statefulParameters
135
- ?.reduce((obj, sp) => {
136
- return {
137
- ...obj,
138
- [sp.name]: {
139
- ...sp.configuration,
140
- isStatefulParameter: true,
141
- }
142
- };
143
- }, {}) ?? {};
144
- return {
145
- ...resourceParameters,
146
- ...statefulParameters,
147
- };
148
- }
149
- initializeDefaultValues(resourceConfiguration) {
150
- if (!resourceConfiguration.parameterConfigurations) {
151
- return {};
152
- }
153
- return Object.fromEntries(Object.entries(resourceConfiguration.parameterConfigurations)
154
- .filter((p) => p[1]?.defaultValue !== undefined)
155
- .map((config) => [config[0], config[1].defaultValue]));
156
- }
157
- validateResourceConfiguration(data) {
158
- if (data.parameterConfigurations && data.statefulParameters) {
159
- const parameters = [...Object.keys(data.parameterConfigurations)];
160
- const statefulParameterSet = new Set(data.statefulParameters.map((sp) => sp.name));
161
- const intersection = parameters.some((p) => statefulParameterSet.has(p));
162
- if (intersection) {
163
- throw new Error(`Resource ${this.typeId} cannot declare a parameter as both stateful and non-stateful`);
164
- }
165
- }
166
- }
167
141
  validateRefreshResults(refresh, desiredMap) {
168
142
  if (!refresh) {
169
143
  return;
@@ -171,14 +145,16 @@ export class Resource {
171
145
  const desiredKeys = new Set(desiredMap.keys());
172
146
  const refreshKeys = new Set(Object.keys(refresh));
173
147
  if (!setsEqual(desiredKeys, refreshKeys)) {
174
- throw new Error(`Resource ${this.configuration.type}
148
+ throw new Error(`Resource ${this.typeId}
175
149
  refresh() must return back exactly the keys that were provided
176
150
  Missing: ${[...desiredKeys].filter((k) => !refreshKeys.has(k))};
177
151
  Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
178
152
  }
179
153
  }
180
154
  async applyTransformParameters(desired) {
181
- for (const [key, tp] of this.transformParameters.entries()) {
155
+ const orderedEntries = [...this.transformParameters.entries()]
156
+ .sort(([keyA], [keyB]) => this.transformParameterOrder.get(keyA) - this.transformParameterOrder.get(keyB));
157
+ for (const [key, tp] of orderedEntries) {
182
158
  if (desired[key] !== null) {
183
159
  const transformedValue = await tp.transform(desired[key]);
184
160
  if (Object.keys(transformedValue).some((k) => desired[k] !== undefined)) {
@@ -199,6 +175,80 @@ Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`);
199
175
  }
200
176
  });
201
177
  }
178
+ async refreshResourceParameters(resourceParameters) {
179
+ const entriesToRefresh = new Map(Object.entries(resourceParameters));
180
+ const currentParameters = await this.refresh(entriesToRefresh);
181
+ this.validateRefreshResults(currentParameters, entriesToRefresh);
182
+ return currentParameters;
183
+ }
184
+ async refreshStatefulParameters(statefulParametersConfig, isStatefulMode) {
185
+ const currentParameters = {};
186
+ const sortedEntries = Object.entries(statefulParametersConfig)
187
+ .sort(([key1], [key2]) => this.statefulParameterOrder.get(key1) - this.statefulParameterOrder.get(key2));
188
+ for (const [key, desiredValue] of sortedEntries) {
189
+ const statefulParameter = this.statefulParameters.get(key);
190
+ if (!statefulParameter) {
191
+ throw new Error(`Stateful parameter ${key} was not found`);
192
+ }
193
+ let currentValue = await statefulParameter.refresh(desiredValue ?? null);
194
+ if (Array.isArray(currentValue)
195
+ && Array.isArray(desiredValue)
196
+ && !isStatefulMode
197
+ && !statefulParameter.options.disableStatelessModeArrayFiltering) {
198
+ currentValue = currentValue.filter((c) => desiredValue?.some((d) => {
199
+ const parameterOptions = statefulParameter.options;
200
+ if (parameterOptions && parameterOptions.isElementEqual) {
201
+ return parameterOptions.isElementEqual(d, c);
202
+ }
203
+ return d === c;
204
+ }));
205
+ }
206
+ currentParameters[key] = currentValue;
207
+ }
208
+ return currentParameters;
209
+ }
210
+ async validate(parameters) {
211
+ return {
212
+ isValid: true,
213
+ };
214
+ }
215
+ ;
202
216
  async applyModify(parameterName, newValue, previousValue, allowDeletes, plan) { }
203
217
  ;
204
218
  }
219
+ class ConfigParser {
220
+ config;
221
+ statefulParametersMap;
222
+ transformParametersMap;
223
+ constructor(config, statefulParameters, transformParameters) {
224
+ this.config = config;
225
+ this.statefulParametersMap = statefulParameters;
226
+ this.transformParametersMap = transformParameters;
227
+ }
228
+ get resourceMetadata() {
229
+ const { resourceMetadata } = splitUserConfig(this.config);
230
+ return resourceMetadata;
231
+ }
232
+ get parameters() {
233
+ const { parameters } = splitUserConfig(this.config);
234
+ return parameters;
235
+ }
236
+ get resourceParameters() {
237
+ const parameters = this.parameters;
238
+ return Object.fromEntries([
239
+ ...Object.entries(parameters).filter(([key]) => !(this.statefulParametersMap.has(key) || this.transformParametersMap.has(key))),
240
+ ]);
241
+ }
242
+ get statefulParameters() {
243
+ const parameters = this.parameters;
244
+ return Object.fromEntries([
245
+ ...Object.entries(parameters).filter(([key]) => this.statefulParametersMap.has(key)),
246
+ ]);
247
+ }
248
+ get transformParameters() {
249
+ const parameters = this.parameters;
250
+ return Object.fromEntries([
251
+ ...Object.entries(parameters).filter(([key]) => this.transformParametersMap.has(key)),
252
+ ]);
253
+ }
254
+ }