diff --git a/src/main/java/gov/cms/madie/madiefhirservice/exceptions/CqlLibraryNotFoundException.java b/src/main/java/gov/cms/madie/madiefhirservice/exceptions/CqlLibraryNotFoundException.java new file mode 100644 index 00000000..d6d12b96 --- /dev/null +++ b/src/main/java/gov/cms/madie/madiefhirservice/exceptions/CqlLibraryNotFoundException.java @@ -0,0 +1,18 @@ +package gov.cms.madie.madiefhirservice.exceptions; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) +@Slf4j +public class CqlLibraryNotFoundException extends RuntimeException { + + private static final String NOT_FOUND_BY_FIND_DATA = + "Cannot find a CQL Library with name: %s, version: %s"; + + public CqlLibraryNotFoundException(String name, String version) { + super(String.format(NOT_FOUND_BY_FIND_DATA, name, version)); + log.error(getMessage()); + } +} diff --git a/src/main/java/gov/cms/madie/madiefhirservice/exceptions/MissingCqlException.java b/src/main/java/gov/cms/madie/madiefhirservice/exceptions/MissingCqlException.java new file mode 100644 index 00000000..ffc6b5e7 --- /dev/null +++ b/src/main/java/gov/cms/madie/madiefhirservice/exceptions/MissingCqlException.java @@ -0,0 +1,17 @@ +package gov.cms.madie.madiefhirservice.exceptions; + +import gov.cms.madie.models.library.CqlLibrary; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) +@Slf4j +public class MissingCqlException extends RuntimeException { + private static final String NONE_FOUND = "Cannot find CQL for library name: %s, version: %s"; + + public MissingCqlException(CqlLibrary library) { + super(String.format(NONE_FOUND, library.getCqlLibraryName(), library.getVersion())); + log.warn(getMessage()); + } +} diff --git a/src/main/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryController.java b/src/main/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryController.java deleted file mode 100644 index 2dd68580..00000000 --- a/src/main/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryController.java +++ /dev/null @@ -1,60 +0,0 @@ -package gov.cms.madie.madiefhirservice.resources; - -import gov.cms.madie.madiefhirservice.services.LibraryService; -import gov.cms.madie.models.library.CqlLibrary; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.hl7.fhir.r4.model.Library; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@Slf4j -@RestController -@RequestMapping(path = "/fhir/libraries") -@Tag(name = "HAPI-FHIR-Library-Controller", description = "API for Library resources in HAPI") -@RequiredArgsConstructor -public class HapiFhirLibraryController { - - private final LibraryService libraryService; - - @Operation( - summary = "Get Library's CQL from HAPI FHIR.", - description = - "Fetches Library resource from HAPI FHIR and returns cql string," - + " typically used by CQL-ELM Translator") - @GetMapping("/cql") - public String getLibraryCql(@RequestParam String name, @RequestParam String version) { - return libraryService.getLibraryCql(name, version); - } - - @Operation( - summary = "Get Library's CQL, ELM JSON and ELM XML from HAPI FHIR", - description = - "Fetches Library resource from HAPI FHIR and returns cql string, ELM JSON string," - + " and ELM XML string.") - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public CqlLibrary getLibraryResourceAsCqlLibrary( - @RequestParam String name, @RequestParam String version) { - return libraryService.getLibraryResourceAsCqlLibrary(name, version); - } - - @Operation( - summary = "Creates new Library Resource in HAPI FHIR. ", - description = - "Creates the new hapi FHIR Library Resource from MADiE Library " - + "if the Library with same name and version does not exists in HAPI FHIR.") - @PostMapping("/create") - public ResponseEntity createLibraryResource(@RequestBody CqlLibrary cqlLibrary) { - Library library = libraryService.createLibraryResourceForCqlLibrary(cqlLibrary); - return ResponseEntity.status(HttpStatus.CREATED).body(library.getUrl()); - } -} diff --git a/src/main/java/gov/cms/madie/madiefhirservice/services/CqlLibraryService.java b/src/main/java/gov/cms/madie/madiefhirservice/services/CqlLibraryService.java new file mode 100644 index 00000000..ab444181 --- /dev/null +++ b/src/main/java/gov/cms/madie/madiefhirservice/services/CqlLibraryService.java @@ -0,0 +1,69 @@ +package gov.cms.madie.madiefhirservice.services; + +import gov.cms.madie.madiefhirservice.exceptions.CqlLibraryNotFoundException; +import gov.cms.madie.models.library.CqlLibrary; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CqlLibraryService { + + private final RestTemplate restTemplate; + + @Value("${madie.library.service.baseUrl}") + private String madieLibraryService; + + @Value("${madie.library.service.versioned.uri}") + private String librariesVersionedUri; + + public CqlLibrary getLibrary(String name, String version, String accessToken) { + URI uri = buildMadieLibraryServiceUri(name, version); + log.debug("Getting Madie library: {} ", uri); + + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", accessToken); + + ResponseEntity responseEntity = + restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), CqlLibrary.class); + + if (responseEntity.getStatusCode().is2xxSuccessful()) { + if (responseEntity.hasBody()) { + log.debug("Retrieved a valid cqlPayload"); + return responseEntity.getBody(); + } else { + log.error("Cannot find Cql payload in the response"); + return null; + } + } else if (responseEntity.getStatusCode().equals(HttpStatus.NOT_FOUND)) { + throw new CqlLibraryNotFoundException(name, version); + } else if (responseEntity.getStatusCode().equals(HttpStatus.CONFLICT)) { + log.error( + "Multiple libraries found with name: {}, version: {}, but only one was expected", + name, + version); + } + return null; + } + + private URI buildMadieLibraryServiceUri(String name, String version) { + return UriComponentsBuilder.fromHttpUrl(madieLibraryService + librariesVersionedUri) + .queryParam("name", name) + .queryParam("version", version) + .build() + .encode() + .toUri(); + } +} diff --git a/src/main/java/gov/cms/madie/madiefhirservice/services/LibraryService.java b/src/main/java/gov/cms/madie/madiefhirservice/services/LibraryService.java index 352956cc..23005a7d 100644 --- a/src/main/java/gov/cms/madie/madiefhirservice/services/LibraryService.java +++ b/src/main/java/gov/cms/madie/madiefhirservice/services/LibraryService.java @@ -2,89 +2,38 @@ import gov.cms.madie.madiefhirservice.cql.LibraryCqlVisitor; import gov.cms.madie.madiefhirservice.cql.LibraryCqlVisitorFactory; -import gov.cms.madie.madiefhirservice.exceptions.DuplicateLibraryException; -import gov.cms.madie.madiefhirservice.exceptions.HapiLibraryNotFoundException; import gov.cms.madie.madiefhirservice.exceptions.LibraryAttachmentNotFoundException; -import gov.cms.madie.madiefhirservice.hapi.HapiFhirServer; +import gov.cms.madie.madiefhirservice.exceptions.MissingCqlException; +import gov.cms.madie.madiefhirservice.utils.BundleUtil; import gov.cms.madie.models.library.CqlLibrary; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.r4.model.Attachment; -import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Library; import org.hl7.fhir.r4.model.Narrative; import org.hl7.fhir.r4.model.Narrative.NarrativeStatus; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import java.util.List; import java.util.Map; -import java.util.Optional; @Service @Slf4j @RequiredArgsConstructor public class LibraryService { - private final HapiFhirServer hapiFhirServer; + + private final CqlLibraryService cqlLibraryService; private final LibraryTranslatorService libraryTranslatorService; private final LibraryCqlVisitorFactory libCqlVisitorFactory; private final HumanReadableService humanReadableService; - public String getLibraryCql(String name, String version) { - - Bundle bundle = hapiFhirServer.fetchLibraryBundleByNameAndVersion(name, version); - if (bundle.hasEntry()) { - return processBundle(name, version, bundle); - } else { - throw new HapiLibraryNotFoundException(name, version); - } - } - - public CqlLibrary getLibraryResourceAsCqlLibrary(String name, String version) { - Bundle bundle = hapiFhirServer.fetchLibraryBundleByNameAndVersion(name, version); - - if (bundle.hasEntry()) { - Optional optional = - hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class); - return optional - .map(libraryTranslatorService::convertToCqlLibrary) - .orElseThrow(() -> new HapiLibraryNotFoundException(name, version)); - } else { - throw new HapiLibraryNotFoundException(name, version); - } - } - - public boolean isLibraryResourcePresent(String name, String version) { - - Bundle bundle = hapiFhirServer.fetchLibraryBundleByNameAndVersion(name, version); - if (!bundle.hasEntry()) { - return false; - } - - return hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class).isPresent(); - } - - private String processBundle(String name, String version, Bundle bundle) { - - Optional optional = hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class); - if (optional.isPresent()) { - return getCqlFromHapiLibrary(optional.get()); - } else { - throw new HapiLibraryNotFoundException(name, version); - } - } - - private String getCqlFromHapiLibrary(Library library) { - - List attachments = library.getContent(); - - if (CollectionUtils.isEmpty(attachments)) { - throw new LibraryAttachmentNotFoundException(library); + public String getLibraryCql(String name, String version, final String accessToken) { + CqlLibrary library = cqlLibraryService.getLibrary(name, version, accessToken); + if (StringUtils.isBlank(library.getCql())) { + throw new MissingCqlException(library); } - Attachment cql = findCqlAttachment(library); - return new String(cql.getData()); + return cqlLibraryService.getLibrary(name, version, accessToken).getCql(); } private Attachment findCqlAttachment(Library library) { @@ -94,23 +43,19 @@ private Attachment findCqlAttachment(Library library) { .orElseThrow(() -> new LibraryAttachmentNotFoundException(library, "text/cql")); } - public Library createLibraryResourceForCqlLibrary(CqlLibrary cqlLibrary) { - boolean isLibraryPresent = - isLibraryResourcePresent( - cqlLibrary.getCqlLibraryName(), cqlLibrary.getVersion().toString()); - - if (isLibraryPresent) { - throw new DuplicateLibraryException( - cqlLibrary.getCqlLibraryName(), cqlLibrary.getVersion().toString()); - } - + public Library cqlLibraryToFhirLibrary(CqlLibrary cqlLibrary, final String bundleType) { Library library = libraryTranslatorService.convertToFhirLibrary(cqlLibrary, null); - library.setText(createLibraryNarrativeText(library)); - hapiFhirServer.createResource(library); + if (BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT.equals(bundleType)) { + library.setText(createLibraryNarrativeText(library)); + } return library; } - public void getIncludedLibraries(String cql, Map libraryMap) { + public void getIncludedLibraries( + String cql, + Map libraryMap, + final String bundleType, + final String accessToken) { if (StringUtils.isBlank(cql) || libraryMap == null) { log.error("Invalid method arguments provided to getIncludedLibraries"); throw new IllegalArgumentException("Please provide valid arguments."); @@ -118,21 +63,16 @@ public void getIncludedLibraries(String cql, Map libraryMap) { LibraryCqlVisitor visitor = libCqlVisitorFactory.visit(cql); for (Pair libraryNameValuePair : visitor.getIncludedLibraries()) { - Optional optionalLibrary = - hapiFhirServer.fetchHapiLibrary( - libraryNameValuePair.getLeft(), libraryNameValuePair.getRight()); - if (optionalLibrary.isPresent()) { - Library library = optionalLibrary.get(); - String key = library.getName() + library.getVersion(); - if (!libraryMap.containsKey(key)) { - libraryMap.put(key, library); - } - Attachment attachment = findCqlAttachment(library); - getIncludedLibraries(new String(attachment.getData()), libraryMap); - } else { - throw new HapiLibraryNotFoundException( - libraryNameValuePair.getLeft(), libraryNameValuePair.getRight()); + CqlLibrary cqlLibrary = + cqlLibraryService.getLibrary( + libraryNameValuePair.getLeft(), libraryNameValuePair.getRight(), accessToken); + Library library = cqlLibraryToFhirLibrary(cqlLibrary, bundleType); + String key = library.getName() + library.getVersion(); + if (!libraryMap.containsKey(key)) { + libraryMap.put(key, library); } + Attachment attachment = findCqlAttachment(library); + getIncludedLibraries(new String(attachment.getData()), libraryMap, bundleType, accessToken); } } diff --git a/src/main/java/gov/cms/madie/madiefhirservice/services/MeasureBundleService.java b/src/main/java/gov/cms/madie/madiefhirservice/services/MeasureBundleService.java index 570f9d00..44221938 100644 --- a/src/main/java/gov/cms/madie/madiefhirservice/services/MeasureBundleService.java +++ b/src/main/java/gov/cms/madie/madiefhirservice/services/MeasureBundleService.java @@ -45,7 +45,8 @@ public class MeasureBundleService { */ public Bundle createMeasureBundle( Measure madieMeasure, Principal principal, String bundleType, String accessToken) { - log.info("Generating measure bundle for measure {}", madieMeasure.getId()); + log.info( + "Generating measure bundle of type [{}] for measure {}", bundleType, madieMeasure.getId()); madieMeasure.setCql(CqlFormatter.formatCql(madieMeasure.getCql(), principal)); org.hl7.fhir.r4.model.Measure measure = @@ -57,7 +58,7 @@ public Bundle createMeasureBundle( new Bundle().setType(Bundle.BundleType.TRANSACTION).addEntry(measureEntryComponent); // Bundle entries for all the library resources of a MADiE Measure List libraryEntryComponents = - createBundleComponentsForLibrariesOfMadieMeasure(madieMeasure); + createBundleComponentsForLibrariesOfMadieMeasure(madieMeasure, bundleType, accessToken); libraryEntryComponents.forEach(bundle::addEntry); if (BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT.equals(bundleType)) { @@ -90,11 +91,12 @@ public Bundle createMeasureBundle( * @return list of Library BundleEntryComponents */ public List createBundleComponentsForLibrariesOfMadieMeasure( - Measure madieMeasure) { + Measure madieMeasure, final String bundleType, final String accessToken) { Library library = getMeasureLibraryResourceForMadieMeasure(madieMeasure); Bundle.BundleEntryComponent mainLibraryBundleComponent = getBundleEntryComponent(library); Map includedLibraryMap = new HashMap<>(); - libraryService.getIncludedLibraries(madieMeasure.getCql(), includedLibraryMap); + libraryService.getIncludedLibraries( + madieMeasure.getCql(), includedLibraryMap, bundleType, accessToken); List libraryBundleComponents = includedLibraryMap.values().stream() .map(this::getBundleEntryComponent) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8837b097..c9af1951 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,6 +14,11 @@ madie: service: base-url: ${ELM_TRANSLATOR_SERVICE_URL:http://localhost:8084/api} effective-data-requirements-uri: /effective-data-requirements + library: + service: + baseUrl: ${CQL_LIBRARY_SERVICE_URL:http://localhost:8082/api} + versioned: + uri: /cql-libraries/versioned #springdoc: # swagger-ui: # path: ${SWAGGER_PATH:/swagger} diff --git a/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerMvcTest.java b/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerMvcTest.java deleted file mode 100644 index 304ab079..00000000 --- a/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerMvcTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package gov.cms.madie.madiefhirservice.resources; - -import gov.cms.madie.madiefhirservice.exceptions.HapiLibraryNotFoundException; -import gov.cms.madie.madiefhirservice.services.LibraryService; -import gov.cms.madie.models.library.CqlLibrary; -import gov.cms.madie.models.common.Version; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.web.servlet.MockMvc; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest({HapiFhirLibraryController.class}) -public class HapiFhirLibraryControllerMvcTest { - - private static final String TEST_USER_ID = "test-okta-user-id-123"; - - @MockBean private LibraryService libraryService; - - @Autowired private MockMvc mockMvc; - - @Test - public void testGetLibraryResourceAsCqlLibraryReturnsNotFound() throws Exception { - when(libraryService.getLibraryResourceAsCqlLibrary(anyString(), anyString())) - .thenThrow(new HapiLibraryNotFoundException("Test", "1.0.000")); - mockMvc - .perform( - get("/fhir/libraries?name=Test&version=1.0.000").with(user(TEST_USER_ID)).with(csrf())) - .andExpect(status().isNotFound()); - } - - @Test - public void testGetLibraryResourceAsCqlLibraryReturnsLibrary() throws Exception { - CqlLibrary library = - CqlLibrary.builder().cqlLibraryName("Test").version(Version.parse("1.0.000")).build(); - when(libraryService.getLibraryResourceAsCqlLibrary(anyString(), anyString())) - .thenReturn(library); - mockMvc - .perform( - get("/fhir/libraries?name=Test&version=1.0.000").with(user(TEST_USER_ID)).with(csrf())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.cqlLibraryName").value("Test")) - .andExpect(jsonPath("$.version").value("1.0.000")); - } -} diff --git a/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerTest.java b/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerTest.java deleted file mode 100644 index 3a9061d6..00000000 --- a/src/test/java/gov/cms/madie/madiefhirservice/resources/HapiFhirLibraryControllerTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package gov.cms.madie.madiefhirservice.resources; - -import gov.cms.madie.madiefhirservice.exceptions.HapiLibraryNotFoundException; -import gov.cms.madie.madiefhirservice.services.LibraryService; -import gov.cms.madie.madiefhirservice.utils.LibraryHelper; -import gov.cms.madie.madiefhirservice.utils.ResourceFileUtil; -import gov.cms.madie.models.library.CqlLibrary; -import org.hl7.fhir.r4.model.Library; -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 org.springframework.http.ResponseEntity; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class HapiFhirLibraryControllerTest implements LibraryHelper, ResourceFileUtil { - - @InjectMocks HapiFhirLibraryController hapiFhirLibraryController; - - @Mock LibraryService libraryService; - - @Test - void findLibraryHapiCql() { - when(libraryService.getLibraryCql("TestLibrary", "1.0.0")).thenReturn("Cql From HAPI FHIR"); - assertEquals( - "Cql From HAPI FHIR", hapiFhirLibraryController.getLibraryCql("TestLibrary", "1.0.0")); - verifyNoMoreInteractions(libraryService); - } - - @Test - void createLibraryResource() { - String cql = getStringFromTestResource("/test-cql/EXM124v7QICore4.cql"); - CqlLibrary cqlLibrary = createCqlLibrary(cql); - Library library = new Library(); - library.setName(cqlLibrary.getCqlLibraryName()); - library.setVersion(cqlLibrary.getVersion().toString()); - library.setUrl("test-url"); - when(libraryService.createLibraryResourceForCqlLibrary(any(CqlLibrary.class))) - .thenReturn(library); - ResponseEntity response = hapiFhirLibraryController.createLibraryResource(cqlLibrary); - verifyNoMoreInteractions(libraryService); - assertNotNull(response.getBody()); - assertEquals(library.getUrl(), response.getBody()); - } - - @Test - void testGetLibraryResourceAsCqlLibraryThrowsException() { - when(libraryService.getLibraryResourceAsCqlLibrary(anyString(), anyString())) - .thenThrow(new HapiLibraryNotFoundException("Test", "1.0.000")); - assertThrows( - HapiLibraryNotFoundException.class, - () -> hapiFhirLibraryController.getLibraryResourceAsCqlLibrary("Test", "1.0.000")); - } - - @Test - void testGetLibraryResourceAsCqlLibraryReturnsLibrary() { - CqlLibrary library = new CqlLibrary(); - when(libraryService.getLibraryResourceAsCqlLibrary(anyString(), anyString())) - .thenReturn(library); - CqlLibrary output = hapiFhirLibraryController.getLibraryResourceAsCqlLibrary("Test", "1.0.000"); - assertThat(output, is(equalTo(library))); - } -} diff --git a/src/test/java/gov/cms/madie/madiefhirservice/services/LibraryServiceTest.java b/src/test/java/gov/cms/madie/madiefhirservice/services/LibraryServiceTest.java index 0cbafc1c..3154b97a 100644 --- a/src/test/java/gov/cms/madie/madiefhirservice/services/LibraryServiceTest.java +++ b/src/test/java/gov/cms/madie/madiefhirservice/services/LibraryServiceTest.java @@ -1,13 +1,12 @@ package gov.cms.madie.madiefhirservice.services; -import ca.uhn.fhir.rest.api.MethodOutcome; import gov.cms.madie.madiefhirservice.cql.LibraryCqlVisitorFactory; -import gov.cms.madie.madiefhirservice.exceptions.DuplicateLibraryException; -import gov.cms.madie.madiefhirservice.exceptions.HapiLibraryNotFoundException; -import gov.cms.madie.madiefhirservice.exceptions.LibraryAttachmentNotFoundException; +import gov.cms.madie.madiefhirservice.exceptions.*; import gov.cms.madie.madiefhirservice.hapi.HapiFhirServer; +import gov.cms.madie.madiefhirservice.utils.BundleUtil; import gov.cms.madie.madiefhirservice.utils.LibraryHelper; import gov.cms.madie.madiefhirservice.utils.ResourceFileUtil; +import gov.cms.madie.models.common.Version; import gov.cms.madie.models.library.CqlLibrary; import org.hl7.fhir.r4.model.Attachment; import org.hl7.fhir.r4.model.Bundle; @@ -22,7 +21,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -34,9 +32,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -44,7 +39,7 @@ class LibraryServiceTest implements LibraryHelper, ResourceFileUtil { @InjectMocks private LibraryService libraryService; - @Mock private HapiFhirServer hapiFhirServer; + @Mock private CqlLibraryService cqlLibraryService; @Mock private LibraryTranslatorService libraryTranslatorService; @Mock private LibraryCqlVisitorFactory libCqlVisitorFactory; @Mock private HumanReadableService humanReadableService; @@ -63,162 +58,50 @@ void buildLibraryBundle() { } @Test - void testSuccessfullyFindCqlInHapiLibrary() { - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) - .thenReturn(Optional.of(fhirHelpersLibrary)); - String testCql = libraryService.getLibraryCql("FHIRHelpers", "4.0.001"); + void testSuccessfullyFindCqlInCqlLibrary() { + when(cqlLibraryService.getLibrary(anyString(), anyString(), anyString())) + .thenReturn( + CqlLibrary.builder() + .cqlLibraryName("FHIRHelpers") + .version(Version.builder().major(4).minor(0).revisionNumber(1).build()) + .cql("Valid FHIRHelpers CQL here") + .build()); + String testCql = libraryService.getLibraryCql("FHIRHelpers", "4.0.001", "TOKEN"); assertTrue(testCql.contains("FHIRHelpers")); } @Test void testLibraryBundleHasNoEntry() { - bundle.setEntry(null); - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); + when(cqlLibraryService.getLibrary(anyString(), anyString(), anyString())) + .thenThrow(new CqlLibraryNotFoundException("FHIRHelpers", "4.0.001")); Throwable exception = assertThrows( - HapiLibraryNotFoundException.class, - () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001")); + CqlLibraryNotFoundException.class, + () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001", "TOKEN")); assertEquals( - "Cannot find a Hapi Fhir Library with name: FHIRHelpers, version: 4.0.001", - exception.getMessage()); - } - - @Test - void testLibraryIsNotReturnedByHapi() { - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) - .thenReturn(Optional.empty()); - - Throwable exception = - assertThrows( - HapiLibraryNotFoundException.class, - () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001")); - assertEquals( - "Cannot find a Hapi Fhir Library with name: FHIRHelpers, version: 4.0.001", - exception.getMessage()); - } - - @Test - void testLibraryDoesNotContainAnyAttachments() { - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) - .thenReturn(Optional.of(fhirHelpersLibrary)); - - fhirHelpersLibrary.setContent(null); - Throwable exception = - assertThrows( - LibraryAttachmentNotFoundException.class, - () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001")); - assertEquals( - "Cannot find any attachment for library name: FHIRHelpers, version: 4.0.001", + "Cannot find a CQL Library with name: FHIRHelpers, version: 4.0.001", exception.getMessage()); } @Test void testLibraryDoesNotContainCqlTextAttachment() { - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) - .thenReturn(Optional.of(fhirHelpersLibrary)); - - fhirHelpersLibrary.getContent().get(0).setContentType("text/test"); + // when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) + // .thenReturn(bundle); + // when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) + // .thenReturn(Optional.of(fhirHelpersLibrary)); + + when(cqlLibraryService.getLibrary(anyString(), anyString(), anyString())) + .thenReturn( + CqlLibrary.builder() + .cqlLibraryName("FHIRHelpers") + .version(Version.builder().major(4).minor(0).revisionNumber(1).build()) + .build()); Throwable exception = assertThrows( - LibraryAttachmentNotFoundException.class, - () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001")); - assertEquals( - "Cannot find attachment type text/cql for library name: FHIRHelpers, version: 4.0.001", - exception.getMessage()); - } - - @Test - void createLibraryResourceForCqlLibraryWhenLibraryIsValidAndNotDuplicate() { - String cql = getStringFromTestResource("/test-cql/EXM124v7QICore4.cql"); - CqlLibrary cqlLibrary = createCqlLibrary(cql); - Library library = createLibrary(cql); - - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(new Bundle()); - when(libraryTranslatorService.convertToFhirLibrary(eq(cqlLibrary), isNull())) - .thenReturn(library); - when(hapiFhirServer.createResource(any(Library.class))).thenReturn(new MethodOutcome()); - when(humanReadableService.generateLibraryHumanReadable(any(Library.class))) - .thenReturn("
Narrative Text
"); - - Library libraryResource = libraryService.createLibraryResourceForCqlLibrary(cqlLibrary); - - assertEquals(libraryResource.getName(), cqlLibrary.getCqlLibraryName()); - assertEquals(libraryResource.getVersion(), cqlLibrary.getVersion().toString()); + MissingCqlException.class, + () -> libraryService.getLibraryCql("FHIRHelpers", "4.0.001", "TOKEN")); assertEquals( - libraryResource.getText().getDivAsString(), - "
Narrative Text
"); - assertEquals(libraryResource.getText().getStatus().getDisplay(), "Extensions"); - } - - @Test - void testGetLibraryResourceAsCqlLibraryHandlesNoEntry() { - bundle.setEntry(null); - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - assertThrows( - HapiLibraryNotFoundException.class, - () -> libraryService.getLibraryResourceAsCqlLibrary("TestLibrary", "1.0.000")); - verify(hapiFhirServer, times(0)) - .findLibraryResourceInBundle(any(Bundle.class), any(Class.class)); - } - - @Test - void testGetLibraryResourceAsCqlLibraryHandlesNoLibrary() { - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(any(Bundle.class), any(Class.class))) - .thenReturn(Optional.empty()); - assertThrows( - HapiLibraryNotFoundException.class, - () -> libraryService.getLibraryResourceAsCqlLibrary("TestLibrary", "1.0.000")); - verify(hapiFhirServer, times(1)) - .findLibraryResourceInBundle(any(Bundle.class), any(Class.class)); - } - - @Test - void testGetLibraryResourceAsCqlLibraryReturnsCqlLibrary() { - Library library = (Library) bundle.getEntry().get(0).getResource(); - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(any(Bundle.class), any(Class.class))) - .thenReturn(Optional.of(library)); - CqlLibrary cqlLibrary = new CqlLibrary(); - when(libraryTranslatorService.convertToCqlLibrary(eq(library))).thenReturn(cqlLibrary); - - CqlLibrary output = libraryService.getLibraryResourceAsCqlLibrary("TestLibrary", "1.0.000"); - assertThat(output, is(equalTo(cqlLibrary))); - verify(hapiFhirServer, times(1)) - .findLibraryResourceInBundle(any(Bundle.class), any(Class.class)); - } - - @Test - void createLibraryResourceForCqlLibraryWhenDuplicateLibrary() { - String cql = getStringFromTestResource("/test-cql/EXM124v7QICore4.cql"); - CqlLibrary cqlLibrary = createCqlLibrary(cql); - when(hapiFhirServer.fetchLibraryBundleByNameAndVersion(anyString(), anyString())) - .thenReturn(bundle); - when(hapiFhirServer.findLibraryResourceInBundle(bundle, Library.class)) - .thenReturn(Optional.of(new Library())); - - Throwable exception = - assertThrows( - DuplicateLibraryException.class, - () -> libraryService.createLibraryResourceForCqlLibrary(cqlLibrary)); - String exceptionMessage = - String.format( - "Library resource with name: %s, version: %s already exists.", - cqlLibrary.getCqlLibraryName(), cqlLibrary.getVersion()); - assertEquals(exceptionMessage, exception.getMessage()); + "Cannot find CQL for library name: FHIRHelpers, version: 4.0.001", exception.getMessage()); } @Test @@ -242,12 +125,21 @@ public void testGetIncludedLibraries() { .setVersion("0.1.0") .setContent(List.of(attachment)); + CqlLibrary cqlLibrary = + CqlLibrary.builder() + .cqlLibraryName("IncludedLibrary") + .version(Version.builder().major(0).minor(1).revisionNumber(0).build()) + .build(); + when(libCqlVisitorFactory.visit(anyString())).thenReturn(visitor1).thenReturn(visitor2); - when(hapiFhirServer.fetchHapiLibrary(anyString(), anyString())) - .thenReturn(Optional.of(library)); + when(cqlLibraryService.getLibrary(anyString(), anyString(), anyString())) + .thenReturn(cqlLibrary); + when(libraryTranslatorService.convertToFhirLibrary(any(CqlLibrary.class), isNull())) + .thenReturn(library); Map includedLibraryMap = new HashMap<>(); - libraryService.getIncludedLibraries(mainLibrary, includedLibraryMap); + libraryService.getIncludedLibraries( + mainLibrary, includedLibraryMap, BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT, "TOKEN"); assertThat(includedLibraryMap.size(), is(equalTo(1))); assertNotNull(includedLibraryMap.get("IncludedLibrary0.1.0")); } @@ -260,7 +152,9 @@ public void testGetIncludedLibrariesWhenBlankCql() { Exception exception = assertThrows( IllegalArgumentException.class, - () -> libraryService.getIncludedLibraries(mainLibrary, libraries)); + () -> + libraryService.getIncludedLibraries( + mainLibrary, libraries, BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT, "TOKEN")); assertThat(exception.getMessage(), is(equalTo("Please provide valid arguments."))); } @@ -279,18 +173,19 @@ public void testGetIncludedLibrariesWhenIncludedLibraryNotInHapi() { var visitor2 = new LibraryCqlVisitorFactory().visit(includedLibrary); when(libCqlVisitorFactory.visit(anyString())).thenReturn(visitor1).thenReturn(visitor2); - when(hapiFhirServer.fetchHapiLibrary(anyString(), anyString())).thenReturn(Optional.empty()); + when(cqlLibraryService.getLibrary(anyString(), anyString(), anyString())) + .thenThrow(new CqlLibraryNotFoundException("Test Exception Here!", "0.1.000")); Map libraries = new HashMap<>(); Exception exception = assertThrows( - HapiLibraryNotFoundException.class, - () -> libraryService.getIncludedLibraries(mainLibrary, libraries)); + CqlLibraryNotFoundException.class, + () -> + libraryService.getIncludedLibraries( + mainLibrary, libraries, BundleUtil.MEASURE_BUNDLE_TYPE_EXPORT, "TOKEN")); assertThat( exception.getMessage(), - is( - equalTo( - "Cannot find a Hapi Fhir Library with name: IncludedLibrary, version: 0.1.000"))); + is(equalTo("Cannot find a CQL Library with name: Test Exception Here!, version: 0.1.000"))); } } diff --git a/src/test/java/gov/cms/madie/madiefhirservice/services/MeasureBundleServiceTest.java b/src/test/java/gov/cms/madie/madiefhirservice/services/MeasureBundleServiceTest.java index b82bd782..add35586 100644 --- a/src/test/java/gov/cms/madie/madiefhirservice/services/MeasureBundleServiceTest.java +++ b/src/test/java/gov/cms/madie/madiefhirservice/services/MeasureBundleServiceTest.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import gov.cms.madie.madiefhirservice.constants.UriConstants; +import gov.cms.madie.madiefhirservice.exceptions.CqlLibraryNotFoundException; import gov.cms.madie.madiefhirservice.exceptions.HapiLibraryNotFoundException; import gov.cms.madie.madiefhirservice.hapi.HapiFhirServer; import gov.cms.madie.madiefhirservice.utils.BundleUtil; @@ -99,7 +100,7 @@ public void testCreateMeasureBundle() { return null; }) .when(libraryService) - .getIncludedLibraries(anyString(), anyMap()); + .getIncludedLibraries(anyString(), anyMap(), anyString(), anyString()); Bundle bundle = measureBundleService.createMeasureBundle( @@ -130,12 +131,12 @@ public void testCreateMeasureBundleWhenIncludedLibraryNotFoundInHapi() { when(libraryTranslatorService.convertToFhirLibrary( any(CqlLibrary.class), any(ProgramUseContext.class))) .thenReturn(library); - doThrow(new HapiLibraryNotFoundException("FHIRHelpers", "4.0.001")) + doThrow(new CqlLibraryNotFoundException("FHIRHelpers", "4.0.001")) .when(libraryService) - .getIncludedLibraries(anyString(), anyMap()); + .getIncludedLibraries(anyString(), any(), anyString(), anyString()); Exception exception = Assertions.assertThrows( - HapiLibraryNotFoundException.class, + CqlLibraryNotFoundException.class, () -> measureBundleService.createMeasureBundle( madieMeasure, @@ -145,7 +146,7 @@ public void testCreateMeasureBundleWhenIncludedLibraryNotFoundInHapi() { assertThat( exception.getMessage(), - is(equalTo("Cannot find a Hapi Fhir Library with name: FHIRHelpers, version: 4.0.001"))); + is(equalTo("Cannot find a CQL Library with name: FHIRHelpers, version: 4.0.001"))); } @Test @@ -177,7 +178,7 @@ public void testCreateMeasureBundleForExport() { return null; }) .when(libraryService) - .getIncludedLibraries(anyString(), anyMap()); + .getIncludedLibraries(anyString(), anyMap(), anyString(), anyString()); when(elmTranslatorClient.getEffectiveDataRequirements( any(Bundle.class), anyString(), anyString(), anyString()))