forked from hapifhir/hapi-fhir-jpaserver-starter
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial work Supporting FML transforms between different FHIR versions …
- Loading branch information
1 parent
036975c
commit 5cdcf65
Showing
13 changed files
with
354 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,7 @@ | |
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; | ||
import org.hl7.fhir.r5.model.StructureDefinition; | ||
import org.hl7.fhir.r5.model.StructureMap; | ||
import org.hl7.fhir.r5.model.UriType; | ||
import org.hl7.fhir.r5.renderers.RendererFactory; | ||
import org.hl7.fhir.r5.renderers.utils.RenderingContext; | ||
import org.hl7.fhir.r5.utils.EOperationOutcome; | ||
|
@@ -63,6 +64,7 @@ | |
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; | ||
import org.hl7.fhir.utilities.ByteProvider; | ||
import org.hl7.fhir.utilities.FhirPublication; | ||
import org.hl7.fhir.utilities.Utilities; | ||
import org.hl7.fhir.utilities.VersionUtilities; | ||
import org.hl7.fhir.utilities.json.model.JsonObject; | ||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; | ||
|
@@ -74,6 +76,7 @@ | |
import org.hl7.fhir.validation.ValidationEngine; | ||
import org.hl7.fhir.validation.instance.InstanceValidator; | ||
|
||
import ch.ahdis.matchbox.engine.MatchboxEngine.FilesystemPackageCacheMode; | ||
import ch.ahdis.matchbox.engine.cli.VersionUtil; | ||
import ch.ahdis.matchbox.mappinglanguage.MatchboxStructureMapUtilities; | ||
import ch.ahdis.matchbox.mappinglanguage.TransformSupportServices; | ||
|
@@ -187,6 +190,8 @@ public MatchboxEngine getEngineR4() throws MatchboxEngineCreationException { | |
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.r4.core.tgz")); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz")); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions.r4#1.0.0.tgz")); | ||
removeStructureMaps(engine); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#[email protected]")); | ||
} catch (final IOException e) { | ||
throw new IgLoadException(e); | ||
} | ||
|
@@ -209,6 +214,16 @@ public MatchboxEngine getEngineR4() throws MatchboxEngineCreationException { | |
return engine; | ||
} | ||
|
||
/** | ||
* remove old StructureMaps from the context, especially from hl7.fhir.uv.extensions.r4#1.0.0 which are replaced by newer versions | ||
* @param engine | ||
*/ | ||
public void removeStructureMaps(MatchboxEngine engine) { | ||
for (StructureMap map : engine.getContext().fetchResourcesByType(StructureMap.class)) { | ||
engine.getContext().dropResource(map); | ||
} | ||
} | ||
|
||
/** | ||
* Returns a FHIR R5 engine configured with hl7 terminology | ||
* | ||
|
@@ -226,6 +241,8 @@ public MatchboxEngine getEngineR5() throws MatchboxEngineCreationException { | |
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.r5.core.tgz")); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.terminology#5.4.0.tgz")); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.extensions#1.0.0.tgz")); | ||
removeStructureMaps(engine); | ||
engine.loadPackage(getClass().getResourceAsStream("/hl7.fhir.uv.xver#[email protected]")); | ||
} catch (final IOException e) { | ||
throw new IgLoadException(e); | ||
} | ||
|
@@ -357,15 +374,25 @@ public Resource transformToFhir(String input, boolean inputJson, String mapUri) | |
public String transform(String input, boolean inputJson, String mapUri, boolean outputJson) | ||
throws FHIRException, IOException { | ||
log.info("Start transform: " + mapUri); | ||
|
||
SimpleWorkerContext context = this.getContext(); | ||
StructureMap map = context.fetchResource(StructureMap.class, mapUri); | ||
|
||
String fhirVersionTarget = getFhirVersion(getCanonicalFromStructureMap(map, StructureMap.StructureMapModelMode.TARGET)); | ||
if (fhirVersionTarget !=null && !fhirVersionTarget.equals(this.getVersion())) { | ||
log.info("Loading additional FHIR version for Target into context" + fhirVersionTarget); | ||
context = getContextForFhirVersion(fhirVersionTarget); | ||
} | ||
|
||
Element transformed = transform(ByteProvider.forBytes(input.getBytes("UTF-8")), (inputJson ? FhirFormat.JSON : FhirFormat.XML), | ||
mapUri); | ||
mapUri, context); | ||
ByteArrayOutputStream boas = new ByteArrayOutputStream(); | ||
if (outputJson) | ||
new org.hl7.fhir.r5.elementmodel.JsonParser(getContext()).compose(transformed, boas, | ||
new org.hl7.fhir.r5.elementmodel.JsonParser(context).compose(transformed, boas, | ||
IParser.OutputStyle.PRETTY, | ||
null); | ||
else | ||
new org.hl7.fhir.r5.elementmodel.XmlParser(getContext()).compose(transformed, boas, | ||
new org.hl7.fhir.r5.elementmodel.XmlParser(context).compose(transformed, boas, | ||
IParser.OutputStyle.PRETTY, | ||
null); | ||
String result = new String(boas.toByteArray()); | ||
|
@@ -378,22 +405,54 @@ public String transform(String input, boolean inputJson, String mapUri, boolean | |
* Adapted transform operation from Validation Engine to use patched | ||
* MatchboxStructureMapUtilities | ||
*/ | ||
public org.hl7.fhir.r5.elementmodel.Element transform(ByteProvider source, FhirFormat cntType, String mapUri) | ||
public org.hl7.fhir.r5.elementmodel.Element transform(ByteProvider source, FhirFormat cntType, String mapUri, SimpleWorkerContext targetContext) | ||
throws FHIRException, IOException { | ||
SimpleWorkerContext context = this.getContext(); | ||
|
||
// usual case is that source and target are in the same FHIR version as in the context, however it could be that either source or target are in a different FHIR version | ||
// if this is the case we do lazy loading of the additional FHIR version into the context | ||
|
||
StructureMap map = context.fetchResource(StructureMap.class, mapUri); | ||
String fhirVersion = getFhirVersion(getCanonicalFromStructureMap(map, StructureMap.StructureMapModelMode.SOURCE)); | ||
if (!fhirVersion.equals(this.getVersion())) { | ||
log.info("Loading additional FHIR version for Source into context" + fhirVersion); | ||
context.loadFromPackage("hl7.fhir.r5.core", fhirVersion); | ||
String fhirVersionSource = getFhirVersion(getCanonicalFromStructureMap(map, StructureMap.StructureMapModelMode.SOURCE)); | ||
if (fhirVersionSource !=null && !fhirVersionSource.equals(this.getVersion())) { | ||
log.info("Loading additional FHIR version for Source into context" + fhirVersionSource); | ||
context = getContextForFhirVersion(fhirVersionSource); | ||
} | ||
org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(source.getBytes()), | ||
cntType); | ||
return transform(src, mapUri); | ||
return transform(src, mapUri, targetContext); | ||
} | ||
|
||
/** | ||
* Adapted transform operation from Validation Engine to use patched | ||
* MatchboxStructureMapUtilities | ||
*/ | ||
public SimpleWorkerContext getContextForFhirVersion(String fhirVersion) | ||
throws FHIRException, IOException { | ||
SimpleWorkerContext contextForFhirVersion = null; | ||
if (fhirVersion.startsWith("4.0")) { | ||
MatchboxEngine engine = new MatchboxEngineBuilder().getEngineR4(); | ||
contextForFhirVersion = engine.getContext(); | ||
} | ||
if (fhirVersion.startsWith("5.0")) { | ||
MatchboxEngine engine = new MatchboxEngineBuilder().getEngineR5(); | ||
contextForFhirVersion = engine.getContext(); | ||
} | ||
if (contextForFhirVersion != null ) { | ||
// we need to copy now all StructureDefinitions from this Version to the new context | ||
for (StructureDefinition sd : contextForFhirVersion.listStructures()) { | ||
StructureDefinition sdn = sd.copy(); | ||
if (sdn.getKind()!=null && sdn.getKind() != StructureDefinition.StructureDefinitionKind.LOGICAL && !"Extensions".equals(sdn.getType())) { | ||
sdn.setUrl(sdn.getUrl().replace("http://hl7.org/fhir/", "http://hl7.org/fhir/"+fhirVersion+"/")); | ||
sdn.addExtension().setUrl("http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace") | ||
.setValue(new UriType("http://hl7.org/fhir")); | ||
this.getContext().cacheResource(sdn); | ||
} | ||
contextForFhirVersion.cacheResource(sd); | ||
} | ||
} | ||
|
||
return contextForFhirVersion; | ||
} | ||
|
||
/** | ||
|
@@ -405,12 +464,12 @@ public org.hl7.fhir.r5.elementmodel.Element transform(ByteProvider source, FhirF | |
* @throws FHIRException | ||
* @throws IOException | ||
*/ | ||
public org.hl7.fhir.r5.elementmodel.Element transform(org.hl7.fhir.r5.elementmodel.Element src, String mapUri) | ||
public org.hl7.fhir.r5.elementmodel.Element transform(org.hl7.fhir.r5.elementmodel.Element src, String mapUri, SimpleWorkerContext targetContext) | ||
throws FHIRException, IOException { | ||
SimpleWorkerContext context = this.getContext(); | ||
List<Base> outputs = new ArrayList<>(); | ||
StructureMapUtilities scu = new MatchboxStructureMapUtilities(context, | ||
new TransformSupportServices(context, outputs), this); | ||
new TransformSupportServices(targetContext!=null ? targetContext : context, outputs), this); | ||
StructureMap map = context.fetchResource(StructureMap.class, mapUri); | ||
if (map == null) { | ||
log.error("Unable to find map " + mapUri + " (Known Maps = " + context.listMapUrls() + ")"); | ||
|
@@ -420,6 +479,7 @@ public org.hl7.fhir.r5.elementmodel.Element transform(org.hl7.fhir.r5.elementmod | |
+ (map.getDateElement() != null && !map.getDateElement().isEmpty() ? "(" + map.getDateElement().asStringValue() + ")" : "")); | ||
|
||
org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map); | ||
|
||
scu.transform(null, src, map, resource); | ||
resource.populatePaths(null); | ||
return resource; | ||
|
@@ -468,6 +528,13 @@ private org.hl7.fhir.r5.elementmodel.Element getTargetResourceFromStructureMap(S | |
throw new FHIRException("Unable to determine resource URL for target type"); | ||
} | ||
|
||
if (Utilities.isAbsoluteUrl(targetTypeUrl)) { | ||
int index = targetTypeUrl.indexOf("/"+this.getVersion().substring(0,3)+"/"); | ||
if (index >= 0) { | ||
targetTypeUrl = targetTypeUrl.substring(0, index)+targetTypeUrl.substring(index+4); | ||
} | ||
} | ||
|
||
StructureDefinition structureDefinition = null; | ||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { | ||
if (sd.getUrl().equalsIgnoreCase(targetTypeUrl)) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+5.94 MB
matchbox-engine/src/main/resources/hl7.fhir.uv.xver#[email protected]
Binary file not shown.
96 changes: 96 additions & 0 deletions
96
matchbox-engine/src/test/java/ch/ahdis/matchbox/engine/tests/FhirXVersTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package ch.ahdis.matchbox.engine.tests; | ||
|
||
/* | ||
* #%L | ||
* Matchbox Engine | ||
* %% | ||
* Copyright (C) 2022 ahdis | ||
* %% | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* #L% | ||
*/ | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import org.apache.commons.io.IOUtils; | ||
import org.hl7.fhir.exceptions.FHIRException; | ||
import org.hl7.fhir.r4.formats.XmlParser; | ||
import org.hl7.fhir.r4.model.Bundle; | ||
import org.hl7.fhir.r4.model.ExplanationOfBenefit; | ||
import org.hl7.fhir.r4.model.Observation; | ||
import org.hl7.fhir.r4.model.Patient; | ||
import org.hl7.fhir.r4.model.Questionnaire; | ||
import org.hl7.fhir.r4.model.Resource; | ||
import org.hl7.fhir.r4.model.StructureDefinition; | ||
import org.hl7.fhir.r4.model.StructureMap; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Disabled; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import ch.ahdis.matchbox.engine.MatchboxEngine; | ||
import ch.ahdis.matchbox.engine.MatchboxEngine.MatchboxEngineBuilder; | ||
|
||
/** | ||
* https://build.fhir.org/ig/HL7/fhir-cross-version/package.tgz | ||
*/ | ||
class FhirXVersTests { | ||
|
||
static private MatchboxEngine engine; | ||
|
||
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FhirMappingLanguageTests.class); | ||
|
||
@BeforeAll | ||
static void setUpBeforeClass() throws Exception { | ||
engine = new MatchboxEngineBuilder().getEngineR4(); | ||
} | ||
|
||
@BeforeEach | ||
void setUp() throws Exception { | ||
} | ||
|
||
public String getFileAsStringFromResources(String file) throws IOException { | ||
InputStream in = FhirXVersTests.class.getResourceAsStream("/xvers" + file); | ||
return IOUtils.toString(in, StandardCharsets.UTF_8); | ||
} | ||
|
||
public StructureDefinition getStructureDefinitionFromFile(String file) throws IOException { | ||
return (StructureDefinition) new org.hl7.fhir.r4.formats.XmlParser() | ||
.parse(FhirMappingLanguageTests.class.getResourceAsStream(file)); | ||
} | ||
|
||
@Test | ||
void testMedication5to4inR4() throws FHIRException, IOException { | ||
MatchboxEngine engine = new MatchboxEngine(FhirXVersTests.engine); | ||
String result = engine.transform(getFileAsStringFromResources("/medication-r5-med0301.json"), true, | ||
"http://hl7.org/fhir/uv/xver/StructureMap/Medication5to4", true); | ||
log.info(result); | ||
CompareUtil.compare(getFileAsStringFromResources("/medication-r4-med0301.json"), result, false); | ||
} | ||
|
||
@Test | ||
void testMedication4to5inR4() throws FHIRException, IOException { | ||
MatchboxEngine engine = new MatchboxEngine(FhirXVersTests.engine); | ||
String result = engine.transform(getFileAsStringFromResources("/medication-r4-med0301.json"), true, | ||
"http://hl7.org/fhir/uv/xver/StructureMap/Medication4to5", true); | ||
log.info(result); | ||
CompareUtil.compare(getFileAsStringFromResources("/medication-r5-med0301.json"), result, false); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.