codify-plugin-lib 1.0.177 → 1.0.179
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/messages/sender.d.ts +1 -0
- package/dist/messages/sender.js +10 -0
- package/dist/plan/change-set.d.ts +8 -3
- package/dist/plan/change-set.js +10 -3
- package/dist/plan/plan.js +7 -4
- package/dist/plugin/plugin.d.ts +1 -1
- package/dist/plugin/plugin.js +8 -1
- package/dist/resource/resource-controller.d.ts +1 -1
- package/dist/resource/resource-controller.js +10 -10
- package/dist/resource/resource-settings.d.ts +6 -0
- package/package.json +2 -2
- package/src/messages/sender.ts +13 -0
- package/src/plan/change-set.test.ts +1 -1
- package/src/plan/change-set.ts +16 -3
- package/src/plan/plan.ts +7 -4
- package/src/plugin/plugin.ts +9 -1
- package/src/resource/resource-controller-stateful-mode.test.ts +15 -7
- package/src/resource/resource-controller.test.ts +4 -2
- package/src/resource/resource-controller.ts +11 -11
- package/src/resource/resource-settings.test.ts +2 -0
- package/src/resource/resource-settings.ts +8 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
declare class CodifyCliSenderImpl {
|
|
5
5
|
private readonly validateIpcMessageV2;
|
|
6
6
|
requestPressKeyToContinuePrompt(message?: string): Promise<void>;
|
|
7
|
+
getCodifyCliCredentials(): Promise<string>;
|
|
7
8
|
private sendAndWaitForResponse;
|
|
8
9
|
}
|
|
9
10
|
export declare const CodifyCliSender: CodifyCliSenderImpl;
|
package/dist/messages/sender.js
CHANGED
|
@@ -17,6 +17,16 @@ class CodifyCliSenderImpl {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
+
async getCodifyCliCredentials() {
|
|
21
|
+
const data = await this.sendAndWaitForResponse({
|
|
22
|
+
cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST,
|
|
23
|
+
data: {},
|
|
24
|
+
});
|
|
25
|
+
if (typeof data.data !== 'string') {
|
|
26
|
+
throw new Error('Expected string back from credentials request');
|
|
27
|
+
}
|
|
28
|
+
return data.data;
|
|
29
|
+
}
|
|
20
30
|
async sendAndWaitForResponse(message) {
|
|
21
31
|
return new Promise((resolve) => {
|
|
22
32
|
const requestId = nanoid(8);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
import { ParsedParameterSetting } from '../resource/parsed-resource-settings.js';
|
|
3
|
+
import { ResourceSettings } from '../resource/resource-settings.js';
|
|
3
4
|
/**
|
|
4
5
|
* A parameter change describes a parameter level change to a resource.
|
|
5
6
|
*/
|
|
@@ -20,6 +21,10 @@ export interface ParameterChange<T extends StringIndexedObject> {
|
|
|
20
21
|
* The new value of the resource (the desired value)
|
|
21
22
|
*/
|
|
22
23
|
newValue: any | null;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the parameter is sensitive
|
|
26
|
+
*/
|
|
27
|
+
isSensitive: boolean;
|
|
23
28
|
}
|
|
24
29
|
export declare class ChangeSet<T extends StringIndexedObject> {
|
|
25
30
|
operation: ResourceOperation;
|
|
@@ -28,9 +33,9 @@ export declare class ChangeSet<T extends StringIndexedObject> {
|
|
|
28
33
|
get desiredParameters(): T;
|
|
29
34
|
get currentParameters(): T;
|
|
30
35
|
static empty<T extends StringIndexedObject>(): ChangeSet<T>;
|
|
31
|
-
static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T>;
|
|
32
|
-
static noop<T extends StringIndexedObject>(parameters: Partial<T>): ChangeSet<T>;
|
|
33
|
-
static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T>;
|
|
36
|
+
static create<T extends StringIndexedObject>(desired: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T>;
|
|
37
|
+
static noop<T extends StringIndexedObject>(parameters: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T>;
|
|
38
|
+
static destroy<T extends StringIndexedObject>(current: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T>;
|
|
34
39
|
static calculateModification<T extends StringIndexedObject>(desired: Partial<T>, current: Partial<T>, parameterSettings?: Partial<Record<keyof T, ParsedParameterSetting>>): ChangeSet<T>;
|
|
35
40
|
/**
|
|
36
41
|
* Calculates the differences between the desired and current parameters,
|
package/dist/plan/change-set.js
CHANGED
|
@@ -24,33 +24,36 @@ export class ChangeSet {
|
|
|
24
24
|
static empty() {
|
|
25
25
|
return new ChangeSet(ResourceOperation.NOOP, []);
|
|
26
26
|
}
|
|
27
|
-
static create(desired) {
|
|
27
|
+
static create(desired, settings) {
|
|
28
28
|
const parameterChanges = Object.entries(desired)
|
|
29
29
|
.map(([k, v]) => ({
|
|
30
30
|
name: k,
|
|
31
31
|
operation: ParameterOperation.ADD,
|
|
32
32
|
previousValue: null,
|
|
33
33
|
newValue: v ?? null,
|
|
34
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
34
35
|
}));
|
|
35
36
|
return new ChangeSet(ResourceOperation.CREATE, parameterChanges);
|
|
36
37
|
}
|
|
37
|
-
static noop(parameters) {
|
|
38
|
+
static noop(parameters, settings) {
|
|
38
39
|
const parameterChanges = Object.entries(parameters)
|
|
39
40
|
.map(([k, v]) => ({
|
|
40
41
|
name: k,
|
|
41
42
|
operation: ParameterOperation.NOOP,
|
|
42
43
|
previousValue: v ?? null,
|
|
43
44
|
newValue: v ?? null,
|
|
45
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
44
46
|
}));
|
|
45
47
|
return new ChangeSet(ResourceOperation.NOOP, parameterChanges);
|
|
46
48
|
}
|
|
47
|
-
static destroy(current) {
|
|
49
|
+
static destroy(current, settings) {
|
|
48
50
|
const parameterChanges = Object.entries(current)
|
|
49
51
|
.map(([k, v]) => ({
|
|
50
52
|
name: k,
|
|
51
53
|
operation: ParameterOperation.REMOVE,
|
|
52
54
|
previousValue: v ?? null,
|
|
53
55
|
newValue: null,
|
|
56
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
54
57
|
}));
|
|
55
58
|
return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
|
|
56
59
|
}
|
|
@@ -98,6 +101,7 @@ export class ChangeSet {
|
|
|
98
101
|
previousValue: current[k] ?? null,
|
|
99
102
|
newValue: desired[k] ?? null,
|
|
100
103
|
operation: ParameterOperation.NOOP,
|
|
104
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
101
105
|
});
|
|
102
106
|
continue;
|
|
103
107
|
}
|
|
@@ -107,6 +111,7 @@ export class ChangeSet {
|
|
|
107
111
|
previousValue: current[k] ?? null,
|
|
108
112
|
newValue: null,
|
|
109
113
|
operation: ParameterOperation.REMOVE,
|
|
114
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
110
115
|
});
|
|
111
116
|
continue;
|
|
112
117
|
}
|
|
@@ -116,6 +121,7 @@ export class ChangeSet {
|
|
|
116
121
|
previousValue: null,
|
|
117
122
|
newValue: desired[k] ?? null,
|
|
118
123
|
operation: ParameterOperation.ADD,
|
|
124
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
119
125
|
});
|
|
120
126
|
continue;
|
|
121
127
|
}
|
|
@@ -124,6 +130,7 @@ export class ChangeSet {
|
|
|
124
130
|
previousValue: current[k] ?? null,
|
|
125
131
|
newValue: desired[k] ?? null,
|
|
126
132
|
operation: ParameterOperation.MODIFY,
|
|
133
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
127
134
|
});
|
|
128
135
|
}
|
|
129
136
|
return parameterChangeSet;
|
package/dist/plan/plan.js
CHANGED
|
@@ -68,15 +68,15 @@ export class Plan {
|
|
|
68
68
|
}
|
|
69
69
|
// CREATE
|
|
70
70
|
if (!filteredCurrentParameters && desired) {
|
|
71
|
-
return new Plan(uuidV4(), ChangeSet.create(desired), core, isStateful);
|
|
71
|
+
return new Plan(uuidV4(), ChangeSet.create(desired, settings), core, isStateful);
|
|
72
72
|
}
|
|
73
73
|
// DESTROY
|
|
74
74
|
if (filteredCurrentParameters && !desired) {
|
|
75
75
|
// We can manually override destroys. If a resource cannot be destroyed (for instance the npm resource relies on NodeJS being created and destroyed)
|
|
76
76
|
if (!settings.canDestroy) {
|
|
77
|
-
return new Plan(uuidV4(), ChangeSet.noop(filteredCurrentParameters), core, isStateful);
|
|
77
|
+
return new Plan(uuidV4(), ChangeSet.noop(filteredCurrentParameters, settings), core, isStateful);
|
|
78
78
|
}
|
|
79
|
-
return new Plan(uuidV4(), ChangeSet.destroy(filteredCurrentParameters), core, isStateful);
|
|
79
|
+
return new Plan(uuidV4(), ChangeSet.destroy(filteredCurrentParameters, settings), core, isStateful);
|
|
80
80
|
}
|
|
81
81
|
// NO-OP, MODIFY or RE-CREATE
|
|
82
82
|
const changeSet = ChangeSet.calculateModification(desired, filteredCurrentParameters, settings.parameterSettings);
|
|
@@ -88,7 +88,10 @@ export class Plan {
|
|
|
88
88
|
throw new Error('Data is empty');
|
|
89
89
|
}
|
|
90
90
|
addDefaultValues();
|
|
91
|
-
return new Plan(uuidV4(), new ChangeSet(data.operation, data.parameters)
|
|
91
|
+
return new Plan(uuidV4(), new ChangeSet(data.operation, data.parameters.map((p) => ({
|
|
92
|
+
...p,
|
|
93
|
+
isSensitive: p.isSensitive ?? false,
|
|
94
|
+
}))), {
|
|
92
95
|
type: data.resourceType,
|
|
93
96
|
name: data.resourceName,
|
|
94
97
|
}, data.isStateful);
|
package/dist/plugin/plugin.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare class Plugin {
|
|
|
11
11
|
constructor(name: string, resourceControllers: Map<string, ResourceController<ResourceConfig>>);
|
|
12
12
|
static create(name: string, resources: Resource<any>[]): Plugin;
|
|
13
13
|
initialize(data: InitializeRequestData): Promise<InitializeResponseData>;
|
|
14
|
-
getResourceInfo(data: GetResourceInfoRequestData):
|
|
14
|
+
getResourceInfo(data: GetResourceInfoRequestData): GetResourceInfoResponseData;
|
|
15
15
|
match(data: MatchRequestData): Promise<MatchResponseData>;
|
|
16
16
|
import(data: ImportRequestData): Promise<ImportResponseData>;
|
|
17
17
|
validate(data: ValidateRequestData): Promise<ValidateResponseData>;
|
package/dist/plugin/plugin.js
CHANGED
|
@@ -33,10 +33,13 @@ export class Plugin {
|
|
|
33
33
|
.map((r) => ({
|
|
34
34
|
dependencies: r.dependencies,
|
|
35
35
|
type: r.typeId,
|
|
36
|
+
sensitiveParameters: Object.entries(r.settings.parameterSettings ?? {})
|
|
37
|
+
.filter(([, v]) => v?.isSensitive)
|
|
38
|
+
.map(([k]) => k),
|
|
36
39
|
}))
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
|
-
|
|
42
|
+
getResourceInfo(data) {
|
|
40
43
|
if (!this.resourceControllers.has(data.type)) {
|
|
41
44
|
throw new Error(`Cannot get info for resource ${data.type}, resource doesn't exist`);
|
|
42
45
|
}
|
|
@@ -48,6 +51,9 @@ export class Plugin {
|
|
|
48
51
|
?? undefined);
|
|
49
52
|
const allowMultiple = resource.settings.allowMultiple !== undefined
|
|
50
53
|
&& resource.settings.allowMultiple !== false;
|
|
54
|
+
const sensitiveParameters = Object.entries(resource.settings.parameterSettings ?? {})
|
|
55
|
+
.filter(([, v]) => v?.isSensitive)
|
|
56
|
+
.map(([k]) => k);
|
|
51
57
|
return {
|
|
52
58
|
plugin: this.name,
|
|
53
59
|
type: data.type,
|
|
@@ -60,6 +66,7 @@ export class Plugin {
|
|
|
60
66
|
import: {
|
|
61
67
|
requiredParameters: requiredPropertyNames,
|
|
62
68
|
},
|
|
69
|
+
sensitiveParameters,
|
|
63
70
|
allowMultiple
|
|
64
71
|
};
|
|
65
72
|
}
|
|
@@ -24,7 +24,7 @@ export declare class ResourceController<T extends StringIndexedObject> {
|
|
|
24
24
|
private applyModify;
|
|
25
25
|
private applyDestroy;
|
|
26
26
|
private validateRefreshResults;
|
|
27
|
-
private
|
|
27
|
+
private applyTransformations;
|
|
28
28
|
private addDefaultValues;
|
|
29
29
|
private removeDefaultValues;
|
|
30
30
|
private refreshNonStatefulParameters;
|
|
@@ -33,7 +33,7 @@ export class ResourceController {
|
|
|
33
33
|
}
|
|
34
34
|
async validate(core, parameters) {
|
|
35
35
|
const originalParameters = structuredClone(parameters);
|
|
36
|
-
await this.
|
|
36
|
+
await this.applyTransformations(parameters, undefined, true);
|
|
37
37
|
this.addDefaultValues(parameters);
|
|
38
38
|
if (this.schemaValidator) {
|
|
39
39
|
// Schema validator uses pre transformation parameters
|
|
@@ -103,9 +103,9 @@ export class ResourceController {
|
|
|
103
103
|
const originalParams = structuredClone(resource.parameters);
|
|
104
104
|
const paramsToMatch = structuredClone(resourceToMatch.parameters);
|
|
105
105
|
this.addDefaultValues(originalParams);
|
|
106
|
-
await this.
|
|
106
|
+
await this.applyTransformations(originalParams);
|
|
107
107
|
this.addDefaultValues(paramsToMatch);
|
|
108
|
-
await this.
|
|
108
|
+
await this.applyTransformations(paramsToMatch);
|
|
109
109
|
const match = parameterMatcher(originalParams, paramsToMatch);
|
|
110
110
|
if (match) {
|
|
111
111
|
return resourceToMatch;
|
|
@@ -120,9 +120,9 @@ export class ResourceController {
|
|
|
120
120
|
originalDesiredConfig: structuredClone(desired),
|
|
121
121
|
};
|
|
122
122
|
this.addDefaultValues(desired);
|
|
123
|
-
await this.
|
|
123
|
+
await this.applyTransformations(desired);
|
|
124
124
|
this.addDefaultValues(state);
|
|
125
|
-
await this.
|
|
125
|
+
await this.applyTransformations(state);
|
|
126
126
|
// Parse data from the user supplied config
|
|
127
127
|
const parsedConfig = new ConfigParser(desired, state, this.parsedSettings.statefulParameters);
|
|
128
128
|
const { allParameters, allNonStatefulParameters, allStatefulParameters, } = parsedConfig;
|
|
@@ -156,7 +156,7 @@ export class ResourceController {
|
|
|
156
156
|
}
|
|
157
157
|
async planDestroy(core, parameters) {
|
|
158
158
|
this.addDefaultValues(parameters);
|
|
159
|
-
await this.
|
|
159
|
+
await this.applyTransformations(parameters);
|
|
160
160
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
161
161
|
const parametersToRefresh = this.settings.importAndDestroy?.refreshKeys
|
|
162
162
|
? {
|
|
@@ -211,7 +211,7 @@ export class ResourceController {
|
|
|
211
211
|
return results.filter(Boolean).flat();
|
|
212
212
|
}
|
|
213
213
|
this.addDefaultValues(parameters);
|
|
214
|
-
await this.
|
|
214
|
+
await this.applyTransformations(parameters);
|
|
215
215
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
216
216
|
const parametersToRefresh = this.getParametersToRefreshForImport(parameters, context);
|
|
217
217
|
// Parse data from the user supplied config
|
|
@@ -227,7 +227,7 @@ export class ResourceController {
|
|
|
227
227
|
const resultParametersArray = currentParametersArray
|
|
228
228
|
?.map((r, idx) => ({ ...r, ...statefulCurrentParameters[idx] }));
|
|
229
229
|
for (const result of resultParametersArray) {
|
|
230
|
-
await this.
|
|
230
|
+
await this.applyTransformations(result, { original: context.originalDesiredConfig });
|
|
231
231
|
this.removeDefaultValues(result, parameters);
|
|
232
232
|
}
|
|
233
233
|
return resultParametersArray?.map((r) => ({ core, parameters: r }));
|
|
@@ -292,7 +292,7 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
292
292
|
`);
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
-
async
|
|
295
|
+
async applyTransformations(config, reverse, skipConfigTransformation = false) {
|
|
296
296
|
if (!config) {
|
|
297
297
|
return;
|
|
298
298
|
}
|
|
@@ -304,7 +304,7 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
304
304
|
? await inputTransformation.from(config[key], reverse.original?.[key])
|
|
305
305
|
: await inputTransformation.to(config[key]);
|
|
306
306
|
}
|
|
307
|
-
if (this.settings.transformation) {
|
|
307
|
+
if (this.settings.transformation && !skipConfigTransformation) {
|
|
308
308
|
const transformed = reverse
|
|
309
309
|
? await this.settings.transformation.from({ ...config }, reverse.original)
|
|
310
310
|
: await this.settings.transformation.to({ ...config });
|
|
@@ -167,6 +167,12 @@ export interface DefaultParameterSetting {
|
|
|
167
167
|
* is mainly used to determine the equality method when performing diffing.
|
|
168
168
|
*/
|
|
169
169
|
type?: ParameterSettingType;
|
|
170
|
+
/**
|
|
171
|
+
* Mark the field as sensitive. Defaults to false. This has two side effects:
|
|
172
|
+
* 1. When displaying this field in the plan, it will be replaced with asterisks
|
|
173
|
+
* 2. When importing, resources with sensitive fields will be skipped unless the user explicitly allows it.
|
|
174
|
+
*/
|
|
175
|
+
isSensitive?: boolean;
|
|
170
176
|
/**
|
|
171
177
|
* Default value for the parameter. If a value is not provided in the config, then this value will be used.
|
|
172
178
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.179",
|
|
4
4
|
"description": "Library plugin library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"ajv": "^8.12.0",
|
|
18
18
|
"ajv-formats": "^2.1.1",
|
|
19
|
-
"codify-schemas": "1.0.
|
|
19
|
+
"codify-schemas": "1.0.83",
|
|
20
20
|
"@npmcli/promise-spawn": "^7.0.1",
|
|
21
21
|
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
|
|
22
22
|
"uuid": "^10.0.0",
|
package/src/messages/sender.ts
CHANGED
|
@@ -21,6 +21,19 @@ class CodifyCliSenderImpl {
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
async getCodifyCliCredentials(): Promise<string> {
|
|
25
|
+
const data = await this.sendAndWaitForResponse(<IpcMessageV2>{
|
|
26
|
+
cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST,
|
|
27
|
+
data: {},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (typeof data.data !== 'string') {
|
|
31
|
+
throw new Error('Expected string back from credentials request');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return data.data;
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
private async sendAndWaitForResponse(message: IpcMessageV2): Promise<IpcMessageV2> {
|
|
25
38
|
return new Promise((resolve) => {
|
|
26
39
|
const requestId = nanoid(8);
|
package/src/plan/change-set.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
2
2
|
|
|
3
3
|
import { ParsedParameterSetting } from '../resource/parsed-resource-settings.js';
|
|
4
|
+
import { ResourceSettings } from '../resource/resource-settings.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A parameter change describes a parameter level change to a resource.
|
|
@@ -25,6 +26,11 @@ export interface ParameterChange<T extends StringIndexedObject> {
|
|
|
25
26
|
* The new value of the resource (the desired value)
|
|
26
27
|
*/
|
|
27
28
|
newValue: any | null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether the parameter is sensitive
|
|
32
|
+
*/
|
|
33
|
+
isSensitive: boolean;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
// Change set will coerce undefined values to null because undefined is not valid JSON
|
|
@@ -60,37 +66,40 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
60
66
|
return new ChangeSet<T>(ResourceOperation.NOOP, []);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T> {
|
|
69
|
+
static create<T extends StringIndexedObject>(desired: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
|
|
64
70
|
const parameterChanges = Object.entries(desired)
|
|
65
71
|
.map(([k, v]) => ({
|
|
66
72
|
name: k,
|
|
67
73
|
operation: ParameterOperation.ADD,
|
|
68
74
|
previousValue: null,
|
|
69
75
|
newValue: v ?? null,
|
|
76
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
70
77
|
}))
|
|
71
78
|
|
|
72
79
|
return new ChangeSet(ResourceOperation.CREATE, parameterChanges);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
static noop<T extends StringIndexedObject>(parameters: Partial<T>): ChangeSet<T> {
|
|
82
|
+
static noop<T extends StringIndexedObject>(parameters: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
|
|
76
83
|
const parameterChanges = Object.entries(parameters)
|
|
77
84
|
.map(([k, v]) => ({
|
|
78
85
|
name: k,
|
|
79
86
|
operation: ParameterOperation.NOOP,
|
|
80
87
|
previousValue: v ?? null,
|
|
81
88
|
newValue: v ?? null,
|
|
89
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
82
90
|
}))
|
|
83
91
|
|
|
84
92
|
return new ChangeSet(ResourceOperation.NOOP, parameterChanges);
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T> {
|
|
95
|
+
static destroy<T extends StringIndexedObject>(current: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
|
|
88
96
|
const parameterChanges = Object.entries(current)
|
|
89
97
|
.map(([k, v]) => ({
|
|
90
98
|
name: k,
|
|
91
99
|
operation: ParameterOperation.REMOVE,
|
|
92
100
|
previousValue: v ?? null,
|
|
93
101
|
newValue: null,
|
|
102
|
+
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
|
|
94
103
|
}))
|
|
95
104
|
|
|
96
105
|
return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
|
|
@@ -160,6 +169,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
160
169
|
previousValue: current[k] ?? null,
|
|
161
170
|
newValue: desired[k] ?? null,
|
|
162
171
|
operation: ParameterOperation.NOOP,
|
|
172
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
163
173
|
})
|
|
164
174
|
|
|
165
175
|
continue;
|
|
@@ -171,6 +181,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
171
181
|
previousValue: current[k] ?? null,
|
|
172
182
|
newValue: null,
|
|
173
183
|
operation: ParameterOperation.REMOVE,
|
|
184
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
174
185
|
})
|
|
175
186
|
|
|
176
187
|
continue;
|
|
@@ -182,6 +193,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
182
193
|
previousValue: null,
|
|
183
194
|
newValue: desired[k] ?? null,
|
|
184
195
|
operation: ParameterOperation.ADD,
|
|
196
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
185
197
|
})
|
|
186
198
|
|
|
187
199
|
continue;
|
|
@@ -192,6 +204,7 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
192
204
|
previousValue: current[k] ?? null,
|
|
193
205
|
newValue: desired[k] ?? null,
|
|
194
206
|
operation: ParameterOperation.MODIFY,
|
|
207
|
+
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
|
|
195
208
|
})
|
|
196
209
|
}
|
|
197
210
|
|
package/src/plan/plan.ts
CHANGED
|
@@ -118,7 +118,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
118
118
|
if (!filteredCurrentParameters && desired) {
|
|
119
119
|
return new Plan(
|
|
120
120
|
uuidV4(),
|
|
121
|
-
ChangeSet.create(desired),
|
|
121
|
+
ChangeSet.create(desired, settings),
|
|
122
122
|
core,
|
|
123
123
|
isStateful,
|
|
124
124
|
)
|
|
@@ -130,7 +130,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
130
130
|
if (!settings.canDestroy) {
|
|
131
131
|
return new Plan(
|
|
132
132
|
uuidV4(),
|
|
133
|
-
ChangeSet.noop(filteredCurrentParameters),
|
|
133
|
+
ChangeSet.noop(filteredCurrentParameters, settings),
|
|
134
134
|
core,
|
|
135
135
|
isStateful,
|
|
136
136
|
)
|
|
@@ -138,7 +138,7 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
138
138
|
|
|
139
139
|
return new Plan(
|
|
140
140
|
uuidV4(),
|
|
141
|
-
ChangeSet.destroy(filteredCurrentParameters),
|
|
141
|
+
ChangeSet.destroy(filteredCurrentParameters, settings),
|
|
142
142
|
core,
|
|
143
143
|
isStateful,
|
|
144
144
|
)
|
|
@@ -171,7 +171,10 @@ export class Plan<T extends StringIndexedObject> {
|
|
|
171
171
|
uuidV4(),
|
|
172
172
|
new ChangeSet<T>(
|
|
173
173
|
data.operation,
|
|
174
|
-
data.parameters
|
|
174
|
+
data.parameters.map((p) => ({
|
|
175
|
+
...p,
|
|
176
|
+
isSensitive: p.isSensitive ?? false,
|
|
177
|
+
})),
|
|
175
178
|
),
|
|
176
179
|
{
|
|
177
180
|
type: data.resourceType,
|
package/src/plugin/plugin.ts
CHANGED
|
@@ -62,11 +62,14 @@ export class Plugin {
|
|
|
62
62
|
.map((r) => ({
|
|
63
63
|
dependencies: r.dependencies,
|
|
64
64
|
type: r.typeId,
|
|
65
|
+
sensitiveParameters: Object.entries(r.settings.parameterSettings ?? {})
|
|
66
|
+
.filter(([, v]) => v?.isSensitive)
|
|
67
|
+
.map(([k]) => k),
|
|
65
68
|
}))
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
getResourceInfo(data: GetResourceInfoRequestData): GetResourceInfoResponseData {
|
|
70
73
|
if (!this.resourceControllers.has(data.type)) {
|
|
71
74
|
throw new Error(`Cannot get info for resource ${data.type}, resource doesn't exist`);
|
|
72
75
|
}
|
|
@@ -84,6 +87,10 @@ export class Plugin {
|
|
|
84
87
|
const allowMultiple = resource.settings.allowMultiple !== undefined
|
|
85
88
|
&& resource.settings.allowMultiple !== false;
|
|
86
89
|
|
|
90
|
+
const sensitiveParameters = Object.entries(resource.settings.parameterSettings ?? {})
|
|
91
|
+
.filter(([, v]) => v?.isSensitive)
|
|
92
|
+
.map(([k]) => k);
|
|
93
|
+
|
|
87
94
|
return {
|
|
88
95
|
plugin: this.name,
|
|
89
96
|
type: data.type,
|
|
@@ -96,6 +103,7 @@ export class Plugin {
|
|
|
96
103
|
import: {
|
|
97
104
|
requiredParameters: requiredPropertyNames,
|
|
98
105
|
},
|
|
106
|
+
sensitiveParameters,
|
|
99
107
|
allowMultiple
|
|
100
108
|
}
|
|
101
109
|
}
|
|
@@ -142,19 +142,23 @@ describe('Resource tests for stateful plans', () => {
|
|
|
142
142
|
name: "propA",
|
|
143
143
|
newValue: "propA",
|
|
144
144
|
previousValue: "propA",
|
|
145
|
-
operation: ParameterOperation.NOOP
|
|
145
|
+
operation: ParameterOperation.NOOP,
|
|
146
|
+
isSensitive: false,
|
|
147
|
+
|
|
146
148
|
},
|
|
147
149
|
{
|
|
148
150
|
name: "propB",
|
|
149
151
|
newValue: 10,
|
|
150
152
|
previousValue: null,
|
|
151
|
-
operation: ParameterOperation.ADD
|
|
153
|
+
operation: ParameterOperation.ADD,
|
|
154
|
+
isSensitive: false,
|
|
152
155
|
},
|
|
153
156
|
{
|
|
154
157
|
name: "propC",
|
|
155
158
|
newValue: 'propC',
|
|
156
159
|
previousValue: 'propC',
|
|
157
|
-
operation: ParameterOperation.NOOP
|
|
160
|
+
operation: ParameterOperation.NOOP,
|
|
161
|
+
isSensitive: false,
|
|
158
162
|
},
|
|
159
163
|
])
|
|
160
164
|
},
|
|
@@ -214,25 +218,29 @@ describe('Resource tests for stateful plans', () => {
|
|
|
214
218
|
name: "propA",
|
|
215
219
|
newValue: "propA",
|
|
216
220
|
previousValue: "propA",
|
|
217
|
-
operation: ParameterOperation.NOOP
|
|
221
|
+
operation: ParameterOperation.NOOP,
|
|
222
|
+
isSensitive: false,
|
|
218
223
|
},
|
|
219
224
|
{
|
|
220
225
|
name: "propB",
|
|
221
226
|
newValue: 10,
|
|
222
227
|
previousValue: null,
|
|
223
|
-
operation: ParameterOperation.ADD
|
|
228
|
+
operation: ParameterOperation.ADD,
|
|
229
|
+
isSensitive: false,
|
|
224
230
|
},
|
|
225
231
|
{
|
|
226
232
|
name: "propC",
|
|
227
233
|
newValue: 'propC',
|
|
228
234
|
previousValue: 'propC',
|
|
229
|
-
operation: ParameterOperation.NOOP
|
|
235
|
+
operation: ParameterOperation.NOOP,
|
|
236
|
+
isSensitive: false,
|
|
230
237
|
},
|
|
231
238
|
{
|
|
232
239
|
name: "propD",
|
|
233
240
|
newValue: 'propD',
|
|
234
241
|
previousValue: null,
|
|
235
|
-
operation: ParameterOperation.ADD
|
|
242
|
+
operation: ParameterOperation.ADD,
|
|
243
|
+
isSensitive: false,
|
|
236
244
|
},
|
|
237
245
|
])
|
|
238
246
|
},
|
|
@@ -80,13 +80,15 @@ describe('Resource tests', () => {
|
|
|
80
80
|
name: 'propA',
|
|
81
81
|
previousValue: 'propABefore',
|
|
82
82
|
newValue: 'propA',
|
|
83
|
-
operation: 'modify'
|
|
83
|
+
operation: 'modify',
|
|
84
|
+
isSensitive: false,
|
|
84
85
|
})
|
|
85
86
|
expect(result.changeSet.parameterChanges[1]).to.deep.eq({
|
|
86
87
|
name: 'propB',
|
|
87
88
|
previousValue: 10,
|
|
88
89
|
newValue: 10,
|
|
89
|
-
operation: 'noop'
|
|
90
|
+
operation: 'noop',
|
|
91
|
+
isSensitive: false,
|
|
90
92
|
})
|
|
91
93
|
})
|
|
92
94
|
|
|
@@ -59,7 +59,7 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
59
59
|
parameters: Partial<T>,
|
|
60
60
|
): Promise<ValidateResponseData['resourceValidations'][0]> {
|
|
61
61
|
const originalParameters = structuredClone(parameters);
|
|
62
|
-
await this.
|
|
62
|
+
await this.applyTransformations(parameters, undefined, true);
|
|
63
63
|
this.addDefaultValues(parameters);
|
|
64
64
|
|
|
65
65
|
if (this.schemaValidator) {
|
|
@@ -143,10 +143,10 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
143
143
|
const paramsToMatch = structuredClone(resourceToMatch.parameters) as Partial<T>;
|
|
144
144
|
|
|
145
145
|
this.addDefaultValues(originalParams);
|
|
146
|
-
await this.
|
|
146
|
+
await this.applyTransformations(originalParams);
|
|
147
147
|
|
|
148
148
|
this.addDefaultValues(paramsToMatch);
|
|
149
|
-
await this.
|
|
149
|
+
await this.applyTransformations(paramsToMatch);
|
|
150
150
|
|
|
151
151
|
const match = parameterMatcher(originalParams, paramsToMatch);
|
|
152
152
|
if (match) {
|
|
@@ -170,10 +170,10 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
170
170
|
};
|
|
171
171
|
|
|
172
172
|
this.addDefaultValues(desired);
|
|
173
|
-
await this.
|
|
173
|
+
await this.applyTransformations(desired);
|
|
174
174
|
|
|
175
175
|
this.addDefaultValues(state);
|
|
176
|
-
await this.
|
|
176
|
+
await this.applyTransformations(state);
|
|
177
177
|
|
|
178
178
|
// Parse data from the user supplied config
|
|
179
179
|
const parsedConfig = new ConfigParser(desired, state, this.parsedSettings.statefulParameters)
|
|
@@ -221,7 +221,7 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
221
221
|
parameters: Partial<T>
|
|
222
222
|
): Promise<Plan<T>> {
|
|
223
223
|
this.addDefaultValues(parameters);
|
|
224
|
-
await this.
|
|
224
|
+
await this.applyTransformations(parameters);
|
|
225
225
|
|
|
226
226
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
227
227
|
const parametersToRefresh = this.settings.importAndDestroy?.refreshKeys
|
|
@@ -298,7 +298,7 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
this.addDefaultValues(parameters);
|
|
301
|
-
await this.
|
|
301
|
+
await this.applyTransformations(parameters);
|
|
302
302
|
|
|
303
303
|
// Use refresh parameters if specified, otherwise try to refresh as many parameters as possible here
|
|
304
304
|
const parametersToRefresh = this.getParametersToRefreshForImport(parameters, context);
|
|
@@ -325,7 +325,7 @@ export class ResourceController<T extends StringIndexedObject> {
|
|
|
325
325
|
?.map((r, idx) => ({ ...r, ...statefulCurrentParameters[idx] }))
|
|
326
326
|
|
|
327
327
|
for (const result of resultParametersArray) {
|
|
328
|
-
await this.
|
|
328
|
+
await this.applyTransformations(result, { original: context.originalDesiredConfig });
|
|
329
329
|
this.removeDefaultValues(result, parameters);
|
|
330
330
|
}
|
|
331
331
|
|
|
@@ -408,9 +408,9 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
private async
|
|
411
|
+
private async applyTransformations(config: Partial<T> | null, reverse?: {
|
|
412
412
|
original: Partial<T> | null
|
|
413
|
-
}): Promise<void> {
|
|
413
|
+
}, skipConfigTransformation = false): Promise<void> {
|
|
414
414
|
if (!config) {
|
|
415
415
|
return;
|
|
416
416
|
}
|
|
@@ -425,7 +425,7 @@ ${JSON.stringify(refresh, null, 2)}
|
|
|
425
425
|
: await inputTransformation.to(config[key]);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
if (this.settings.transformation) {
|
|
428
|
+
if (this.settings.transformation && !skipConfigTransformation) {
|
|
429
429
|
const transformed = reverse
|
|
430
430
|
? await this.settings.transformation.from({ ...config }, reverse.original)
|
|
431
431
|
: await this.settings.transformation.to({ ...config })
|
|
@@ -717,12 +717,14 @@ describe('Resource parameter tests', () => {
|
|
|
717
717
|
operation: ParameterOperation.NOOP,
|
|
718
718
|
previousValue: null,
|
|
719
719
|
newValue: 'setting',
|
|
720
|
+
isSensitive: false,
|
|
720
721
|
},
|
|
721
722
|
{
|
|
722
723
|
name: 'propB',
|
|
723
724
|
operation: ParameterOperation.NOOP,
|
|
724
725
|
previousValue: 64,
|
|
725
726
|
newValue: 64,
|
|
727
|
+
isSensitive: false,
|
|
726
728
|
}
|
|
727
729
|
])
|
|
728
730
|
)
|
|
@@ -163,7 +163,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
|
|
|
163
163
|
* @param input
|
|
164
164
|
* @param context
|
|
165
165
|
*/
|
|
166
|
-
refreshMapper?: (input: Partial<T>, context: RefreshContext<T>) => Partial<T
|
|
166
|
+
refreshMapper?: (input: Partial<T>, context: RefreshContext<T>) => Partial<T>;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -207,6 +207,13 @@ export interface DefaultParameterSetting {
|
|
|
207
207
|
*/
|
|
208
208
|
type?: ParameterSettingType;
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Mark the field as sensitive. Defaults to false. This has two side effects:
|
|
212
|
+
* 1. When displaying this field in the plan, it will be replaced with asterisks
|
|
213
|
+
* 2. When importing, resources with sensitive fields will be skipped unless the user explicitly allows it.
|
|
214
|
+
*/
|
|
215
|
+
isSensitive?: boolean;
|
|
216
|
+
|
|
210
217
|
/**
|
|
211
218
|
* Default value for the parameter. If a value is not provided in the config, then this value will be used.
|
|
212
219
|
*/
|