Skip to content

Commit 90df563

Browse files
Kevin/import and destroy improvements (#14)
* feat: Added itemType parameter, it's like type but for items of arrays (sets default transformation and isElementEqual fns) * feat: Changed parameter inputTransformations to have to: and from: so they can be mapped back * feat: Added required parameters as a default matcher for when multiple resources are returned. Updated vitest * fix: updated typing for schema * feat: Used the reverse transformation to alter the import output to match a user's config + added tests * feat: Added reverse transformation for object level transforms * feat: Revised how setting parameters are set in the resource config * feat: Updated to import and destroy and added allow multiple to getResourceInfo * feat: Updated requiredParameters to identifyingParameters * feat: Added feature to remove un-necessary default values from imports * feat: Added matcher request to match multiple requests together * feat: Bug fixes and improvements * feat: Allow resources with stateful parameters to allow multple as well * feat: Updated validation checks to ensure that only one of an element exists if allowMultiple: false * fix: Fixed bug where certain parameters are not passed to stateful parameters * fix: Changed error message type for multiple resources found * fix: Added transformations for matcher. * feat: Added variable resolution for paths. Changed match length to be greater than 1 * feat: Added refresh context * fix: Fixed defaultRefreshValue not being used if the parameter has a default value * feat: Improved transformations by providing the original back in from, that way the user has the option to return the original if they are the same. Added a custom refreshMapper to apply logic for generating the refresh keys * fix: Pass in the original desired parameter (before transformations) for original. Changed directory to return original element if it matches * fix: Bug fixes for directory transformations * fix: Fix array transformation using resolve equals fn instead it should be using elementEqualsFn * fix: Fix bash history being written
1 parent 9dc7ad6 commit 90df563

18 files changed

+1907
-912
lines changed

package-lock.json

Lines changed: 474 additions & 762 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codify-plugin-lib",
3-
"version": "1.0.131",
3+
"version": "1.0.166",
44
"description": "Library plugin library",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
@@ -16,7 +16,7 @@
1616
"dependencies": {
1717
"ajv": "^8.12.0",
1818
"ajv-formats": "^2.1.1",
19-
"codify-schemas": "1.0.63",
19+
"codify-schemas": "1.0.73",
2020
"@npmcli/promise-spawn": "^7.0.1",
2121
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
2222
"uuid": "^10.0.0",
@@ -34,7 +34,7 @@
3434
"@types/uuid": "^10.0.0",
3535
"@types/lodash.isequal": "^4.5.8",
3636
"chai-as-promised": "^7.1.1",
37-
"vitest": "^1.4.0",
37+
"vitest": "^3.0.5",
3838
"vitest-mock-extended": "^1.3.1",
3939
"sinon": "^17.0.1",
4040
"eslint": "^8.51.0",

src/messages/handlers.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
IpcMessageSchema,
1414
IpcMessageV2,
1515
IpcMessageV2Schema,
16+
MatchRequestDataSchema,
17+
MatchResponseDataSchema,
1618
MessageStatus,
1719
PlanRequestDataSchema,
1820
PlanResponseDataSchema,
@@ -40,6 +42,11 @@ const SupportedRequests: Record<string, { handler: (plugin: Plugin, data: any) =
4042
requestValidator: GetResourceInfoRequestDataSchema,
4143
responseValidator: GetResourceInfoResponseDataSchema
4244
},
45+
'match': {
46+
handler: async (plugin: Plugin, data: any) => plugin.match(data),
47+
requestValidator: MatchRequestDataSchema,
48+
responseValidator: MatchResponseDataSchema
49+
},
4350
'import': {
4451
handler: async (plugin: Plugin, data: any) => plugin.import(data),
4552
requestValidator: ImportRequestDataSchema,
@@ -106,7 +113,7 @@ export class MessageHandler {
106113

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

112119
process.send!({

src/plan/plan.test.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,120 @@ describe('Plan entity tests', () => {
230230
])
231231
})
232232
})
233+
234+
it('Can use the requiredParameters to match the correct resources together', async () => {
235+
const resource1 = new class extends TestResource {
236+
getSettings(): ResourceSettings<TestConfig> {
237+
return {
238+
id: 'type',
239+
parameterSettings: {
240+
propA: { type: 'string' },
241+
propB: { type: 'string', canModify: true },
242+
},
243+
allowMultiple: {
244+
identifyingParameters: ['propA']
245+
}
246+
}
247+
}
248+
249+
async refresh(): Promise<Partial<any> | null> {
250+
return [{
251+
propA: 'same',
252+
propB: 'old',
253+
}, {
254+
propA: 'different',
255+
propB: 'different',
256+
}]
257+
}
258+
}
259+
260+
const controller = new ResourceController(resource1);
261+
const plan = await controller.plan(
262+
{ type: 'type' },
263+
{ propA: 'same', propB: 'new' },
264+
null,
265+
false
266+
)
267+
268+
expect(plan.changeSet).toMatchObject({
269+
operation: ResourceOperation.MODIFY,
270+
parameterChanges: expect.arrayContaining([
271+
expect.objectContaining({
272+
name: 'propA',
273+
previousValue: 'same',
274+
newValue: 'same',
275+
operation: 'noop'
276+
}),
277+
expect.objectContaining({
278+
name: 'propB',
279+
previousValue: 'old',
280+
newValue: 'new',
281+
operation: 'modify'
282+
})
283+
])
284+
})
285+
})
286+
287+
it('Can use the schema to determine required parameters for multiple allowed', async () => {
288+
const resource1 = new class extends TestResource {
289+
getSettings(): ResourceSettings<TestConfig> {
290+
return {
291+
id: 'type',
292+
parameterSettings: {
293+
propA: { type: 'string' },
294+
propB: { type: 'string', canModify: true },
295+
},
296+
allowMultiple: true,
297+
schema: {
298+
'$schema': 'http://json-schema.org/draft-07/schema',
299+
'$id': 'https://www.codifycli.com/type.json',
300+
'type': 'object',
301+
'properties': {
302+
propA: { type: 'string' },
303+
propB: { type: 'string' }
304+
},
305+
required: ['propA']
306+
}
307+
}
308+
}
309+
310+
async refresh(): Promise<Partial<any> | null> {
311+
return [{
312+
propA: 'same',
313+
propB: 'old',
314+
}, {
315+
propA: 'different',
316+
propB: 'different',
317+
}]
318+
}
319+
}
320+
321+
const controller = new ResourceController(resource1);
322+
const plan = await controller.plan(
323+
{ type: 'type' },
324+
{ propA: 'same', propB: 'new' },
325+
null,
326+
false
327+
)
328+
329+
expect(plan.changeSet).toMatchObject({
330+
operation: ResourceOperation.MODIFY,
331+
parameterChanges: expect.arrayContaining([
332+
expect.objectContaining({
333+
name: 'propA',
334+
previousValue: 'same',
335+
newValue: 'same',
336+
operation: 'noop'
337+
}),
338+
expect.objectContaining({
339+
name: 'propB',
340+
previousValue: 'old',
341+
newValue: 'new',
342+
operation: 'modify'
343+
})
344+
])
345+
})
346+
})
233347
})
234348

235349
function createTestResource() {

src/plan/plan.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export class Plan<T extends StringIndexedObject> {
241241
desired: Partial<T> | null,
242242
currentArray: Partial<T>[] | null,
243243
state: Partial<T> | null,
244-
settings: ResourceSettings<T>,
244+
settings: ParsedResourceSettings<T>,
245245
isStateful: boolean,
246246
}): Partial<T> | null {
247247
const {
@@ -260,13 +260,23 @@ export class Plan<T extends StringIndexedObject> {
260260
return null;
261261
}
262262

263+
const { matcher: parameterMatcher, id } = settings;
264+
const matcher = (desired: Partial<T>, currentArray: Partial<T>[]): Partial<T> | undefined => {
265+
const matched = currentArray.filter((c) => parameterMatcher(desired, c))
266+
if (matched.length > 1) {
267+
console.log(`Resource: ${id} did not uniquely match resources when allow multiple is set to true`)
268+
}
269+
270+
return matched[0];
271+
}
272+
263273
if (isStateful) {
264274
return state
265-
? settings.allowMultiple.matcher(state, currentArray)
275+
? matcher(state, currentArray) ?? null
266276
: null
267277
}
268278

269-
return settings.allowMultiple.matcher(desired!, currentArray);
279+
return matcher(desired!, currentArray) ?? null;
270280
}
271281

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

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

390400
if (idx === -1) {
391401
continue;
392402
}
393403

394-
desiredCopy.splice(counter, 1)
395-
const [element] = currentCopy.splice(idx, 1)
404+
desired.splice(counter, 1)
405+
const [element] = current.splice(idx, 1)
396406
result.push(element)
397407
}
398408

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

416-
/**
417-
* Convert the plan to a JSON response object
418-
*/
426+
/** Convert the plan to a JSON response object */
419427
toResponse(): PlanResponseData {
420428
return {
421429
planId: this.id,

0 commit comments

Comments
 (0)