Skip to content

Commit 90826bc

Browse files
committed
feat: Added reverse transformation for object level transforms
1 parent 97de6a0 commit 90826bc

File tree

6 files changed

+133
-26
lines changed

6 files changed

+133
-26
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codify-plugin-lib",
3-
"version": "1.0.135",
3+
"version": "1.0.136",
44
"description": "Library plugin library",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",

src/resource/parsed-resource-settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
4444

4545
removeStatefulParametersBeforeDestroy?: boolean | undefined;
4646
dependencies?: string[] | undefined;
47-
inputTransformation?: ((desired: Partial<T>) => unknown) | undefined;
47+
transformation?: InputTransformation;
4848
private settings: ResourceSettings<T>;
4949

5050
constructor(settings: ResourceSettings<T>) {
@@ -54,7 +54,7 @@ export class ParsedResourceSettings<T extends StringIndexedObject> implements Re
5454
this.allowMultiple = settings.allowMultiple;
5555
this.removeStatefulParametersBeforeDestroy = settings.removeStatefulParametersBeforeDestroy;
5656
this.dependencies = settings.dependencies;
57-
this.inputTransformation = settings.inputTransformation;
57+
this.transformation = settings.transformation;
5858

5959
this.validateSettings();
6060
}

src/resource/resource-controller.test.ts

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { CreatePlan, DestroyPlan, ModifyPlan } from '../plan/plan-types.js';
77
import { ParameterChange } from '../plan/change-set.js';
88
import { ResourceController } from './resource-controller.js';
99
import { TestConfig, testPlan, TestResource, TestStatefulParameter } from '../utils/test-utils.test.js';
10-
import { untildify } from '../utils/utils.js';
10+
import { tildify, untildify } from '../utils/utils.js';
1111
import { ArrayStatefulParameter, StatefulParameter } from '../stateful-parameter/stateful-parameter.js';
1212
import { Plan } from '../plan/plan.js';
1313

@@ -20,9 +20,11 @@ describe('Resource tests', () => {
2020
id: 'type',
2121
dependencies: ['homebrew', 'python'],
2222
parameterSettings: {
23-
propA: { canModify: true, inputTransformation: (input) => untildify(input) },
23+
propA: {
24+
canModify: true,
25+
transformation: { to: (input) => untildify(input), from: (input) => tildify(input) }
26+
},
2427
},
25-
inputTransformation: (config) => ({ propA: config.propA, propC: config.propB }),
2628
}
2729
}
2830

@@ -544,7 +546,7 @@ describe('Resource tests', () => {
544546
parameterSettings: {
545547
propD: {
546548
type: 'array',
547-
inputTransformation: {
549+
transformation: {
548550
to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
549551
Object.entries(h)
550552
.map(([k, v]) => [
@@ -619,4 +621,94 @@ describe('Resource tests', () => {
619621
}
620622
})
621623
})
624+
625+
it('Applies reverse input transformations for imports (object level)', async () => {
626+
const resource = new class extends TestResource {
627+
getSettings(): ResourceSettings<TestConfig> {
628+
return {
629+
id: 'resourceType',
630+
parameterSettings: {
631+
propD: {
632+
type: 'array',
633+
}
634+
},
635+
transformation: {
636+
to: (input: any) => ({
637+
...input,
638+
propD: input.propD?.map((h) => Object.fromEntries(
639+
Object.entries(h)
640+
.map(([k, v]) => [
641+
k,
642+
typeof v === 'boolean'
643+
? (v ? 'yes' : 'no') // The file takes 'yes' or 'no' instead of booleans
644+
: v,
645+
])
646+
)
647+
)
648+
}),
649+
from: (output: any) => ({
650+
...output,
651+
propD: output.propD?.map((h) => Object.fromEntries(
652+
Object.entries(h)
653+
.map(([k, v]) => [
654+
k,
655+
v === 'yes' || v === 'no'
656+
? (v === 'yes')
657+
: v,
658+
])
659+
))
660+
})
661+
}
662+
}
663+
}
664+
665+
async refresh(parameters: Partial<TestConfig>): Promise<Partial<TestConfig> | null> {
666+
return {
667+
propD: [
668+
{
669+
Host: 'new.com',
670+
AddKeysToAgent: true,
671+
IdentityFile: 'id_ed25519'
672+
},
673+
{
674+
Host: 'github.com',
675+
AddKeysToAgent: true,
676+
UseKeychain: true,
677+
},
678+
{
679+
Match: 'User bob,joe,phil',
680+
PasswordAuthentication: true,
681+
}
682+
],
683+
}
684+
}
685+
}
686+
687+
const controller = new ResourceController(resource);
688+
const plan = await controller.import({ type: 'resourceType' }, {});
689+
690+
expect(plan![0]).toMatchObject({
691+
'core': {
692+
'type': 'resourceType'
693+
},
694+
'parameters': {
695+
'propD': [
696+
{
697+
'Host': 'new.com',
698+
'AddKeysToAgent': true,
699+
'IdentityFile': 'id_ed25519'
700+
},
701+
{
702+
'Host': 'github.com',
703+
'AddKeysToAgent': true,
704+
'UseKeychain': true
705+
},
706+
{
707+
'Match': 'User bob,joe,phil',
708+
'PasswordAuthentication': true
709+
}
710+
]
711+
}
712+
})
713+
})
622714
});

src/resource/resource-controller.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,11 @@ ${JSON.stringify(refresh, null, 2)}
351351
: await inputTransformation.to(config[key]);
352352
}
353353

354-
if (this.settings.inputTransformation) {
355-
const transformed = await this.settings.inputTransformation({ ...config })
354+
if (this.settings.transformation) {
355+
const transformed = reverse
356+
? await this.settings.transformation.from({ ...config })
357+
: await this.settings.transformation.to({ ...config })
358+
356359
Object.keys(config).forEach((k) => delete config[k])
357360
Object.assign(config, transformed);
358361
}

src/resource/resource-settings.test.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -569,10 +569,16 @@ describe('Resource parameter tests', () => {
569569
getSettings(): ResourceSettings<TestConfig> {
570570
return {
571571
id: 'resourceType',
572-
inputTransformation: (desired) => ({
573-
propA: 'propA',
574-
propB: 10,
575-
})
572+
transformation: {
573+
to: (desired) => ({
574+
propA: 'propA',
575+
propB: 10,
576+
}),
577+
from: (current) => ({
578+
propA: 'propA',
579+
propB: 10,
580+
})
581+
}
576582
}
577583
}
578584

@@ -604,10 +610,16 @@ describe('Resource parameter tests', () => {
604610
getSettings(): ResourceSettings<TestConfig> {
605611
return {
606612
id: 'resourceType',
607-
inputTransformation: (desired) => ({
608-
propA: 'propA',
609-
propB: 10,
610-
})
613+
transformation: {
614+
to: (desired) => ({
615+
propA: 'propA',
616+
propB: 10,
617+
}),
618+
from: (desired) => ({
619+
propA: 'propA',
620+
propB: 10,
621+
})
622+
}
611623
}
612624
}
613625

@@ -852,7 +864,7 @@ describe('Resource parameter tests', () => {
852864
parameterSettings: {
853865
propD: {
854866
type: 'array',
855-
inputTransformation: {
867+
transformation: {
856868
to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
857869
Object.entries(h)
858870
.map(([k, v]) => [
@@ -918,7 +930,7 @@ describe('Resource parameter tests', () => {
918930
getSettings(): any {
919931
return {
920932
type: 'array',
921-
inputTransformation: {
933+
transformation: {
922934
to: (hosts: Record<string, unknown>[]) => hosts.map((h) => Object.fromEntries(
923935
Object.entries(h)
924936
.map(([k, v]) => [

src/resource/resource-settings.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
8181
*
8282
* @param desired
8383
*/
84-
inputTransformation?: (desired: Partial<T>) => Promise<unknown> | unknown;
84+
transformation?: InputTransformation;
8585

8686
/**
8787
* Customize the import and destory behavior of the resource. By default, <code>codify import</code> and <code>codify destroy</code> will call
@@ -189,7 +189,7 @@ export interface DefaultParameterSetting {
189189
*
190190
* @param input The original parameter value from the desired config.
191191
*/
192-
inputTransformation?: InputTransformation;
192+
transformation?: InputTransformation;
193193

194194
/**
195195
* Customize the equality comparison for a parameter. This is used in the diffing algorithm for generating the plan.
@@ -354,10 +354,10 @@ export function resolveParameterTransformFn(
354354
parameter: ParameterSetting
355355
): InputTransformation | undefined {
356356

357-
if (parameter.type === 'stateful' && !parameter.inputTransformation) {
357+
if (parameter.type === 'stateful' && !parameter.transformation) {
358358
const sp = (parameter as StatefulParameterSetting).definition.getSettings();
359-
if (sp.inputTransformation) {
360-
return (parameter as StatefulParameterSetting).definition?.getSettings()?.inputTransformation
359+
if (sp.transformation) {
360+
return (parameter as StatefulParameterSetting).definition?.getSettings()?.transformation
361361
}
362362

363363
return sp.type ? ParameterTransformationDefaults[sp.type] : undefined;
@@ -366,7 +366,7 @@ export function resolveParameterTransformFn(
366366
if (parameter.type === 'array'
367367
&& (parameter as ArrayParameterSetting).itemType
368368
&& ParameterTransformationDefaults[(parameter as ArrayParameterSetting).itemType!]
369-
&& !parameter.inputTransformation
369+
&& !parameter.transformation
370370
) {
371371
const itemType = (parameter as ArrayParameterSetting).itemType!;
372372
const itemTransformation = ParameterTransformationDefaults[itemType]!;
@@ -381,5 +381,5 @@ export function resolveParameterTransformFn(
381381
}
382382
}
383383

384-
return parameter.inputTransformation ?? ParameterTransformationDefaults[parameter.type as ParameterSettingType] ?? undefined;
384+
return parameter.transformation ?? ParameterTransformationDefaults[parameter.type as ParameterSettingType] ?? undefined;
385385
}

0 commit comments

Comments
 (0)