codify-plugin-lib 1.0.150 → 1.0.152

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.
@@ -39,6 +39,7 @@ export class Plugin {
39
39
  const resource = this.resourceControllers.get(data.type);
40
40
  const schema = resource.settings.schema;
41
41
  const requiredPropertyNames = (resource.settings.importAndDestroy?.requiredParameters
42
+ ?? (typeof resource.settings.allowMultiple === 'object' ? resource.settings.allowMultiple.identifyingParameters : null)
42
43
  ?? schema?.required
43
44
  ?? undefined);
44
45
  const allowMultiple = resource.settings.allowMultiple !== undefined
@@ -64,22 +65,7 @@ export class Plugin {
64
65
  if (!resource) {
65
66
  throw new Error(`Resource of type ${resourceConfig.core.type} could not be found for match`);
66
67
  }
67
- if (!resource.settings.allowMultiple) {
68
- return { match: array.find((r) => r.core.type === resourceConfig.core.type) };
69
- }
70
- const parameterMatcher = resource?.parsedSettings.matcher;
71
- const match = array.find((r) => {
72
- if (resourceConfig.core.type !== r.core.type) {
73
- return false;
74
- }
75
- // If the user specifies the same name for the resource and it's not auto-generated (a number) then it's the same resource
76
- if (resourceConfig.core.name === r.core.name
77
- && resourceConfig.core.name
78
- && Number.isInteger(Number.parseInt(resourceConfig.core.name, 10))) {
79
- return true;
80
- }
81
- return parameterMatcher(resourceConfig.parameters, r.parameters);
82
- });
68
+ const match = await resource.match(resourceConfig, array);
83
69
  return { match };
84
70
  }
85
71
  async import(data) {
@@ -121,10 +107,7 @@ export class Plugin {
121
107
  return !controller.parsedSettings.allowMultiple && v > 1;
122
108
  });
123
109
  if (invalidMultipleConfigs.length > 0) {
124
- throw new Error(`Multiples of the following configs were found but only 1 is allowed.
125
-
126
- [${invalidMultipleConfigs.map(([k]) => k).join(', ')}]
127
- `);
110
+ throw new Error(`Multiples of the following configs were found but only 1 is allowed. [${invalidMultipleConfigs.map(([k, v]) => `${v}x ${k}`).join(', ')}] found.`);
128
111
  }
129
112
  await this.crossValidateResources(data.configs);
130
113
  return {
@@ -15,6 +15,7 @@ export declare class ResourceController<T extends StringIndexedObject> {
15
15
  constructor(resource: Resource<T>);
16
16
  initialize(): Promise<void>;
17
17
  validate(core: ResourceConfig, parameters: Partial<T>): Promise<ValidateResponseData['resourceValidations'][0]>;
18
+ match(resource: ResourceJson, array: Array<ResourceJson>): Promise<ResourceJson | undefined>;
18
19
  plan(core: ResourceConfig, desired: Partial<T> | null, state: Partial<T> | null, isStateful?: boolean): Promise<Plan<T>>;
19
20
  planDestroy(core: ResourceConfig, parameters: Partial<T>): Promise<Plan<T>>;
20
21
  apply(plan: Plan<T>): Promise<void>;
@@ -71,6 +71,37 @@ export class ResourceController {
71
71
  schemaValidationErrors: [],
72
72
  };
73
73
  }
74
+ async match(resource, array) {
75
+ if (resource.core.type !== this.typeId) {
76
+ throw new Error(`Unknown type passed into match method: ${resource.core.type} for ${this.typeId}`);
77
+ }
78
+ if (!this.parsedSettings.allowMultiple) {
79
+ return array.find((r) => r.core.type === resource.core.type);
80
+ }
81
+ const { name, type } = resource.core;
82
+ const parameterMatcher = this.parsedSettings.matcher;
83
+ for (const resourceToMatch of array) {
84
+ if (type !== resourceToMatch.core.type) {
85
+ return undefined;
86
+ }
87
+ // If the user specifies the same name for the resource and it's not auto-generated (a number) then it's the same resource
88
+ if (name === resourceToMatch.core.name
89
+ && name
90
+ && Number.isInteger(Number.parseInt(name, 10))) {
91
+ return resourceToMatch;
92
+ }
93
+ const originalParams = structuredClone(resource.parameters);
94
+ const paramsToMatch = structuredClone(resourceToMatch.parameters);
95
+ this.addDefaultValues(originalParams);
96
+ await this.applyTransformParameters(originalParams);
97
+ this.addDefaultValues(paramsToMatch);
98
+ await this.applyTransformParameters(paramsToMatch);
99
+ const match = parameterMatcher(originalParams, paramsToMatch);
100
+ if (match) {
101
+ return resourceToMatch;
102
+ }
103
+ }
104
+ }
74
105
  async plan(core, desired, state, isStateful = false) {
75
106
  this.validatePlanInputs(core, desired, state, isStateful);
76
107
  this.addDefaultValues(desired);
@@ -96,7 +96,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
96
96
  preventImport?: boolean;
97
97
  /**
98
98
  * Customize the required parameters needed to import this resource. By default, the `requiredParameters` are taken
99
- * from the JSON schema. The `requiredParameters` parameter must be declared if a complex required is declared in
99
+ * from the identifyingParameters for allowMultiple. The `requiredParameters` parameter must be declared if a complex required is declared in
100
100
  * the schema (contains `oneOf`, `anyOf`, `allOf`, `if`, `then`, `else`).
101
101
  * <br>
102
102
  * The user will be prompted for the required parameters before the import starts. This is done because for most resources
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.150",
3
+ "version": "1.0.152",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -70,6 +70,7 @@ export class Plugin {
70
70
  const schema = resource.settings.schema as JSONSchemaType<any> | undefined;
71
71
  const requiredPropertyNames = (
72
72
  resource.settings.importAndDestroy?.requiredParameters
73
+ ?? (typeof resource.settings.allowMultiple === 'object' ? resource.settings.allowMultiple.identifyingParameters : null)
73
74
  ?? schema?.required
74
75
  ?? undefined
75
76
  ) as any;
@@ -101,27 +102,7 @@ export class Plugin {
101
102
  throw new Error(`Resource of type ${resourceConfig.core.type} could not be found for match`);
102
103
  }
103
104
 
104
- if (!resource.settings.allowMultiple) {
105
- return { match: array.find((r) => r.core.type === resourceConfig.core.type) }
106
- }
107
-
108
- const parameterMatcher = resource?.parsedSettings.matcher;
109
- const match = array.find((r) => {
110
- if (resourceConfig.core.type !== r.core.type) {
111
- return false;
112
- }
113
-
114
- // If the user specifies the same name for the resource and it's not auto-generated (a number) then it's the same resource
115
- if (resourceConfig.core.name === r.core.name
116
- && resourceConfig.core.name
117
- && Number.isInteger(Number.parseInt(resourceConfig.core.name, 10))
118
- ) {
119
- return true;
120
- }
121
-
122
- return parameterMatcher(resourceConfig.parameters, r.parameters);
123
- });
124
-
105
+ const match = await resource.match(resourceConfig, array);
125
106
  return { match }
126
107
  }
127
108
 
@@ -179,10 +160,7 @@ export class Plugin {
179
160
 
180
161
  if (invalidMultipleConfigs.length > 0) {
181
162
  throw new Error(
182
- `Multiples of the following configs were found but only 1 is allowed.
183
-
184
- [${invalidMultipleConfigs.map(([k]) => k).join(', ')}]
185
- `)
163
+ `Multiples of the following configs were found but only 1 is allowed. [${invalidMultipleConfigs.map(([k, v]) => `${v}x ${k}`).join(', ')}] found.`)
186
164
  }
187
165
 
188
166
  await this.crossValidateResources(data.configs);
@@ -102,6 +102,48 @@ export class ResourceController<T extends StringIndexedObject> {
102
102
  }
103
103
  }
104
104
 
105
+ async match(resource: ResourceJson, array: Array<ResourceJson>): Promise<ResourceJson | undefined> {
106
+ if (resource.core.type !== this.typeId) {
107
+ throw new Error(`Unknown type passed into match method: ${resource.core.type} for ${this.typeId}`);
108
+ }
109
+
110
+ if (!this.parsedSettings.allowMultiple) {
111
+ return array.find((r) => r.core.type === resource.core.type)
112
+ }
113
+
114
+
115
+ const { name, type } = resource.core;
116
+ const parameterMatcher = this.parsedSettings.matcher;
117
+
118
+ for (const resourceToMatch of array) {
119
+ if (type !== resourceToMatch.core.type) {
120
+ return undefined;
121
+ }
122
+
123
+ // If the user specifies the same name for the resource and it's not auto-generated (a number) then it's the same resource
124
+ if (name === resourceToMatch.core.name
125
+ && name
126
+ && Number.isInteger(Number.parseInt(name, 10))
127
+ ) {
128
+ return resourceToMatch;
129
+ }
130
+
131
+ const originalParams = structuredClone(resource.parameters) as Partial<T>;
132
+ const paramsToMatch = structuredClone(resourceToMatch.parameters) as Partial<T>;
133
+
134
+ this.addDefaultValues(originalParams);
135
+ await this.applyTransformParameters(originalParams);
136
+
137
+ this.addDefaultValues(paramsToMatch);
138
+ await this.applyTransformParameters(paramsToMatch);
139
+
140
+ const match = parameterMatcher(originalParams, paramsToMatch);
141
+ if (match) {
142
+ return resourceToMatch;
143
+ }
144
+ }
145
+ }
146
+
105
147
  async plan(
106
148
  core: ResourceConfig,
107
149
  desired: Partial<T> | null,
@@ -113,7 +113,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
113
113
 
114
114
  /**
115
115
  * Customize the required parameters needed to import this resource. By default, the `requiredParameters` are taken
116
- * from the JSON schema. The `requiredParameters` parameter must be declared if a complex required is declared in
116
+ * from the identifyingParameters for allowMultiple. The `requiredParameters` parameter must be declared if a complex required is declared in
117
117
  * the schema (contains `oneOf`, `anyOf`, `allOf`, `if`, `then`, `else`).
118
118
  * <br>
119
119
  * The user will be prompted for the required parameters before the import starts. This is done because for most resources