Skip to content

Commit

Permalink
Merge pull request #193 from MeasureAuthoringTool/MAT-6651
Browse files Browse the repository at this point in the history
MAT-6651 Crosswalk QI-Core type over to correct FHIR type for dataRequirement.type correctly
  • Loading branch information
adongare authored Feb 6, 2024
2 parents f89e740 + 102f8eb commit 29d29ab
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gov.cms.madie.madiefhirservice.dto;

import lombok.Builder;
import lombok.Data;

import java.util.Set;

@Data
@Builder
public class CqlLibraryDetails {
private String cql;
private String libraryName;
private Set<String> expressions;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import ca.uhn.fhir.context.FhirContext;
import gov.cms.madie.madiefhirservice.config.ElmTranslatorClientConfig;
import gov.cms.madie.madiefhirservice.dto.CqlLibraryDetails;
import gov.cms.madie.madiefhirservice.exceptions.CqlElmTranslationServiceException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r5.model.Library;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
Expand All @@ -23,39 +23,31 @@ public class ElmTranslatorClient {

private ElmTranslatorClientConfig elmTranslatorClientConfig;
private RestTemplate elmTranslatorRestTemplate;
private final FhirContext fhirContext;
private final FhirContext fhirContextForR5;

public Library getEffectiveDataRequirements(
Bundle bundleResource, String libraryName, String measureId, String accessToken) {
public Library getModuleDefinitionLibrary(
CqlLibraryDetails libraryDetails, boolean recursive, String accessToken) {
try {
log.info("Getting data requirements for measure: {}", measureId);
log.info(
"Getting Module Definition Library for library: {}", libraryDetails.getLibraryName());
URI uri =
UriComponentsBuilder.fromHttpUrl(
elmTranslatorClientConfig.getCqlElmServiceBaseUrl()
+ elmTranslatorClientConfig.getEffectiveDataRequirementsDataUri())
.queryParam("libraryName", libraryName)
.queryParam("measureId", measureId)
.queryParam("recursive", recursive)
.build()
.encode()
.toUri();

HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, accessToken);

String bundleStr =
fhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundleResource);

HttpEntity<String> bundleEntity = new HttpEntity<>(bundleStr, headers);
HttpEntity<CqlLibraryDetails> bundleEntity = new HttpEntity<>(libraryDetails, headers);
String effectiveDrJson =
elmTranslatorRestTemplate
.exchange(uri, HttpMethod.PUT, bundleEntity, String.class)
.getBody();
Library effectiveDataRequirements =
fhirContextForR5.newJsonParser().parseResource(Library.class, effectiveDrJson);
// effectiveDataRequirements needs to have fixed id: effective-data-requirements
effectiveDataRequirements.setId("effective-data-requirements");
return effectiveDataRequirements;
return fhirContextForR5.newJsonParser().parseResource(Library.class, effectiveDrJson);
} catch (Exception ex) {
log.error(
"An error occurred getting effective data requirements "
Expand All @@ -66,4 +58,13 @@ public Library getEffectiveDataRequirements(
ex);
}
}

public Library getEffectiveDataRequirements(
CqlLibraryDetails libraryDetails, boolean recursive, String accessToken) {
Library effectiveDataRequirements =
getModuleDefinitionLibrary(libraryDetails, recursive, accessToken);
// effectiveDataRequirements needs to have fixed id: effective-data-requirements
effectiveDataRequirements.setId("effective-data-requirements");
return effectiveDataRequirements;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gov.cms.madie.madiefhirservice.services;

import gov.cms.madie.madiefhirservice.constants.UriConstants;
import gov.cms.madie.madiefhirservice.dto.CqlLibraryDetails;
import gov.cms.madie.madiefhirservice.utils.BundleUtil;
import gov.cms.madie.madiefhirservice.utils.FhirResourceHelpers;
import gov.cms.madie.madiefhirservice.utils.ResourceUtils;
Expand All @@ -22,8 +23,10 @@
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
Expand All @@ -47,6 +50,7 @@ public Bundle createMeasureBundle(

org.hl7.fhir.r4.model.Measure measure =
measureTranslatorService.createFhirMeasureForMadieMeasure(madieMeasure);
Set<String> expressions = getExpressions(measure);

// Bundle entry for Measure resource
Bundle.BundleEntryComponent measureEntryComponent =
Expand All @@ -55,14 +59,21 @@ public Bundle createMeasureBundle(
new Bundle().setType(Bundle.BundleType.TRANSACTION).addEntry(measureEntryComponent);
// Bundle entries for all the library resources of a MADiE Measure
List<Bundle.BundleEntryComponent> libraryEntryComponents =
createBundleComponentsForLibrariesOfMadieMeasure(madieMeasure, bundleType, accessToken);
createBundleComponentsForLibrariesOfMadieMeasure(
expressions, madieMeasure, bundleType, accessToken);
libraryEntryComponents.forEach(bundle::addEntry);

if (BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT.equals(bundleType)) {
CqlLibraryDetails libraryDetails =
CqlLibraryDetails.builder()
.libraryName(madieMeasure.getCqlLibraryName())
.cql(madieMeasure.getCql())
.expressions(expressions)
.build();
// get effective DataRequirements
log.info("Getting effective data requirements for measure: {}", measure.getId());
org.hl7.fhir.r5.model.Library effectiveDataRequirements =
elmTranslatorClient.getEffectiveDataRequirements(
bundle, madieMeasure.getCqlLibraryName(), madieMeasure.getId(), accessToken);
elmTranslatorClient.getEffectiveDataRequirements(libraryDetails, true, accessToken);
// get human-readable for measure
String humanReadable =
humanReadableService.generateMeasureHumanReadable(
Expand All @@ -87,8 +98,12 @@ public Bundle createMeasureBundle(
* @return list of Library BundleEntryComponents
*/
public List<Bundle.BundleEntryComponent> createBundleComponentsForLibrariesOfMadieMeasure(
Measure madieMeasure, final String bundleType, final String accessToken) {
Library library = getMeasureLibraryResourceForMadieMeasure(madieMeasure);
Set<String> expressions,
Measure madieMeasure,
final String bundleType,
final String accessToken) {
Library library =
getMeasureLibraryResourceForMadieMeasure(expressions, madieMeasure, accessToken);
Bundle.BundleEntryComponent mainLibraryBundleComponent =
FhirResourceHelpers.getBundleEntryComponent(library, "Transaction");
Map<String, Library> includedLibraryMap = new HashMap<>();
Expand All @@ -104,14 +119,28 @@ public List<Bundle.BundleEntryComponent> createBundleComponentsForLibrariesOfMad
}

/**
* Creates Library resource for main library of MADiE Measure
* Creates a Library resource for main library of MADiE Measure
*
* @param madieMeasure instance of MADiE Measure
* @return Library
* @param expressions- measure populations, SDEs, Stratification
* @param madieMeasure
* @param accessToken
* @return library- r4 library
*/
public Library getMeasureLibraryResourceForMadieMeasure(Measure madieMeasure) {
public Library getMeasureLibraryResourceForMadieMeasure(
Set<String> expressions, Measure madieMeasure, String accessToken) {
log.info("Preparing Measure library resource for measure: {}", madieMeasure.getId());
CqlLibrary cqlLibrary = createCqlLibraryForMadieMeasure(madieMeasure);
return libraryTranslatorService.convertToFhirLibrary(cqlLibrary);
CqlLibraryDetails libraryDetails =
CqlLibraryDetails.builder()
.libraryName(cqlLibrary.getCqlLibraryName())
.cql(cqlLibrary.getCql())
.expressions(expressions)
.build();
Library library = libraryTranslatorService.convertToFhirLibrary(cqlLibrary);
org.hl7.fhir.r5.model.Library r5moduleDefinition =
elmTranslatorClient.getModuleDefinitionLibrary(libraryDetails, false, accessToken);
updateLibraryDataRequirements(library, r5moduleDefinition);
return library;
}

/**
Expand Down Expand Up @@ -157,4 +186,35 @@ private void addEffectiveDataRequirementsToMeasure(
.setValue(new Reference().setReference("#effective-data-requirements"));
measure.getExtension().add(extension);
}

private Set<String> getExpressions(org.hl7.fhir.r4.model.Measure r5Measure) {
Set<String> expressionSet = new HashSet<>();
r5Measure
.getSupplementalData()
.forEach(supData -> expressionSet.add(supData.getCriteria().getExpression()));
r5Measure
.getGroup()
.forEach(
groupMember -> {
groupMember
.getPopulation()
.forEach(
population -> expressionSet.add(population.getCriteria().getExpression()));
groupMember
.getStratifier()
.forEach(
stratifier -> expressionSet.add(stratifier.getCriteria().getExpression()));
});
return expressionSet;
}

private void updateLibraryDataRequirements(
org.hl7.fhir.r4.model.Library library,
org.hl7.fhir.r5.model.Library r5moduleDefinitionLibrary) {
var versionConvertor_40_50 = new VersionConvertor_40_50(new BaseAdvisor_40_50());
org.hl7.fhir.r4.model.Library r4moduleDefinitionLibrary =
(org.hl7.fhir.r4.model.Library)
versionConvertor_40_50.convertResource(r5moduleDefinitionLibrary);
library.setDataRequirement(r4moduleDefinitionLibrary.getDataRequirement());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import ca.uhn.fhir.context.FhirContext;
import gov.cms.madie.madiefhirservice.config.ElmTranslatorClientConfig;
import gov.cms.madie.madiefhirservice.dto.CqlLibraryDetails;
import gov.cms.madie.madiefhirservice.exceptions.CqlElmTranslationServiceException;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r5.model.Library;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -36,24 +36,19 @@ public class ElmTranslatorClientTest {

@InjectMocks private ElmTranslatorClient elmTranslatorClient;

private Bundle bundle;

@BeforeEach
void beforeEach() {
lenient().when(elmTranslatorClientConfig.getCqlElmServiceBaseUrl()).thenReturn("http://test");
lenient()
.when(elmTranslatorClientConfig.getEffectiveDataRequirementsDataUri())
.thenReturn("/geteffectivedatarequirements");
bundle = new Bundle().setType(Bundle.BundleType.TRANSACTION);
}

@Test
public void testGetEffectiveDataRequirementsThrowsException() {
assertThrows(
CqlElmTranslationServiceException.class,
() ->
elmTranslatorClient.getEffectiveDataRequirements(
bundle, "TEST_LIBRARYNAME", "TEST_TOKEN", "TEST_MEASURE_ID"));
() -> elmTranslatorClient.getEffectiveDataRequirements(null, false, "TEST_TOKEN"));
}

@Test
Expand All @@ -70,17 +65,15 @@ public void testGetEffectiveDataRequirementsSuccess() {
+ " } ]\n"
+ " }\n"
+ "}";
CqlLibraryDetails libraryDetails = CqlLibraryDetails.builder().libraryName("Test").build();
when(restTemplate.exchange(
any(URI.class), eq(HttpMethod.PUT), any(HttpEntity.class), any(Class.class)))
.thenReturn(ResponseEntity.ok(effectiveDR));

when(fhirContext.newJsonParser())
.thenReturn(FhirContext.forR4().newJsonParser())
.thenReturn(FhirContext.forR5().newJsonParser());
when(fhirContext.newJsonParser()).thenReturn(FhirContext.forR5().newJsonParser());

Library output =
elmTranslatorClient.getEffectiveDataRequirements(
bundle, "TEST_LIBRARY", "TEST_MEASURE_ID", "TEST_TOKEN");
elmTranslatorClient.getEffectiveDataRequirements(libraryDetails, false, "TEST_TOKEN");
assertThat(output.getId(), is(equalTo("effective-data-requirements")));
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package gov.cms.madie.madiefhirservice.services;

import com.fasterxml.jackson.core.JsonProcessingException;

import ca.uhn.fhir.rest.api.MethodOutcome;
import com.fasterxml.jackson.core.JsonProcessingException;
import gov.cms.madie.madiefhirservice.constants.UriConstants;
import gov.cms.madie.madiefhirservice.dto.CqlLibraryDetails;
import gov.cms.madie.madiefhirservice.exceptions.CqlLibraryNotFoundException;
import gov.cms.madie.madiefhirservice.utils.BundleUtil;
import gov.cms.madie.madiefhirservice.utils.MeasureTestHelper;
import gov.cms.madie.madiefhirservice.utils.ResourceFileUtil;
import gov.cms.madie.models.library.CqlLibrary;
import gov.cms.madie.models.measure.Measure;

import java.security.Principal;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Library;
Expand All @@ -23,13 +21,15 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.security.Principal;
import java.util.Map;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
Expand Down Expand Up @@ -84,7 +84,9 @@ public void testCreateMeasureBundle() {
.thenReturn(measure);

when(libraryTranslatorService.convertToFhirLibrary(any(CqlLibrary.class))).thenReturn(library);

when(elmTranslatorClient.getModuleDefinitionLibrary(
any(CqlLibraryDetails.class), anyBoolean(), anyString()))
.thenReturn(effectiveDataRequirements);
doAnswer(
invocation -> {
Object[] args = invocation.getArguments();
Expand Down Expand Up @@ -127,7 +129,7 @@ public void testCreateMeasureBundle() {
}

@Test
public void testCreateMeasureBundleWhenIncludedLibraryNotFoundInHapi() {
public void testCreateMeasureBundleWhenIncludedLibraryNotFound() {
when(measureTranslatorService.createFhirMeasureForMadieMeasure(madieMeasure))
.thenReturn(measure);

Expand All @@ -136,6 +138,9 @@ public void testCreateMeasureBundleWhenIncludedLibraryNotFoundInHapi() {
doThrow(new CqlLibraryNotFoundException("FHIRHelpers", "4.0.001"))
.when(libraryService)
.getIncludedLibraries(anyString(), any(), anyString(), anyString());
when(elmTranslatorClient.getModuleDefinitionLibrary(
any(CqlLibraryDetails.class), anyBoolean(), anyString()))
.thenReturn(effectiveDataRequirements);
Exception exception =
Assertions.assertThrows(
CqlLibraryNotFoundException.class,
Expand Down Expand Up @@ -169,8 +174,12 @@ public void testCreateMeasureBundleForExport() {
.getIncludedLibraries(anyString(), anyMap(), anyString(), anyString());

when(elmTranslatorClient.getEffectiveDataRequirements(
any(Bundle.class), anyString(), anyString(), anyString()))
any(CqlLibraryDetails.class), anyBoolean(), anyString()))
.thenReturn(effectiveDataRequirements);
when(elmTranslatorClient.getModuleDefinitionLibrary(
any(CqlLibraryDetails.class), anyBoolean(), anyString()))
.thenReturn(effectiveDataRequirements);

when(humanReadableService.generateMeasureHumanReadable(
any(Measure.class), any(Bundle.class), any(org.hl7.fhir.r5.model.Library.class)))
.thenReturn(humanReadable);
Expand Down

0 comments on commit 29d29ab

Please sign in to comment.