@codifycli/plugin-core 1.0.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -0
- package/.eslintrc.json +30 -0
- package/.github/workflows/release.yaml +19 -0
- package/.github/workflows/unit-test-ci.yaml +18 -0
- package/.prettierrc.json +1 -0
- package/bin/build.js +189 -0
- package/dist/bin/build.d.ts +1 -0
- package/dist/bin/build.js +80 -0
- package/dist/bin/deploy-plugin.d.ts +2 -0
- package/dist/bin/deploy-plugin.js +8 -0
- package/dist/common/errors.d.ts +8 -0
- package/dist/common/errors.js +24 -0
- package/dist/entities/change-set.d.ts +24 -0
- package/dist/entities/change-set.js +152 -0
- package/dist/entities/errors.d.ts +4 -0
- package/dist/entities/errors.js +7 -0
- package/dist/entities/plan-types.d.ts +25 -0
- package/dist/entities/plan-types.js +1 -0
- package/dist/entities/plan.d.ts +15 -0
- package/dist/entities/plan.js +127 -0
- package/dist/entities/plugin.d.ts +16 -0
- package/dist/entities/plugin.js +80 -0
- package/dist/entities/resource-options.d.ts +31 -0
- package/dist/entities/resource-options.js +76 -0
- package/dist/entities/resource-types.d.ts +11 -0
- package/dist/entities/resource-types.js +1 -0
- package/dist/entities/resource.d.ts +42 -0
- package/dist/entities/resource.js +303 -0
- package/dist/entities/stateful-parameter.d.ts +29 -0
- package/dist/entities/stateful-parameter.js +46 -0
- package/dist/entities/transform-parameter.d.ts +4 -0
- package/dist/entities/transform-parameter.js +2 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +7 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +26 -0
- package/dist/messages/handlers.d.ts +14 -0
- package/dist/messages/handlers.js +134 -0
- package/dist/messages/sender.d.ts +11 -0
- package/dist/messages/sender.js +57 -0
- package/dist/plan/change-set.d.ts +53 -0
- package/dist/plan/change-set.js +153 -0
- package/dist/plan/plan-types.d.ts +23 -0
- package/dist/plan/plan-types.js +1 -0
- package/dist/plan/plan.d.ts +66 -0
- package/dist/plan/plan.js +328 -0
- package/dist/plugin/plugin.d.ts +24 -0
- package/dist/plugin/plugin.js +200 -0
- package/dist/pty/background-pty.d.ts +21 -0
- package/dist/pty/background-pty.js +127 -0
- package/dist/pty/index.d.ts +50 -0
- package/dist/pty/index.js +20 -0
- package/dist/pty/promise-queue.d.ts +5 -0
- package/dist/pty/promise-queue.js +26 -0
- package/dist/pty/seqeuntial-pty.d.ts +17 -0
- package/dist/pty/seqeuntial-pty.js +119 -0
- package/dist/pty/vitest.config.d.ts +2 -0
- package/dist/pty/vitest.config.js +11 -0
- package/dist/resource/config-parser.d.ts +11 -0
- package/dist/resource/config-parser.js +21 -0
- package/dist/resource/parsed-resource-settings.d.ts +47 -0
- package/dist/resource/parsed-resource-settings.js +196 -0
- package/dist/resource/resource-controller.d.ts +36 -0
- package/dist/resource/resource-controller.js +402 -0
- package/dist/resource/resource-settings.d.ts +303 -0
- package/dist/resource/resource-settings.js +147 -0
- package/dist/resource/resource.d.ts +144 -0
- package/dist/resource/resource.js +44 -0
- package/dist/resource/stateful-parameter.d.ts +165 -0
- package/dist/resource/stateful-parameter.js +94 -0
- package/dist/scripts/deploy.d.ts +1 -0
- package/dist/scripts/deploy.js +2 -0
- package/dist/stateful-parameter/stateful-parameter-controller.d.ts +21 -0
- package/dist/stateful-parameter/stateful-parameter-controller.js +81 -0
- package/dist/stateful-parameter/stateful-parameter.d.ts +144 -0
- package/dist/stateful-parameter/stateful-parameter.js +43 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +5 -0
- package/dist/utils/codify-spawn.d.ts +29 -0
- package/dist/utils/codify-spawn.js +136 -0
- package/dist/utils/debug.d.ts +2 -0
- package/dist/utils/debug.js +10 -0
- package/dist/utils/file-utils.d.ts +23 -0
- package/dist/utils/file-utils.js +186 -0
- package/dist/utils/functions.d.ts +12 -0
- package/dist/utils/functions.js +74 -0
- package/dist/utils/index.d.ts +46 -0
- package/dist/utils/index.js +271 -0
- package/dist/utils/internal-utils.d.ts +12 -0
- package/dist/utils/internal-utils.js +74 -0
- package/dist/utils/load-resources.d.ts +1 -0
- package/dist/utils/load-resources.js +46 -0
- package/dist/utils/package-json-utils.d.ts +12 -0
- package/dist/utils/package-json-utils.js +34 -0
- package/dist/utils/pty-local-storage.d.ts +2 -0
- package/dist/utils/pty-local-storage.js +2 -0
- package/dist/utils/spawn-2.d.ts +5 -0
- package/dist/utils/spawn-2.js +7 -0
- package/dist/utils/spawn.d.ts +29 -0
- package/dist/utils/spawn.js +124 -0
- package/dist/utils/utils.d.ts +18 -0
- package/dist/utils/utils.js +86 -0
- package/dist/utils/verbosity-level.d.ts +5 -0
- package/dist/utils/verbosity-level.js +9 -0
- package/package.json +59 -0
- package/rollup.config.js +24 -0
- package/src/common/errors.test.ts +43 -0
- package/src/common/errors.ts +31 -0
- package/src/errors.ts +8 -0
- package/src/index.test.ts +6 -0
- package/src/index.ts +30 -0
- package/src/messages/handlers.test.ts +329 -0
- package/src/messages/handlers.ts +181 -0
- package/src/messages/sender.ts +69 -0
- package/src/plan/change-set.test.ts +280 -0
- package/src/plan/change-set.ts +236 -0
- package/src/plan/plan-types.ts +27 -0
- package/src/plan/plan.test.ts +413 -0
- package/src/plan/plan.ts +499 -0
- package/src/plugin/plugin.test.ts +533 -0
- package/src/plugin/plugin.ts +291 -0
- package/src/pty/background-pty.test.ts +69 -0
- package/src/pty/background-pty.ts +154 -0
- package/src/pty/index.test.ts +129 -0
- package/src/pty/index.ts +66 -0
- package/src/pty/promise-queue.ts +33 -0
- package/src/pty/seqeuntial-pty.ts +151 -0
- package/src/pty/sequential-pty.test.ts +194 -0
- package/src/resource/config-parser.ts +42 -0
- package/src/resource/parsed-resource-settings.test.ts +186 -0
- package/src/resource/parsed-resource-settings.ts +307 -0
- package/src/resource/resource-controller-stateful-mode.test.ts +253 -0
- package/src/resource/resource-controller.test.ts +1081 -0
- package/src/resource/resource-controller.ts +563 -0
- package/src/resource/resource-settings.test.ts +1213 -0
- package/src/resource/resource-settings.ts +545 -0
- package/src/resource/resource.ts +157 -0
- package/src/stateful-parameter/stateful-parameter-controller.test.ts +244 -0
- package/src/stateful-parameter/stateful-parameter-controller.ts +111 -0
- package/src/stateful-parameter/stateful-parameter.ts +160 -0
- package/src/utils/debug.ts +11 -0
- package/src/utils/file-utils.test.ts +7 -0
- package/src/utils/file-utils.ts +231 -0
- package/src/utils/functions.ts +103 -0
- package/src/utils/index.ts +340 -0
- package/src/utils/internal-utils.test.ts +52 -0
- package/src/utils/pty-local-storage.ts +3 -0
- package/src/utils/test-utils.test.ts +96 -0
- package/src/utils/verbosity-level.ts +11 -0
- package/tsconfig.json +26 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { JSONSchemaType } from 'ajv';
|
|
2
|
+
import { LinuxDistro, OS, StringIndexedObject } from 'codify-schemas';
|
|
3
|
+
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
4
|
+
import { ArrayParameterSetting, DefaultParameterSetting, InputTransformation, ResourceSettings } from './resource-settings.js';
|
|
5
|
+
export interface ParsedStatefulParameterSetting extends DefaultParameterSetting {
|
|
6
|
+
type: 'stateful';
|
|
7
|
+
controller: StatefulParameterController<any, unknown>;
|
|
8
|
+
order?: number;
|
|
9
|
+
nestedSettings: ParsedParameterSetting;
|
|
10
|
+
}
|
|
11
|
+
export type ParsedArrayParameterSetting = {
|
|
12
|
+
isElementEqual: (a: unknown, b: unknown) => boolean;
|
|
13
|
+
isEqual: (a: unknown, b: unknown) => boolean;
|
|
14
|
+
} & ArrayParameterSetting;
|
|
15
|
+
export type ParsedParameterSetting = {
|
|
16
|
+
isEqual: (desired: unknown, current: unknown) => boolean;
|
|
17
|
+
} & (DefaultParameterSetting | ParsedArrayParameterSetting | ParsedStatefulParameterSetting);
|
|
18
|
+
export declare class ParsedResourceSettings<T extends StringIndexedObject> implements ResourceSettings<T> {
|
|
19
|
+
private cache;
|
|
20
|
+
id: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
schema?: Partial<JSONSchemaType<T | any>>;
|
|
23
|
+
allowMultiple?: {
|
|
24
|
+
identifyingParameters?: string[];
|
|
25
|
+
matcher?: (desired: Partial<T>, current: Partial<T>) => boolean;
|
|
26
|
+
findAllParameters?: () => Promise<Array<Partial<T>>>;
|
|
27
|
+
} | boolean;
|
|
28
|
+
removeStatefulParametersBeforeDestroy?: boolean | undefined;
|
|
29
|
+
dependencies?: string[] | undefined;
|
|
30
|
+
transformation?: InputTransformation;
|
|
31
|
+
operatingSystems: Array<OS>;
|
|
32
|
+
linuxDistros?: Array<LinuxDistro>;
|
|
33
|
+
isSensitive?: boolean;
|
|
34
|
+
private settings;
|
|
35
|
+
constructor(settings: ResourceSettings<T>);
|
|
36
|
+
get typeId(): string;
|
|
37
|
+
get canDestroy(): boolean;
|
|
38
|
+
get statefulParameters(): Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
|
|
39
|
+
get parameterSettings(): Record<keyof T, ParsedParameterSetting>;
|
|
40
|
+
get defaultValues(): Partial<Record<keyof T, unknown>>;
|
|
41
|
+
get inputTransformations(): Partial<Record<keyof T, InputTransformation>>;
|
|
42
|
+
get statefulParameterOrder(): Map<keyof T, number>;
|
|
43
|
+
get matcher(): (desired: Partial<T>, current: Partial<T>) => boolean;
|
|
44
|
+
private validateSettings;
|
|
45
|
+
private validateParameterEqualsFn;
|
|
46
|
+
private getFromCacheOrCreate;
|
|
47
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ZodObject, z } from 'zod';
|
|
2
|
+
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
3
|
+
import { resolveElementEqualsFn, resolveEqualsFn, resolveMatcher, resolveParameterTransformFn } from './resource-settings.js';
|
|
4
|
+
export class ParsedResourceSettings {
|
|
5
|
+
cache = new Map();
|
|
6
|
+
id;
|
|
7
|
+
description;
|
|
8
|
+
schema;
|
|
9
|
+
allowMultiple;
|
|
10
|
+
removeStatefulParametersBeforeDestroy;
|
|
11
|
+
dependencies;
|
|
12
|
+
transformation;
|
|
13
|
+
operatingSystems;
|
|
14
|
+
linuxDistros;
|
|
15
|
+
isSensitive;
|
|
16
|
+
settings;
|
|
17
|
+
constructor(settings) {
|
|
18
|
+
this.settings = settings;
|
|
19
|
+
const { parameterSettings, schema, ...rest } = settings;
|
|
20
|
+
Object.assign(this, rest);
|
|
21
|
+
if (schema) {
|
|
22
|
+
this.schema = schema instanceof ZodObject
|
|
23
|
+
? z.toJSONSchema(schema.strict(), {
|
|
24
|
+
target: 'draft-7',
|
|
25
|
+
override(ctx) {
|
|
26
|
+
ctx.jsonSchema.title = settings.id;
|
|
27
|
+
ctx.jsonSchema.description = settings.description ?? `${settings.id} resource. Can be used to manage ${settings.id}`;
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
: schema;
|
|
31
|
+
}
|
|
32
|
+
this.validateSettings();
|
|
33
|
+
}
|
|
34
|
+
get typeId() {
|
|
35
|
+
return this.id;
|
|
36
|
+
}
|
|
37
|
+
get canDestroy() {
|
|
38
|
+
return !this.settings.importAndDestroy?.preventDestroy;
|
|
39
|
+
}
|
|
40
|
+
get statefulParameters() {
|
|
41
|
+
return this.getFromCacheOrCreate('statefulParameters', () => {
|
|
42
|
+
const statefulParameters = Object.entries(this.settings.parameterSettings ?? {})
|
|
43
|
+
.filter(([, p]) => p?.type === 'stateful')
|
|
44
|
+
.map(([k, v]) => [
|
|
45
|
+
k,
|
|
46
|
+
new StatefulParameterController(v.definition)
|
|
47
|
+
]);
|
|
48
|
+
return new Map(statefulParameters);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
get parameterSettings() {
|
|
52
|
+
return this.getFromCacheOrCreate('parameterSetting', () => {
|
|
53
|
+
const settings = Object.entries(this.settings.parameterSettings ?? {})
|
|
54
|
+
.map(([k, v]) => [k, v])
|
|
55
|
+
.map(([k, v]) => {
|
|
56
|
+
v.isEqual = resolveEqualsFn(v);
|
|
57
|
+
if (v.type === 'stateful') {
|
|
58
|
+
const spController = this.statefulParameters.get(k);
|
|
59
|
+
const parsed = {
|
|
60
|
+
...v,
|
|
61
|
+
controller: spController,
|
|
62
|
+
nestedSettings: spController?.parsedSettings,
|
|
63
|
+
};
|
|
64
|
+
return [k, parsed];
|
|
65
|
+
}
|
|
66
|
+
if (v.type === 'array') {
|
|
67
|
+
const parsed = {
|
|
68
|
+
...v,
|
|
69
|
+
isElementEqual: resolveElementEqualsFn(v)
|
|
70
|
+
};
|
|
71
|
+
return [k, parsed];
|
|
72
|
+
}
|
|
73
|
+
return [k, v];
|
|
74
|
+
});
|
|
75
|
+
return Object.fromEntries(settings);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
get defaultValues() {
|
|
79
|
+
return this.getFromCacheOrCreate('defaultValues', () => {
|
|
80
|
+
if (!this.settings.parameterSettings) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
const defaultValues = Object.fromEntries(Object.entries(this.settings.parameterSettings)
|
|
84
|
+
.filter(([, v]) => v.default !== undefined)
|
|
85
|
+
.map(([k, v]) => [k, v.default]));
|
|
86
|
+
const statefulParameterDefaultValues = Object.fromEntries(Object.entries(this.settings.parameterSettings)
|
|
87
|
+
.filter(([, v]) => v?.type === 'stateful')
|
|
88
|
+
.filter(([, v]) => v.definition.getSettings().default !== undefined)
|
|
89
|
+
.map(([k, v]) => [k, v.definition.getSettings().default]));
|
|
90
|
+
return { ...defaultValues, ...statefulParameterDefaultValues };
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
get inputTransformations() {
|
|
94
|
+
return this.getFromCacheOrCreate('inputTransformations', () => {
|
|
95
|
+
if (!this.settings.parameterSettings) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
return Object.fromEntries(Object.entries(this.settings.parameterSettings)
|
|
99
|
+
.filter(([_, v]) => resolveParameterTransformFn(v) !== undefined)
|
|
100
|
+
.map(([k, v]) => [k, resolveParameterTransformFn(v)]));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
get statefulParameterOrder() {
|
|
104
|
+
return this.getFromCacheOrCreate('stateParameterOrder', () => {
|
|
105
|
+
const entries = Object.entries(this.settings.parameterSettings ?? {})
|
|
106
|
+
.filter(([, v]) => v?.type === 'stateful')
|
|
107
|
+
.map(([k, v]) => [k, v]);
|
|
108
|
+
const orderedEntries = entries.filter(([, v]) => v.order !== undefined);
|
|
109
|
+
const unorderedEntries = entries.filter(([, v]) => v.order === undefined);
|
|
110
|
+
orderedEntries.sort((a, b) => a[1].order - b[1].order);
|
|
111
|
+
const resultArray = [
|
|
112
|
+
...orderedEntries.map(([k]) => k),
|
|
113
|
+
...unorderedEntries.map(([k]) => k)
|
|
114
|
+
];
|
|
115
|
+
return new Map(resultArray.map((key, idx) => [key, idx]));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
get matcher() {
|
|
119
|
+
return resolveMatcher(this);
|
|
120
|
+
}
|
|
121
|
+
validateSettings() {
|
|
122
|
+
// validate parameter settings
|
|
123
|
+
if (this.settings.parameterSettings) {
|
|
124
|
+
for (const [k, v] of Object.entries(this.settings.parameterSettings)) {
|
|
125
|
+
if (!v) {
|
|
126
|
+
throw new Error(`Resource: ${this.id}. Parameter setting ${k} was left undefined`);
|
|
127
|
+
}
|
|
128
|
+
this.validateParameterEqualsFn(v, k);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (Object.entries(this.parameterSettings).some(([k, v]) => v.type === 'stateful'
|
|
132
|
+
&& typeof this.settings.allowMultiple === 'object' && this.settings.allowMultiple?.identifyingParameters?.includes(k))) {
|
|
133
|
+
throw new Error(`Resource: ${this.id}. Stateful parameters are not allowed to be identifying parameters for allowMultiple.`);
|
|
134
|
+
}
|
|
135
|
+
const schema = this.schema;
|
|
136
|
+
if (!this.settings.importAndDestroy && (schema?.oneOf
|
|
137
|
+
&& Array.isArray(schema.oneOf)
|
|
138
|
+
&& schema.oneOf.some((s) => s.required))
|
|
139
|
+
|| (schema?.anyOf
|
|
140
|
+
&& Array.isArray(schema.anyOf)
|
|
141
|
+
&& schema.anyOf.some((s) => s.required))
|
|
142
|
+
|| (schema?.allOf
|
|
143
|
+
&& Array.isArray(schema.allOf)
|
|
144
|
+
&& schema.allOf.some((s) => s.required))
|
|
145
|
+
|| (schema?.then
|
|
146
|
+
&& Array.isArray(schema.then)
|
|
147
|
+
&& schema.then.some((s) => s.required))
|
|
148
|
+
|| (schema?.else
|
|
149
|
+
&& Array.isArray(schema.else)
|
|
150
|
+
&& schema.else.some((s) => s.required))) {
|
|
151
|
+
throw new Error(`In the schema of ${this.settings.id}, a conditional required was declared (within anyOf, allOf, oneOf, else, or then) but an` +
|
|
152
|
+
'import.requiredParameters was not found in the resource settings. This is required because Codify uses the required parameter to' +
|
|
153
|
+
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when ' +
|
|
154
|
+
'required is declared conditionally.');
|
|
155
|
+
}
|
|
156
|
+
if (this.settings.importAndDestroy) {
|
|
157
|
+
const { requiredParameters, refreshKeys, defaultRefreshValues } = this.settings.importAndDestroy;
|
|
158
|
+
const requiredParametersNotInSchema = requiredParameters
|
|
159
|
+
?.filter((p) => schema && !(schema.properties[p]));
|
|
160
|
+
if (schema && requiredParametersNotInSchema && requiredParametersNotInSchema.length > 0) {
|
|
161
|
+
throw new Error(`The following properties were declared in settings.import.requiredParameters but were not found in the schema:
|
|
162
|
+
${JSON.stringify(requiredParametersNotInSchema, null, 2)}`);
|
|
163
|
+
}
|
|
164
|
+
const refreshKeyNotInSchema = refreshKeys
|
|
165
|
+
?.filter((k) => schema && !(schema.properties[k]));
|
|
166
|
+
if (schema && refreshKeyNotInSchema && refreshKeyNotInSchema.length > 0) {
|
|
167
|
+
throw new Error(`The following properties were declared in settings.import.refreshKeys but were not found in the schema:
|
|
168
|
+
${JSON.stringify(requiredParametersNotInSchema, null, 2)}`);
|
|
169
|
+
}
|
|
170
|
+
const refreshValueNotInRefreshKey = Object.entries(defaultRefreshValues ?? {})
|
|
171
|
+
.filter(([k]) => schema && !(schema.properties[k])).map(([k]) => k);
|
|
172
|
+
if (schema && refreshValueNotInRefreshKey.length > 0) {
|
|
173
|
+
throw new Error(`Properties declared in defaultRefreshValues must already be declared in refreshKeys:
|
|
174
|
+
${JSON.stringify(refreshValueNotInRefreshKey, null, 2)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
validateParameterEqualsFn(parameter, key) {
|
|
179
|
+
if (parameter.type === 'stateful') {
|
|
180
|
+
const nestedSettings = parameter.definition.getSettings();
|
|
181
|
+
if (nestedSettings.type === 'stateful') {
|
|
182
|
+
throw new Error(`Nested stateful parameters are not allowed for ${key}`);
|
|
183
|
+
}
|
|
184
|
+
this.validateParameterEqualsFn(nestedSettings, key);
|
|
185
|
+
}
|
|
186
|
+
// The rest of the types have defaults set already
|
|
187
|
+
}
|
|
188
|
+
getFromCacheOrCreate(key, create) {
|
|
189
|
+
if (this.cache.has(key)) {
|
|
190
|
+
return this.cache.get(key);
|
|
191
|
+
}
|
|
192
|
+
const result = create();
|
|
193
|
+
this.cache.set(key, result);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Ajv, ValidateFunction } from 'ajv';
|
|
2
|
+
import { ResourceConfig, ResourceJson, StringIndexedObject, ValidateResponseData } from 'codify-schemas';
|
|
3
|
+
import { Plan } from '../plan/plan.js';
|
|
4
|
+
import { ParsedResourceSettings } from './parsed-resource-settings.js';
|
|
5
|
+
import { Resource } from './resource.js';
|
|
6
|
+
import { ResourceSettings } from './resource-settings.js';
|
|
7
|
+
export declare class ResourceController<T extends StringIndexedObject> {
|
|
8
|
+
readonly resource: Resource<T>;
|
|
9
|
+
readonly settings: ResourceSettings<T>;
|
|
10
|
+
readonly parsedSettings: ParsedResourceSettings<T>;
|
|
11
|
+
readonly typeId: string;
|
|
12
|
+
readonly dependencies: string[];
|
|
13
|
+
protected ajv?: Ajv;
|
|
14
|
+
protected schemaValidator?: ValidateFunction;
|
|
15
|
+
constructor(resource: Resource<T>);
|
|
16
|
+
initialize(): Promise<void>;
|
|
17
|
+
validate(core: ResourceConfig, parameters: Partial<T>): Promise<ValidateResponseData['resourceValidations'][0]>;
|
|
18
|
+
match(resource: ResourceJson, array: Array<ResourceJson>): Promise<ResourceJson | undefined>;
|
|
19
|
+
plan(core: ResourceConfig, desired: Partial<T> | null, state: Partial<T> | null, isStateful?: boolean, commandType?: string): Promise<Plan<T>>;
|
|
20
|
+
planDestroy(core: ResourceConfig, parameters: Partial<T>): Promise<Plan<T>>;
|
|
21
|
+
apply(plan: Plan<T>): Promise<void>;
|
|
22
|
+
import(core: ResourceConfig, parameters: Partial<T>, autoSearchAll?: boolean): Promise<Array<ResourceJson> | null>;
|
|
23
|
+
private applyCreate;
|
|
24
|
+
private applyModify;
|
|
25
|
+
private applyDestroy;
|
|
26
|
+
private validateRefreshResults;
|
|
27
|
+
private applyTransformations;
|
|
28
|
+
private addDefaultValues;
|
|
29
|
+
private removeDefaultValues;
|
|
30
|
+
private refreshNonStatefulParameters;
|
|
31
|
+
private refreshStatefulParameters;
|
|
32
|
+
private validatePlanInputs;
|
|
33
|
+
private getSortedStatefulParameterChanges;
|
|
34
|
+
private getAllParameterKeys;
|
|
35
|
+
private getParametersToRefreshForImport;
|
|
36
|
+
}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
2
|
+
import cleanDeep from 'clean-deep';
|
|
3
|
+
import { ParameterOperation, ResourceOperation } from 'codify-schemas';
|
|
4
|
+
import { Plan } from '../plan/plan.js';
|
|
5
|
+
import { ConfigParser } from './config-parser.js';
|
|
6
|
+
import { ParsedResourceSettings } from './parsed-resource-settings.js';
|
|
7
|
+
export class ResourceController {
|
|
8
|
+
resource;
|
|
9
|
+
settings;
|
|
10
|
+
parsedSettings;
|
|
11
|
+
typeId;
|
|
12
|
+
dependencies;
|
|
13
|
+
ajv;
|
|
14
|
+
schemaValidator;
|
|
15
|
+
constructor(resource) {
|
|
16
|
+
this.resource = resource;
|
|
17
|
+
this.settings = resource.getSettings();
|
|
18
|
+
this.typeId = this.settings.id;
|
|
19
|
+
this.dependencies = this.settings.dependencies ?? [];
|
|
20
|
+
this.parsedSettings = new ParsedResourceSettings(this.settings);
|
|
21
|
+
if (this.parsedSettings.schema) {
|
|
22
|
+
this.ajv = new Ajv({
|
|
23
|
+
allErrors: true,
|
|
24
|
+
strict: true,
|
|
25
|
+
strictRequired: false,
|
|
26
|
+
allowUnionTypes: true
|
|
27
|
+
});
|
|
28
|
+
this.schemaValidator = this.ajv.compile(this.parsedSettings.schema);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async initialize() {
|
|
32
|
+
return this.resource.initialize();
|
|
33
|
+
}
|
|
34
|
+
async validate(core, parameters) {
|
|
35
|
+
const originalParameters = structuredClone(parameters);
|
|
36
|
+
await this.applyTransformations(parameters, undefined, true);
|
|
37
|
+
this.addDefaultValues(parameters);
|
|
38
|
+
if (this.schemaValidator) {
|
|
39
|
+
// Schema validator uses pre transformation parameters
|
|
40
|
+
const isValid = this.schemaValidator(
|
|
41
|
+
// @ts-expect-error Non esm package
|
|
42
|
+
cleanDeep(originalParameters, {
|
|
43
|
+
nullValues: true,
|
|
44
|
+
undefinedValues: true,
|
|
45
|
+
emptyArrays: false,
|
|
46
|
+
emptyStrings: false,
|
|
47
|
+
emptyObjects: false,
|
|
48
|
+
NaNValues: false
|
|
49
|
+
}));
|
|
50
|
+
if (!isValid) {
|
|
51
|
+
return {
|
|
52
|
+
isValid: false,
|
|
53
|
+
resourceName: core.name,
|
|
54
|
+
resourceType: core.type,
|
|
55
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let isValid = true;
|
|
60
|
+
let customValidationErrorMessage;
|
|
61
|
+
try {
|
|
62
|
+
await this.resource.validate(parameters);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
isValid = false;
|
|
66
|
+
customValidationErrorMessage = error.message;
|
|
67
|
+
}
|
|
68
|
+
if (!isValid) {
|
|
69
|
+
return {
|
|
70
|
+
customValidationErrorMessage,
|
|
71
|
+
isValid: false,
|
|
72
|
+
resourceName: core.name,
|
|
73
|
+
resourceType: core.type,
|
|
74
|
+
schemaValidationErrors: this.schemaValidator?.errors ?? [],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
isValid: true,
|
|
79
|
+
resourceName: core.name,
|
|
80
|
+
resourceType: core.type,
|
|
81
|
+
schemaValidationErrors: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async match(resource, array) {
|
|
85
|
+
if (resource.core.type !== this.typeId) {
|
|
86
|
+
throw new Error(`Unknown type passed into match method: ${resource.core.type} for ${this.typeId}`);
|
|
87
|
+
}
|
|
88
|
+
if (!this.parsedSettings.allowMultiple) {
|
|
89
|
+
return array.find((r) => r.core.type === resource.core.type);
|
|
90
|
+
}
|
|
91
|
+
const { name, type } = resource.core;
|
|
92
|
+
const parameterMatcher = this.parsedSettings.matcher;
|
|
93
|
+
for (const resourceToMatch of array) {
|
|
94
|
+
if (type !== resourceToMatch.core.type) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
// If the user specifies the same name for the resource and it's not auto-generated (a number) then it's the same resource
|
|
98
|
+
if (name === resourceToMatch.core.name
|
|
99
|
+
&& name
|
|
100
|
+
&& Number.isInteger(Number.parseInt(name, 10))) {
|
|
101
|
+
return resourceToMatch;
|
|
102
|
+
}
|
|
103
|
+
const originalParams = structuredClone(resource.parameters);
|
|
104
|
+
const paramsToMatch = structuredClone(resourceToMatch.parameters);
|
|
105
|
+
this.addDefaultValues(originalParams);
|
|
106
|
+
await this.applyTransformations(originalParams);
|
|
107
|
+
this.addDefaultValues(paramsToMatch);
|
|
108
|
+
await this.applyTransformations(paramsToMatch);
|
|
109
|
+
const match = parameterMatcher(originalParams, paramsToMatch);
|
|
110
|
+
if (match) {
|
|
111
|
+
return resourceToMatch;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async plan(core, desired, state, isStateful = false, commandType = 'plan') {
|
|
116
|
+
this.validatePlanInputs(core, desired, state, isStateful);
|
|
117
|
+
const context = {
|
|
118
|
+
commandType: commandType,
|
|
119
|
+
isStateful,
|
|
120
|
+
originalDesiredConfig: structuredClone(desired),
|
|
121
|
+
};
|
|
122
|
+
this.addDefaultValues(desired);
|
|
123
|
+
await this.applyTransformations(desired);
|
|
124
|
+
this.addDefaultValues(state);
|
|
125
|
+
await this.applyTransformations(state);
|
|
126
|
+
// Parse data from the user supplied config
|
|
127
|
+
const parsedConfig = new ConfigParser(desired, state, this.parsedSettings.statefulParameters);
|
|
128
|
+
const { allParameters, allNonStatefulParameters, allStatefulParameters, } = parsedConfig;
|
|
129
|
+
// Refresh resource parameters. This refreshes the parameters that configure the resource itself
|
|
130
|
+
const currentArray = await this.refreshNonStatefulParameters(allNonStatefulParameters, context);
|
|
131
|
+
// Short circuit here. If the resource is non-existent, there's no point checking stateful parameters
|
|
132
|
+
if (currentArray === null
|
|
133
|
+
|| currentArray === undefined
|
|
134
|
+
|| currentArray.length === 0
|
|
135
|
+
|| currentArray.filter(Boolean).length === 0) {
|
|
136
|
+
return Plan.calculate({
|
|
137
|
+
desired,
|
|
138
|
+
currentArray,
|
|
139
|
+
state,
|
|
140
|
+
core,
|
|
141
|
+
settings: this.parsedSettings,
|
|
142
|
+
isStateful,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Refresh stateful parameters. These parameters have state external to the resource. Each variation of the
|
|
146
|
+
// current parameters (each array element) is passed into the stateful parameter refresh.
|
|
147
|
+
const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, currentArray, allParameters);
|
|
148
|
+
return Plan.calculate({
|
|
149
|
+
desired,
|
|
150
|
+
currentArray: currentArray.map((c, idx) => ({ ...c, ...statefulCurrentParameters[idx] })),
|
|
151
|
+
state,
|
|
152
|
+
core,
|
|
153
|
+
settings: this.parsedSettings,
|
|
154
|
+
isStateful
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async planDestroy(core, parameters) {
|
|
158
|
+
this.addDefaultValues(parameters);
|
|
159
|
+
await this.applyTransformations(parameters);
|
|
160
|
+
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
161
|
+
const parametersToRefresh = this.settings.importAndDestroy?.refreshKeys
|
|
162
|
+
? {
|
|
163
|
+
...Object.fromEntries(this.settings.importAndDestroy?.refreshKeys.map((k) => [k, null])),
|
|
164
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
165
|
+
...parameters,
|
|
166
|
+
}
|
|
167
|
+
: {
|
|
168
|
+
...Object.fromEntries(this.getAllParameterKeys().map((k) => [k, null])),
|
|
169
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
170
|
+
...parameters,
|
|
171
|
+
};
|
|
172
|
+
return this.plan(core, null, parametersToRefresh, true);
|
|
173
|
+
}
|
|
174
|
+
async apply(plan) {
|
|
175
|
+
if (plan.getResourceType() !== this.typeId) {
|
|
176
|
+
throw new Error(`Internal error: Plan set to wrong resource during apply. Expected ${this.typeId} but got: ${plan.getResourceType()}`);
|
|
177
|
+
}
|
|
178
|
+
switch (plan.changeSet.operation) {
|
|
179
|
+
case ResourceOperation.CREATE: {
|
|
180
|
+
return this.applyCreate(plan);
|
|
181
|
+
}
|
|
182
|
+
case ResourceOperation.MODIFY: {
|
|
183
|
+
return this.applyModify(plan);
|
|
184
|
+
}
|
|
185
|
+
case ResourceOperation.RECREATE: {
|
|
186
|
+
await this.applyDestroy(plan);
|
|
187
|
+
return this.applyCreate(plan);
|
|
188
|
+
}
|
|
189
|
+
case ResourceOperation.DESTROY: {
|
|
190
|
+
return this.applyDestroy(plan);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async import(core, parameters, autoSearchAll = false) {
|
|
195
|
+
if (this.settings.importAndDestroy?.preventImport) {
|
|
196
|
+
throw new Error(`Type: ${this.typeId} cannot be imported`);
|
|
197
|
+
}
|
|
198
|
+
const context = {
|
|
199
|
+
commandType: 'import',
|
|
200
|
+
isStateful: true,
|
|
201
|
+
originalDesiredConfig: structuredClone(parameters),
|
|
202
|
+
};
|
|
203
|
+
// Auto search means that no required parameters will be provided. We will try to generate it ourselves or return an
|
|
204
|
+
// empty array if they can't be.
|
|
205
|
+
if (autoSearchAll && this.settings.allowMultiple) {
|
|
206
|
+
if (this.settings.allowMultiple === true || !this.settings.allowMultiple.findAllParameters?.()) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const parametersToImport = await this.settings.allowMultiple.findAllParameters?.();
|
|
210
|
+
const results = await Promise.all(parametersToImport.map((p) => this.import(core, p).catch(() => null)));
|
|
211
|
+
return results.filter(Boolean).flat();
|
|
212
|
+
}
|
|
213
|
+
this.addDefaultValues(parameters);
|
|
214
|
+
await this.applyTransformations(parameters);
|
|
215
|
+
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
216
|
+
const parametersToRefresh = this.getParametersToRefreshForImport(parameters, context);
|
|
217
|
+
// Parse data from the user supplied config
|
|
218
|
+
const parsedConfig = new ConfigParser(parametersToRefresh, null, this.parsedSettings.statefulParameters);
|
|
219
|
+
const { allParameters, allNonStatefulParameters, allStatefulParameters, } = parsedConfig;
|
|
220
|
+
const currentParametersArray = await this.refreshNonStatefulParameters(allNonStatefulParameters, context);
|
|
221
|
+
if (currentParametersArray === null
|
|
222
|
+
|| currentParametersArray === undefined
|
|
223
|
+
|| currentParametersArray.filter(Boolean).length === 0) {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
const statefulCurrentParameters = await this.refreshStatefulParameters(allStatefulParameters, currentParametersArray, allParameters);
|
|
227
|
+
const resultParametersArray = currentParametersArray
|
|
228
|
+
?.map((r, idx) => ({ ...r, ...statefulCurrentParameters[idx] }));
|
|
229
|
+
for (const result of resultParametersArray) {
|
|
230
|
+
await this.applyTransformations(result, { original: context.originalDesiredConfig });
|
|
231
|
+
this.removeDefaultValues(result, parameters);
|
|
232
|
+
}
|
|
233
|
+
return resultParametersArray?.map((r) => ({ core, parameters: r }));
|
|
234
|
+
}
|
|
235
|
+
async applyCreate(plan) {
|
|
236
|
+
await this.resource.create(plan);
|
|
237
|
+
const statefulParameterChanges = this.getSortedStatefulParameterChanges(plan.changeSet.parameterChanges);
|
|
238
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
239
|
+
const statefulParameter = this.parsedSettings.statefulParameters.get(parameterChange.name);
|
|
240
|
+
await statefulParameter.add(parameterChange.newValue, plan);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async applyModify(plan) {
|
|
244
|
+
const parameterChanges = plan
|
|
245
|
+
.changeSet
|
|
246
|
+
.parameterChanges
|
|
247
|
+
.filter((c) => c.operation !== ParameterOperation.NOOP);
|
|
248
|
+
const statelessParameterChanges = parameterChanges
|
|
249
|
+
.filter((pc) => !this.parsedSettings.statefulParameters.has(pc.name));
|
|
250
|
+
for (const pc of statelessParameterChanges) {
|
|
251
|
+
await this.resource.modify(pc, plan);
|
|
252
|
+
}
|
|
253
|
+
const statefulParameterChanges = this.getSortedStatefulParameterChanges(plan.changeSet.parameterChanges);
|
|
254
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
255
|
+
const statefulParameter = this.parsedSettings.statefulParameters.get(parameterChange.name);
|
|
256
|
+
switch (parameterChange.operation) {
|
|
257
|
+
case ParameterOperation.ADD: {
|
|
258
|
+
await statefulParameter.add(parameterChange.newValue, plan);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case ParameterOperation.MODIFY: {
|
|
262
|
+
await statefulParameter.modify(parameterChange.newValue, parameterChange.previousValue, plan);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case ParameterOperation.REMOVE: {
|
|
266
|
+
await statefulParameter.remove(parameterChange.previousValue, plan);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async applyDestroy(plan) {
|
|
273
|
+
// If this option is set (defaults to false), then stateful parameters need to be destroyed
|
|
274
|
+
// as well. This means that the stateful parameter wouldn't have been normally destroyed with applyDestroy()
|
|
275
|
+
if (this.settings.removeStatefulParametersBeforeDestroy) {
|
|
276
|
+
const statefulParameterChanges = this.getSortedStatefulParameterChanges(plan.changeSet.parameterChanges);
|
|
277
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
278
|
+
const statefulParameter = this.parsedSettings.statefulParameters.get(parameterChange.name);
|
|
279
|
+
await statefulParameter.remove(parameterChange.previousValue, plan);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
await this.resource.destroy(plan);
|
|
283
|
+
}
|
|
284
|
+
validateRefreshResults(refresh) {
|
|
285
|
+
if (!refresh) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (!this.settings.allowMultiple && refresh.length > 1) {
|
|
289
|
+
throw new Error(`Resource: ${this.settings.id}. Allow multiple was set to false but multiple refresh results were returned.
|
|
290
|
+
|
|
291
|
+
${JSON.stringify(refresh, null, 2)}
|
|
292
|
+
`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async applyTransformations(config, reverse, skipConfigTransformation = false) {
|
|
296
|
+
if (!config) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
for (const [key, inputTransformation] of Object.entries(this.parsedSettings.inputTransformations)) {
|
|
300
|
+
if (config[key] === undefined || !inputTransformation) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
config[key] = reverse
|
|
304
|
+
? await inputTransformation.from(config[key], reverse.original?.[key])
|
|
305
|
+
: await inputTransformation.to(config[key]);
|
|
306
|
+
}
|
|
307
|
+
if (this.settings.transformation && !skipConfigTransformation) {
|
|
308
|
+
const transformed = reverse
|
|
309
|
+
? await this.settings.transformation.from({ ...config }, reverse.original)
|
|
310
|
+
: await this.settings.transformation.to({ ...config });
|
|
311
|
+
Object.keys(config).forEach((k) => delete config[k]);
|
|
312
|
+
Object.assign(config, transformed);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
addDefaultValues(config) {
|
|
316
|
+
if (!config) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
for (const [key, defaultValue] of Object.entries(this.parsedSettings.defaultValues)) {
|
|
320
|
+
if (defaultValue !== undefined && (config[key] === undefined || config[key] === null)) {
|
|
321
|
+
config[key] = defaultValue;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
removeDefaultValues(newConfig, originalConfig) {
|
|
326
|
+
if (!newConfig) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
for (const [key, defaultValue] of Object.entries(this.parsedSettings.defaultValues)) {
|
|
330
|
+
if (defaultValue !== undefined && (newConfig[key] === defaultValue || originalConfig[key] === undefined || originalConfig[key] === null)) {
|
|
331
|
+
delete newConfig[key];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async refreshNonStatefulParameters(resourceParameters, context) {
|
|
336
|
+
const result = await this.resource.refresh(resourceParameters, context);
|
|
337
|
+
const currentParametersArray = Array.isArray(result) || result === null
|
|
338
|
+
? result
|
|
339
|
+
: [result];
|
|
340
|
+
this.validateRefreshResults(currentParametersArray);
|
|
341
|
+
return currentParametersArray;
|
|
342
|
+
}
|
|
343
|
+
// Refresh stateful parameters
|
|
344
|
+
// This refreshes parameters that are stateful (they can be added, deleted separately from the resource)
|
|
345
|
+
async refreshStatefulParameters(statefulParametersConfig, currentArray, allParameters) {
|
|
346
|
+
const result = Array.from({ length: currentArray.length }, () => ({}));
|
|
347
|
+
const sortedEntries = Object.entries(statefulParametersConfig)
|
|
348
|
+
.sort(([key1], [key2]) => this.parsedSettings.statefulParameterOrder.get(key1) - this.parsedSettings.statefulParameterOrder.get(key2));
|
|
349
|
+
for (const [idx, refreshedParams] of currentArray.entries()) {
|
|
350
|
+
await Promise.all(sortedEntries.map(async ([key, desiredValue]) => {
|
|
351
|
+
const statefulParameter = this.parsedSettings.statefulParameters.get(key);
|
|
352
|
+
if (!statefulParameter) {
|
|
353
|
+
throw new Error(`Stateful parameter ${key} was not found`);
|
|
354
|
+
}
|
|
355
|
+
result[idx][key] = await statefulParameter.refresh(desiredValue ?? null, { ...allParameters, ...refreshedParams });
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
validatePlanInputs(core, desired, current, isStateful) {
|
|
361
|
+
if (!core || !core.type) {
|
|
362
|
+
throw new Error('Core parameters type must be defined');
|
|
363
|
+
}
|
|
364
|
+
if (!desired && !current) {
|
|
365
|
+
throw new Error('Desired config and current config cannot both be missing');
|
|
366
|
+
}
|
|
367
|
+
if (!isStateful && !desired) {
|
|
368
|
+
throw new Error('Desired config must be provided in non-stateful mode');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
getSortedStatefulParameterChanges(parameterChanges) {
|
|
372
|
+
return parameterChanges
|
|
373
|
+
.filter((pc) => this.parsedSettings.statefulParameters.has(pc.name))
|
|
374
|
+
.sort((a, b) => this.parsedSettings.statefulParameterOrder.get(a.name) - this.parsedSettings.statefulParameterOrder.get(b.name));
|
|
375
|
+
}
|
|
376
|
+
getAllParameterKeys() {
|
|
377
|
+
return this.parsedSettings.schema
|
|
378
|
+
? Object.keys(this.parsedSettings.schema?.properties)
|
|
379
|
+
: Object.keys(this.parsedSettings.parameterSettings);
|
|
380
|
+
}
|
|
381
|
+
getParametersToRefreshForImport(parameters, context) {
|
|
382
|
+
if (this.settings.importAndDestroy?.refreshMapper) {
|
|
383
|
+
return this.settings.importAndDestroy?.refreshMapper(parameters, context);
|
|
384
|
+
}
|
|
385
|
+
return this.settings.importAndDestroy?.refreshKeys
|
|
386
|
+
? {
|
|
387
|
+
...Object.fromEntries(this.settings.importAndDestroy?.refreshKeys.map((k) => [k, null])),
|
|
388
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
389
|
+
...parameters,
|
|
390
|
+
...(Object.fromEntries(// If a default value was used, but it was also declared in the defaultRefreshValues, prefer the defaultRefreshValue instead
|
|
391
|
+
Object.entries(parameters).filter(([k, v]) => this.parsedSettings.defaultValues[k] !== undefined
|
|
392
|
+
&& v === this.parsedSettings.defaultValues[k]
|
|
393
|
+
&& context.originalDesiredConfig?.[k] === undefined
|
|
394
|
+
&& this.settings.importAndDestroy?.defaultRefreshValues?.[k] !== undefined).map(([k]) => [k, this.settings.importAndDestroy.defaultRefreshValues[k]])))
|
|
395
|
+
}
|
|
396
|
+
: {
|
|
397
|
+
...Object.fromEntries(this.getAllParameterKeys().map((k) => [k, null])),
|
|
398
|
+
...this.settings.importAndDestroy?.defaultRefreshValues,
|
|
399
|
+
...parameters,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|