Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codify-plugin-lib",
"version": "1.0.177",
"version": "1.0.180",
"description": "Library plugin library",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand All @@ -16,7 +16,7 @@
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"codify-schemas": "1.0.77",
"codify-schemas": "1.0.83",
"@npmcli/promise-spawn": "^7.0.1",
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
"uuid": "^10.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/plan/change-set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('Change set tests', () => {
it('Correctly diffs two resource configs (destory)', () => {
const cs = ChangeSet.destroy({
propA: 'prop',
propB: 'propB'
propB: 'propB',
});

expect(cs.parameterChanges.length).to.eq(2);
Expand Down
19 changes: 16 additions & 3 deletions src/plan/change-set.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';

import { ParsedParameterSetting } from '../resource/parsed-resource-settings.js';
import { ResourceSettings } from '../resource/resource-settings.js';

/**
* A parameter change describes a parameter level change to a resource.
Expand All @@ -25,6 +26,11 @@ export interface ParameterChange<T extends StringIndexedObject> {
* The new value of the resource (the desired value)
*/
newValue: any | null;

/**
* Whether the parameter is sensitive
*/
isSensitive: boolean;
}

// Change set will coerce undefined values to null because undefined is not valid JSON
Expand Down Expand Up @@ -60,37 +66,40 @@ export class ChangeSet<T extends StringIndexedObject> {
return new ChangeSet<T>(ResourceOperation.NOOP, []);
}

static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T> {
static create<T extends StringIndexedObject>(desired: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
const parameterChanges = Object.entries(desired)
.map(([k, v]) => ({
name: k,
operation: ParameterOperation.ADD,
previousValue: null,
newValue: v ?? null,
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
}))

return new ChangeSet(ResourceOperation.CREATE, parameterChanges);
}

static noop<T extends StringIndexedObject>(parameters: Partial<T>): ChangeSet<T> {
static noop<T extends StringIndexedObject>(parameters: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
const parameterChanges = Object.entries(parameters)
.map(([k, v]) => ({
name: k,
operation: ParameterOperation.NOOP,
previousValue: v ?? null,
newValue: v ?? null,
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
}))

return new ChangeSet(ResourceOperation.NOOP, parameterChanges);
}

static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T> {
static destroy<T extends StringIndexedObject>(current: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
const parameterChanges = Object.entries(current)
.map(([k, v]) => ({
name: k,
operation: ParameterOperation.REMOVE,
previousValue: v ?? null,
newValue: null,
isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
}))

return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
Expand Down Expand Up @@ -160,6 +169,7 @@ export class ChangeSet<T extends StringIndexedObject> {
previousValue: current[k] ?? null,
newValue: desired[k] ?? null,
operation: ParameterOperation.NOOP,
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
})

continue;
Expand All @@ -171,6 +181,7 @@ export class ChangeSet<T extends StringIndexedObject> {
previousValue: current[k] ?? null,
newValue: null,
operation: ParameterOperation.REMOVE,
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
})

continue;
Expand All @@ -182,6 +193,7 @@ export class ChangeSet<T extends StringIndexedObject> {
previousValue: null,
newValue: desired[k] ?? null,
operation: ParameterOperation.ADD,
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
})

continue;
Expand All @@ -192,6 +204,7 @@ export class ChangeSet<T extends StringIndexedObject> {
previousValue: current[k] ?? null,
newValue: desired[k] ?? null,
operation: ParameterOperation.MODIFY,
isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
})
}

Expand Down
11 changes: 7 additions & 4 deletions src/plan/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class Plan<T extends StringIndexedObject> {
if (!filteredCurrentParameters && desired) {
return new Plan(
uuidV4(),
ChangeSet.create(desired),
ChangeSet.create(desired, settings),
core,
isStateful,
)
Expand All @@ -130,15 +130,15 @@ export class Plan<T extends StringIndexedObject> {
if (!settings.canDestroy) {
return new Plan(
uuidV4(),
ChangeSet.noop(filteredCurrentParameters),
ChangeSet.noop(filteredCurrentParameters, settings),
core,
isStateful,
)
}

return new Plan(
uuidV4(),
ChangeSet.destroy(filteredCurrentParameters),
ChangeSet.destroy(filteredCurrentParameters, settings),
core,
isStateful,
)
Expand Down Expand Up @@ -171,7 +171,10 @@ export class Plan<T extends StringIndexedObject> {
uuidV4(),
new ChangeSet<T>(
data.operation,
data.parameters
data.parameters.map((p) => ({
...p,
isSensitive: p.isSensitive ?? false,
})),
),
{
type: data.resourceType,
Expand Down
36 changes: 31 additions & 5 deletions src/plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,28 @@ export class Plugin {

return {
resourceDefinitions: [...this.resourceControllers.values()]
.map((r) => ({
dependencies: r.dependencies,
type: r.typeId,
}))
.map((r) => {
const sensitiveParameters = Object.entries(r.settings.parameterSettings ?? {})
.filter(([, v]) => v?.isSensitive)
.map(([k]) => k);

// Here we add '*' if the resource is sensitive but no sensitive parameters are found. This works because the import
// sensitivity check only checks for the existance of a sensitive parameter whereas the parameter blocking one blocks
// on a specific sensitive parameter.
if (r.settings.isSensitive && sensitiveParameters.length === 0) {
sensitiveParameters.push('*');
}

return {
dependencies: r.dependencies,
type: r.typeId,
sensitiveParameters,
}
})
}
}

async getResourceInfo(data: GetResourceInfoRequestData): Promise<GetResourceInfoResponseData> {
getResourceInfo(data: GetResourceInfoRequestData): GetResourceInfoResponseData {
if (!this.resourceControllers.has(data.type)) {
throw new Error(`Cannot get info for resource ${data.type}, resource doesn't exist`);
}
Expand All @@ -84,6 +98,17 @@ export class Plugin {
const allowMultiple = resource.settings.allowMultiple !== undefined
&& resource.settings.allowMultiple !== false;

// Here we add '*' if the resource is sensitive but no sensitive parameters are found. This works because the import
// sensitivity check only checks for the existance of a sensitive parameter whereas the parameter blocking one blocks
// on a specific sensitive parameter.
const sensitiveParameters = Object.entries(resource.settings.parameterSettings ?? {})
.filter(([, v]) => v?.isSensitive)
.map(([k]) => k);

if (resource.settings.isSensitive && sensitiveParameters.length === 0) {
sensitiveParameters.push('*');
}

return {
plugin: this.name,
type: data.type,
Expand All @@ -96,6 +121,7 @@ export class Plugin {
import: {
requiredParameters: requiredPropertyNames,
},
sensitiveParameters,
allowMultiple
}
}
Expand Down
22 changes: 15 additions & 7 deletions src/resource/resource-controller-stateful-mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,23 @@ describe('Resource tests for stateful plans', () => {
name: "propA",
newValue: "propA",
previousValue: "propA",
operation: ParameterOperation.NOOP
operation: ParameterOperation.NOOP,
isSensitive: false,

},
{
name: "propB",
newValue: 10,
previousValue: null,
operation: ParameterOperation.ADD
operation: ParameterOperation.ADD,
isSensitive: false,
},
{
name: "propC",
newValue: 'propC',
previousValue: 'propC',
operation: ParameterOperation.NOOP
operation: ParameterOperation.NOOP,
isSensitive: false,
},
])
},
Expand Down Expand Up @@ -214,25 +218,29 @@ describe('Resource tests for stateful plans', () => {
name: "propA",
newValue: "propA",
previousValue: "propA",
operation: ParameterOperation.NOOP
operation: ParameterOperation.NOOP,
isSensitive: false,
},
{
name: "propB",
newValue: 10,
previousValue: null,
operation: ParameterOperation.ADD
operation: ParameterOperation.ADD,
isSensitive: false,
},
{
name: "propC",
newValue: 'propC',
previousValue: 'propC',
operation: ParameterOperation.NOOP
operation: ParameterOperation.NOOP,
isSensitive: false,
},
{
name: "propD",
newValue: 'propD',
previousValue: null,
operation: ParameterOperation.ADD
operation: ParameterOperation.ADD,
isSensitive: false,
},
])
},
Expand Down
6 changes: 4 additions & 2 deletions src/resource/resource-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ describe('Resource tests', () => {
name: 'propA',
previousValue: 'propABefore',
newValue: 'propA',
operation: 'modify'
operation: 'modify',
isSensitive: false,
})
expect(result.changeSet.parameterChanges[1]).to.deep.eq({
name: 'propB',
previousValue: 10,
newValue: 10,
operation: 'noop'
operation: 'noop',
isSensitive: false,
})
})

Expand Down
2 changes: 2 additions & 0 deletions src/resource/resource-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,14 @@ describe('Resource parameter tests', () => {
operation: ParameterOperation.NOOP,
previousValue: null,
newValue: 'setting',
isSensitive: false,
},
{
name: 'propB',
operation: ParameterOperation.NOOP,
previousValue: 64,
newValue: 64,
isSensitive: false,
}
])
)
Expand Down
15 changes: 14 additions & 1 deletion src/resource/resource-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export interface ResourceSettings<T extends StringIndexedObject> {
*/
schema?: Partial<JSONSchemaType<T | any>>;

/**
* Mark the resource as sensitive. Defaults to false. This prevents the resource from automatically being imported by init and import.
* This differs from the parameter level sensitivity which also prevents the parameter value from being displayed in the plan.
*/
isSensitive?: boolean;

/**
* Allow multiple of the same resource to unique. Set truthy if
* multiples are allowed, for example for applications, there can be multiple copy of the same application installed
Expand Down Expand Up @@ -163,7 +169,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
* @param input
* @param context
*/
refreshMapper?: (input: Partial<T>, context: RefreshContext<T>) => Partial<T>
refreshMapper?: (input: Partial<T>, context: RefreshContext<T>) => Partial<T>;
}
}

Expand Down Expand Up @@ -207,6 +213,13 @@ export interface DefaultParameterSetting {
*/
type?: ParameterSettingType;

/**
* Mark the field as sensitive. Defaults to false. This has two side effects:
* 1. When displaying this field in the plan, it will be replaced with asterisks
* 2. When importing, resources with sensitive fields will be skipped unless the user explicitly allows it.
*/
isSensitive?: boolean;

/**
* Default value for the parameter. If a value is not provided in the config, then this value will be used.
*/
Expand Down