@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,307 @@
|
|
|
1
|
+
import { JSONSchemaType } from 'ajv';
|
|
2
|
+
import { LinuxDistro, OS, StringIndexedObject } from 'codify-schemas';
|
|
3
|
+
import { ZodObject, z } from 'zod';
|
|
4
|
+
|
|
5
|
+
import { StatefulParameterController } from '../stateful-parameter/stateful-parameter-controller.js';
|
|
6
|
+
import {
|
|
7
|
+
ArrayParameterSetting,
|
|
8
|
+
DefaultParameterSetting,
|
|
9
|
+
InputTransformation,
|
|
10
|
+
ParameterSetting,
|
|
11
|
+
ResourceSettings,
|
|
12
|
+
StatefulParameterSetting,
|
|
13
|
+
resolveElementEqualsFn,
|
|
14
|
+
resolveEqualsFn,
|
|
15
|
+
resolveMatcher,
|
|
16
|
+
resolveParameterTransformFn
|
|
17
|
+
} from './resource-settings.js';
|
|
18
|
+
|
|
19
|
+
export interface ParsedStatefulParameterSetting extends DefaultParameterSetting {
|
|
20
|
+
type: 'stateful',
|
|
21
|
+
controller: StatefulParameterController<any, unknown>
|
|
22
|
+
order?: number,
|
|
23
|
+
nestedSettings: ParsedParameterSetting;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type ParsedArrayParameterSetting = {
|
|
27
|
+
isElementEqual: (a: unknown, b: unknown) => boolean;
|
|
28
|
+
isEqual: (a: unknown, b: unknown) => boolean;
|
|
29
|
+
} & ArrayParameterSetting
|
|
30
|
+
|
|
31
|
+
export type ParsedParameterSetting =
|
|
32
|
+
{
|
|
33
|
+
isEqual: (desired: unknown, current: unknown) => boolean;
|
|
34
|
+
} & (DefaultParameterSetting
|
|
35
|
+
| ParsedArrayParameterSetting
|
|
36
|
+
| ParsedStatefulParameterSetting)
|
|
37
|
+
|
|
38
|
+
export class ParsedResourceSettings<T extends StringIndexedObject> implements ResourceSettings<T> {
|
|
39
|
+
private cache = new Map<string, unknown>();
|
|
40
|
+
id!: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
|
|
43
|
+
schema?: Partial<JSONSchemaType<T | any>>;
|
|
44
|
+
allowMultiple?: {
|
|
45
|
+
identifyingParameters?: string[];
|
|
46
|
+
matcher?: (desired: Partial<T>, current: Partial<T>) => boolean;
|
|
47
|
+
findAllParameters?: () => Promise<Array<Partial<T>>>
|
|
48
|
+
} | boolean;
|
|
49
|
+
|
|
50
|
+
removeStatefulParametersBeforeDestroy?: boolean | undefined;
|
|
51
|
+
dependencies?: string[] | undefined;
|
|
52
|
+
transformation?: InputTransformation;
|
|
53
|
+
|
|
54
|
+
operatingSystems!: Array<OS>;
|
|
55
|
+
linuxDistros?: Array<LinuxDistro>;
|
|
56
|
+
|
|
57
|
+
isSensitive?: boolean;
|
|
58
|
+
|
|
59
|
+
private settings: ResourceSettings<T>;
|
|
60
|
+
|
|
61
|
+
constructor(settings: ResourceSettings<T>) {
|
|
62
|
+
this.settings = settings;
|
|
63
|
+
const { parameterSettings, schema, ...rest } = settings;
|
|
64
|
+
|
|
65
|
+
Object.assign(this, rest);
|
|
66
|
+
|
|
67
|
+
if (schema) {
|
|
68
|
+
this.schema = schema instanceof ZodObject
|
|
69
|
+
? z.toJSONSchema(schema.strict(), {
|
|
70
|
+
target: 'draft-7',
|
|
71
|
+
override(ctx) {
|
|
72
|
+
ctx.jsonSchema.title = settings.id;
|
|
73
|
+
ctx.jsonSchema.description = settings.description ?? `${settings.id} resource. Can be used to manage ${settings.id}`;
|
|
74
|
+
}
|
|
75
|
+
}) as JSONSchemaType<T>
|
|
76
|
+
: schema;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.validateSettings();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get typeId(): string {
|
|
83
|
+
return this.id;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get canDestroy(): boolean {
|
|
87
|
+
return !this.settings.importAndDestroy?.preventDestroy;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get statefulParameters(): Map<keyof T, StatefulParameterController<T, T[keyof T]>> {
|
|
91
|
+
return this.getFromCacheOrCreate('statefulParameters', () => {
|
|
92
|
+
|
|
93
|
+
const statefulParameters = Object.entries(this.settings.parameterSettings ?? {})
|
|
94
|
+
.filter(([, p]) => p?.type === 'stateful')
|
|
95
|
+
.map(([k, v]) => [
|
|
96
|
+
k,
|
|
97
|
+
new StatefulParameterController((v as StatefulParameterSetting).definition)
|
|
98
|
+
] as const)
|
|
99
|
+
|
|
100
|
+
return new Map(statefulParameters) as Map<keyof T, StatefulParameterController<T, T[keyof T]>>;
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get parameterSettings(): Record<keyof T, ParsedParameterSetting> {
|
|
105
|
+
return this.getFromCacheOrCreate('parameterSetting', () => {
|
|
106
|
+
|
|
107
|
+
const settings = Object.entries(this.settings.parameterSettings ?? {})
|
|
108
|
+
.map(([k, v]) => [k, v!] as const)
|
|
109
|
+
.map(([k, v]) => {
|
|
110
|
+
v.isEqual = resolveEqualsFn(v);
|
|
111
|
+
|
|
112
|
+
if (v.type === 'stateful') {
|
|
113
|
+
const spController = this.statefulParameters.get(k);
|
|
114
|
+
const parsed = {
|
|
115
|
+
...v,
|
|
116
|
+
controller: spController,
|
|
117
|
+
nestedSettings: spController?.parsedSettings,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return [k, parsed as ParsedStatefulParameterSetting];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (v.type === 'array') {
|
|
124
|
+
const parsed = {
|
|
125
|
+
...v,
|
|
126
|
+
isElementEqual: resolveElementEqualsFn(v as ArrayParameterSetting)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [k, parsed as ParsedArrayParameterSetting];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return [k, v];
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return Object.fromEntries(settings);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get defaultValues(): Partial<Record<keyof T, unknown>> {
|
|
140
|
+
return this.getFromCacheOrCreate('defaultValues', () => {
|
|
141
|
+
|
|
142
|
+
if (!this.settings.parameterSettings) {
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const defaultValues = Object.fromEntries(
|
|
147
|
+
Object.entries(this.settings.parameterSettings)
|
|
148
|
+
.filter(([, v]) => v!.default !== undefined)
|
|
149
|
+
.map(([k, v]) => [k, v!.default] as const)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const statefulParameterDefaultValues = Object.fromEntries(
|
|
153
|
+
Object.entries(this.settings.parameterSettings)
|
|
154
|
+
.filter(([, v]) => v?.type === 'stateful')
|
|
155
|
+
.filter(([, v]) => (v as StatefulParameterSetting).definition.getSettings().default !== undefined)
|
|
156
|
+
.map(([k, v]) => [k, (v as StatefulParameterSetting).definition.getSettings().default] as const)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return { ...defaultValues, ...statefulParameterDefaultValues } as Partial<Record<keyof T, unknown>>;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get inputTransformations(): Partial<Record<keyof T, InputTransformation>> {
|
|
164
|
+
return this.getFromCacheOrCreate('inputTransformations', () => {
|
|
165
|
+
if (!this.settings.parameterSettings) {
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return Object.fromEntries(
|
|
170
|
+
Object.entries(this.settings.parameterSettings)
|
|
171
|
+
.filter(([_, v]) => resolveParameterTransformFn(v!) !== undefined)
|
|
172
|
+
.map(([k, v]) => [k, resolveParameterTransformFn(v!)] as const)
|
|
173
|
+
) as Record<keyof T, InputTransformation>;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get statefulParameterOrder(): Map<keyof T, number> {
|
|
178
|
+
return this.getFromCacheOrCreate('stateParameterOrder', () => {
|
|
179
|
+
|
|
180
|
+
const entries = Object.entries(this.settings.parameterSettings ?? {})
|
|
181
|
+
.filter(([, v]) => v?.type === 'stateful')
|
|
182
|
+
.map(([k, v]) => [k, v as StatefulParameterSetting] as const)
|
|
183
|
+
|
|
184
|
+
const orderedEntries = entries.filter(([, v]) => v.order !== undefined)
|
|
185
|
+
const unorderedEntries = entries.filter(([, v]) => v.order === undefined)
|
|
186
|
+
|
|
187
|
+
orderedEntries.sort((a, b) => a[1].order! - b[1].order!);
|
|
188
|
+
|
|
189
|
+
const resultArray = [
|
|
190
|
+
...orderedEntries.map(([k]) => k),
|
|
191
|
+
...unorderedEntries.map(([k]) => k)
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
return new Map(resultArray.map((key, idx) => [key, idx]));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get matcher(): (desired: Partial<T>, current: Partial<T>) => boolean {
|
|
199
|
+
return resolveMatcher(this);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private validateSettings(): void {
|
|
203
|
+
// validate parameter settings
|
|
204
|
+
if (this.settings.parameterSettings) {
|
|
205
|
+
for (const [k, v] of Object.entries(this.settings.parameterSettings)) {
|
|
206
|
+
if (!v) {
|
|
207
|
+
throw new Error(`Resource: ${this.id}. Parameter setting ${k} was left undefined`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.validateParameterEqualsFn(v, k);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (Object.entries(this.parameterSettings).some(([k, v]) =>
|
|
215
|
+
v.type === 'stateful'
|
|
216
|
+
&& typeof this.settings.allowMultiple === 'object' && this.settings.allowMultiple?.identifyingParameters?.includes(k))) {
|
|
217
|
+
throw new Error(`Resource: ${this.id}. Stateful parameters are not allowed to be identifying parameters for allowMultiple.`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const schema = this.schema as JSONSchemaType<any>;
|
|
221
|
+
if (!this.settings.importAndDestroy && (schema?.oneOf
|
|
222
|
+
&& Array.isArray(schema.oneOf)
|
|
223
|
+
&& schema.oneOf.some((s) => s.required)
|
|
224
|
+
)
|
|
225
|
+
|| (schema?.anyOf
|
|
226
|
+
&& Array.isArray(schema.anyOf)
|
|
227
|
+
&& schema.anyOf.some((s) => s.required)
|
|
228
|
+
)
|
|
229
|
+
|| (schema?.allOf
|
|
230
|
+
&& Array.isArray(schema.allOf)
|
|
231
|
+
&& schema.allOf.some((s) => s.required)
|
|
232
|
+
)
|
|
233
|
+
|| (schema?.then
|
|
234
|
+
&& Array.isArray(schema.then)
|
|
235
|
+
&& schema.then.some((s) => s.required)
|
|
236
|
+
)
|
|
237
|
+
|| (schema?.else
|
|
238
|
+
&& Array.isArray(schema.else)
|
|
239
|
+
&& schema.else.some((s) => s.required)
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
throw new Error(`In the schema of ${this.settings.id}, a conditional required was declared (within anyOf, allOf, oneOf, else, or then) but an` +
|
|
243
|
+
'import.requiredParameters was not found in the resource settings. This is required because Codify uses the required parameter to' +
|
|
244
|
+
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when ' +
|
|
245
|
+
'required is declared conditionally.'
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (this.settings.importAndDestroy) {
|
|
250
|
+
const { requiredParameters, refreshKeys, defaultRefreshValues } = this.settings.importAndDestroy;
|
|
251
|
+
|
|
252
|
+
const requiredParametersNotInSchema = requiredParameters
|
|
253
|
+
?.filter(
|
|
254
|
+
(p) => schema && !(schema.properties[p])
|
|
255
|
+
)
|
|
256
|
+
if (schema && requiredParametersNotInSchema && requiredParametersNotInSchema.length > 0) {
|
|
257
|
+
throw new Error(`The following properties were declared in settings.import.requiredParameters but were not found in the schema:
|
|
258
|
+
${JSON.stringify(requiredParametersNotInSchema, null, 2)}`)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const refreshKeyNotInSchema = refreshKeys
|
|
262
|
+
?.filter(
|
|
263
|
+
(k) => schema && !(schema.properties[k])
|
|
264
|
+
)
|
|
265
|
+
if (schema && refreshKeyNotInSchema && refreshKeyNotInSchema.length > 0) {
|
|
266
|
+
throw new Error(`The following properties were declared in settings.import.refreshKeys but were not found in the schema:
|
|
267
|
+
${JSON.stringify(requiredParametersNotInSchema, null, 2)}`)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const refreshValueNotInRefreshKey =
|
|
271
|
+
Object.entries(defaultRefreshValues ?? {})
|
|
272
|
+
.filter(
|
|
273
|
+
([k]) => schema && !(schema.properties[k])
|
|
274
|
+
).map(([k]) => k)
|
|
275
|
+
|
|
276
|
+
if (schema && refreshValueNotInRefreshKey.length > 0) {
|
|
277
|
+
throw new Error(`Properties declared in defaultRefreshValues must already be declared in refreshKeys:
|
|
278
|
+
${JSON.stringify(refreshValueNotInRefreshKey, null, 2)}`)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private validateParameterEqualsFn(parameter: ParameterSetting, key: string): void {
|
|
284
|
+
if (parameter.type === 'stateful') {
|
|
285
|
+
const nestedSettings = (parameter as StatefulParameterSetting).definition.getSettings();
|
|
286
|
+
|
|
287
|
+
if (nestedSettings.type === 'stateful') {
|
|
288
|
+
throw new Error(`Nested stateful parameters are not allowed for ${key}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.validateParameterEqualsFn(nestedSettings, key);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// The rest of the types have defaults set already
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private getFromCacheOrCreate<T2>(key: string, create: () => T2): T2 {
|
|
298
|
+
if (this.cache.has(key)) {
|
|
299
|
+
return this.cache.get(key) as T2
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const result = create();
|
|
303
|
+
|
|
304
|
+
this.cache.set(key, result)
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { OS, ParameterOperation, ResourceOperation } from 'codify-schemas';
|
|
3
|
+
import { TestConfig, TestResource, TestStatefulParameter } from '../utils/test-utils.test.js';
|
|
4
|
+
import { ResourceSettings } from './resource-settings.js';
|
|
5
|
+
import { ResourceController } from './resource-controller.js';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
describe('Resource tests for stateful plans', () => {
|
|
9
|
+
it('Supports delete operations ', async () => {
|
|
10
|
+
const resource = new class extends TestResource {
|
|
11
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
12
|
+
return {
|
|
13
|
+
propA: 'propADifferent',
|
|
14
|
+
propB: undefined,
|
|
15
|
+
propC: 'propCDifferent',
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const controller = new ResourceController(resource);
|
|
21
|
+
const plan = await controller.plan(
|
|
22
|
+
{ type: 'type' },
|
|
23
|
+
null,
|
|
24
|
+
{
|
|
25
|
+
propA: 'propA',
|
|
26
|
+
propB: 10,
|
|
27
|
+
propC: 'propC',
|
|
28
|
+
}, true
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(plan).toMatchObject({
|
|
32
|
+
changeSet: {
|
|
33
|
+
operation: ResourceOperation.DESTROY,
|
|
34
|
+
parameterChanges: [
|
|
35
|
+
{
|
|
36
|
+
name: "propA",
|
|
37
|
+
previousValue: "propADifferent",
|
|
38
|
+
newValue: null,
|
|
39
|
+
operation: ParameterOperation.REMOVE
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'propB',
|
|
43
|
+
previousValue: null,
|
|
44
|
+
newValue: null,
|
|
45
|
+
operation: ParameterOperation.REMOVE
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "propC",
|
|
49
|
+
previousValue: "propCDifferent",
|
|
50
|
+
newValue: null,
|
|
51
|
+
operation: ParameterOperation.REMOVE
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
coreParameters: {
|
|
56
|
+
type: 'type'
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('Supports create operations', async () => {
|
|
62
|
+
const resource = new class extends TestResource {
|
|
63
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const controller = new ResourceController(resource);
|
|
69
|
+
const plan = await controller.plan(
|
|
70
|
+
{ type: 'resource' },
|
|
71
|
+
{
|
|
72
|
+
propA: 'propA',
|
|
73
|
+
propB: 10,
|
|
74
|
+
propC: 'propC',
|
|
75
|
+
},
|
|
76
|
+
null,
|
|
77
|
+
true
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(plan).toMatchObject({
|
|
81
|
+
changeSet: {
|
|
82
|
+
operation: ResourceOperation.CREATE,
|
|
83
|
+
parameterChanges: [
|
|
84
|
+
{
|
|
85
|
+
name: "propA",
|
|
86
|
+
newValue: "propA",
|
|
87
|
+
previousValue: null,
|
|
88
|
+
operation: ParameterOperation.ADD
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "propB",
|
|
92
|
+
newValue: 10,
|
|
93
|
+
previousValue: null,
|
|
94
|
+
operation: ParameterOperation.ADD
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "propC",
|
|
98
|
+
newValue: 'propC',
|
|
99
|
+
previousValue: null,
|
|
100
|
+
operation: ParameterOperation.ADD
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
coreParameters: {
|
|
105
|
+
type: 'resource'
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('Supports re-create operations', async () => {
|
|
111
|
+
const resource = new class extends TestResource {
|
|
112
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
113
|
+
return {
|
|
114
|
+
propA: 'propA',
|
|
115
|
+
propC: 'propC',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const controller = new ResourceController(resource)
|
|
121
|
+
const plan = await controller.plan(
|
|
122
|
+
{ type: 'type' },
|
|
123
|
+
{
|
|
124
|
+
type: 'type',
|
|
125
|
+
propA: 'propA',
|
|
126
|
+
propB: 10,
|
|
127
|
+
propC: 'propC',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: 'type',
|
|
131
|
+
propA: 'propA',
|
|
132
|
+
propC: 'propC'
|
|
133
|
+
},
|
|
134
|
+
true
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(plan).toMatchObject({
|
|
138
|
+
changeSet: {
|
|
139
|
+
operation: ResourceOperation.RECREATE,
|
|
140
|
+
parameterChanges: expect.arrayContaining([
|
|
141
|
+
{
|
|
142
|
+
name: "propA",
|
|
143
|
+
newValue: "propA",
|
|
144
|
+
previousValue: "propA",
|
|
145
|
+
operation: ParameterOperation.NOOP,
|
|
146
|
+
isSensitive: false,
|
|
147
|
+
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "propB",
|
|
151
|
+
newValue: 10,
|
|
152
|
+
previousValue: null,
|
|
153
|
+
operation: ParameterOperation.ADD,
|
|
154
|
+
isSensitive: false,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "propC",
|
|
158
|
+
newValue: 'propC',
|
|
159
|
+
previousValue: 'propC',
|
|
160
|
+
operation: ParameterOperation.NOOP,
|
|
161
|
+
isSensitive: false,
|
|
162
|
+
},
|
|
163
|
+
])
|
|
164
|
+
},
|
|
165
|
+
coreParameters: {
|
|
166
|
+
type: 'type'
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('Supports stateful parameters', async () => {
|
|
172
|
+
const statefulParameter = new class extends TestStatefulParameter {
|
|
173
|
+
async refresh(): Promise<string | null> {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const resource = new class extends TestResource {
|
|
179
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
180
|
+
return {
|
|
181
|
+
id: 'type',
|
|
182
|
+
operatingSystems: [OS.Darwin],
|
|
183
|
+
parameterSettings: {
|
|
184
|
+
propD: { type: 'stateful', definition: statefulParameter },
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async refresh(): Promise<Partial<TestConfig> | null> {
|
|
190
|
+
return {
|
|
191
|
+
propA: 'propA',
|
|
192
|
+
propC: 'propC',
|
|
193
|
+
propB: undefined
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const controller = new ResourceController(resource);
|
|
199
|
+
const plan = await controller.plan(
|
|
200
|
+
{ type: 'type' },
|
|
201
|
+
{
|
|
202
|
+
propA: 'propA',
|
|
203
|
+
propB: 10,
|
|
204
|
+
propC: 'propC',
|
|
205
|
+
propD: 'propD'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
propA: 'propA',
|
|
209
|
+
propC: 'propC'
|
|
210
|
+
},
|
|
211
|
+
true
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
expect(plan).toMatchObject({
|
|
215
|
+
changeSet: {
|
|
216
|
+
operation: ResourceOperation.RECREATE,
|
|
217
|
+
parameterChanges: expect.arrayContaining([
|
|
218
|
+
{
|
|
219
|
+
name: "propA",
|
|
220
|
+
newValue: "propA",
|
|
221
|
+
previousValue: "propA",
|
|
222
|
+
operation: ParameterOperation.NOOP,
|
|
223
|
+
isSensitive: false,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "propB",
|
|
227
|
+
newValue: 10,
|
|
228
|
+
previousValue: null,
|
|
229
|
+
operation: ParameterOperation.ADD,
|
|
230
|
+
isSensitive: false,
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "propC",
|
|
234
|
+
newValue: 'propC',
|
|
235
|
+
previousValue: 'propC',
|
|
236
|
+
operation: ParameterOperation.NOOP,
|
|
237
|
+
isSensitive: false,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "propD",
|
|
241
|
+
newValue: 'propD',
|
|
242
|
+
previousValue: null,
|
|
243
|
+
operation: ParameterOperation.ADD,
|
|
244
|
+
isSensitive: false,
|
|
245
|
+
},
|
|
246
|
+
])
|
|
247
|
+
},
|
|
248
|
+
coreParameters: {
|
|
249
|
+
type: 'type'
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
})
|