Merge pull request #218 from ahdis/oliveregger_fml_conformsto

FHIR Mapping Language support for conformsTo function
This commit is contained in:
Grahame Grieve 2020-06-06 07:00:23 +10:00 committed by GitHub
commit eaab390aa4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 46 additions and 329 deletions

View File

@ -1,149 +0,0 @@
package org.hl7.fhir.r4.test;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.elementmodel.Manager;
import org.hl7.fhir.r4.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.terminologies.ConceptMapEngine;
import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.utils.StructureMapUtilities;
import org.hl7.fhir.r4.utils.StructureMapUtilities.ITransformerServices;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.cache.ToolsVersion;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.Assert.assertTrue;
@Disabled
public class FHIRMappingLanguageTests implements ITransformerServices {
private List<Resource> outputs = new ArrayList<Resource>();
static private SimpleWorkerContext context;
static private JsonParser jsonParser;
public static Stream<Arguments> data()
throws FileNotFoundException, IOException, ParserConfigurationException, SAXException {
Document tests = XMLUtil.parseFileToDom(TestingUtilities.resourceNameToFile("fml", "manifest.xml"));
Element test = XMLUtil.getFirstChild(tests.getDocumentElement());
List<Arguments> objects = new ArrayList();
while (test != null && test.getNodeName().equals("test")) {
objects.add(Arguments.of(test.getAttribute("name"), test.getAttribute("source"), test.getAttribute("map"),
test.getAttribute("output")));
test = XMLUtil.getNextSibling(test);
}
return objects.stream();
}
@BeforeAll
static public void setUp() throws Exception {
if (context == null) {
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.core", "4.0.0"));
jsonParser = new JsonParser();
jsonParser.setOutputStyle(OutputStyle.PRETTY);
}
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("data")
public void test(String name, String source, String map, String output) throws Exception {
String fileSource = TestingUtilities.resourceNameToFile("fml", source);
String fileMap = TestingUtilities.resourceNameToFile("fml", map);
String fileOutput = TestingUtilities.resourceNameToFile("fml", output);
String fileOutputRes = TestingUtilities.resourceNameToFile("fml", output) + ".out";
outputs.clear();
boolean ok = false;
String msg = null;
Resource resource = null;
try {
StructureMapUtilities scu = new StructureMapUtilities(context, this);
org.hl7.fhir.r4.elementmodel.Element src = Manager.parse(context,
new ByteArrayInputStream(TextFile.fileToBytes(fileSource)), FhirFormat.JSON);
StructureMap structureMap = scu.parse(TextFile.fileToString(fileMap), name);
String typeName = scu.getTargetType(structureMap).getType();
resource = ResourceFactory.createResource(typeName);
scu.transform(null, src, structureMap, resource);
ok = true;
} catch (Exception e) {
ok = false;
msg = e.getMessage();
}
if (ok) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
jsonParser.compose(boas, resource);
log(boas.toString());
TextFile.bytesToFile(boas.toByteArray(), fileOutputRes);
msg = TestingUtilities.checkJsonIsSame(fileOutputRes, fileOutput);
assertTrue(msg, Utilities.noString(msg));
} else
assertTrue("Error, but proper output was expected (" + msg + ")", output.equals("$error"));
}
@Override
public void log(String message) {
System.out.println(message);
}
@Override
public Base createType(Object appInfo, String name) throws FHIRException {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, name);
if (sd != null && sd.getKind() == StructureDefinitionKind.LOGICAL) {
return Manager.build(context, sd);
} else {
if (name.startsWith("http://hl7.org/fhir/StructureDefinition/"))
name = name.substring("http://hl7.org/fhir/StructureDefinition/".length());
return ResourceFactory.createResourceOrType(name);
}
}
@Override
public Base createResource(Object appInfo, Base res, boolean atRootofTransform) {
if (atRootofTransform)
outputs.add((Resource) res);
return res;
}
@Override
public Coding translate(Object appInfo, Coding source, String conceptMapUrl) throws FHIRException {
ConceptMapEngine cme = new ConceptMapEngine(context);
return cme.translate(source, conceptMapUrl);
}
@Override
public Base resolveReference(Object appContext, String url) throws FHIRException {
throw new FHIRException("resolveReference is not supported yet");
}
@Override
public List<Base> performSearch(Object appContext, String url) throws FHIRException {
throw new FHIRException("performSearch is not supported yet");
}
}

View File

@ -1 +0,0 @@
*.out

View File

@ -1,6 +0,0 @@
<fml-tests>
<test name="http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2patassignment" source="qr.json" map="qr2pat-assignment.map" output="qr2pat-assignment-res.json" />
<test name="http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2patgender" source="qr.json" map="qr2pat-gender.map" output="qr2pat-gender-res.json" />
<test name="http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2pathumannametwice" source="qr.json" map="qr2pat-humannametwice.map" output="qr2pat-humannametwice-res.json" />
<test name="http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2pathumannameshared" source="qr.json" map="qr2pat-humannameshared.map" output="qr2pat-humannameshared-res.json" />
</fml-tests>

View File

@ -1,39 +0,0 @@
{
"resourceType": "QuestionnaireResponse",
"status": "in-progress",
"item": [
{
"linkId": "patient",
"text": "Patient",
"item": [
{
"linkId": "patient.lastname",
"text": "Name",
"answer": [
{
"valueString": "Brönnimann-Bertholet"
}
]
},
{
"linkId": "patient.firstname",
"text": "Vorname",
"answer": [
{
"valueString": "Elisabeth"
}
]
},
{
"linkId": "patient.sex",
"text": "Geschlecht",
"answer": [
{
"valueString": "female"
}
]
}
]
}
]
}

View File

@ -1,4 +0,0 @@
{
"resourceType" : "Patient",
"gender" : "female"
}

View File

@ -1,8 +0,0 @@
map "http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2patassignment" = "qr2patassignment"
uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" alias QuestionnaireResponse as source
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as target
group QuestionnaireResponse(source src : QuestionnaireResponse, target tgt : Patient) {
src -> tgt.gender = 'female' "Simple Assignment";
}

View File

@ -1,4 +0,0 @@
{
"resourceType" : "Patient",
"gender" : "female"
}

View File

@ -1,12 +0,0 @@
map "http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2patgender" = "qr2patgender"
uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" alias QuestionnaireResponse as source
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as target
group QuestionnaireResponse(source src : QuestionnaireResponse, target tgt : Patient) {
src.item as item -> tgt as patient then item(item, patient);
}
group item(source src, target tgt: Patient) {
src.item as item where linkId.value in ('patient.sex') -> tgt.gender = (item.answer.valueString);
}

View File

@ -1,8 +0,0 @@
{
"resourceType" : "Patient",
"name" : [{
"family" : "Brönnimann-Bertholet",
"given" : ["Elisabeth"]
}],
"gender" : "female"
}

View File

@ -1,26 +0,0 @@
map "http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2pathumannameshared" = "qr2pathumannametwice"
uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" alias QuestionnaireResponse as source
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as target
group entry(source src : QuestionnaireResponse, target tgt : Patient) {
src.item as item then item(item, tgt);
}
group item(source src, target tgt) {
src.item as item then item(item, tgt);
src.item as item where linkId.value = 'patient.lastname' -> tgt.name as name share patientName then humanNameFamily(item, name);
src.item as item where linkId.value = 'patient.firstname' -> tgt.name as name share patientName then humanNameGiven(item, name);
src.item as item where linkId.value = 'patient.sex' -> tgt.gender = (item.answer.valueString);
}
group humanNameFamily(source src, target tgt: HumanName) {
src.answer as answer -> tgt.family = (answer.valueString);
}
group humanNameGiven(source src, target tgt: HumanName) {
src.answer as answer -> tgt.given = (answer.valueString);
}
group administrativeGender(source src, target tgt: code) {
src.answer as answer -> tgt = (answer.valueString);
}

View File

@ -1,10 +0,0 @@
{
"resourceType" : "Patient",
"name" : [{
"family" : "Brönnimann-Bertholet"
},
{
"given" : ["Elisabeth"]
}],
"gender" : "female"
}

View File

@ -1,26 +0,0 @@
map "http://github.com/hapifhir/org.hl7.fhir.core/org.hl7.fhir.r4.tests/qr2pathumannametwice" = "qr2pathumannametwice"
uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" alias QuestionnaireResponse as source
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as target
group entry(source src : QuestionnaireResponse, target tgt : Patient) {
src.item as item then item(item, tgt);
}
group item(source src, target tgt) {
src.item as item then item(item, tgt);
src.item as item where linkId.value = 'patient.lastname' -> tgt.name as name then humanNameFamily(item, name);
src.item as item where linkId.value = 'patient.firstname' -> tgt.name as name then humanNameGiven(item, name);
src.item as item where linkId.value = 'patient.sex' -> tgt.gender = (item.answer.valueString);
}
group humanNameFamily(source src, target tgt: HumanName) {
src.answer as answer -> tgt.family = (answer.valueString);
}
group humanNameGiven(source src, target tgt: HumanName) {
src.answer as answer -> tgt.given = (answer.valueString);
}
group administrativeGender(source src, target tgt: code) {
src.answer as answer -> tgt = (answer.valueString);
}

View File

@ -1,33 +1,33 @@
package org.hl7.fhir.r5.context;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

View File

@ -213,18 +213,26 @@ public class StructureMapUtilities {
return services.resolveReference(appContext, url);
}
private boolean noErrorValidationMessages(List<ValidationMessage> valerrors) {
boolean ok = true;
for (ValidationMessage v : valerrors)
ok = ok && !v.getLevel().isError();
return ok;
}
@Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
IResourceValidator val = worker.newValidator();
List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>();
if (item instanceof Resource) {
val.validate(appContext, valerrors, (Resource) item, url);
boolean ok = true;
for (ValidationMessage v : valerrors)
ok = ok && v.getLevel().isError();
return ok;
return noErrorValidationMessages(valerrors);
}
throw new NotImplementedException("Not done yet (FFHIRPathHostServices.conformsToProfile), when item is element");
if (item instanceof Element) {
val.validate(appContext, valerrors, (Element) item, url);
return noErrorValidationMessages(valerrors);
}
throw new NotImplementedException("Not done yet (FFHIRPathHostServices.conformsToProfile), when item is not element or not resource");
}
@Override

View File

@ -36,8 +36,8 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.cache.ToolsVersion;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.hl7.fhir.validation.instance.InstanceValidatorFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@ -68,14 +68,16 @@ public class FHIRMappingLanguageTests implements ITransformerServices {
@BeforeAll
public static void setUp() throws Exception {
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.core", "4.0.1"));
context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.r5.core", "current"));
if (context.getValidatorFactory() == null) {
context.setValidatorFactory(new InstanceValidatorFactory());
}
jsonParser = new JsonParser();
jsonParser.setOutputStyle(OutputStyle.PRETTY);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("data")
@Disabled // Test fails: java.lang.AssertionError: Error, but proper output was expected (This does not appear to be a FHIR resource (unknown name "QuestionnaireResponse")
public void test(String name, String source, String map, String output) throws Exception {
InputStream fileSource = TestingUtilities.loadTestResourceStream("r5", "fml", source);