From e91f68e326c9f55dfba957c7fce21e74b7b72d58 Mon Sep 17 00:00:00 2001 From: Mint Thompson Date: Mon, 26 Aug 2024 08:54:48 -0400 Subject: [PATCH] Add optional argument for making clone of predefined resources (#1500) The FHIRDefinitions class provides a public method to return all predefined resources. Hypothetically, the caller of this method could alter the predefined resources, which is why it could be useful to return a clone of these resources. However, in all places this method is called by SUSHI, the resources are used for a search, not for any modifications. By not cloning the resources for the search, significant time savings are possible. Just in case someone else is using SUSHI as a dependency and calling this method and expecting to receive a clone, the default behavior is to return a clone. When searching for slices, the slice of an element is always a sibling of that element. Therefore, it is preferable to search the smaller list containing only the element's parent's children, when possible. This takes advantage of the underlying ElementDefinition tree. Because the root element's parent is undefined, use the full element list when working with the root element. This shouldn't be possible in most cases, as revealed by the test coverage, but the implementation is defensive just in case something unusual has happened to the root element. --- src/fhirdefs/FHIRDefinitions.ts | 10 +++++++--- src/fhirtypes/ElementDefinition.ts | 26 ++++++++++++++++---------- src/utils/Processing.ts | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/fhirdefs/FHIRDefinitions.ts b/src/fhirdefs/FHIRDefinitions.ts index 0b33e32cf..3ac06c77a 100644 --- a/src/fhirdefs/FHIRDefinitions.ts +++ b/src/fhirdefs/FHIRDefinitions.ts @@ -35,8 +35,12 @@ export class FHIRDefinitions extends BaseFHIRDefinitions implements Fishable { return flatten(Array.from(this.supplementalFHIRDefinitions.keys())); } - allPredefinedResources(): any[] { - return Array.from(this.predefinedResources.values()).map(v => cloneDeep(v)); + allPredefinedResources(makeClone = true): any[] { + if (makeClone) { + return Array.from(this.predefinedResources.values()).map(v => cloneDeep(v)); + } else { + return Array.from(this.predefinedResources.values()); + } } add(definition: any): void { @@ -81,7 +85,7 @@ export class FHIRDefinitions extends BaseFHIRDefinitions implements Fishable { const resource = this.fishForFHIR(item, ...types); if ( resource && - this.allPredefinedResources().find( + this.allPredefinedResources(false).find( predefResource => predefResource.id === resource.id && predefResource.resourceType === resource.resourceType && diff --git a/src/fhirtypes/ElementDefinition.ts b/src/fhirtypes/ElementDefinition.ts index 8f75c8434..19d484a7d 100644 --- a/src/fhirtypes/ElementDefinition.ts +++ b/src/fhirtypes/ElementDefinition.ts @@ -774,11 +774,12 @@ export class ElementDefinition { getSlices() { if (this.sliceName) { - return this.structDef.elements.filter( + // this.parent() should never be undefined if this element has a sliceName, but code defensively just in case + return (this.parent()?.children(true) ?? this.structDef.elements).filter( e => e.id !== this.id && e.path === this.path && e.id.startsWith(`${this.id}/`) ); } else { - return this.structDef.elements.filter( + return (this.parent()?.children(true) ?? this.structDef.elements).filter( e => e.id !== this.id && e.path === this.path && e.id.startsWith(`${this.id}:`) ); } @@ -898,12 +899,14 @@ export class ElementDefinition { const slicedElement = this.slicedElement(); if (slicedElement) { const parentSlice = this.findParentSlice(); - const sliceSiblings = this.structDef.elements.filter( - el => - this !== el && - slicedElement === el.slicedElement() && - parentSlice === el.findParentSlice() - ); + const sliceSiblings = this.parent() + .children(true) + .filter( + el => + this !== el && + slicedElement === el.slicedElement() && + parentSlice === el.findParentSlice() + ); const newParentMin = min + sliceSiblings.reduce((sum, el) => sum + el.min, 0); // if this is a reslice, the parent element will also be a slice of the sliced element. // if this is not a reslice, the parent element is the sliced element. @@ -985,8 +988,9 @@ export class ElementDefinition { return parentNameParts.slice(0, i + 1).join('/'); }) .reverse(); + const elementsToSearch = this.parent()?.children(true) ?? this.structDef.elements; for (const parentName of potentialParentNames) { - const potentialParent = this.structDef.elements.find(el => { + const potentialParent = elementsToSearch.find(el => { return el.sliceName === parentName && el.slicedElement() === slicedElement; }); if (potentialParent) { @@ -2586,7 +2590,9 @@ export class ElementDefinition { */ slicedElement(): ElementDefinition | undefined { if (this.sliceName) { - return this.structDef.elements.find(e => e.id === this.id.slice(0, this.id.lastIndexOf(':'))); + return (this.parent()?.children(true) ?? this.structDef.elements).find( + e => e.id === this.id.slice(0, this.id.lastIndexOf(':')) + ); } } diff --git a/src/utils/Processing.ts b/src/utils/Processing.ts index 23cdbc274..1b7f61802 100644 --- a/src/utils/Processing.ts +++ b/src/utils/Processing.ts @@ -543,7 +543,7 @@ export function writeFHIRResources( logger.info('Exporting FHIR resources as JSON...'); let count = 0; const skippedResources: string[] = []; - const predefinedResources = defs.allPredefinedResources(); + const predefinedResources = defs.allPredefinedResources(false); const writeResources = ( resources: { getFileName: () => string;