Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DXCDT-374: Update field by address for keyword preservation #744

Merged
60 changes: 59 additions & 1 deletion src/keywordPreservation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get as getByDotNotation } from 'dot-prop';
import { get as getByDotNotation, set as setByDotNotation } from 'dot-prop';
import { KeywordMappings } from './types';
import { keywordReplaceArrayRegExp, keywordReplaceStringRegExp } from './tools/utils';

Expand Down Expand Up @@ -102,3 +102,61 @@ export const getAssetsValueByAddress = (address: string, assets: any): any => {
getByDotNotation(assets, directions[0])
);
};

// convertAddressToDotNotation will convert the proprietary address into conventional
// JS object notation. Performing this conversion simplifies the process
// of updating a specific property for a given asset tree using the dot-prop library
export const convertAddressToDotNotation = (
assets: any,
address: string,
finalAddressTrail = ''
): string => {
const directions = address.split('.');

if (directions[0] === '') return finalAddressTrail;

if (directions[0].charAt(0) === '[') {
const identifier = directions[0].substring(1, directions[0].length - 1).split('=')[0];
const identifierValue = directions[0].substring(1, directions[0].length - 1).split('=')[1];

let targetIndex = -1;

assets.forEach((item: any, index: number) => {
if (item[identifier] === identifierValue) {
targetIndex = index;
}
});

if (targetIndex === -1)
throw new Error(`Cannot find ${directions[0]} in ${JSON.stringify(assets)}`);

return convertAddressToDotNotation(
assets[targetIndex],
directions.slice(1).join('.'),
`${finalAddressTrail}.${targetIndex}`
);
}

return convertAddressToDotNotation(
getByDotNotation(assets, directions[0]),
directions.slice(1).join('.'),
finalAddressTrail === '' ? directions[0] : `${finalAddressTrail}.${directions[0]}`
);
};

export const updateAssetsByAddress = (
assets: object,
address: string,
newValue: string
): object => {
const dotNotationAddress = convertAddressToDotNotation(assets, address);

const doesPropertyExist = getByDotNotation(assets, dotNotationAddress) !== undefined;

if (!doesPropertyExist) {
throw new Error(`cannot update assets by address: ${address} because it does not exist.`);
}

setByDotNotation(assets, dotNotationAddress, newValue);
return assets;
};
129 changes: 129 additions & 0 deletions test/keywordPreservation.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { expect } from 'chai';
import { get as getDotNotation } from 'dot-prop';
import {
shouldFieldBePreserved,
getPreservableFieldsFromAssets,
getAssetsValueByAddress,
convertAddressToDotNotation,
updateAssetsByAddress,
} from '../src/keywordPreservation';

describe('#Keyword Preservation', () => {
Expand Down Expand Up @@ -148,3 +151,129 @@ describe('getAssetsValueByAddress', () => {
);
});
});

describe('convertAddressToDotNotation', () => {
const mockAssets = {
tenant: {
friendly_name: 'Friendly Tenant Name',
},
actions: [
{
name: 'action-1',
code: "window.alert('Foo')",
},
{
name: 'action-2',
nestedProperty: {
array: [
{
name: 'foo',
},
{
name: 'bar',
arrayProperty: 'baz',
},
],
},
},
],
};

it('should convert proprietary address to conventional JS object notation (aka "dot notation")', () => {
expect(convertAddressToDotNotation(mockAssets, 'tenant.friendly_name')).to.equal(
'tenant.friendly_name'
);
expect(getDotNotation(mockAssets, 'tenant.friendly_name')).to.equal(
mockAssets.tenant.friendly_name
);

expect(convertAddressToDotNotation(mockAssets, 'actions.[name=action-1].code')).to.equal(
'actions.0.code'
);
expect(getDotNotation(mockAssets, 'actions.0.code')).to.equal(mockAssets.actions[0].code);

expect(
convertAddressToDotNotation(
mockAssets,
'actions.[name=action-2].nestedProperty.array.[name=bar].arrayProperty'
)
).to.equal('actions.1.nestedProperty.array.1.arrayProperty');

expect(getDotNotation(mockAssets, 'actions.1.nestedProperty.array.1.arrayProperty')).to.equal(
mockAssets.actions[1].nestedProperty?.array[1].arrayProperty
);
});

it('should throw if provided address is invalid', () => {
expect(() =>
convertAddressToDotNotation(mockAssets, 'actions.[name=this-action-does-not-exist].code')
).to.throw(
`Cannot find [name=this-action-does-not-exist] in [{"name":"action-1","code":"window.alert('Foo')"},{"name":"action-2","nestedProperty":{"array":[{"name":"foo"},{"name":"bar","arrayProperty":"baz"}]}}]`
);
});
});

describe('updateAssetsByAddress', () => {
const mockAssetTree = {
tenant: {
display_name: 'This is my tenant display name',
},
clients: [
{
name: 'client-1',
display_name: 'Some Display Name',
},
{
name: 'client-2',
display_name: 'This is the target value',
},
{
name: 'client-3',
connections: [
{
connection_name: 'connection-1',
display_name: 'My connection display name',
},
],
},
],
};
it('should update an specific asset field for a provided address', () => {
expect(
updateAssetsByAddress(
mockAssetTree,
'clients.[name=client-3].connections.[connection_name=connection-1].display_name',
'New connection display name'
)
).to.deep.equal(
(() => {
const newAssets = mockAssetTree;
//@ts-ignore because we know this value is defined
newAssets.clients[2].connections[0].display_name = 'New connection display name';
return newAssets;
})()
);

expect(
updateAssetsByAddress(mockAssetTree, 'tenant.display_name', 'This is the new display name')
).to.deep.equal(
(() => {
const newAssets = mockAssetTree;
newAssets.tenant.display_name = 'This is the new display name';
return newAssets;
})()
);
});

it('should throw errors if invalid addresses provided', () => {
expect(() =>
updateAssetsByAddress(mockAssetTree, 'clients.[name=this-client-does-not-exist]', '_')
).to.throw();

expect(() =>
updateAssetsByAddress(mockAssetTree, 'tenant.this_property_does_not_exist', '_')
).to.throw(
'cannot update assets by address: tenant.this_property_does_not_exist because it does not exist.'
);
});
});