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

MAT-7964: Fixing issue where ELMJson translator wasn't updated on Ver… #768

Merged
Merged
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
36 changes: 3 additions & 33 deletions src/main/java/cms/gov/madie/measure/services/BundleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
import java.lang.reflect.InvocationTargetException;

import cms.gov.madie.measure.dto.PackageDto;
import cms.gov.madie.measure.exceptions.CqlElmTranslationErrorException;
import cms.gov.madie.measure.exceptions.InvalidResourceStateException;
import gov.cms.madie.models.measure.ElmJson;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException;

import cms.gov.madie.measure.exceptions.BundleOperationException;
Expand All @@ -27,8 +22,8 @@
public class BundleService {

private final FhirServicesClient fhirServicesClient;
private final ElmTranslatorClient elmTranslatorClient;
private final ExportRepository exportRepository;
private final ElmToJsonService elmToJsonService;

/**
* Get the bundle for measure. For draft measure- generate bundle because for draft measure,
Expand All @@ -42,7 +37,7 @@ public String bundleMeasure(Measure measure, String accessToken, String bundleTy
// for draft measures
if (measure.getMeasureMetaData().isDraft()) {
try {
retrieveElmJson(measure, accessToken);
elmToJsonService.retrieveElmJson(measure, accessToken);
return fhirServicesClient.getMeasureBundle(measure, accessToken, bundleType);
} catch (RestClientException | IllegalArgumentException ex) {
log.error("An error occurred while bundling measure {}", measure.getId(), ex);
Expand All @@ -66,7 +61,7 @@ public PackageDto getMeasureExport(Measure measure, String accessToken) {
// for draft measures
if (measure.getMeasureMetaData().isDraft()) {
try {
retrieveElmJson(measure, accessToken);
elmToJsonService.retrieveElmJson(measure, accessToken);
return PackageDto.builder()
.fromStorage(false)
.exportPackage(fhirServicesClient.getMeasureBundleExport(measure, accessToken))
Expand Down Expand Up @@ -105,29 +100,4 @@ public PackageDto getMeasureExport(Measure measure, String accessToken) {
throw new BundleOperationException("Measure", measure.getId(), ex);
}
}

protected void retrieveElmJson(Measure measure, String accessToken) {
if (StringUtils.isBlank(measure.getCql())) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since there is no associated CQL.");
}

if (measure.isCqlErrors()) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since CQL errors exist.");
}

if (CollectionUtils.isEmpty(measure.getGroups())) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since there are no associated population criteria.");
}

final ElmJson elmJson =
elmTranslatorClient.getElmJson(measure.getCql(), measure.getModel(), accessToken);
if (elmTranslatorClient.hasErrors(elmJson)) {
throw new CqlElmTranslationErrorException(measure.getMeasureName());
}
measure.setElmJson(elmJson.getJson());
measure.setElmXml(elmJson.getXml());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cms.gov.madie.measure.services;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import cms.gov.madie.measure.exceptions.CqlElmTranslationErrorException;
import cms.gov.madie.measure.exceptions.InvalidResourceStateException;
import gov.cms.madie.models.measure.ElmJson;
import gov.cms.madie.models.measure.Measure;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
@Service
public class ElmToJsonService {
private final ElmTranslatorClient elmTranslatorClient;

protected void retrieveElmJson(Measure measure, String accessToken) {
if (StringUtils.isBlank(measure.getCql())) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since there is no associated CQL.");
}

if (measure.isCqlErrors()) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since CQL errors exist.");
}

if (CollectionUtils.isEmpty(measure.getGroups())) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since there are no associated population criteria.");
}

final ElmJson elmJson =
elmTranslatorClient.getElmJson(measure.getCql(), measure.getModel(), accessToken);
if (elmTranslatorClient.hasErrors(elmJson)) {
throw new CqlElmTranslationErrorException(measure.getMeasureName());
}
measure.setElmJson(elmJson.getJson());
measure.setElmXml(elmJson.getXml());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,18 @@ public void validateGroups(Measure measure) {
}

if (measure.getMeasureMetaData() != null && measure.getMeasureMetaData().isDraft()) {
measure.getGroups().forEach(
group -> {
if (StringUtils.isBlank(group.getImprovementNotation())) {
throw new InvalidResourceStateException(
"Measure", measure.getId(), "since there is at least one Population Criteria " +
"with no improvement notation.");
}
}
);
measure
.getGroups()
.forEach(
group -> {
if (StringUtils.isBlank(group.getImprovementNotation())) {
throw new InvalidResourceStateException(
"Measure",
measure.getId(),
"since there is at least one Population Criteria "
+ "with no improvement notation.");
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class VersionService {
private final QdmPackageService qdmPackageService;
private final ExportService exportService;
private final TestCaseSequenceService sequenceService;
private final AppConfigService appConfigService;
private final ElmToJsonService elmToJsonService;

public enum VersionValidationResult {
VALID,
Expand Down Expand Up @@ -111,10 +111,12 @@ private Measure versionQdmMeasure(
*/
private Measure versionFhirMeasure(
String versionType, String username, String accessToken, Measure measure) throws Exception {
elmToJsonService.retrieveElmJson(measure, accessToken);
Measure upversionedMeasure = version(versionType, username, measure);
var measureBundle =
fhirServicesClient.getMeasureBundle(upversionedMeasure, accessToken, "export");
saveMeasureBundle(upversionedMeasure, measureBundle, username);

return applyMeasureVersion(versionType, username, upversionedMeasure);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@

import cms.gov.madie.measure.dto.PackageDto;
import cms.gov.madie.measure.exceptions.BundleOperationException;
import cms.gov.madie.measure.exceptions.CqlElmTranslationErrorException;
import cms.gov.madie.measure.exceptions.CqlElmTranslationServiceException;
import cms.gov.madie.measure.exceptions.InvalidResourceStateException;
import cms.gov.madie.measure.repositories.ExportRepository;
import cms.gov.madie.measure.utils.ResourceUtil;
import gov.cms.madie.models.common.ModelType;
import gov.cms.madie.models.measure.ElmJson;
import gov.cms.madie.models.measure.Export;
import gov.cms.madie.models.measure.Group;
import gov.cms.madie.models.measure.Measure;
Expand Down Expand Up @@ -54,6 +50,7 @@ class BundleServiceTest implements ResourceUtil {
@Mock private FhirServicesClient fhirServicesClient;
@Mock private ElmTranslatorClient elmTranslatorClient;
@Mock private ExportRepository exportRepository;
@Mock private ElmToJsonService elmToJsonService;

@InjectMocks private BundleService bundleService;

Expand Down Expand Up @@ -104,61 +101,22 @@ void testBundleMeasureReturnsNullForNullMeasure() {
assertThat(output, is(nullValue()));
}

@Test
void testBundleMeasureWhenThereIsNoCql() {
measure.setCql(null);
assertThrows(
InvalidResourceStateException.class,
() -> bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation"));
}

@Test
void testBundleMeasureWhenThereAreCqlErrors() {
measure.setCqlErrors(true);
assertThrows(
InvalidResourceStateException.class,
() -> bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation"));
}

@Test
void testBundleMeasureThrowsOperationException() {
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenReturn(ElmJson.builder().json("{}").xml("<></>").build());

when(fhirServicesClient.getMeasureBundle(any(Measure.class), anyString(), anyString()))
.thenThrow(new HttpClientErrorException(HttpStatus.FORBIDDEN));
assertThrows(
BundleOperationException.class,
() -> bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation"));
}

@Test
void testBundleMeasureThrowsCqlElmTranslationServiceException() {
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenThrow(
new CqlElmTranslationServiceException(
"There was an error calling CQL-ELM translation service", new Exception()));
assertThrows(
CqlElmTranslationServiceException.class,
() -> bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation"));
}

@Test
void testBundleMeasureThrowsCqlElmTranslatorExceptionWithErrors() {
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenReturn(ElmJson.builder().json("{}").xml("<></>").build());
when(elmTranslatorClient.hasErrors(any(ElmJson.class))).thenReturn(true);
assertThrows(
CqlElmTranslationErrorException.class,
() -> bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation"));
}

@Test
void testBundleMeasureReturnsBundleStringForDraftMeasure() {
final String json = "{\"message\": \"GOOD JSON\"}";
when(fhirServicesClient.getMeasureBundle(any(Measure.class), anyString(), anyString()))
.thenReturn(json);
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenReturn(ElmJson.builder().json("{}").xml("<></>").build());

assertThat(measure.getMeasureMetaData().isDraft(), is(equalTo(true)));
String output = bundleService.bundleMeasure(measure, "Bearer TOKEN", "calculation");
assertThat(output, is(equalTo(json)));
Expand Down Expand Up @@ -256,8 +214,7 @@ void testExportBundleMeasureForDraftMeasure() throws IOException {
.developers(List.of(Organization.builder().name("ICF").build()))
.build());
measure.setModel("QI-Core v4.1.1");
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenReturn(ElmJson.builder().json("{}").xml("<></>").build());

// doThrow(new
// HttpClientErrorException(HttpStatus.FORBIDDEN)).when(fhirServicesClient).getMeasureBundleExport(any(Measure.class), eq("")))
byte[] exportBytes = "TEST".getBytes();
Expand All @@ -283,8 +240,7 @@ void testExportBundleMeasureForDraftMeasureThrowsException() throws IOException
.developers(List.of(Organization.builder().name("ICF").build()))
.build());
measure.setModel("QI-Core v4.1.1");
when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenReturn(ElmJson.builder().json("{}").xml("<></>").build());

doThrow(new HttpClientErrorException(HttpStatus.FORBIDDEN))
.when(fhirServicesClient)
.getMeasureBundleExport(any(Measure.class), eq("Bearer TOKEN"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package cms.gov.madie.measure.services;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import cms.gov.madie.measure.exceptions.CqlElmTranslationServiceException;
import cms.gov.madie.measure.exceptions.InvalidResourceStateException;
import cms.gov.madie.measure.utils.ResourceUtil;
import gov.cms.madie.models.common.ModelType;
import gov.cms.madie.models.common.Version;
import gov.cms.madie.models.measure.Group;
import gov.cms.madie.models.measure.Measure;
import gov.cms.madie.models.measure.MeasureGroupTypes;
import gov.cms.madie.models.measure.MeasureMetaData;
import gov.cms.madie.models.measure.Population;
import gov.cms.madie.models.measure.PopulationType;

@ExtendWith(MockitoExtension.class)
class ElmToJsonServiceTest implements ResourceUtil {

@Mock private ElmTranslatorClient elmTranslatorClient;

@InjectMocks private ElmToJsonService elmToJsonService;

private Measure measure;

@BeforeEach
public void setUp() {
Group group =
Group.builder()
.id("xyz-p12r-12ert")
.populationBasis("Encounter")
.measureGroupTypes(List.of(MeasureGroupTypes.PROCESS))
.populations(
List.of(
new Population(
"id-1", PopulationType.INITIAL_POPULATION, "FactorialOfFive", null, null)))
.groupDescription("Description")
.scoringUnit("test-scoring-unit")
.build();

List<Group> groups = new ArrayList<>();
groups.add(group);
String elmJson = getData("/test_elm.json");
MeasureMetaData metaData = MeasureMetaData.builder().draft(true).build();
measure =
Measure.builder()
.active(true)
.id("xyz-p13r-13ert")
.cql("test cql")
.model(ModelType.QDM_5_6.getValue())
.cqlErrors(false)
.elmJson(elmJson)
.measureSetId("IDIDID")
.measureName("MSR01")
.version(new Version(0, 0, 1))
.groups(groups)
.measureMetaData(metaData)
.createdAt(Instant.now())
.createdBy("test user")
.lastModifiedAt(Instant.now())
.lastModifiedBy("test user")
.build();
}

@Test
void testBundleMeasureThrowsCqlElmTranslationServiceException() {

when(elmTranslatorClient.getElmJson(anyString(), anyString(), anyString()))
.thenThrow(
new CqlElmTranslationServiceException(
"There was an error calling CQL-ELM translation service", new Exception()));
assertThrows(
CqlElmTranslationServiceException.class,
() -> elmToJsonService.retrieveElmJson(measure, "calculation"));
}

@Test
void testBundleMeasureWhenThereAreCqlErrors() {
measure.setCqlErrors(true);
assertThrows(
InvalidResourceStateException.class,
() -> elmToJsonService.retrieveElmJson(measure, "calculation"));
}

@Test
void testBundleMeasureWhenThereIsNoCql() {
measure.setCql(null);
assertThrows(
InvalidResourceStateException.class,
() -> elmToJsonService.retrieveElmJson(measure, "calculation"));
}
}
Loading
Loading