codify-plugin-lib 1.0.95 → 1.0.96
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/plan/change-set.js +17 -24
- package/dist/resource/parsed-resource-settings.js +4 -4
- package/dist/resource/resource-settings.d.ts +2 -1
- package/dist/resource/resource-settings.js +9 -1
- package/package.json +1 -1
- package/src/plan/change-set.ts +19 -27
- package/src/plugin/plugin.test.ts +24 -24
- package/src/plugin/plugin.ts +2 -2
- package/src/resource/parsed-resource-settings.test.ts +25 -26
- package/src/resource/parsed-resource-settings.ts +11 -5
- package/src/resource/resource-settings.test.ts +72 -0
- package/src/resource/resource-settings.ts +14 -2
package/dist/plan/change-set.js
CHANGED
|
@@ -81,46 +81,39 @@ export class ChangeSet {
|
|
|
81
81
|
// Filter out null and undefined values or else the diff below will not work
|
|
82
82
|
const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => v !== null && v !== undefined));
|
|
83
83
|
const current = Object.fromEntries(Object.entries(currentParameters).filter(([, v]) => v !== null && v !== undefined));
|
|
84
|
-
for (const
|
|
85
|
-
if (desired
|
|
84
|
+
for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
|
|
85
|
+
if (ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
|
|
86
86
|
parameterChangeSet.push({
|
|
87
87
|
name: k,
|
|
88
|
-
previousValue:
|
|
88
|
+
previousValue: current[k] ?? null,
|
|
89
|
+
newValue: desired[k] ?? null,
|
|
90
|
+
operation: ParameterOperation.NOOP,
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if ((desired?.[k] === null || desired?.[k] === undefined) && (current?.[k] !== null && current?.[k] !== undefined)) {
|
|
95
|
+
parameterChangeSet.push({
|
|
96
|
+
name: k,
|
|
97
|
+
previousValue: current[k] ?? null,
|
|
89
98
|
newValue: null,
|
|
90
99
|
operation: ParameterOperation.REMOVE,
|
|
91
100
|
});
|
|
92
|
-
delete current[k];
|
|
93
101
|
continue;
|
|
94
102
|
}
|
|
95
|
-
if (
|
|
103
|
+
if ((current?.[k] === null || current?.[k] === undefined) && (desired?.[k] !== null && desired?.[k] !== undefined)) {
|
|
96
104
|
parameterChangeSet.push({
|
|
97
105
|
name: k,
|
|
98
|
-
previousValue:
|
|
106
|
+
previousValue: null,
|
|
99
107
|
newValue: desired[k] ?? null,
|
|
100
|
-
operation: ParameterOperation.
|
|
108
|
+
operation: ParameterOperation.ADD,
|
|
101
109
|
});
|
|
102
|
-
delete current[k];
|
|
103
|
-
delete desired[k];
|
|
104
110
|
continue;
|
|
105
111
|
}
|
|
106
112
|
parameterChangeSet.push({
|
|
107
113
|
name: k,
|
|
108
|
-
previousValue:
|
|
114
|
+
previousValue: current[k] ?? null,
|
|
109
115
|
newValue: desired[k] ?? null,
|
|
110
|
-
operation: ParameterOperation.
|
|
111
|
-
});
|
|
112
|
-
delete current[k];
|
|
113
|
-
delete desired[k];
|
|
114
|
-
}
|
|
115
|
-
if (Object.keys(current).length > 0) {
|
|
116
|
-
throw new Error('Diff algorithm error');
|
|
117
|
-
}
|
|
118
|
-
for (const [k, v] of Object.entries(desired)) {
|
|
119
|
-
parameterChangeSet.push({
|
|
120
|
-
name: k,
|
|
121
|
-
previousValue: null,
|
|
122
|
-
newValue: v ?? null,
|
|
123
|
-
operation: ParameterOperation.ADD,
|
|
116
|
+
operation: ParameterOperation.MODIFY,
|
|
124
117
|
});
|
|
125
118
|
}
|
|
126
119
|
return parameterChangeSet;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveEqualsFn } from './resource-settings.js';
|
|
1
|
+
import { resolveEqualsFn, resolveParameterTransformFn } from './resource-settings.js';
|
|
2
2
|
export class ParsedResourceSettings {
|
|
3
3
|
cache = new Map();
|
|
4
4
|
id;
|
|
@@ -61,8 +61,8 @@ export class ParsedResourceSettings {
|
|
|
61
61
|
return {};
|
|
62
62
|
}
|
|
63
63
|
return Object.fromEntries(Object.entries(this.settings.parameterSettings)
|
|
64
|
-
.filter(([, v]) => v
|
|
65
|
-
.map(([k, v]) => [k, v
|
|
64
|
+
.filter(([, v]) => resolveParameterTransformFn(v) !== undefined)
|
|
65
|
+
.map(([k, v]) => [k, resolveParameterTransformFn(v)]));
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
get statefulParameterOrder() {
|
|
@@ -112,7 +112,7 @@ export class ParsedResourceSettings {
|
|
|
112
112
|
&& schema.else.some((s) => s.required))) {
|
|
113
113
|
throw new Error(`In the schema of ${this.settings.id}, a conditional required was declared (within anyOf, allOf, oneOf, else, or then) but an` +
|
|
114
114
|
'import.requiredParameters was not found in the resource settings. This is required because Codify uses the required parameter to' +
|
|
115
|
-
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when' +
|
|
115
|
+
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when ' +
|
|
116
116
|
'required is declared conditionally.');
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -61,7 +61,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
|
|
|
61
61
|
* config with desired config. Certain types will have additional options to help support it. For example the type
|
|
62
62
|
* stateful requires a stateful parameter definition and type array takes an isElementEqual method.
|
|
63
63
|
*/
|
|
64
|
-
export type ParameterSettingType = 'any' | 'array' | 'boolean' | 'directory' | 'number' | 'stateful' | 'string' | 'version';
|
|
64
|
+
export type ParameterSettingType = 'any' | 'array' | 'boolean' | 'directory' | 'number' | 'setting' | 'stateful' | 'string' | 'version';
|
|
65
65
|
/**
|
|
66
66
|
* Typing information for the parameter setting. This represents a setting on a specific parameter within a
|
|
67
67
|
* resource. Options for configuring parameters operations including overriding the equals function, adding default values
|
|
@@ -150,3 +150,4 @@ export interface StatefulParameterSetting extends DefaultParameterSetting {
|
|
|
150
150
|
order?: number;
|
|
151
151
|
}
|
|
152
152
|
export declare function resolveEqualsFn(parameter: ParameterSetting, key: string): (desired: unknown, current: unknown) => boolean;
|
|
153
|
+
export declare function resolveParameterTransformFn(parameter: ParameterSetting): ((input: any) => Promise<any> | any) | undefined;
|
|
@@ -5,7 +5,8 @@ const ParameterEqualsDefaults = {
|
|
|
5
5
|
'directory': (a, b) => path.resolve(untildify(String(a))) === path.resolve(untildify(String(b))),
|
|
6
6
|
'number': (a, b) => Number(a) === Number(b),
|
|
7
7
|
'string': (a, b) => String(a) === String(b),
|
|
8
|
-
'version': (desired, current) => String(current).includes(String(desired))
|
|
8
|
+
'version': (desired, current) => String(current).includes(String(desired)),
|
|
9
|
+
'setting': (a, b) => true,
|
|
9
10
|
};
|
|
10
11
|
export function resolveEqualsFn(parameter, key) {
|
|
11
12
|
if (parameter.type === 'array') {
|
|
@@ -16,3 +17,10 @@ export function resolveEqualsFn(parameter, key) {
|
|
|
16
17
|
}
|
|
17
18
|
return parameter.isEqual ?? ParameterEqualsDefaults[parameter.type] ?? (((a, b) => a === b));
|
|
18
19
|
}
|
|
20
|
+
const ParameterTransformationDefaults = {
|
|
21
|
+
'directory': (a) => path.resolve(untildify(String(a))),
|
|
22
|
+
'string': String,
|
|
23
|
+
};
|
|
24
|
+
export function resolveParameterTransformFn(parameter) {
|
|
25
|
+
return parameter.inputTransformation ?? ParameterTransformationDefaults[parameter.type] ?? undefined;
|
|
26
|
+
}
|
package/package.json
CHANGED
package/src/plan/change-set.ts
CHANGED
|
@@ -141,53 +141,45 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
141
141
|
Object.entries(currentParameters).filter(([, v]) => v !== null && v !== undefined)
|
|
142
142
|
) as Partial<T>
|
|
143
143
|
|
|
144
|
-
for (const
|
|
145
|
-
if (desired
|
|
144
|
+
for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
|
|
145
|
+
if (ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
|
|
146
146
|
parameterChangeSet.push({
|
|
147
147
|
name: k,
|
|
148
|
-
previousValue:
|
|
148
|
+
previousValue: current[k] ?? null,
|
|
149
|
+
newValue: desired[k] ?? null,
|
|
150
|
+
operation: ParameterOperation.NOOP,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if ((desired?.[k] === null || desired?.[k] === undefined) && (current?.[k] !== null && current?.[k] !== undefined)) {
|
|
157
|
+
parameterChangeSet.push({
|
|
158
|
+
name: k,
|
|
159
|
+
previousValue: current[k] ?? null,
|
|
149
160
|
newValue: null,
|
|
150
161
|
operation: ParameterOperation.REMOVE,
|
|
151
162
|
})
|
|
152
163
|
|
|
153
|
-
delete current[k];
|
|
154
164
|
continue;
|
|
155
165
|
}
|
|
156
166
|
|
|
157
|
-
if (
|
|
167
|
+
if ((current?.[k] === null || current?.[k] === undefined) && (desired?.[k] !== null && desired?.[k] !== undefined)) {
|
|
158
168
|
parameterChangeSet.push({
|
|
159
169
|
name: k,
|
|
160
|
-
previousValue:
|
|
170
|
+
previousValue: null,
|
|
161
171
|
newValue: desired[k] ?? null,
|
|
162
|
-
operation: ParameterOperation.
|
|
172
|
+
operation: ParameterOperation.ADD,
|
|
163
173
|
})
|
|
164
174
|
|
|
165
|
-
delete current[k];
|
|
166
|
-
delete desired[k];
|
|
167
175
|
continue;
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
parameterChangeSet.push({
|
|
171
179
|
name: k,
|
|
172
|
-
previousValue:
|
|
180
|
+
previousValue: current[k] ?? null,
|
|
173
181
|
newValue: desired[k] ?? null,
|
|
174
|
-
operation: ParameterOperation.
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
delete current[k];
|
|
178
|
-
delete desired[k];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (Object.keys(current).length > 0) {
|
|
182
|
-
throw new Error('Diff algorithm error');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
for (const [k, v] of Object.entries(desired)) {
|
|
186
|
-
parameterChangeSet.push({
|
|
187
|
-
name: k,
|
|
188
|
-
previousValue: null,
|
|
189
|
-
newValue: v ?? null,
|
|
190
|
-
operation: ParameterOperation.ADD,
|
|
182
|
+
operation: ParameterOperation.MODIFY,
|
|
191
183
|
})
|
|
192
184
|
}
|
|
193
185
|
|
|
@@ -104,21 +104,21 @@ describe('Plugin tests', () => {
|
|
|
104
104
|
|
|
105
105
|
it('Can get resource info', async () => {
|
|
106
106
|
const schema = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
'$schema': 'http://json-schema.org/draft-07/schema',
|
|
108
|
+
'$id': 'https://www.codifycli.com/asdf-schema.json',
|
|
109
|
+
'title': 'Asdf resource',
|
|
110
|
+
'type': 'object',
|
|
111
|
+
'properties': {
|
|
112
|
+
'plugins': {
|
|
113
|
+
'type': 'array',
|
|
114
|
+
'description': 'Asdf plugins to install. See: https://github.com/asdf-community for a full list',
|
|
115
|
+
'items': {
|
|
116
|
+
'type': 'string'
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
'required': ['plugins'],
|
|
121
|
+
'additionalProperties': false
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
|
|
@@ -142,21 +142,21 @@ describe('Plugin tests', () => {
|
|
|
142
142
|
|
|
143
143
|
it('Get resource info to default import to the one specified in the resource settings', async () => {
|
|
144
144
|
const schema = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
'$schema': 'http://json-schema.org/draft-07/schema',
|
|
146
|
+
'$id': 'https://www.codifycli.com/asdf-schema.json',
|
|
147
|
+
'title': 'Asdf resource',
|
|
148
|
+
'type': 'object',
|
|
149
|
+
'properties': {
|
|
150
|
+
'plugins': {
|
|
151
|
+
'type': 'array',
|
|
152
|
+
'description': 'Asdf plugins to install. See: https://github.com/asdf-community for a full list',
|
|
153
|
+
'items': {
|
|
154
|
+
'type': 'string'
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
},
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
'required': ['plugins'],
|
|
159
|
+
'additionalProperties': false
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
|
package/src/plugin/plugin.ts
CHANGED
|
@@ -62,8 +62,8 @@ export class Plugin {
|
|
|
62
62
|
const schema = resource.settings.schema as JSONSchemaType<any> | undefined;
|
|
63
63
|
const requiredPropertyNames = (
|
|
64
64
|
resource.settings.import?.requiredParameters
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
?? schema?.required
|
|
66
|
+
?? null
|
|
67
67
|
) as null | string[];
|
|
68
68
|
|
|
69
69
|
return {
|
|
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
import { ResourceSettings } from './resource-settings.js';
|
|
3
3
|
import { ParsedResourceSettings } from './parsed-resource-settings.js';
|
|
4
4
|
import { TestConfig } from '../utils/test-utils.test.js';
|
|
5
|
-
import { JSONSchemaType } from 'ajv';
|
|
6
5
|
|
|
7
6
|
describe('Resource options parser tests', () => {
|
|
8
7
|
it('Parses default values from options', () => {
|
|
@@ -25,37 +24,37 @@ describe('Resource options parser tests', () => {
|
|
|
25
24
|
|
|
26
25
|
it('Throws an error when an import.requiredParameters is not declared', () => {
|
|
27
26
|
const schema = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
'$schema': 'http://json-schema.org/draft-07/schema',
|
|
28
|
+
'$id': 'https://www.codifycli.com/git-clone.json',
|
|
29
|
+
'title': 'Git-clone resource',
|
|
30
|
+
'type': 'object',
|
|
31
|
+
'properties': {
|
|
32
|
+
'remote': {
|
|
33
|
+
'type': 'string',
|
|
34
|
+
'description': 'Remote tracking url to clone repo from. Equivalent to repository and only one should be specified'
|
|
36
35
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
'repository': {
|
|
37
|
+
'type': 'string',
|
|
38
|
+
'description': 'Remote repository to clone repo from. Equivalent to remote and only one should be specified'
|
|
40
39
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
'parentDirectory': {
|
|
41
|
+
'type': 'string',
|
|
42
|
+
'description': 'Parent directory to clone into. The folder name will use default git semantics which extracts the last part of the clone url. Only one of parentDirectory or directory can be specified'
|
|
44
43
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
'directory': {
|
|
45
|
+
'type': 'string',
|
|
46
|
+
'description': 'Directory to clone contents into. This value is directly passed into git clone. This differs from parent directory in that the last part of the path will be the folder name of the repo'
|
|
48
47
|
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
'autoVerifySSH': {
|
|
49
|
+
'type': 'boolean',
|
|
50
|
+
'description': 'Automatically verifies the ssh connection for ssh git clones. Defaults to true.'
|
|
52
51
|
}
|
|
53
52
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
{
|
|
58
|
-
{
|
|
53
|
+
'additionalProperties': false,
|
|
54
|
+
'oneOf': [
|
|
55
|
+
{ 'required': ['repository', 'directory'] },
|
|
56
|
+
{ 'required': ['repository', 'parentDirectory'] },
|
|
57
|
+
{ 'required': ['remote', 'directory'] }
|
|
59
58
|
]
|
|
60
59
|
}
|
|
61
60
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { JSONSchemaType } from 'ajv';
|
|
1
2
|
import { StringIndexedObject } from 'codify-schemas';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ParameterSetting,
|
|
6
|
+
ResourceSettings,
|
|
7
|
+
StatefulParameterSetting,
|
|
8
|
+
resolveEqualsFn,
|
|
9
|
+
resolveParameterTransformFn
|
|
10
|
+
} from './resource-settings.js';
|
|
4
11
|
import { StatefulParameter as StatefulParameterImpl } from './stateful-parameter.js'
|
|
5
|
-
import { JSONSchemaType } from 'ajv';
|
|
6
12
|
|
|
7
13
|
export class ParsedResourceSettings<T extends StringIndexedObject> implements ResourceSettings<T> {
|
|
8
14
|
private cache = new Map<string, unknown>();
|
|
@@ -88,8 +94,8 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
88
94
|
|
|
89
95
|
return Object.fromEntries(
|
|
90
96
|
Object.entries(this.settings.parameterSettings)
|
|
91
|
-
.filter(([, v]) => v
|
|
92
|
-
.map(([k, v]) => [k, v
|
|
97
|
+
.filter(([, v]) => resolveParameterTransformFn(v!) !== undefined)
|
|
98
|
+
.map(([k, v]) => [k, resolveParameterTransformFn(v!)] as const)
|
|
93
99
|
) as Record<keyof T, (a: unknown) => unknown>;
|
|
94
100
|
});
|
|
95
101
|
}
|
|
@@ -157,7 +163,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
|
|
|
157
163
|
) {
|
|
158
164
|
throw new Error(`In the schema of ${this.settings.id}, a conditional required was declared (within anyOf, allOf, oneOf, else, or then) but an` +
|
|
159
165
|
'import.requiredParameters was not found in the resource settings. This is required because Codify uses the required parameter to' +
|
|
160
|
-
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when' +
|
|
166
|
+
'determine the prompt to ask users during imports. It can\'t parse which parameters are needed when ' +
|
|
161
167
|
'required is declared conditionally.'
|
|
162
168
|
)
|
|
163
169
|
}
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from '../utils/test-utils.test.js';
|
|
12
12
|
import { ArrayParameterSetting, ParameterSetting, ResourceSettings } from './resource-settings.js';
|
|
13
13
|
import { ResourceController } from './resource-controller.js';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
14
16
|
|
|
15
17
|
describe('Resource parameter tests', () => {
|
|
16
18
|
it('Generates a resource plan that includes stateful parameters (create)', async () => {
|
|
@@ -540,4 +542,74 @@ describe('Resource parameter tests', () => {
|
|
|
540
542
|
}
|
|
541
543
|
};
|
|
542
544
|
})
|
|
545
|
+
|
|
546
|
+
it('Applies default input transformations', async () => {
|
|
547
|
+
const home = os.homedir()
|
|
548
|
+
const testPath = path.join(home, 'test/folder');
|
|
549
|
+
|
|
550
|
+
const resource = new class extends TestResource {
|
|
551
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
552
|
+
return {
|
|
553
|
+
id: 'resourceType',
|
|
554
|
+
parameterSettings: {
|
|
555
|
+
propA: { type: 'directory' }
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
561
|
+
return { propA: testPath }
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const controller = new ResourceController(resource);
|
|
566
|
+
const plan = await controller.plan({ type: 'resourceType', propA: '~/test/folder' } as any);
|
|
567
|
+
|
|
568
|
+
expect(plan.changeSet.parameterChanges[0]).toMatchObject({
|
|
569
|
+
operation: ParameterOperation.NOOP,
|
|
570
|
+
newValue: testPath,
|
|
571
|
+
previousValue: testPath,
|
|
572
|
+
})
|
|
573
|
+
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it('Ignores setting parameters when planning', async () => {
|
|
577
|
+
const resource = new class extends TestResource {
|
|
578
|
+
getSettings(): ResourceSettings<TestConfig> {
|
|
579
|
+
return {
|
|
580
|
+
id: 'resourceType',
|
|
581
|
+
parameterSettings: {
|
|
582
|
+
propA: { type: 'setting' },
|
|
583
|
+
propB: { type: 'number' }
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
|
|
589
|
+
return { propB: 64 }
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
const controller = new ResourceController(resource);
|
|
594
|
+
const plan = await controller.plan({ type: 'resourceType', propA: 'setting', propB: 64 } as any);
|
|
595
|
+
|
|
596
|
+
expect(plan.changeSet.parameterChanges).toMatchObject(
|
|
597
|
+
expect.arrayContaining([
|
|
598
|
+
{
|
|
599
|
+
name: 'propA',
|
|
600
|
+
operation: ParameterOperation.NOOP,
|
|
601
|
+
previousValue: null,
|
|
602
|
+
newValue: 'setting',
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: 'propB',
|
|
606
|
+
operation: ParameterOperation.NOOP,
|
|
607
|
+
previousValue: 64,
|
|
608
|
+
newValue: 64,
|
|
609
|
+
}
|
|
610
|
+
])
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
expect(plan.changeSet.operation).to.eq(ResourceOperation.NOOP);
|
|
614
|
+
})
|
|
543
615
|
})
|
|
@@ -81,6 +81,7 @@ export type ParameterSettingType =
|
|
|
81
81
|
| 'boolean'
|
|
82
82
|
| 'directory'
|
|
83
83
|
| 'number'
|
|
84
|
+
| 'setting'
|
|
84
85
|
| 'stateful'
|
|
85
86
|
| 'string'
|
|
86
87
|
| 'version';
|
|
@@ -192,10 +193,10 @@ const ParameterEqualsDefaults: Partial<Record<ParameterSettingType, (a: unknown,
|
|
|
192
193
|
'directory': (a: unknown, b: unknown) => path.resolve(untildify(String(a))) === path.resolve(untildify(String(b))),
|
|
193
194
|
'number': (a: unknown, b: unknown) => Number(a) === Number(b),
|
|
194
195
|
'string': (a: unknown, b: unknown) => String(a) === String(b),
|
|
195
|
-
'version': (desired: unknown, current: unknown) => String(current).includes(String(desired))
|
|
196
|
+
'version': (desired: unknown, current: unknown) => String(current).includes(String(desired)),
|
|
197
|
+
'setting': (a: unknown, b: unknown) => true,
|
|
196
198
|
}
|
|
197
199
|
|
|
198
|
-
|
|
199
200
|
export function resolveEqualsFn(parameter: ParameterSetting, key: string): (desired: unknown, current: unknown) => boolean {
|
|
200
201
|
if (parameter.type === 'array') {
|
|
201
202
|
return parameter.isEqual ?? areArraysEqual.bind(areArraysEqual, parameter as ArrayParameterSetting)
|
|
@@ -207,3 +208,14 @@ export function resolveEqualsFn(parameter: ParameterSetting, key: string): (desi
|
|
|
207
208
|
|
|
208
209
|
return parameter.isEqual ?? ParameterEqualsDefaults[parameter.type as ParameterSettingType] ?? (((a, b) => a === b));
|
|
209
210
|
}
|
|
211
|
+
|
|
212
|
+
const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, (input: any) => Promise<any> | any>> = {
|
|
213
|
+
'directory': (a: unknown) => path.resolve(untildify(String(a))),
|
|
214
|
+
'string': String,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function resolveParameterTransformFn(
|
|
218
|
+
parameter: ParameterSetting
|
|
219
|
+
): ((input: any) => Promise<any> | any) | undefined {
|
|
220
|
+
return parameter.inputTransformation ?? ParameterTransformationDefaults[parameter.type as ParameterSettingType] ?? undefined;
|
|
221
|
+
}
|