Skip to content

Commit f92dbee

Browse files
committed
[CODE-112] Update validation API
1 parent ffa2c77 commit f92dbee

10 files changed

Lines changed: 107 additions & 175 deletions

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/dist
2+
/node_modules

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codify-plugin-lib",
3-
"version": "1.0.71",
3+
"version": "1.0.73",
44
"description": "",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
@@ -14,7 +14,7 @@
1414
"dependencies": {
1515
"ajv": "^8.12.0",
1616
"ajv-formats": "^2.1.1",
17-
"codify-schemas": "1.0.39",
17+
"codify-schemas": "1.0.42",
1818
"@npmcli/promise-spawn": "^7.0.1"
1919
},
2020
"devDependencies": {

src/entities/plugin.test.ts

Lines changed: 22 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { Plugin } from './plugin.js';
33
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
44
import { Resource } from './resource.js';
55
import { Plan } from './plan.js';
6-
import { ValidationResult } from './resource-types.js';
7-
import { ApplyValidationError } from './errors.js';
6+
import { spy } from 'sinon';
87

98
interface TestConfig extends StringIndexedObject {
109
propA: string;
@@ -27,36 +26,19 @@ class TestResource extends Resource<TestConfig> {
2726
return Promise.resolve(undefined);
2827
}
2928

30-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
29+
async refresh(): Promise<Partial<TestConfig> | null> {
3130
return {
3231
propA: 'a',
3332
propB: 10,
3433
propC: 'c',
3534
};
3635
}
37-
38-
async validateResource(config: unknown): Promise<ValidationResult> {
39-
return {
40-
isValid: true
41-
}
42-
}
4336
}
4437

4538
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 plugin = Plugin.create('testPlugin', [resource])
39+
it('Can apply resource', async () => {
40+
const resource= spy(new TestResource())
41+
const plugin = Plugin.create('testPlugin', [resource as any])
6042

6143
const plan = {
6244
operation: ResourceOperation.CREATE,
@@ -66,45 +48,13 @@ describe('Plugin tests', () => {
6648
]
6749
};
6850

69-
// If this doesn't throw then it passes the test
7051
await plugin.apply({ plan });
52+
expect(resource.applyCreate.calledOnce).to.be.true;
7153
});
7254

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-
const plugin = Plugin.create('testPlugin', [resource])
84-
85-
const plan = {
86-
operation: ResourceOperation.CREATE,
87-
resourceType: 'testResource',
88-
parameters: [
89-
{ name: 'propA', operation: ParameterOperation.ADD, newValue: 'abc', previousValue: null },
90-
]
91-
};
92-
93-
await expect(async () => plugin.apply({ plan })).rejects.toThrowError(expect.any(ApplyValidationError));
94-
});
95-
96-
it('Validates that deletes were successfully applied', async () => {
97-
const resource = new class extends TestResource {
98-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
99-
}
100-
101-
// Return null to indicate that the resource was deleted
102-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
103-
return null;
104-
}
105-
}
106-
107-
const testPlugin = Plugin.create('testPlugin', [resource])
55+
it('Can destroy resource', async () => {
56+
const resource = spy(new TestResource());
57+
const testPlugin = Plugin.create('testPlugin', [resource as any])
10858

10959
const plan = {
11060
operation: ResourceOperation.DESTROY,
@@ -114,46 +64,13 @@ describe('Plugin tests', () => {
11464
]
11565
};
11666

117-
// If this doesn't throw then it passes the test
11867
await testPlugin.apply({ plan })
68+
expect(resource.applyDestroy.calledOnce).to.be.true;
11969
});
12070

121-
it('Validates that deletes were successfully applied (error)', async () => {
122-
const resource = new class extends TestResource {
123-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
124-
}
125-
126-
// Return a value to indicate that the resource still exists
127-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
128-
return { propA: 'abc' };
129-
}
130-
}
131-
132-
const testPlugin = Plugin.create('testPlugin', [resource])
133-
134-
const plan = {
135-
operation: ResourceOperation.DESTROY,
136-
resourceType: 'testResource',
137-
parameters: [
138-
{ name: 'propA', operation: ParameterOperation.REMOVE, newValue: null, previousValue: 'abc' },
139-
]
140-
};
141-
142-
// If this doesn't throw then it passes the test
143-
expect(async () => await testPlugin.apply({ plan })).rejects.toThrowError(expect.any(ApplyValidationError));
144-
});
145-
146-
it('Validates that re-create was successfully applied', async () => {
147-
const resource = new class extends TestResource {
148-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
149-
}
150-
151-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
152-
return { propA: 'def'};
153-
}
154-
}
155-
156-
const testPlugin = Plugin.create('testPlugin', [resource])
71+
it('Can re-create resource', async () => {
72+
const resource = spy(new TestResource())
73+
const testPlugin = Plugin.create('testPlugin', [resource as any])
15774

15875
const plan = {
15976
operation: ResourceOperation.RECREATE,
@@ -163,31 +80,24 @@ describe('Plugin tests', () => {
16380
]
16481
};
16582

166-
// If this doesn't throw then it passes the test
16783
await testPlugin.apply({ plan })
84+
expect(resource.applyDestroy.calledOnce).to.be.true;
85+
expect(resource.applyCreate.calledOnce).to.be.true;
16886
});
16987

170-
it('Validates that modify was successfully applied (error)', async () => {
171-
const resource = new class extends TestResource {
172-
async applyCreate(plan: Plan<TestConfig>): Promise<void> {
173-
}
174-
175-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
176-
return { propA: 'abc' };
177-
}
178-
}
179-
180-
const testPlugin = Plugin.create('testPlugin', [resource])
88+
it('Can modify resource', async () => {
89+
const resource = spy(new TestResource())
90+
const testPlugin = Plugin.create('testPlugin', [resource as any])
18191

18292
const plan = {
183-
operation: ResourceOperation.DESTROY,
93+
operation: ResourceOperation.MODIFY,
18494
resourceType: 'testResource',
18595
parameters: [
186-
{ name: 'propA', operation: ParameterOperation.REMOVE, newValue: 'def', previousValue: 'abc' },
96+
{ name: 'propA', operation: ParameterOperation.MODIFY, newValue: 'def', previousValue: 'abc' },
18797
]
18898
};
18999

190-
// If this doesn't throw then it passes the test
191-
expect(async () => await testPlugin.apply({ plan })).rejects.toThrowError(expect.any(ApplyValidationError));
100+
await testPlugin.apply({ plan })
101+
expect(resource.applyModify.calledOnce).to.be.true;
192102
});
193103
});

src/entities/plugin.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,17 @@ export class Plugin {
5050
throw new Error(`Resource type not found: ${config.type}`);
5151
}
5252

53-
const { parameters } = splitUserConfig(config);
54-
const validateResult = await this.resources.get(config.type)!.validateResource(parameters);
55-
56-
validationResults.push({
57-
...validateResult,
58-
resourceType: config.type,
59-
resourceName: config.name,
60-
});
53+
const { parameters, resourceMetadata } = splitUserConfig(config);
54+
const validation = await this.resources
55+
.get(config.type)!
56+
.validate(parameters, resourceMetadata);
57+
58+
validationResults.push(validation);
6159
}
6260

6361
await this.crossValidateResources(data.configs);
6462
return {
65-
validationResults
63+
resourceValidations: validationResults
6664
};
6765
}
6866

src/entities/resource-parameters.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,9 @@ describe('Resource parameter tests', () => {
471471
const plan = await resource.plan({ type: 'resourceType', propC: 'abc' } as any);
472472

473473
expect(resource.refresh.called).to.be.true;
474-
expect(resource.refresh.getCall(0).firstArg.has('propA')).to.be.true;
475-
expect(resource.refresh.getCall(0).firstArg.has('propB')).to.be.true;
476-
expect(resource.refresh.getCall(0).firstArg.has('propC')).to.be.false;
474+
expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
475+
expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
476+
expect(resource.refresh.getCall(0).firstArg['propC']).to.not.exist;
477477

478478
expect(plan.desiredConfig?.propA).to.eq('propA');
479479
expect(plan.desiredConfig?.propB).to.eq(10);
@@ -513,8 +513,8 @@ describe('Resource parameter tests', () => {
513513
const plan = await resource.plan({ type: 'resourceType', propA: 'propA', propB: 10 } as any);
514514

515515
expect(transformParameter.transform.called).to.be.false;
516-
expect(resource.refresh.getCall(0).firstArg.has('propA')).to.be.true;
517-
expect(resource.refresh.getCall(0).firstArg.has('propB')).to.be.true;
516+
expect(resource.refresh.getCall(0).firstArg['propA']).to.exist;
517+
expect(resource.refresh.getCall(0).firstArg['propB']).to.exist;
518518

519519
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
520520
})

src/entities/resource-stateful-mode.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('Resource tests for stateful plans', () => {
1212
super({ type: 'resource' });
1313
}
1414

15-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
15+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
1616
return {
1717
propA: 'propADifferent',
1818
propB: undefined,
@@ -61,7 +61,7 @@ describe('Resource tests for stateful plans', () => {
6161
super({ type: 'resource' });
6262
}
6363

64-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
64+
async refresh(): Promise<Partial<TestConfig> | null> {
6565
return null;
6666
}
6767
}
@@ -113,7 +113,7 @@ describe('Resource tests for stateful plans', () => {
113113
super({ type: 'resource' });
114114
}
115115

116-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
116+
async refresh(): Promise<Partial<TestConfig> | null> {
117117
return {
118118
propA: 'propA',
119119
propC: 'propC',
@@ -184,7 +184,7 @@ describe('Resource tests for stateful plans', () => {
184184
});
185185
}
186186

187-
async refresh(keys: Map<string, unknown>): Promise<Partial<TestConfig> | null> {
187+
async refresh(): Promise<Partial<TestConfig> | null> {
188188
return {
189189
propA: 'propA',
190190
propC: 'propC',

src/entities/resource-types.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ export type ErrorMessage = string;
55
*/
66
export interface ResourceParameterOptions {
77
/**
8-
* Chose if the resource should be re-created or modified if this parameter is changed. Defaults to false (re-create).
8+
* Default value for the parameter. If a value is not provided in the config, the library will use this value.
99
*/
10-
modifyOnChange?: boolean;
10+
default?: unknown;
1111
/**
1212
* Customize the equality comparison for a parameter.
1313
* @param desired
1414
* @param current
1515
*/
1616
isEqual?: (desired: any, current: any) => boolean;
1717
/**
18-
* Default value for the parameter. If a value is not provided in the config, the library will use this value.
18+
* Chose if the resource should be re-created or modified if this parameter is changed. Defaults to false (re-create).
1919
*/
20-
default?: unknown;
20+
modifyOnChange?: boolean;
2121
}
2222

2323
/**
@@ -29,8 +29,3 @@ export interface ResourceDefinition {
2929
type: string;
3030
}
3131
}
32-
33-
export interface ValidationResult {
34-
isValid: boolean;
35-
errors?: unknown[],
36-
}

0 commit comments

Comments
 (0)