Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2 -> v1 plugin #748

Merged
merged 22 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions external/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
<groupId>org.opencds.cqf.cql</groupId>
<artifactId>evaluator.fhir</artifactId>
</dependency>
<!-- <dependency>
<groupId>com.converter</groupId>
<artifactId>cqf-ruler-v1-converter-plugin</artifactId>
<version>1.0.0</version>
</dependency> -->
TahaAttari marked this conversation as resolved.
Show resolved Hide resolved
<dependency>
<groupId>org.opencds.cqf.cql</groupId>
<artifactId>evaluator.library</artifactId>
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@

<modules>
<module>example</module>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed example from the modules so that it's obvious that you need to run mvn package inside the example folder separately

<module>v1-converter</module>
<module>external</module>
<module>test</module>
<module>core</module>
Expand Down
66 changes: 66 additions & 0 deletions v1-converter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opencds.cqf.ruler</groupId>
<artifactId>cqf-ruler</artifactId>
<version>0.15.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

<groupId>com.converter</groupId>
<artifactId>cqf-ruler-v1-converter-plugin</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.opencds.cqf.ruler</groupId>
<artifactId>cqf-ruler-core</artifactId>
<version>0.15.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opencds.cqf.ruler</groupId>
<artifactId>cqf-ruler-test</artifactId>
<version>0.15.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<configuration>
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
21 changes: 21 additions & 0 deletions v1-converter/src/main/java/com/converter/ConverterConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.converter;

import org.opencds.cqf.external.annotations.OnR4Condition;
import org.opencds.cqf.ruler.api.OperationProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConverterConfig {
@Bean
public ConverterProperties converterProperties() {
return new ConverterProperties();
}

@Bean
@Conditional(OnR4Condition.class)
public OperationProvider converterProvider() {
return new ConverterProvider();
}
}
19 changes: 19 additions & 0 deletions v1-converter/src/main/java/com/converter/ConverterProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.converter;
import org.hl7.fhir.r5.model.IdType;
import org.opencds.cqf.ruler.behavior.DaoRegistryUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;

import ca.uhn.fhir.jpa.api.dao.DaoRegistry;

@ConfigurationProperties(prefix = "converter")
public class ConverterProperties implements DaoRegistryUser {
@Autowired
private DaoRegistry myDaoRegistry;

public final IdType v1PlanDefinitionId = new IdType("PlanDefinition", "plandefinition-ersd-skeleton");

public DaoRegistry getDaoRegistry() {
return myDaoRegistry;
}
}
131 changes: 131 additions & 0 deletions v1-converter/src/main/java/com/converter/ConverterProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.converter;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.MetadataResource;
import org.hl7.fhir.r4.model.PlanDefinition;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.UsageContext;
import org.opencds.cqf.ruler.api.OperationProvider;
import org.springframework.beans.factory.annotation.Autowired;

import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;

public class ConverterProvider implements OperationProvider {
@Autowired
ConverterProperties converterProperties;

/**
* Implements the $convert-v1 operation which transforms an ersd v2 Bundle
* into an ersd v1 compatible bundle
* @return a greeting
*/
@Description(shortDefinition = "Converts a v2 ERSD bundle into a v1 ERSD bundle", value = "Converts a v2 ERSD bundle into a v1 ERSD bundle")
@Operation(idempotent = true, name = "$convert-v1")
public Bundle convert_v1(
RequestDetails requestDetails,
@OperationParam(name = "resource") IBaseResource maybeBundle) throws UnprocessableEntityException {
if (maybeBundle == null) {
throw new UnprocessableEntityException("Resource is missing");
}
if (!(maybeBundle instanceof IBaseBundle )) {
throw new UnprocessableEntityException("Resource is not a bundle");
}
Bundle v2 = (Bundle) maybeBundle;
removeRootSpecificationLibrary(v2);
final PlanDefinition v1PlanDefinition = getV1PlanDefinition(requestDetails);
v2.getEntry().stream()
.forEach(entry -> {
if (entry.getResource() instanceof MetadataResource) {
MetadataResource resource = (MetadataResource) entry.getResource();
checkAndUpdateV2PlanDefinition(entry, v1PlanDefinition);
updateV2TriggeringValueSets(resource, v1PlanDefinition.getUrl());
updateV2TriggeringValueSetLibrary(resource);
removeUSPHProfiles(resource);
resource.setExperimentalElement(null);
}
});
return v2;
}
private void removeRootSpecificationLibrary(Bundle v2) {
List<BundleEntryComponent> filteredRootLib = v2.getEntry().stream()
.filter(entry -> entry.hasResource())
.filter(entry -> {
if (entry.getResource().hasMeta()
&& entry.getResource().getMeta().getProfile().stream().anyMatch(canonical -> canonical.getValue().contains("us-ph-specification-library"))) {
return false;
} else {
return true;
}
}).collect(Collectors.toList());
v2.setEntry(filteredRootLib);
}
private void checkAndUpdateV2PlanDefinition(BundleEntryComponent entry, PlanDefinition v1PlanDefinition) {
if (entry.getResource().getResourceType() == ResourceType.PlanDefinition
&& entry.getResource().hasMeta()
&& entry.getResource().getMeta().getProfile().stream().anyMatch(canonical -> canonical.getValue().contains("/ersd-plandefinition"))) {
entry.setResource(v1PlanDefinition);
}
}
private void updateV2TriggeringValueSetLibrary(MetadataResource resource) {
if (resource.getResourceType() == ResourceType.ValueSet
&& resource.hasMeta()
&& resource.getMeta().getProfile().stream().anyMatch(canonical -> canonical.getValue().contains("us-ph-triggering-valueset-library"))
) {
List<UsageContext> filteredUseContexts = resource.getUseContext().stream()
.filter(useContext -> !useContext.getCode().getCode().equals("reporting") && !useContext.getCode().getCode().equals("specification-type"))
.collect(Collectors.toList());
resource.setUseContext(filteredUseContexts);
}
}
private void updateV2TriggeringValueSets(MetadataResource resource, String v1PlanDefinitionUrl) {
if (resource.getResourceType() == ResourceType.ValueSet
&& resource.hasMeta()
&& resource.getMeta().getProfile().stream().anyMatch(canonical -> canonical.getValue().contains("us-ph-triggering-valueset"))) {
resource.getUseContext().stream().forEach(useContext -> {
if (useContext.getCode().getCode().equals("program")) {
useContext.setValue(new Reference(v1PlanDefinitionUrl));
}
});
List<UsageContext> filteredUseContexts = resource.getUseContext().stream()
.filter(useContext -> !useContext.getCode().getCode().equals("reporting") && !useContext.getCode().getCode().equals("priority"))
.collect(Collectors.toList());
resource.setUseContext(filteredUseContexts);
}
}
private void removeUSPHProfiles(MetadataResource resource){
if (resource.hasMeta() && resource.getMeta().hasProfile()) {
// need to remove us ph meta profiles
List<CanonicalType> filteredProfiles = resource.getMeta().getProfile().stream()
.filter(profile -> !profile.getValueAsString().contains("us-ph")).collect(Collectors.toList());
resource.getMeta().setProfile(filteredProfiles);
}
}
private PlanDefinition getV1PlanDefinition(RequestDetails requestDetails) throws ResourceNotFoundException {
Optional<PlanDefinition> maybePlanDefinition = Optional.ofNullable(null);
try {
PlanDefinition v1PlanDefinition = (PlanDefinition) converterProperties
.getDaoRegistry()
.getResourceDao(converterProperties.v1PlanDefinitionId.getResourceType())
.read(converterProperties.v1PlanDefinitionId, requestDetails);
maybePlanDefinition = Optional.of(v1PlanDefinition);
} catch (ResourceNotFoundException | ResourceGoneException e) {
throw new ResourceNotFoundException("Could not find V1 PlanDefinition");
}
return maybePlanDefinition.orElseThrow(() -> new ResourceNotFoundException("Could not find V1 PlanDefinition"));
}
}
2 changes: 2 additions & 0 deletions v1-converter/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.converter.ConverterConfig
495 changes: 495 additions & 0 deletions v1-converter/src/main/resources/ersd-v1-plandefinition-skeleton.json

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions v1-converter/src/test/java/com/converter/ConverterProviderIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.converter;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.junit.jupiter.api.Test;
import org.opencds.cqf.ruler.test.RestIntegrationTest;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
ConverterConfig.class }, properties = { "hapi.fhir.fhir_version=r4" })
class ConverterProviderIT extends RestIntegrationTest {
@Test
void testConverterConfig() {
loadResource("ersd-v1-plandefinition-skeleton.json");
Bundle v2Bundle = (Bundle) loadResource("ersd-bundle-example.json");
Parameters v2BundleParams = new Parameters();
ParametersParameterComponent part = v2BundleParams.addParameter()
.setName("resource")
.setResource(v2Bundle);
Bundle v1Bundle = getClient()
.operation()
.onServer()
.named("$convert-v1")
.withParameters(v2BundleParams)
.returnResourceType(Bundle.class)
.execute();

assertNotNull(v1Bundle);
}
}
Loading