codify-plugin-lib 1.0.63 → 1.0.65
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 +7 -0
- package/dist/entities/errors.js +9 -0
- package/dist/entities/plan-types.d.ts +1 -1
- package/dist/entities/plan.d.ts +1 -1
- package/dist/entities/plan.js +3 -3
- package/dist/entities/plugin.d.ts +2 -1
- package/dist/entities/plugin.js +10 -0
- package/dist/entities/resource-types.d.ts +1 -1
- package/dist/entities/stateful-parameter.d.ts +1 -1
- package/dist/entities/stateful-parameter.js +1 -1
- package/dist/messages/handlers.js +16 -2
- package/package.json +1 -1
- package/src/entities/errors.ts +17 -0
- package/src/entities/plan-types.ts +1 -1
- package/src/entities/plan.ts +4 -4
- package/src/entities/plugin.test.ts +96 -0
- package/src/entities/plugin.ts +18 -1
- package/src/entities/resource-parameters.test.ts +1 -1
- package/src/entities/resource-types.ts +1 -1
- package/src/entities/resource.test.ts +4 -16
- package/src/entities/stateful-parameter.ts +1 -1
- package/src/messages/handlers.ts +17 -2
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { Plan } from './plan.js';
|
|
2
|
+
import { StringIndexedObject } from 'codify-schemas';
|
|
1
3
|
export declare class SudoError extends Error {
|
|
2
4
|
command: string;
|
|
3
5
|
constructor(command: string);
|
|
4
6
|
}
|
|
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,3 +5,12 @@ 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
|
+
}
|
package/dist/entities/plan.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class Plan<T extends StringIndexedObject> {
|
|
|
8
8
|
constructor(id: string, changeSet: ChangeSet<T>, resourceMetadata: ResourceConfig);
|
|
9
9
|
static create<T extends StringIndexedObject>(desiredParameters: Partial<T> | null, currentParameters: Partial<T> | null, resourceMetadata: ResourceConfig, options: PlanOptions<T>): Plan<T>;
|
|
10
10
|
getResourceType(): string;
|
|
11
|
-
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues
|
|
11
|
+
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues?: Partial<Record<keyof T, unknown>>): Plan<T>;
|
|
12
12
|
get desiredConfig(): T;
|
|
13
13
|
get currentConfig(): T;
|
|
14
14
|
toResponse(): PlanResponseData;
|
package/dist/entities/plan.js
CHANGED
|
@@ -31,8 +31,8 @@ export class Plan {
|
|
|
31
31
|
if (statefulParameterNames.has(curr.name)) {
|
|
32
32
|
newOperation = ResourceOperation.MODIFY;
|
|
33
33
|
}
|
|
34
|
-
else if (parameterOptions[curr.name]?.
|
|
35
|
-
newOperation = parameterOptions[curr.name].
|
|
34
|
+
else if (parameterOptions[curr.name]?.modifyOnChange) {
|
|
35
|
+
newOperation = parameterOptions[curr.name].modifyOnChange ? ResourceOperation.MODIFY : ResourceOperation.RECREATE;
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
newOperation = ResourceOperation.RECREATE;
|
|
@@ -55,7 +55,7 @@ export class Plan {
|
|
|
55
55
|
name: data.resourceName,
|
|
56
56
|
});
|
|
57
57
|
function addDefaultValues() {
|
|
58
|
-
Object.entries(defaultValues)
|
|
58
|
+
Object.entries(defaultValues ?? {})
|
|
59
59
|
.forEach(([key, defaultValue]) => {
|
|
60
60
|
const configValueExists = data
|
|
61
61
|
.parameters
|
|
@@ -4,7 +4,8 @@ import { Plan } from './plan.js';
|
|
|
4
4
|
export declare class Plugin {
|
|
5
5
|
name: string;
|
|
6
6
|
resources: Map<string, Resource<ResourceConfig>>;
|
|
7
|
-
planStorage: Map<string, Plan<
|
|
7
|
+
planStorage: Map<string, Plan<any>>;
|
|
8
|
+
static create(name: string, resources: Resource<any>[]): Plugin;
|
|
8
9
|
constructor(name: string, resources: Map<string, Resource<ResourceConfig>>);
|
|
9
10
|
initialize(): Promise<InitializeResponseData>;
|
|
10
11
|
validate(data: ValidateRequestData): Promise<ValidateResponseData>;
|
package/dist/entities/plugin.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import { ResourceOperation } from 'codify-schemas';
|
|
1
2
|
import { Plan } from './plan.js';
|
|
2
3
|
import { splitUserConfig } from '../utils/utils.js';
|
|
4
|
+
import { ApplyValidationError } from './errors.js';
|
|
3
5
|
export class Plugin {
|
|
4
6
|
name;
|
|
5
7
|
resources;
|
|
6
8
|
planStorage;
|
|
9
|
+
static create(name, resources) {
|
|
10
|
+
const resourceMap = new Map(resources.map((r) => [r.typeId, r]));
|
|
11
|
+
return new Plugin(name, resourceMap);
|
|
12
|
+
}
|
|
7
13
|
constructor(name, resources) {
|
|
8
14
|
this.name = name;
|
|
9
15
|
this.resources = resources;
|
|
@@ -58,6 +64,10 @@ export class Plugin {
|
|
|
58
64
|
throw new Error('Malformed plan with resource that cannot be found');
|
|
59
65
|
}
|
|
60
66
|
await resource.apply(plan);
|
|
67
|
+
const validationPlan = await resource.plan({ ...plan.resourceMetadata, ...plan.desiredConfig });
|
|
68
|
+
if (validationPlan.changeSet.operation !== ResourceOperation.NOOP) {
|
|
69
|
+
throw new ApplyValidationError(plan, validationPlan);
|
|
70
|
+
}
|
|
61
71
|
}
|
|
62
72
|
resolvePlan(data) {
|
|
63
73
|
const { planId, plan: planRequest } = data;
|
|
@@ -19,7 +19,7 @@ export declare abstract class StatefulParameter<T extends StringIndexedObject, V
|
|
|
19
19
|
}
|
|
20
20
|
export declare abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> extends StatefulParameter<T, any> {
|
|
21
21
|
options: ArrayStatefulParameterOptions<V>;
|
|
22
|
-
constructor(options
|
|
22
|
+
constructor(options?: ArrayStatefulParameterOptions<V>);
|
|
23
23
|
applyAdd(valuesToAdd: V[], plan: Plan<T>): Promise<void>;
|
|
24
24
|
applyModify(newValues: V[], previousValues: V[], allowDeletes: boolean, plan: Plan<T>): Promise<void>;
|
|
25
25
|
applyRemove(valuesToRemove: V[], plan: Plan<T>): Promise<void>;
|
|
@@ -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 { SudoError } from '../entities/errors.js';
|
|
4
|
+
import { ApplyValidationError, SudoError } from '../entities/errors.js';
|
|
5
5
|
const SupportedRequests = {
|
|
6
6
|
'initialize': {
|
|
7
7
|
requestValidator: InitializeRequestDataSchema,
|
|
@@ -83,12 +83,26 @@ export class MessageHandler {
|
|
|
83
83
|
}
|
|
84
84
|
const cmd = message.cmd + '_Response';
|
|
85
85
|
if (e instanceof SudoError) {
|
|
86
|
-
process.send?.({
|
|
86
|
+
return process.send?.({
|
|
87
87
|
cmd,
|
|
88
88
|
status: MessageStatus.ERROR,
|
|
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
|
+
}
|
|
92
106
|
const isDebug = process.env.DEBUG?.includes('*') ?? false;
|
|
93
107
|
process.send?.({
|
|
94
108
|
cmd,
|
package/package.json
CHANGED
package/src/entities/errors.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Plan } from './plan.js';
|
|
2
|
+
import { StringIndexedObject } from 'codify-schemas';
|
|
3
|
+
|
|
1
4
|
export class SudoError extends Error {
|
|
2
5
|
command: string;
|
|
3
6
|
|
|
@@ -6,3 +9,17 @@ export class SudoError extends Error {
|
|
|
6
9
|
this.command = command;
|
|
7
10
|
}
|
|
8
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
|
+
}
|
|
@@ -5,7 +5,7 @@ export interface ParameterOptions {
|
|
|
5
5
|
/**
|
|
6
6
|
* Chose if the resource should be re-created or modified if this parameter is changed. Defaults to false (re-creates resource on change).
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
modifyOnChange?: boolean;
|
|
9
9
|
/**
|
|
10
10
|
* Customize the equality comparison for a parameter.
|
|
11
11
|
* @param a
|
package/src/entities/plan.ts
CHANGED
|
@@ -55,8 +55,8 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
55
55
|
let newOperation: ResourceOperation;
|
|
56
56
|
if (statefulParameterNames.has(curr.name)) {
|
|
57
57
|
newOperation = ResourceOperation.MODIFY // All stateful parameters are modify only
|
|
58
|
-
} else if (parameterOptions[curr.name]?.
|
|
59
|
-
newOperation = parameterOptions[curr.name].
|
|
58
|
+
} else if (parameterOptions[curr.name]?.modifyOnChange) {
|
|
59
|
+
newOperation = parameterOptions[curr.name].modifyOnChange ? ResourceOperation.MODIFY : ResourceOperation.RECREATE;
|
|
60
60
|
} else {
|
|
61
61
|
newOperation = ResourceOperation.RECREATE; // Default to Re-create. Should handle the majority of use cases
|
|
62
62
|
}
|
|
@@ -75,7 +75,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
75
75
|
return this.resourceMetadata.type
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues
|
|
78
|
+
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues?: Partial<Record<keyof T, unknown>>): Plan<T> {
|
|
79
79
|
if (!data) {
|
|
80
80
|
throw new Error('Data is empty');
|
|
81
81
|
}
|
|
@@ -95,7 +95,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
95
95
|
);
|
|
96
96
|
|
|
97
97
|
function addDefaultValues(): void {
|
|
98
|
-
Object.entries(defaultValues)
|
|
98
|
+
Object.entries(defaultValues ?? {})
|
|
99
99
|
.forEach(([key, defaultValue]) => {
|
|
100
100
|
const configValueExists = data!
|
|
101
101
|
.parameters
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { Plugin } from './plugin.js';
|
|
3
|
+
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
4
|
+
import { Resource } from './resource.js';
|
|
5
|
+
import { Plan } from './plan.js';
|
|
6
|
+
import { ValidationResult } from './resource-types.js';
|
|
7
|
+
import { ApplyValidationError } from './errors.js';
|
|
8
|
+
|
|
9
|
+
interface TestConfig extends StringIndexedObject {
|
|
10
|
+
propA: string;
|
|
11
|
+
propB: number;
|
|
12
|
+
propC?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class TestResource extends Resource<TestConfig> {
|
|
16
|
+
constructor() {
|
|
17
|
+
super({
|
|
18
|
+
type: 'testResource'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
applyCreate(plan: Plan<TestConfig>): Promise<void> {
|
|
23
|
+
return Promise.resolve(undefined);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
applyDestroy(plan: Plan<TestConfig>): Promise<void> {
|
|
27
|
+
return Promise.resolve(undefined);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
31
|
+
return {
|
|
32
|
+
propA: 'a',
|
|
33
|
+
propB: 10,
|
|
34
|
+
propC: 'c',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async validateResource(config: unknown): Promise<ValidationResult> {
|
|
39
|
+
return {
|
|
40
|
+
isValid: true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('Plugin tests', () => {
|
|
46
|
+
it('Validates that applies were successfully applied', async () => {
|
|
47
|
+
const resource = new class extends TestResource {
|
|
48
|
+
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Refresh has to line up with desired for the apply to go through
|
|
52
|
+
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
53
|
+
return {
|
|
54
|
+
propA: 'abc'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
60
|
+
|
|
61
|
+
const desiredPlan = {
|
|
62
|
+
operation: ResourceOperation.CREATE,
|
|
63
|
+
resourceType: 'testResource',
|
|
64
|
+
parameters: [
|
|
65
|
+
{ name: 'propA', operation: ParameterOperation.ADD, newValue: 'abc', previousValue: null },
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// If this doesn't throw then it passes the test
|
|
70
|
+
await testPlugin.apply({ plan: desiredPlan });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('Validates that applies were successfully applied (error)', async () => {
|
|
74
|
+
const resource = new class extends TestResource {
|
|
75
|
+
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Return null to indicate that the resource was not created
|
|
79
|
+
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const testPlugin = Plugin.create('testPlugin', [resource])
|
|
85
|
+
|
|
86
|
+
const desiredPlan = {
|
|
87
|
+
operation: ResourceOperation.CREATE,
|
|
88
|
+
resourceType: 'testResource',
|
|
89
|
+
parameters: [
|
|
90
|
+
{ name: 'propA', operation: ParameterOperation.ADD, newValue: 'abc', previousValue: null },
|
|
91
|
+
]
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
await expect(async () => testPlugin.apply({ plan: desiredPlan })).rejects.toThrowError(expect.any(ApplyValidationError));
|
|
95
|
+
});
|
|
96
|
+
});
|
package/src/entities/plugin.ts
CHANGED
|
@@ -5,14 +5,24 @@ import {
|
|
|
5
5
|
PlanRequestData,
|
|
6
6
|
PlanResponseData,
|
|
7
7
|
ResourceConfig,
|
|
8
|
+
ResourceOperation,
|
|
8
9
|
ValidateRequestData,
|
|
9
10
|
ValidateResponseData
|
|
10
11
|
} from 'codify-schemas';
|
|
11
12
|
import { Plan } from './plan.js';
|
|
12
13
|
import { splitUserConfig } from '../utils/utils.js';
|
|
14
|
+
import { ApplyValidationError } from './errors.js';
|
|
13
15
|
|
|
14
16
|
export class Plugin {
|
|
15
|
-
planStorage: Map<string, Plan<
|
|
17
|
+
planStorage: Map<string, Plan<any>>;
|
|
18
|
+
|
|
19
|
+
static create(name: string, resources: Resource<any>[]) {
|
|
20
|
+
const resourceMap = new Map<string, Resource<any>>(
|
|
21
|
+
resources.map((r) => [r.typeId, r] as const)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return new Plugin(name, resourceMap);
|
|
25
|
+
}
|
|
16
26
|
|
|
17
27
|
constructor(
|
|
18
28
|
public name: string,
|
|
@@ -82,6 +92,13 @@ export class Plugin {
|
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
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({ ...plan.resourceMetadata, ...plan.desiredConfig });
|
|
99
|
+
if (validationPlan.changeSet.operation !== ResourceOperation.NOOP) {
|
|
100
|
+
throw new ApplyValidationError(plan, validationPlan);
|
|
101
|
+
}
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
private resolvePlan(data: ApplyRequestData): Plan<ResourceConfig> {
|
|
@@ -7,7 +7,7 @@ export interface ResourceParameterOptions {
|
|
|
7
7
|
/**
|
|
8
8
|
* Chose if the resource should be re-created or modified if this parameter is changed. Defaults to false (re-create).
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
modifyOnChange?: boolean;
|
|
11
11
|
/**
|
|
12
12
|
* Customize the equality comparison for a parameter.
|
|
13
13
|
* @param desired
|
|
@@ -195,8 +195,8 @@ describe('Resource tests', () => {
|
|
|
195
195
|
super({
|
|
196
196
|
type: 'resource',
|
|
197
197
|
parameterOptions: {
|
|
198
|
-
propA: {
|
|
199
|
-
propB: {
|
|
198
|
+
propA: { modifyOnChange: true },
|
|
199
|
+
propB: { modifyOnChange: true },
|
|
200
200
|
}
|
|
201
201
|
});
|
|
202
202
|
}
|
|
@@ -218,12 +218,6 @@ describe('Resource tests', () => {
|
|
|
218
218
|
|
|
219
219
|
it('Validates the resource options correct (pass)', () => {
|
|
220
220
|
const statefulParameter = new class extends StatefulParameter<TestConfig, string> {
|
|
221
|
-
constructor() {
|
|
222
|
-
super({
|
|
223
|
-
name: 'propC',
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
221
|
async refresh(): Promise<string | null> {
|
|
228
222
|
return null;
|
|
229
223
|
}
|
|
@@ -244,7 +238,7 @@ describe('Resource tests', () => {
|
|
|
244
238
|
type: 'type',
|
|
245
239
|
dependencies: ['homebrew', 'python'],
|
|
246
240
|
parameterOptions: {
|
|
247
|
-
propA: {
|
|
241
|
+
propA: { modifyOnChange: true },
|
|
248
242
|
propB: { statefulParameter },
|
|
249
243
|
propC: { isEqual: (a, b) => true },
|
|
250
244
|
}
|
|
@@ -255,12 +249,6 @@ describe('Resource tests', () => {
|
|
|
255
249
|
|
|
256
250
|
it('Validates the resource options correct (fail)', () => {
|
|
257
251
|
const statefulParameter = new class extends StatefulParameter<TestConfig, string> {
|
|
258
|
-
constructor() {
|
|
259
|
-
super({
|
|
260
|
-
name: 'propC',
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
252
|
async refresh(): Promise<string | null> {
|
|
265
253
|
return null;
|
|
266
254
|
}
|
|
@@ -281,7 +269,7 @@ describe('Resource tests', () => {
|
|
|
281
269
|
type: 'type',
|
|
282
270
|
dependencies: ['homebrew', 'python'],
|
|
283
271
|
parameterOptions: {
|
|
284
|
-
propA: {
|
|
272
|
+
propA: { modifyOnChange: true },
|
|
285
273
|
propB: { statefulParameter },
|
|
286
274
|
propC: { isEqual: (a, b) => true },
|
|
287
275
|
}
|
|
@@ -41,7 +41,7 @@ export abstract class StatefulParameter<T extends StringIndexedObject, V extends
|
|
|
41
41
|
export abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> extends StatefulParameter<T, any>{
|
|
42
42
|
options: ArrayStatefulParameterOptions<V>;
|
|
43
43
|
|
|
44
|
-
constructor(options: ArrayStatefulParameterOptions<V>) {
|
|
44
|
+
constructor(options: ArrayStatefulParameterOptions<V> = {}) {
|
|
45
45
|
super(options);
|
|
46
46
|
this.options = options;
|
|
47
47
|
}
|
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 { SudoError } from '../entities/errors.js';
|
|
18
|
+
import { ApplyValidationError, 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': {
|
|
@@ -117,13 +117,28 @@ export class MessageHandler {
|
|
|
117
117
|
const cmd = message.cmd + '_Response';
|
|
118
118
|
|
|
119
119
|
if (e instanceof SudoError) {
|
|
120
|
-
process.send?.({
|
|
120
|
+
return process.send?.({
|
|
121
121
|
cmd,
|
|
122
122
|
status: MessageStatus.ERROR,
|
|
123
123
|
data: `Plugin: '${this.plugin.name}'. Forbidden usage of sudo for command '${e.command}'. Please contact the plugin developer to fix this.`,
|
|
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
|
+
|
|
127
142
|
const isDebug = process.env.DEBUG?.includes('*') ?? false;
|
|
128
143
|
|
|
129
144
|
process.send?.({
|