codify-plugin-lib 1.0.67 → 1.0.69
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/dist/entities/errors.d.ts +0 -7
- package/dist/entities/errors.js +0 -9
- package/dist/entities/plan-types.d.ts +12 -6
- package/dist/entities/plugin.js +0 -6
- package/dist/messages/handlers.js +1 -15
- package/package.json +1 -1
- package/src/entities/errors.ts +0 -14
- package/src/entities/plan-types.ts +14 -4
- package/src/entities/plugin.ts +0 -12
- package/src/entities/resource.test.ts +37 -4
- package/src/entities/resource.ts +3 -3
- package/src/messages/handlers.ts +1 -16
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import { Plan } from './plan.js';
|
|
2
|
-
import { StringIndexedObject } from 'codify-schemas';
|
|
3
1
|
export declare class SudoError extends Error {
|
|
4
2
|
command: string;
|
|
5
3
|
constructor(command: string);
|
|
6
4
|
}
|
|
7
|
-
export declare class ApplyValidationError<T extends StringIndexedObject> extends Error {
|
|
8
|
-
desiredPlan: Plan<T>;
|
|
9
|
-
validatedPlan: Plan<T>;
|
|
10
|
-
constructor(desiredPlan: Plan<T>, validatedPlan: Plan<T>);
|
|
11
|
-
}
|
package/dist/entities/errors.js
CHANGED
|
@@ -5,12 +5,3 @@ export class SudoError extends Error {
|
|
|
5
5
|
this.command = command;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
export class ApplyValidationError extends Error {
|
|
9
|
-
desiredPlan;
|
|
10
|
-
validatedPlan;
|
|
11
|
-
constructor(desiredPlan, validatedPlan) {
|
|
12
|
-
super();
|
|
13
|
-
this.desiredPlan = desiredPlan;
|
|
14
|
-
this.validatedPlan = validatedPlan;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -11,9 +11,15 @@ export interface PlanOptions<T> {
|
|
|
11
11
|
statefulMode: boolean;
|
|
12
12
|
parameterOptions?: Record<keyof T, ParameterOptions>;
|
|
13
13
|
}
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export
|
|
19
|
-
|
|
14
|
+
export interface CreatePlan<T extends StringIndexedObject> extends Plan<T> {
|
|
15
|
+
desiredConfig: T;
|
|
16
|
+
currentConfig: null;
|
|
17
|
+
}
|
|
18
|
+
export interface DestroyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
19
|
+
desiredConfig: null;
|
|
20
|
+
currentConfig: T;
|
|
21
|
+
}
|
|
22
|
+
export interface ModifyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
23
|
+
desiredConfig: T;
|
|
24
|
+
currentConfig: T;
|
|
25
|
+
}
|
package/dist/entities/plugin.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { ResourceOperation } from 'codify-schemas';
|
|
2
1
|
import { Plan } from './plan.js';
|
|
3
2
|
import { splitUserConfig } from '../utils/utils.js';
|
|
4
|
-
import { ApplyValidationError } from './errors.js';
|
|
5
3
|
export class Plugin {
|
|
6
4
|
name;
|
|
7
5
|
resources;
|
|
@@ -64,10 +62,6 @@ export class Plugin {
|
|
|
64
62
|
throw new Error('Malformed plan with resource that cannot be found');
|
|
65
63
|
}
|
|
66
64
|
await resource.apply(plan);
|
|
67
|
-
const validationPlan = await resource.plan(plan.desiredConfig, plan.currentConfig, true);
|
|
68
|
-
if (validationPlan.changeSet.operation !== ResourceOperation.NOOP) {
|
|
69
|
-
throw new ApplyValidationError(plan, validationPlan);
|
|
70
|
-
}
|
|
71
65
|
}
|
|
72
66
|
resolvePlan(data) {
|
|
73
67
|
const { planId, plan: planRequest } = data;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import addFormats from 'ajv-formats';
|
|
2
2
|
import { ApplyRequestDataSchema, ApplyResponseDataSchema, InitializeRequestDataSchema, InitializeResponseDataSchema, IpcMessageSchema, MessageStatus, PlanRequestDataSchema, PlanResponseDataSchema, ResourceSchema, ValidateRequestDataSchema, ValidateResponseDataSchema } from 'codify-schemas';
|
|
3
3
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
4
|
-
import {
|
|
4
|
+
import { SudoError } from '../entities/errors.js';
|
|
5
5
|
const SupportedRequests = {
|
|
6
6
|
'initialize': {
|
|
7
7
|
requestValidator: InitializeRequestDataSchema,
|
|
@@ -89,20 +89,6 @@ export class MessageHandler {
|
|
|
89
89
|
data: `Plugin: '${this.plugin.name}'. Forbidden usage of sudo for command '${e.command}'. Please contact the plugin developer to fix this.`,
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
|
-
if (e instanceof ApplyValidationError) {
|
|
93
|
-
return process.send?.({
|
|
94
|
-
cmd,
|
|
95
|
-
status: MessageStatus.ERROR,
|
|
96
|
-
data: `Plugin: '${this.plugin.name}'. Apply validation was not successful (additional changes are needed to match the desired plan).
|
|
97
|
-
|
|
98
|
-
Validation plan:
|
|
99
|
-
${JSON.stringify(e.validatedPlan, null, 2)},
|
|
100
|
-
|
|
101
|
-
User desired plan:
|
|
102
|
-
${JSON.stringify(e.desiredPlan, null, 2)}
|
|
103
|
-
`
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
92
|
const isDebug = process.env.DEBUG?.includes('*') ?? false;
|
|
107
93
|
process.send?.({
|
|
108
94
|
cmd,
|
package/package.json
CHANGED
package/src/entities/errors.ts
CHANGED
|
@@ -9,17 +9,3 @@ export class SudoError extends Error {
|
|
|
9
9
|
this.command = command;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
export class ApplyValidationError<T extends StringIndexedObject> extends Error {
|
|
14
|
-
desiredPlan: Plan<T>;
|
|
15
|
-
validatedPlan: Plan<T>;
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
desiredPlan: Plan<T>,
|
|
19
|
-
validatedPlan: Plan<T>
|
|
20
|
-
) {
|
|
21
|
-
super();
|
|
22
|
-
this.desiredPlan = desiredPlan;
|
|
23
|
-
this.validatedPlan = validatedPlan;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -28,7 +28,17 @@ export interface PlanOptions<T> {
|
|
|
28
28
|
parameterOptions?: Record<keyof T, ParameterOptions>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
export interface CreatePlan<T extends StringIndexedObject> extends Plan<T> {
|
|
32
|
+
desiredConfig: T;
|
|
33
|
+
currentConfig: null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DestroyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
37
|
+
desiredConfig: null;
|
|
38
|
+
currentConfig: T;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ModifyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
42
|
+
desiredConfig: T;
|
|
43
|
+
currentConfig: T;
|
|
44
|
+
}
|
package/src/entities/plugin.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from 'codify-schemas';
|
|
12
12
|
import { Plan } from './plan.js';
|
|
13
13
|
import { splitUserConfig } from '../utils/utils.js';
|
|
14
|
-
import { ApplyValidationError } from './errors.js';
|
|
15
14
|
|
|
16
15
|
export class Plugin {
|
|
17
16
|
planStorage: Map<string, Plan<any>>;
|
|
@@ -92,17 +91,6 @@ export class Plugin {
|
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
await resource.apply(plan);
|
|
95
|
-
|
|
96
|
-
// Perform a validation check after to ensure that the plan was properly applied.
|
|
97
|
-
// Sometimes no errors are returned (exit code 0) but the apply was not successful
|
|
98
|
-
const validationPlan = await resource.plan(
|
|
99
|
-
plan.desiredConfig,
|
|
100
|
-
plan.currentConfig,
|
|
101
|
-
true,
|
|
102
|
-
);
|
|
103
|
-
if (validationPlan.changeSet.operation !== ResourceOperation.NOOP) {
|
|
104
|
-
throw new ApplyValidationError(plan, validationPlan);
|
|
105
|
-
}
|
|
106
94
|
}
|
|
107
95
|
|
|
108
96
|
private resolvePlan(data: ApplyRequestData): Plan<ResourceConfig> {
|
|
@@ -6,6 +6,8 @@ import { describe, expect, it } from 'vitest'
|
|
|
6
6
|
import { ValidationResult } from './resource-types.js';
|
|
7
7
|
import { StatefulParameter } from './stateful-parameter.js';
|
|
8
8
|
import { ResourceOptions } from './resource-options.js';
|
|
9
|
+
import { CreatePlan, DestroyPlan, ModifyPlan } from './plan-types.js';
|
|
10
|
+
import { ParameterChange } from './change-set.js';
|
|
9
11
|
|
|
10
12
|
export interface TestConfig extends StringIndexedObject {
|
|
11
13
|
propA: string;
|
|
@@ -221,12 +223,15 @@ describe('Resource tests', () => {
|
|
|
221
223
|
async refresh(): Promise<string | null> {
|
|
222
224
|
return null;
|
|
223
225
|
}
|
|
226
|
+
|
|
224
227
|
applyAdd(valueToAdd: string, plan: Plan<TestConfig>): Promise<void> {
|
|
225
228
|
throw new Error('Method not implemented.');
|
|
226
229
|
}
|
|
230
|
+
|
|
227
231
|
applyModify(newValue: string, previousValue: string, allowDeletes: boolean, plan: Plan<TestConfig>): Promise<void> {
|
|
228
232
|
throw new Error('Method not implemented.');
|
|
229
233
|
}
|
|
234
|
+
|
|
230
235
|
applyRemove(valueToRemove: string, plan: Plan<TestConfig>): Promise<void> {
|
|
231
236
|
throw new Error('Method not implemented.');
|
|
232
237
|
}
|
|
@@ -252,12 +257,15 @@ describe('Resource tests', () => {
|
|
|
252
257
|
async refresh(): Promise<string | null> {
|
|
253
258
|
return null;
|
|
254
259
|
}
|
|
260
|
+
|
|
255
261
|
applyAdd(valueToAdd: string, plan: Plan<TestConfig>): Promise<void> {
|
|
256
262
|
throw new Error('Method not implemented.');
|
|
257
263
|
}
|
|
264
|
+
|
|
258
265
|
applyModify(newValue: string, previousValue: string, allowDeletes: boolean, plan: Plan<TestConfig>): Promise<void> {
|
|
259
266
|
throw new Error('Method not implemented.');
|
|
260
267
|
}
|
|
268
|
+
|
|
261
269
|
applyRemove(valueToRemove: string, plan: Plan<TestConfig>): Promise<void> {
|
|
262
270
|
throw new Error('Method not implemented.');
|
|
263
271
|
}
|
|
@@ -300,7 +308,7 @@ describe('Resource tests', () => {
|
|
|
300
308
|
}
|
|
301
309
|
}
|
|
302
310
|
|
|
303
|
-
const plan = await resource.plan({ type: 'resource'})
|
|
311
|
+
const plan = await resource.plan({ type: 'resource' })
|
|
304
312
|
expect(plan.currentConfig?.propA).to.eq('propAAfter');
|
|
305
313
|
expect(plan.desiredConfig?.propA).to.eq('propADefault');
|
|
306
314
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
|
|
@@ -326,7 +334,7 @@ describe('Resource tests', () => {
|
|
|
326
334
|
}
|
|
327
335
|
}
|
|
328
336
|
|
|
329
|
-
const plan = await resource.plan({ type: 'resource'})
|
|
337
|
+
const plan = await resource.plan({ type: 'resource' })
|
|
330
338
|
expect(plan.currentConfig?.propE).to.eq('propEDefault');
|
|
331
339
|
expect(plan.desiredConfig?.propE).to.eq('propEDefault');
|
|
332
340
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
@@ -348,7 +356,7 @@ describe('Resource tests', () => {
|
|
|
348
356
|
}
|
|
349
357
|
}
|
|
350
358
|
|
|
351
|
-
const plan = await resource.plan({ type: 'resource'})
|
|
359
|
+
const plan = await resource.plan({ type: 'resource' })
|
|
352
360
|
expect(plan.currentConfig).to.be.null
|
|
353
361
|
expect(plan.desiredConfig!.propE).to.eq('propEDefault');
|
|
354
362
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.CREATE);
|
|
@@ -376,7 +384,7 @@ describe('Resource tests', () => {
|
|
|
376
384
|
}
|
|
377
385
|
}
|
|
378
386
|
|
|
379
|
-
const plan = await resource.plan({ type: 'resource', propA: 'propA'})
|
|
387
|
+
const plan = await resource.plan({ type: 'resource', propA: 'propA' })
|
|
380
388
|
expect(plan.currentConfig?.propA).to.eq('propAAfter');
|
|
381
389
|
expect(plan.desiredConfig?.propA).to.eq('propA');
|
|
382
390
|
expect(plan.changeSet.operation).to.eq(ResourceOperation.RECREATE);
|
|
@@ -398,4 +406,29 @@ describe('Resource tests', () => {
|
|
|
398
406
|
propA: 'propADefault',
|
|
399
407
|
})
|
|
400
408
|
})
|
|
409
|
+
|
|
410
|
+
it('Has the correct typing for applys', () => {
|
|
411
|
+
const resource = new class extends Resource<TestConfig> {
|
|
412
|
+
constructor() {
|
|
413
|
+
super({ type: 'type' });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async refresh(values: Map<keyof TestConfig, unknown>): Promise<Partial<TestConfig> | null> {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async applyCreate(plan: CreatePlan<TestConfig>): Promise<void> {
|
|
421
|
+
plan.desiredConfig.propA
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async applyDestroy(plan: DestroyPlan<TestConfig>): Promise<void> {
|
|
425
|
+
plan.currentConfig.propB
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async applyModify(pc: ParameterChange<TestConfig>, plan: ModifyPlan<TestConfig>): Promise<void> {
|
|
429
|
+
plan.desiredConfig.propA
|
|
430
|
+
plan.currentConfig.propB
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
})
|
|
401
434
|
});
|
package/src/entities/resource.ts
CHANGED
|
@@ -147,7 +147,7 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
private async _applyCreate(plan: Plan<T>): Promise<void> {
|
|
150
|
-
await this.applyCreate(plan);
|
|
150
|
+
await this.applyCreate(plan as CreatePlan<T>);
|
|
151
151
|
|
|
152
152
|
const statefulParameterChanges = plan.changeSet.parameterChanges
|
|
153
153
|
.filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
|
|
@@ -170,7 +170,7 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
170
170
|
|
|
171
171
|
for (const pc of statelessParameterChanges) {
|
|
172
172
|
// TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
|
|
173
|
-
await this.applyModify(pc, plan);
|
|
173
|
+
await this.applyModify(pc, plan as ModifyPlan<T>);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
const statefulParameterChanges = parameterChanges
|
|
@@ -212,7 +212,7 @@ export abstract class Resource<T extends StringIndexedObject> {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
await this.applyDestroy(plan);
|
|
215
|
+
await this.applyDestroy(plan as DestroyPlan<T>);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
private validateRefreshResults(refresh: Partial<T> | null, desiredMap: Map<keyof T, T[keyof T]>) {
|
package/src/messages/handlers.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
ValidateResponseDataSchema
|
|
16
16
|
} from 'codify-schemas';
|
|
17
17
|
import Ajv2020, { SchemaObject, ValidateFunction } from 'ajv/dist/2020.js';
|
|
18
|
-
import {
|
|
18
|
+
import { SudoError } from '../entities/errors.js';
|
|
19
19
|
|
|
20
20
|
const SupportedRequests: Record<string, { requestValidator: SchemaObject; responseValidator: SchemaObject; handler: (plugin: Plugin, data: any) => Promise<unknown> }> = {
|
|
21
21
|
'initialize': {
|
|
@@ -124,21 +124,6 @@ export class MessageHandler {
|
|
|
124
124
|
})
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
if (e instanceof ApplyValidationError) {
|
|
128
|
-
return process.send?.({
|
|
129
|
-
cmd,
|
|
130
|
-
status: MessageStatus.ERROR,
|
|
131
|
-
data: `Plugin: '${this.plugin.name}'. Apply validation was not successful (additional changes are needed to match the desired plan).
|
|
132
|
-
|
|
133
|
-
Validation plan:
|
|
134
|
-
${JSON.stringify(e.validatedPlan, null, 2)},
|
|
135
|
-
|
|
136
|
-
User desired plan:
|
|
137
|
-
${JSON.stringify(e.desiredPlan, null, 2)}
|
|
138
|
-
`
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
|
|
142
127
|
const isDebug = process.env.DEBUG?.includes('*') ?? false;
|
|
143
128
|
|
|
144
129
|
process.send?.({
|