Skip to content
This repository was archived by the owner on Jul 13, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docker/Transformation-Engine/database/h2.mv.db
Binary file not shown.
1,494 changes: 1,494 additions & 0 deletions docker/Transformation-Engine/database/h2.trace.db

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nhsd.fhir.converter.service.mapping;

import ca.uhn.fhir.context.FhirVersionEnum;
import org.hl7.fhir.convertors.conv30_40.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.stereotype.Component;

@Component
public class Converter {
private final VersionConvertor_30_40 converter;

public Converter(VersionConvertor_30_40 converter) {
this.converter = converter;
}

public IBaseResource convert(IBaseResource resource, FhirVersionEnum version) {
if (FhirVersionEnum.R4 == version) {
return converter.convertResource((org.hl7.fhir.r4.model.Resource) resource);
} else {
return converter.convertResource((Resource) resource);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package nhsd.fhir.converter.service.mapping;

import ca.uhn.fhir.context.FhirVersionEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

@Service
public class ConverterService {

private final Converter converter;
private final Transformer transformer;
private final FhirParser fhirParser;

public ConverterService(Converter converter, Transformer transformer, FhirParser fhirParser) {
this.converter = converter;
this.transformer = transformer;
this.fhirParser = fhirParser;
}

public String convert(String resource, MediaType mediaType, FhirVersionEnum fhirVersion) throws ParserConfigurationException, IOException, SAXException {
Class<? extends IBaseResource> resourceType = ResourceTypeFactory.createResourceType(resource, mediaType, fhirVersion);

IBaseResource fhirResource = fhirParser.parse(resource, resourceType, mediaType);
IBaseResource converted = converter.convert(fhirResource, fhirVersion);
IBaseResource transformed = transformer.transform(converted);

FhirVersionEnum outVersion = fhirVersion == FhirVersionEnum.R4 ? FhirVersionEnum.DSTU3 : FhirVersionEnum.R4;

return fhirParser.encode(transformed, mediaType, outVersion);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package nhsd.fhir.converter.service.mapping;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40;
import org.hl7.fhir.convertors.conv30_40.VersionConvertor_30_40;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FhirConfig {
@Bean
public FhirContext stu3FhirContext() {
return FhirContext.forDstu3();
}

@Bean
public FhirContext r4FhirContext() {
return FhirContext.forR4();
}

@Bean
public IParser stu3XmlParser(FhirContext stu3FhirContext) {
return stu3FhirContext.newXmlParser();
}

@Bean
public IParser stu3JsonParser(FhirContext stu3FhirContext) {
return stu3FhirContext.newJsonParser();
}

@Bean
public IParser r4XmlParser(FhirContext r4FhirContext) {
return r4FhirContext.newXmlParser();
}

@Bean
public IParser r4JsonParser(FhirContext r4FhirContext) {
return r4FhirContext.newJsonParser();
}

@Bean
public VersionConvertor_30_40 Converter30To40() {
return new VersionConvertor_30_40(new BaseAdvisor_30_40());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package nhsd.fhir.converter.service.mapping;

import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

@Component
public class FhirParser {
private final IParser stu3JsonParser;
private final IParser r4JsonParser;
private final IParser stu3XmlParser;
private final IParser r4XmlParser;

public FhirParser(IParser stu3JsonParser, IParser r4JsonParser, IParser stu3XmlParser, IParser r4XmlParser) {
this.stu3JsonParser = stu3JsonParser;
this.r4JsonParser = r4JsonParser;
this.stu3XmlParser = stu3XmlParser;
this.r4XmlParser = r4XmlParser;
}

public <T extends IBaseResource> T parse(String resource, Class<T> resourceType, MediaType mediaType) {
boolean isR4 = resourceType.getName().contains("r4");

if (MediaType.APPLICATION_JSON == mediaType) {
if (isR4) {
return r4JsonParser.parseResource(resourceType, resource);
} else {
return stu3JsonParser.parseResource(resourceType, resource);
}
} else if (MediaType.APPLICATION_XML == mediaType) {
if (isR4) {
return r4XmlParser.parseResource(resourceType, resource);
} else {
return stu3XmlParser.parseResource(resourceType, resource);
}
}
throw new IllegalStateException("Invalid Content-Type");
}

public String encode(IBaseResource resource, MediaType mediaType, FhirVersionEnum fhirVersion) {
boolean isR4 = FhirVersionEnum.R4 == fhirVersion;

if (MediaType.APPLICATION_JSON == mediaType) {
if (isR4) {
return r4JsonParser.encodeResourceToString(resource);
} else {
return stu3JsonParser.encodeResourceToString(resource);
}
} else if (MediaType.APPLICATION_XML == mediaType) {
if (isR4) {
return r4XmlParser.encodeResourceToString(resource);
} else {
return stu3XmlParser.encodeResourceToString(resource);
}
}
throw new IllegalStateException("Invalid Accept header");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package nhsd.fhir.converter.service.mapping;

import ca.uhn.fhir.context.FhirVersionEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.MedicationStatement;
import org.json.JSONObject;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;

@Component
public class ResourceTypeFactory {
public static Class<? extends IBaseResource> createResourceType(String fhirResource, MediaType mediaType, FhirVersionEnum fhirVersion) throws ParserConfigurationException, IOException, SAXException {
String resourceType = getResourceType(fhirResource, mediaType);
switch (resourceType) {
case "MedicationRequest":
return FhirVersionEnum.R4 == fhirVersion ?
MedicationRequest.class
: org.hl7.fhir.dstu3.model.MedicationRequest.class;
case "MedicationStatement":
return FhirVersionEnum.R4 == fhirVersion ?
MedicationStatement.class
: org.hl7.fhir.dstu3.model.MedicationStatement.class;
}
throw new IllegalStateException("Resource not supported");
}

private static String getResourceType(final String fhirSchema, MediaType mediaType) throws ParserConfigurationException, IOException, SAXException {
if ("xml".equals(mediaType.getSubtype())) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(fhirSchema)));

return doc.getFirstChild().getNodeName();
} else {
JSONObject json = new JSONObject(fhirSchema);

return json.getString("resourceType");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nhsd.fhir.converter.service.mapping;

import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.model.StructureMap;
import org.hl7.fhir.r4.utils.StructureMapUtilities;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;

@Component
public class Transformer {
static private String medicationRequestStu3ToR4Fml;
private final StructureMap structureMap;
private final StructureMapUtilities structureMapUtilities;

public Transformer() throws IOException {
IWorkerContext worker = new SimpleWorkerContext();
structureMapUtilities = new StructureMapUtilities(worker);
structureMap = structureMapUtilities.parse(medicationRequestStu3ToR4Fml, "src");
}

IBaseResource transform(IBaseResource resource) {
org.hl7.fhir.r4.model.Resource source = (org.hl7.fhir.r4.model.Resource) resource;
org.hl7.fhir.r4.model.Resource target = source.copy();

structureMapUtilities.transform("source", source, structureMap, target);

return target;
}

static {
// InputStream is = Transformer.class.getClassLoader().getResourceAsStream("fml/MedicationRequest/STU3_to_R4.fml");
InputStream is = Transformer.class.getClassLoader().getResourceAsStream("fml/MedicationRequest/extension.fml");
try {
medicationRequestStu3ToR4Fml = new String(is.readAllBytes());
} catch (IOException e) {
System.err.println("Can't load fml mapping file");
}

}
}

This file was deleted.

35 changes: 35 additions & 0 deletions docker/Transformation-Engine/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
spring:
datasource:
url: 'jdbc:h2:file:./database/h2'
username: sa
password: null
driverClassName: org.h2.Driver
max-active: 15

# database connection pool size
hikari:
maximum-pool-size: 10
flyway:
check-location: false
baselineOnMigrate: true
jpa:
properties:
hibernate.format_sql: false
hibernate.show_sql: false
# hibernate.dialect: org.hibernate.dialect.h2dialect
# hibernate.hbm2ddl.auto: update
# hibernate.jdbc.batch_size: 20
# hibernate.cache.use_query_cache: false
# hibernate.cache.use_second_level_cache: false
# hibernate.cache.use_structured_entries: false
# hibernate.cache.use_minimal_puts: false
### These settings will enable fulltext search with lucene
hibernate.search.enabled: true
# hibernate.search.backend.type: lucene
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer
# hibernate.search.backend.directory.type: local-filesystem
# hibernate.search.backend.directory.root: target/lucenefiles
# hibernate.search.backend.lucene_version: lucene_current
batch:
job:
enabled: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
map "http://hl7.org/fhir/StructureMap/DomainResource3to4" = "R3 to R4 Conversions for DomainResource"

uses "http://hl7.org/fhir/3.0/StructureDefinition/DomainResource" alias DomainResourceR3 as source
uses "http://hl7.org/fhir/4.0/StructureDefinition/DomainResource" alias DomainResource as target

//imports "http://hl7.org/fhir/StructureMap/*3to4"

group DomainResource(source src : DomainResourceR3, target tgt : DomainResource) {
src.status -> tgt.status;
//src.contained -> tgt.contained;
//src.extension -> tgt.extension;
//src.modifierExtension -> tgt.modifierExtension;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
map "http://basic.test/medication-request" = "basic-test-medication-request"
uses "http://hl7.org/fhir/StructureDefinition/MedicationRequest" alias MedicationRequest as source
uses "http://hl7.org/fhir/StructureDefinition/MedicationRequest" alias MedicationRequest as target
uses "http://hl7.org/fhir/StructureDefinition/CodeableConcept" alias CodeableConcept as source
uses "http://hl7.org/fhir/StructureDefinition/CodeableConcept" alias CodeableConcept as target
uses "http://hl7.org/fhir/StructureDefinition/Coding" alias Coding as source
uses "http://hl7.org/fhir/StructureDefinition/Coding" alias Coding as target
group MedicationRequest(source src : MedicationRequest, target tgt : MedicationRequest) {
src.extension as ext where $this.url='https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-PrescriptionType-1'
-> tgt.extension as tgtext, tgtext.url='UPDATED DEFINITION HERE' then {
ext.value : CodeableConcept as vs -> tgtext.value = create('CodeableConcept') as vt then CodeableConceptPrescriptionType(vs, vt);
};
}
group CodeableConceptPrescriptionType(source src : CodeableConcept, target tgt : CodeableConcept) {
// src.coding -> tgt.coding; the short form is only woking when you have a <<type>>
src.coding -> tgt.coding;
src.text as text -> tgt.text = text;
}
group Coding(source src : Coding, target tgt : Coding) <<type+>> {
src.system as system where $this='https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-PrescriptionType-1' -> tgt.system = 'UPDATED CODESYSTEM HERE';
src.version as version -> tgt.version = version;
src.code as code -> tgt.code = code;
src.display as display -> tgt.display = display;
src.userSelected as userSelected -> tgt.userSelected = userSelected;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void callConverterToConvert_R4_to_R3_json_json() throws Exception {

//then
assertEquals(responseEntity.getStatusCode(), HttpStatus.OK);
assertEquals(responseEntity.getBody(), staticR3Json);
assertEquals(responseEntity.getBody(), staticR3Json.trim());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nhsd.fhir.converter.service.transformers;
package nhsd.fhir.converter.service.converter;

import ca.uhn.fhir.context.FhirVersionEnum;
import nhsd.fhir.converter.service.converter.BundleConverter;
import org.apache.commons.io.FileUtils;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nhsd.fhir.converter.service.transformers;
package nhsd.fhir.converter.service.converter;

import ca.uhn.fhir.context.FhirVersionEnum;
import nhsd.fhir.converter.service.converter.MedicationRequestConverter;
import org.apache.commons.io.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nhsd.fhir.converter.service.transformers;
package nhsd.fhir.converter.service.converter;

import ca.uhn.fhir.context.FhirVersionEnum;
import nhsd.fhir.converter.service.converter.MedicationStatementConverter;
import org.apache.commons.io.FileUtils;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
Expand Down
Loading