Skip to content

Commit e016312

Browse files
authored
Allow types to be replaced when using an Override
1 parent 10fb45d commit e016312

File tree

2 files changed

+110
-28
lines changed

2 files changed

+110
-28
lines changed

lib/preprocess/ConfigPreprocessorOverride.ts

+37-28
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Resource } from 'rdf-object';
22
import type { RdfObjectLoader } from 'rdf-object/lib/RdfObjectLoader';
33
import type { Logger } from 'winston';
4-
import { IRIS_OO } from '../rdf/Iris';
4+
import { IRIS_OO, IRIS_RDF } from '../rdf/Iris';
55
import { uniqueTypes } from '../rdf/ResourceUtil';
66
import { ErrorResourcesContext } from '../util/ErrorResourcesContext';
77
import type { IConfigPreprocessor, IConfigPreprocessorTransform } from './IConfigPreprocessor';
@@ -43,17 +43,24 @@ export class ConfigPreprocessorOverride implements IConfigPreprocessor<Record<st
4343
* @param handleResponse - Override values that were found for this resource.
4444
*/
4545
public transform(config: Resource, handleResponse: Record<string, Resource>): IConfigPreprocessorTransform {
46-
for (const id of Object.keys(config.properties)) {
47-
const overrideValue = handleResponse[id];
48-
if (overrideValue) {
49-
config.properties[id] = [ overrideValue ];
46+
// We know this has exactly 1 result due to the canHandle call
47+
const configType = uniqueTypes(config, this.componentResources)[0];
48+
const overrideType = handleResponse[IRIS_RDF.type]?.value;
49+
// In case the type changes we have to delete all the original properties as those correspond to the old type
50+
if (overrideType && configType.value !== overrideType) {
51+
for (const id of Object.keys(config.properties)) {
52+
delete config.properties[id];
5053
}
5154
}
55+
for (const property of Object.keys(handleResponse)) {
56+
config.properties[property] = [ handleResponse[property] ];
57+
}
58+
5259
return { rawConfig: config, finishTransformation: false };
5360
}
5461

5562
/**
56-
* Clear all cached overrides so they will be calculated again on the next call.
63+
* Clear all cached overrides, so they will be calculated again on the next call.
5764
*/
5865
public reset(): void {
5966
this.overrides = undefined;
@@ -168,24 +175,31 @@ export class ConfigPreprocessorOverride implements IConfigPreprocessor<Record<st
168175
* @param chain - The chain of Overrides, with a normal resource as the last entry in the array.
169176
*/
170177
protected chainToOverrideObject(chain: Resource[]): { target: string; values: Record<string, Resource> } {
171-
const { target, type } = this.getChainTarget(chain);
178+
const target = this.getChainTarget(chain);
172179

173180
// Apply all overrides sequentially, starting from the one closest to the target.
174181
// This ensures the most recent override has priority.
175-
const parameters = this.componentResources[type.value].properties.parameters;
176-
const mergedOverride: Record<string, Resource> = {};
182+
let mergedOverride: Record<string, Resource> = {};
177183
for (let i = chain.length - 2; i >= 0; --i) {
178-
const filteredObject = this.filterOverrideObject(chain[i], target, parameters);
179-
Object.assign(mergedOverride, filteredObject);
184+
const validatedObject = this.extractOverrideParameters(chain[i], target);
185+
// In case an Override has a different type, the properties of the target don't matter any more,
186+
// as the object is being replaced completely.
187+
const mergedType = mergedOverride[IRIS_RDF.type]?.value;
188+
const overrideType = validatedObject[IRIS_RDF.type]?.value;
189+
if (overrideType && overrideType !== mergedType) {
190+
mergedOverride = validatedObject;
191+
} else {
192+
Object.assign(mergedOverride, validatedObject);
193+
}
180194
}
181195
return { target: target.value, values: mergedOverride };
182196
}
183197

184198
/**
185-
* Finds the final target and its type in an override chain.
199+
* Finds the final target and validates its type value.
186200
* @param chain - The chain to find the target of.
187201
*/
188-
protected getChainTarget(chain: Resource[]): { target: Resource; type: Resource } {
202+
protected getChainTarget(chain: Resource[]): Resource {
189203
const target = chain[chain.length - 1];
190204
const types = uniqueTypes(target, this.componentResources);
191205
if (!types || types.length === 0) {
@@ -200,17 +214,15 @@ export class ConfigPreprocessorOverride implements IConfigPreprocessor<Record<st
200214
override: chain[chain.length - 2],
201215
});
202216
}
203-
return { target, type: types[0] };
217+
return target;
204218
}
205219

206220
/**
207-
* Extracts all relevant parameters of an Override with their corresponding new value.
221+
* Extracts all parameters of an Override with their corresponding value.
208222
* @param override - The Override to apply.
209-
* @param target - The target resource to apply the Override to.
210-
* @param parameters - The parameters that are relevant for the target.
223+
* @param target - The target resource to apply the Override to. Only used for error messages.
211224
*/
212-
protected filterOverrideObject(override: Resource, target: Resource, parameters: Resource[]):
213-
Record<string, Resource> {
225+
protected extractOverrideParameters(override: Resource, target: Resource): Record<string, Resource> {
214226
const overrideObjects = override.properties[IRIS_OO.overrideParameters];
215227
if (!overrideObjects || overrideObjects.length === 0) {
216228
this.logger.warn(`No overrideParameters found for ${override.value}.`);
@@ -224,22 +236,19 @@ export class ConfigPreprocessorOverride implements IConfigPreprocessor<Record<st
224236
const overrideObject = overrideObjects[0];
225237

226238
// Only keep the parameters that are known to the type of the target object
227-
const filteredObject: Record<string, Resource> = {};
228-
for (const parameter of parameters) {
229-
const overrideValues = overrideObject.properties[parameter.value];
230-
if (!overrideValues || overrideValues.length === 0) {
231-
continue;
232-
}
239+
const validatedObject: Record<string, Resource> = {};
240+
for (const parameter of Object.keys(overrideObject.properties)) {
241+
const overrideValues = overrideObject.properties[parameter];
233242
if (overrideValues.length > 1) {
234-
throw new ErrorResourcesContext(`Detected multiple values for override parameter ${parameter.value} in Override ${override.value}. RDF lists should be used for defining multiple values.`, {
243+
throw new ErrorResourcesContext(`Detected multiple values for override parameter ${parameter} in Override ${override.value}. RDF lists should be used for defining multiple values.`, {
235244
arguments: overrideValues,
236245
target,
237246
override,
238247
});
239248
}
240-
filteredObject[parameter.value] = overrideValues[0];
249+
validatedObject[parameter] = overrideValues[0];
241250
}
242-
return filteredObject;
251+
return validatedObject;
243252
}
244253
}
245254

test/unit/preprocess/ConfigPreprocessorOverride-test.ts

+73
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,77 @@ describe('ConfigPreprocessorOverride', () => {
362362
});
363363
expect(() => preprocessor.canHandle(config)).toThrow(`Detected multiple values for override parameter ex:param1 in Override ex:myOverride. RDF lists should be used for defining multiple values.`);
364364
});
365+
366+
it('can replace the type of an object.', async(): Promise<void> => {
367+
const config = objectLoader.createCompactedResource({
368+
'@id': 'ex:myComponentInstance',
369+
types: 'ex:Component',
370+
'ex:param1': '"value1"',
371+
'ex:param2': '"value2"',
372+
});
373+
const overrideInstance = objectLoader.createCompactedResource({
374+
'@id': 'ex:myOverride',
375+
types: 'oo:Override',
376+
overrideInstance: 'ex:myComponentInstance',
377+
overrideParameters: {
378+
'@type': 'ex:ExtraType',
379+
'ex:param3': '"hello"',
380+
},
381+
});
382+
const overrideProperties = overrideInstance.properties[IRIS_OO.overrideParameters][0].properties;
383+
const override = preprocessor.canHandle(config)!;
384+
const { rawConfig, finishTransformation } = preprocessor.transform(config, override);
385+
expect(finishTransformation).toBe(false);
386+
expect(rawConfig).toBe(config);
387+
expect(rawConfig.properties['ex:param3'][0]).toBe(overrideProperties['ex:param3'][0]);
388+
expect(rawConfig.properties['ex:param1']).toHaveLength(0);
389+
expect(rawConfig.properties['ex:param2']).toHaveLength(0);
390+
});
391+
392+
it('can chain type changes', () => {
393+
const config = objectLoader.createCompactedResource({
394+
'@id': 'ex:myComponentInstance',
395+
types: 'ex:Component',
396+
'ex:param1': '"value1"',
397+
'ex:param2': '"value2"',
398+
});
399+
const overrideInstance1 = objectLoader.createCompactedResource({
400+
'@id': 'ex:myOverride1',
401+
types: 'oo:Override',
402+
overrideInstance: 'ex:myComponentInstance',
403+
overrideParameters: {
404+
'ex:param1': '"value1-1"',
405+
},
406+
});
407+
const overrideInstance2 = objectLoader.createCompactedResource({
408+
'@id': 'ex:myOverride2',
409+
types: 'oo:Override',
410+
overrideInstance: 'ex:myOverride1',
411+
overrideParameters: {
412+
'@type': 'ex:ExtraType',
413+
'ex:param3': '"value3"',
414+
'ex:param4': '"value4"',
415+
},
416+
});
417+
const overrideInstance3 = objectLoader.createCompactedResource({
418+
'@id': 'ex:myOverride3',
419+
types: 'oo:Override',
420+
overrideInstance: 'ex:myOverride2',
421+
overrideParameters: {
422+
'ex:param4': '"value4-2"',
423+
},
424+
});
425+
const override2Properties = overrideInstance2.properties[IRIS_OO.overrideParameters][0].properties;
426+
const override3Properties = overrideInstance3.properties[IRIS_OO.overrideParameters][0].properties;
427+
const override = preprocessor.canHandle(config)!;
428+
const { rawConfig, finishTransformation } = preprocessor.transform(config, override);
429+
expect(finishTransformation).toBe(false);
430+
expect(rawConfig).toBe(config);
431+
expect(rawConfig.properties['ex:param1']).toHaveLength(0);
432+
expect(rawConfig.properties['ex:param2']).toHaveLength(0);
433+
expect(rawConfig.properties['ex:param3'][0]).toBe(override2Properties['ex:param3'][0]);
434+
expect(rawConfig.properties['ex:param3'][0].value).toBe('value3');
435+
expect(rawConfig.properties['ex:param4'][0]).toBe(override3Properties['ex:param4'][0]);
436+
expect(rawConfig.properties['ex:param4'][0].value).toBe('value4-2');
437+
});
365438
});

0 commit comments

Comments
 (0)