Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
28bd520
feat: Added itemType parameter, it's like type but for items of array…
kevinwang5658 Feb 10, 2025
9bf8e80
feat: Changed parameter inputTransformations to have to: and from: so…
kevinwang5658 Feb 10, 2025
93c041b
feat: Added required parameters as a default matcher for when multipl…
kevinwang5658 Feb 15, 2025
72e69fc
fix: updated typing for schema
kevinwang5658 Feb 15, 2025
97de6a0
feat: Used the reverse transformation to alter the import output to m…
kevinwang5658 Feb 15, 2025
90826bc
feat: Added reverse transformation for object level transforms
kevinwang5658 Feb 15, 2025
580520f
feat: Revised how setting parameters are set in the resource config
kevinwang5658 Feb 15, 2025
c933cd1
feat: Updated to import and destroy and added allow multiple to getRe…
kevinwang5658 Feb 16, 2025
c7549e3
feat: Updated requiredParameters to identifyingParameters
kevinwang5658 Feb 19, 2025
0af7980
feat: Added feature to remove un-necessary default values from imports
kevinwang5658 Feb 19, 2025
724818d
feat: Added matcher request to match multiple requests together
kevinwang5658 Feb 19, 2025
8a7fabe
feat: Bug fixes and improvements
kevinwang5658 Feb 21, 2025
59fdf3a
feat: Allow resources with stateful parameters to allow multple as well
kevinwang5658 Feb 21, 2025
a247e4f
feat: Updated validation checks to ensure that only one of an element…
kevinwang5658 Feb 21, 2025
ba3fd92
fix: Fixed bug where certain parameters are not passed to stateful pa…
kevinwang5658 Feb 21, 2025
63fff9e
fix: Changed error message type for multiple resources found
kevinwang5658 Feb 21, 2025
970c80f
fix: Added transformations for matcher.
kevinwang5658 Feb 21, 2025
4e20a5e
feat: Added variable resolution for paths. Changed match length to be…
kevinwang5658 Feb 22, 2025
62b60a9
feat: Added refresh context
kevinwang5658 Feb 22, 2025
2607ad1
fix: Fixed defaultRefreshValue not being used if the parameter has a …
kevinwang5658 Feb 22, 2025
ef29ea2
feat: Improved transformations by providing the original back in from…
kevinwang5658 Feb 23, 2025
eab6d42
fix: Pass in the original desired parameter (before transformations) …
kevinwang5658 Feb 23, 2025
dacf48d
fix: Bug fixes for directory transformations
kevinwang5658 Feb 23, 2025
03b3fa4
fix: Fix array transformation using resolve equals fn instead it shou…
kevinwang5658 Feb 23, 2025
56d6342
fix: Fix bash history being written
kevinwang5658 Mar 4, 2025
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
1,236 changes: 474 additions & 762 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codify-plugin-lib",
"version": "1.0.131",
"version": "1.0.166",
"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.63",
"codify-schemas": "1.0.73",
"@npmcli/promise-spawn": "^7.0.1",
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
"uuid": "^10.0.0",
Expand All @@ -34,7 +34,7 @@
"@types/uuid": "^10.0.0",
"@types/lodash.isequal": "^4.5.8",
"chai-as-promised": "^7.1.1",
"vitest": "^1.4.0",
"vitest": "^3.0.5",
"vitest-mock-extended": "^1.3.1",
"sinon": "^17.0.1",
"eslint": "^8.51.0",
Expand Down
9 changes: 8 additions & 1 deletion src/messages/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
IpcMessageSchema,
IpcMessageV2,
IpcMessageV2Schema,
MatchRequestDataSchema,
MatchResponseDataSchema,
MessageStatus,
PlanRequestDataSchema,
PlanResponseDataSchema,
Expand Down Expand Up @@ -40,6 +42,11 @@ const SupportedRequests: Record<string, { handler: (plugin: Plugin, data: any) =
requestValidator: GetResourceInfoRequestDataSchema,
responseValidator: GetResourceInfoResponseDataSchema
},
'match': {
handler: async (plugin: Plugin, data: any) => plugin.match(data),
requestValidator: MatchRequestDataSchema,
responseValidator: MatchResponseDataSchema
},
'import': {
handler: async (plugin: Plugin, data: any) => plugin.import(data),
requestValidator: ImportRequestDataSchema,
Expand Down Expand Up @@ -106,7 +113,7 @@ export class MessageHandler {

const responseValidator = this.responseValidators.get(message.cmd);
if (responseValidator && !responseValidator(result)) {
throw new Error(`Plugin: ${this.plugin}. Malformed response data: ${JSON.stringify(responseValidator.errors, null, 2)}`)
throw new Error(`Plugin: ${this.plugin.name}. Malformed response data: ${JSON.stringify(responseValidator.errors, null, 2)}. Received ${JSON.stringify(result, null, 2)}`);
}

process.send!({
Expand Down
114 changes: 114 additions & 0 deletions src/plan/plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,120 @@ describe('Plan entity tests', () => {
])
})
})

it('Can use the requiredParameters to match the correct resources together', async () => {
const resource1 = new class extends TestResource {
getSettings(): ResourceSettings<TestConfig> {
return {
id: 'type',
parameterSettings: {
propA: { type: 'string' },
propB: { type: 'string', canModify: true },
},
allowMultiple: {
identifyingParameters: ['propA']
}
}
}

async refresh(): Promise<Partial<any> | null> {
return [{
propA: 'same',
propB: 'old',
}, {
propA: 'different',
propB: 'different',
}]
}
}

const controller = new ResourceController(resource1);
const plan = await controller.plan(
{ type: 'type' },
{ propA: 'same', propB: 'new' },
null,
false
)

expect(plan.changeSet).toMatchObject({
operation: ResourceOperation.MODIFY,
parameterChanges: expect.arrayContaining([
expect.objectContaining({
name: 'propA',
previousValue: 'same',
newValue: 'same',
operation: 'noop'
}),
expect.objectContaining({
name: 'propB',
previousValue: 'old',
newValue: 'new',
operation: 'modify'
})
])
})
})

it('Can use the schema to determine required parameters for multiple allowed', async () => {
const resource1 = new class extends TestResource {
getSettings(): ResourceSettings<TestConfig> {
return {
id: 'type',
parameterSettings: {
propA: { type: 'string' },
propB: { type: 'string', canModify: true },
},
allowMultiple: true,
schema: {
'$schema': 'http://json-schema.org/draft-07/schema',
'$id': 'https://www.codifycli.com/type.json',
'type': 'object',
'properties': {
propA: { type: 'string' },
propB: { type: 'string' }
},
required: ['propA']
}
}
}

async refresh(): Promise<Partial<any> | null> {
return [{
propA: 'same',
propB: 'old',
}, {
propA: 'different',
propB: 'different',
}]
}
}

const controller = new ResourceController(resource1);
const plan = await controller.plan(
{ type: 'type' },
{ propA: 'same', propB: 'new' },
null,
false
)

expect(plan.changeSet).toMatchObject({
operation: ResourceOperation.MODIFY,
parameterChanges: expect.arrayContaining([
expect.objectContaining({
name: 'propA',
previousValue: 'same',
newValue: 'same',
operation: 'noop'
}),
expect.objectContaining({
name: 'propB',
previousValue: 'old',
newValue: 'new',
operation: 'modify'
})
])
})
})
})

function createTestResource() {
Expand Down
28 changes: 18 additions & 10 deletions src/plan/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export class Plan<T extends StringIndexedObject> {
desired: Partial<T> | null,
currentArray: Partial<T>[] | null,
state: Partial<T> | null,
settings: ResourceSettings<T>,
settings: ParsedResourceSettings<T>,
isStateful: boolean,
}): Partial<T> | null {
const {
Expand All @@ -260,13 +260,23 @@ export class Plan<T extends StringIndexedObject> {
return null;
}

const { matcher: parameterMatcher, id } = settings;
const matcher = (desired: Partial<T>, currentArray: Partial<T>[]): Partial<T> | undefined => {
const matched = currentArray.filter((c) => parameterMatcher(desired, c))
if (matched.length > 1) {
console.log(`Resource: ${id} did not uniquely match resources when allow multiple is set to true`)
}

return matched[0];
}

if (isStateful) {
return state
? settings.allowMultiple.matcher(state, currentArray)
? matcher(state, currentArray) ?? null
: null
}

return settings.allowMultiple.matcher(desired!, currentArray);
return matcher(desired!, currentArray) ?? null;
}

/**
Expand Down Expand Up @@ -384,15 +394,15 @@ export class Plan<T extends StringIndexedObject> {
const defaultFilterMethod = ((desired: any[], current: any[]) => {
const result = [];

for (let counter = desiredCopy.length - 1; counter >= 0; counter--) {
const idx = currentCopy.findIndex((e2) => matcher(desiredCopy[counter], e2))
for (let counter = desired.length - 1; counter >= 0; counter--) {
const idx = currentCopy.findIndex((e2) => matcher(desired[counter], e2))

if (idx === -1) {
continue;
}

desiredCopy.splice(counter, 1)
const [element] = currentCopy.splice(idx, 1)
desired.splice(counter, 1)
const [element] = current.splice(idx, 1)
result.push(element)
}

Expand All @@ -413,9 +423,7 @@ export class Plan<T extends StringIndexedObject> {
return this.changeSet.operation !== ResourceOperation.NOOP;
}

/**
* Convert the plan to a JSON response object
*/
/** Convert the plan to a JSON response object */
toResponse(): PlanResponseData {
return {
planId: this.id,
Expand Down
Loading