diff --git a/src/main/java/gov/cms/madie/terminology/controller/VsacController.java b/src/main/java/gov/cms/madie/terminology/controller/VsacController.java index 19b80a8..6424a2e 100644 --- a/src/main/java/gov/cms/madie/terminology/controller/VsacController.java +++ b/src/main/java/gov/cms/madie/terminology/controller/VsacController.java @@ -1,10 +1,8 @@ package gov.cms.madie.terminology.controller; import java.util.List; -import java.util.stream.Collectors; import java.security.Principal; import java.util.Optional; -import gov.cms.madie.terminology.dto.QdmValueSet; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -22,7 +20,6 @@ import generated.vsac.nlm.nih.gov.RetrieveMultipleValueSetsResponse; import gov.cms.madie.models.cql.terminology.CqlCode; -import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria; import gov.cms.madie.terminology.models.UmlsUser; import gov.cms.madie.terminology.service.VsacService; import lombok.RequiredArgsConstructor; @@ -66,43 +63,6 @@ protected String serializeFhirValueset(ValueSet fhirValueSet) { return fhirContext.newJsonParser().encodeResourceToString(fhirValueSet); } - @PutMapping(path = "/value-sets/searches", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity searchValueSets( - Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) { - log.debug("VsacController::getValueSets"); - - final String username = principal.getName(); - Optional umlsUser = vsacService.findByHarpId(username); - if (umlsUser.isPresent()) { - - List vsacValueSets = - vsacService.getValueSets(searchCriteria, umlsUser.get()); - - List fhirValueSets = vsacService.convertToFHIRValueSets(vsacValueSets); - String serializedValueSets = - fhirValueSets.stream().map(this::serializeFhirValueset).collect(Collectors.joining(", ")); - - return ResponseEntity.ok().body("[" + serializedValueSets + "]"); - } - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - - @PutMapping("/qdm/value-sets/searches") - public ResponseEntity> getQdmValueSets( - Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) { - log.debug("VsacController::getQdmValueSets"); - - final String username = principal.getName(); - Optional umlsUser = vsacService.findByHarpId(username); - if (umlsUser.isPresent()) { - List qdmValueSets = - vsacService.getValueSetsInQdmFormat(searchCriteria, umlsUser.get()); - - return ResponseEntity.ok().body(qdmValueSets); - } - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - @PutMapping(path = "/validations/codes", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> validateCodes( Principal principal, diff --git a/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java b/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java index cfb4a56..1fbdc1c 100644 --- a/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java +++ b/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java @@ -1,5 +1,6 @@ package gov.cms.madie.terminology.controller; +import ca.uhn.fhir.context.FhirContext; import gov.cms.madie.models.measure.ManifestExpansion; import gov.cms.madie.terminology.dto.Code; import gov.cms.madie.terminology.dto.QdmValueSet; @@ -12,6 +13,7 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.ValueSet; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -21,6 +23,7 @@ import java.security.Principal; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @RestController @RequestMapping(path = "/terminology") @@ -29,6 +32,7 @@ public class VsacFhirTerminologyController { private final FhirTerminologyService fhirTerminologyService; private final VsacService vsacService; + private final FhirContext fhirContext; @GetMapping("/manifest-list") public ResponseEntity> getManifests(Principal principal) { @@ -39,18 +43,30 @@ public ResponseEntity> getManifests(Principal principal) } @PutMapping("/value-sets/expansion/qdm") - public ResponseEntity> getValueSetsExpansions( + public ResponseEntity> getQdmValueSetsExpansions( Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) { final String username = principal.getName(); - log.info( - "User [{}] is attempting to fetch value sets expansions from VSAC FHIR Terminology Server.", - username); + log.info("User [{}] is attempting to fetch QDM value sets expansions.", username); UmlsUser umlsUser = vsacService.verifyUmlsAccess(username); List qdmValueSets = fhirTerminologyService.getValueSetsExpansionsForQdm(searchCriteria, umlsUser); return ResponseEntity.ok().body(qdmValueSets); } + @PutMapping("/value-sets/expansion/fhir") + public ResponseEntity getFhirValueSetsExpansions( + Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) { + final String username = principal.getName(); + log.info("User [{}] is attempting to fetch FHIR value sets expansions.", username); + UmlsUser umlsUser = vsacService.verifyUmlsAccess(username); + List fhirValueSets = + fhirTerminologyService.getValueSetsExpansion(searchCriteria, umlsUser); + String serializedValueSets = + fhirValueSets.stream().map(this::serializeFhirValueSet).collect(Collectors.joining(", ")); + + return ResponseEntity.ok().body("[" + serializedValueSets + "]"); + } + @GetMapping(path = "/update-code-systems", produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("#request.getHeader('api-key') == #apiKey") public ResponseEntity> retrieveAndUpdateCodeSystems( @@ -106,4 +122,8 @@ public ResponseEntity> getCodesAndCodeSystems( return ResponseEntity.ok() .body(fhirTerminologyService.retrieveCodesAndCodeSystems(codeList, user.getApiKey())); } + + private String serializeFhirValueSet(ValueSet fhirValueSet) { + return fhirContext.newJsonParser().encodeResourceToString(fhirValueSet); + } } diff --git a/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java b/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java index 920b5f5..088aef0 100644 --- a/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java +++ b/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java @@ -16,6 +16,7 @@ import org.hl7.fhir.r4.model.*; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import org.springframework.web.util.UriComponentsBuilder; import java.time.Instant; @@ -51,12 +52,11 @@ public List getManifests(UmlsUser umlsUser) { } // spin off requests based on values provided in the expansion - public List recursivelyRequestAllValueSetsExpansionsForQDM( - List allValueSets, + public List recursivelyRequestAllValueSetsExpansions( + List allValueSets, String apiKey, ValueSetsSearchCriteria.ValueSetParams vsParam, - ValueSetsSearchCriteria valueSetsSearchCriteria, - List codeSystemEntries) { + ValueSetsSearchCriteria valueSetsSearchCriteria) { IParser parser = fhirContext.newJsonParser(); String resource = fhirTerminologyServiceWebClient.getValueSetResource( @@ -67,59 +67,45 @@ public List recursivelyRequestAllValueSetsExpansionsForQDM( valueSetsSearchCriteria.getActiveOnly(), valueSetsSearchCriteria.getManifestExpansion()); - ValueSet valueSetResource = parser.parseResource(ValueSet.class, resource); - var total = valueSetResource.getExpansion().getTotal(); // total valuesets + ValueSet valueSet = parser.parseResource(ValueSet.class, resource); + // total number of pages in expansion, max 1000 codes supported in one-page request + var total = valueSet.getExpansion().getTotal(); - List concepts = - getValueSetConcepts(valueSetResource, codeSystemEntries, "QDM"); log.info( "vs total [{}] count: [{}] offset: [{}], oid: [{}]", total, vsParam.getCount(), vsParam.getOffset(), vsParam.getOid()); - - // Check if the ValueSet with the same oid already exists in allValueSets - QdmValueSet existingValueSet = + ValueSet existingValueSet = allValueSets.stream() - .filter(vs -> vs.getOid().equals(vsParam.getOid())) + .filter(vs -> valueSet.getIdPart().equals(valueSet.getIdPart())) .findFirst() .orElse(null); - if (existingValueSet != null) { - List updatedConcepts = new ArrayList<>(existingValueSet.getConcepts()); - updatedConcepts.addAll(concepts); - // Create a new QdmValueSet with the updated concepts - QdmValueSet updatedValueSet = - QdmValueSet.builder() - .oid(existingValueSet.getOid()) - .displayName(existingValueSet.getDisplayName()) - .version(existingValueSet.getVersion()) - .concepts(updatedConcepts) - .build(); - // Replace the existing QdmValueSet in the list - allValueSets.set(allValueSets.indexOf(existingValueSet), updatedValueSet); + // Check if the ValueSet with the same oid already exists in allValueSets, update if exists + // This happens if value set has multiple page requests. + if (existingValueSet == null) { + allValueSets.add(valueSet); } else { - allValueSets.add( - QdmValueSet.builder() - .oid(valueSetResource.getIdPart()) - .displayName(valueSetResource.getName()) - .version(valueSetResource.getVersion()) - .concepts(concepts) - .build()); + existingValueSet.getExpansion().getContains().addAll(valueSet.getExpansion().getContains()); } // if the total results in the searchSet are still greater than our current offset + the count // of our last request, then we request again if (vsParam.getOffset() + vsParam.getCount() <= total) { vsParam.setOffset(vsParam.getOffset() + 1000); - return recursivelyRequestAllValueSetsExpansionsForQDM( - allValueSets, apiKey, vsParam, valueSetsSearchCriteria, codeSystemEntries); + return recursivelyRequestAllValueSetsExpansions( + allValueSets, apiKey, vsParam, valueSetsSearchCriteria); } return allValueSets; } - public List getValueSetsExpansionsForQdm( + public List getValueSetsExpansion( ValueSetsSearchCriteria valueSetsSearchCriteria, UmlsUser umlsUser) { - List codeSystemEntries = mappingService.getCodeSystemEntries(); + if (valueSetsSearchCriteria == null + || CollectionUtils.isEmpty(valueSetsSearchCriteria.getValueSetParams())) { + return Collections.emptyList(); + } + return valueSetsSearchCriteria.getValueSetParams().stream() .map( vsParam -> { @@ -129,14 +115,30 @@ public List getValueSetsExpansionsForQdm( }) .flatMap( vsParam -> - recursivelyRequestAllValueSetsExpansionsForQDM( - new ArrayList<>(), - umlsUser.getApiKey(), - vsParam, - valueSetsSearchCriteria, - codeSystemEntries) + recursivelyRequestAllValueSetsExpansions( + new ArrayList<>(), umlsUser.getApiKey(), vsParam, valueSetsSearchCriteria) .stream()) - .collect(Collectors.toList()); + .toList(); + } + + public List getValueSetsExpansionsForQdm( + ValueSetsSearchCriteria valueSetsSearchCriteria, UmlsUser umlsUser) { + List codeSystemEntries = mappingService.getCodeSystemEntries(); + List fhirValueSets = getValueSetsExpansion(valueSetsSearchCriteria, umlsUser); + + return fhirValueSets.stream() + .map( + fhirValueSet -> { + List concepts = + getValueSetConcepts(fhirValueSet, codeSystemEntries, "QDM"); + return QdmValueSet.builder() + .oid(fhirValueSet.getIdPart()) + .displayName(fhirValueSet.getName()) + .version(fhirValueSet.getVersion()) + .concepts(concepts) + .build(); + }) + .toList(); } /** diff --git a/src/main/java/gov/cms/madie/terminology/service/VsacService.java b/src/main/java/gov/cms/madie/terminology/service/VsacService.java index 07bab6d..8d79df9 100644 --- a/src/main/java/gov/cms/madie/terminology/service/VsacService.java +++ b/src/main/java/gov/cms/madie/terminology/service/VsacService.java @@ -6,7 +6,6 @@ import gov.cms.madie.models.mapping.CodeSystemEntry; import gov.cms.madie.terminology.dto.Code; import gov.cms.madie.terminology.dto.CodeStatus; -import gov.cms.madie.terminology.dto.QdmValueSet; import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria; import gov.cms.madie.terminology.exceptions.VsacUnauthorizedException; import gov.cms.madie.terminology.mapper.VsacToFhirValueSetMapper; @@ -27,8 +26,6 @@ import java.util.Optional; import java.util.stream.Collectors; -import static generated.vsac.nlm.nih.gov.RetrieveMultipleValueSetsResponse.DescribedValueSet; - @Service @Slf4j @RequiredArgsConstructor @@ -68,13 +65,6 @@ public ValueSet convertToFHIRValueSet(RetrieveMultipleValueSetsResponse vsacValu return vsacToFhirValueSetMapper.convertToFHIRValueSet(vsacValueSetResponse); } - public List convertToFHIRValueSets( - List vsacValueSets) { - return vsacValueSets.stream() - .map(vsacToFhirValueSetMapper::convertToFHIRValueSet) - .collect(Collectors.toList()); - } - public List getValueSets( ValueSetsSearchCriteria searchCriteria, UmlsUser umlsUser) { List valueSetParams = @@ -92,12 +82,6 @@ public List getValueSets( .collect(Collectors.toList()); } - public List getValueSetsInQdmFormat( - ValueSetsSearchCriteria searchCriteria, UmlsUser umlsUser) { - List vsacValueSets = getValueSets(searchCriteria, umlsUser); - return convertToQdmValueSets(vsacValueSets); - } - /** * @param cqlCodes List of objects generated by Antlr from user entered CQL string. * @param umlsUser umls information from db @@ -319,42 +303,6 @@ public UmlsUser saveUmlsUser(String harpId, String apiKey) { return umlsUserRepository.save(umlsUser); } - private List convertToQdmValueSets( - List valueSetsResponses) { - return valueSetsResponses.stream() - .map( - response -> { - var describedValueSet = response.getDescribedValueSet(); - List concepts = getValueSetConcepts(describedValueSet); - return QdmValueSet.builder() - .oid(describedValueSet.getID()) - .displayName(describedValueSet.getDisplayName()) - .version(describedValueSet.getVersion()) - .concepts(concepts) - .build(); - }) - .toList(); - } - - private List getValueSetConcepts(DescribedValueSet valueSet) { - var conceptList = valueSet.getConceptList(); - if (conceptList == null) { - log.info("Empty value set:{}", valueSet.getID()); - return List.of(); - } - return conceptList.getConcepts().stream() - .map( - concept -> - QdmValueSet.Concept.builder() - .code(concept.getCode()) - .codeSystemName(concept.getCodeSystemName()) - .codeSystemVersion(concept.getCodeSystemVersion()) - .codeSystemOid(concept.getCodeSystem()) - .displayName(concept.getDisplayName()) - .build()) - .toList(); - } - public Optional findByHarpId(String harpId) { return umlsUserRepository.findByHarpId(harpId); } diff --git a/src/test/java/gov/cms/madie/terminology/controller/VsacControllerMvcTest.java b/src/test/java/gov/cms/madie/terminology/controller/VsacControllerMvcTest.java deleted file mode 100644 index 5b0f975..0000000 --- a/src/test/java/gov/cms/madie/terminology/controller/VsacControllerMvcTest.java +++ /dev/null @@ -1,235 +0,0 @@ -package gov.cms.madie.terminology.controller; - -import ca.uhn.fhir.context.FhirContext; -import generated.vsac.nlm.nih.gov.RetrieveMultipleValueSetsResponse; -import gov.cms.madie.terminology.dto.QdmValueSet; -import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria; -import gov.cms.madie.terminology.helpers.TestHelpers; -import gov.cms.madie.terminology.models.UmlsUser; -import gov.cms.madie.terminology.service.VsacService; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Unmarshaller; -import org.hl7.fhir.r4.model.ValueSet; -import org.junit.jupiter.api.BeforeEach; -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.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.web.reactive.function.client.WebClientResponseException; - -import java.io.File; -import java.io.IOException; -import java.security.Principal; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest(VsacController.class) -public class VsacControllerMvcTest { - private static final String TEST_USR = "FAKE"; - - @MockBean private FhirContext fhirContext; - - @MockBean private VsacService vsacService; - - @Autowired private MockMvc mockMvc; - - private RetrieveMultipleValueSetsResponse svsValueSet; - private ValueSet fhirValueSet; - private static final String TEST_USER = "test.user"; - private static final String TEST_API_KEY = "te$tKey"; - - @BeforeEach - public void setup() throws JAXBException, IOException { - File file = TestHelpers.getTestResourceFile("/value-sets/svs_office_visit.xml"); - JAXBContext jaxbContext = JAXBContext.newInstance(RetrieveMultipleValueSetsResponse.class); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - svsValueSet = (RetrieveMultipleValueSetsResponse) jaxbUnmarshaller.unmarshal(file); - - fhirValueSet = - TestHelpers.getFhirTestResource("/value-sets/fhir_office_visit.json", ValueSet.class); - } - - @Test - void testSearchValueSets() throws Exception { - String searchCriteria = - "{\n" - + " \"profile\": \"eCQM Update 2030-05-05\",\n" - + " \"includeDraft\": true,\n" - + " \"valueSetParams\": [\n" - + " {\n" - + " \"oid\": \"2.16.840.1.113883.3.464.1003.101.12.1001\"\n" - + " }\n" - + " ]\n" - + "}"; - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); - - UmlsUser mockUmlsUser = mock(UmlsUser.class); - Optional optionalUmlsUser = Optional.of(mockUmlsUser); - when(vsacService.findByHarpId(anyString())).thenReturn(optionalUmlsUser); - - when(vsacService.getValueSets(any(ValueSetsSearchCriteria.class), any())) - .thenReturn(List.of(svsValueSet)); - when(vsacService.convertToFHIRValueSets(List.of(svsValueSet))) - .thenReturn(List.of(fhirValueSet)); - when(fhirContext.newJsonParser()).thenReturn(FhirContext.forR4().newJsonParser()); - - MvcResult result = - mockMvc - .perform( - MockMvcRequestBuilders.put("/vsac/value-sets/searches") - .with(user(TEST_USR)) - .with(csrf()) - .content(searchCriteria) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()) - .andReturn(); - String content = result.getResponse().getContentAsString(); - verify(vsacService, times(1)).getValueSets(any(ValueSetsSearchCriteria.class), any()); - assertThat(content, containsString("\"resourceType\":\"ValueSet\"")); - assertThat(content, containsString("\"name\":\"Office Visit\"")); - assertThat(content, containsString("\"system\":\"2.16.840.1.113883.6.12\"")); - assertThat(content, containsString("\"system\":\"2.16.840.1.113883.6.96\"")); - assertThat(content, containsString("\"id\":\"2.16.840.1.113883.3.464.1003.101.12.1001\"")); - } - - @Test - void testSearchValueSetsWhenNoValueSetFound() throws Exception { - String searchCriteria = - "{\n" - + " \"profile\": \"eCQM Update 2030-05-05\",\n" - + " \"includeDraft\": true,\n" - + " \"valueSetParams\": [\n" - + " {\n" - + " \"oid\": \"2.16.840.1.113883.3.464.1003.101.12.1001\"\n" - + " }\n" - + " ]\n" - + "}"; - - UmlsUser mockUmlsUser = mock(UmlsUser.class); - Optional optionalUmlsUser = Optional.of(mockUmlsUser); - when(vsacService.findByHarpId(anyString())).thenReturn(optionalUmlsUser); - - when(vsacService.getValueSets(any(ValueSetsSearchCriteria.class), any())) - .thenReturn(List.of(svsValueSet)); - when(vsacService.convertToFHIRValueSets(List.of(svsValueSet))) - .thenReturn(List.of(fhirValueSet)); - when(fhirContext.newJsonParser()).thenReturn(FhirContext.forR4().newJsonParser()); - - doThrow(new WebClientResponseException(404, "Error", null, null, null)) - .when(vsacService) - .getValueSets(any(ValueSetsSearchCriteria.class), any()); - - MvcResult result = - mockMvc - .perform( - MockMvcRequestBuilders.put("/vsac/value-sets/searches") - .with(user(TEST_USR)) - .with(csrf()) - .content(searchCriteria) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isNotFound()) - .andReturn(); - assertThat( - result.getResponse().getContentAsString(), containsString("\"message\":\"404 Error\"")); - } - - @Test - void testSearchQdmValueSets() throws Exception { - String searchCriteria = - "{\n" - + " \"profile\": \"eCQM Update 2030-05-05\",\n" - + " \"includeDraft\": true,\n" - + " \"valueSetParams\": [\n" - + " {\n" - + " \"oid\": \"2.16.840.1.113883.3.464.1003.101.12.1001\"\n" - + " }\n" - + " ]\n" - + "}"; - var concept = - QdmValueSet.Concept.builder() - .code("185463005") - .codeSystemName("SNOMEDCT") - .codeSystemOid("2.16.840.1.113883.6.96") - .codeSystemVersion("2023-03") - .displayName("Visit out of hours (procedure)") - .build(); - QdmValueSet vs = - QdmValueSet.builder() - .oid("2.16.840.1.113883.3.464.1003.101.12.1001") - .displayName("Encounter Inpatient") - .concepts(List.of(concept)) - .build(); - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); - - UmlsUser mockUmlsUser = mock(UmlsUser.class); - Optional optionalUmlsUser = Optional.of(mockUmlsUser); - when(vsacService.findByHarpId(anyString())).thenReturn(optionalUmlsUser); - - when(vsacService.getValueSetsInQdmFormat(any(ValueSetsSearchCriteria.class), any())) - .thenReturn(List.of(vs)); - - MvcResult result = - mockMvc - .perform( - MockMvcRequestBuilders.put("/vsac/qdm/value-sets/searches") - .with(user(TEST_USR)) - .with(csrf()) - .content(searchCriteria) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()) - .andReturn(); - String content = result.getResponse().getContentAsString(); - verify(vsacService, times(1)) - .getValueSetsInQdmFormat(any(ValueSetsSearchCriteria.class), any()); - - assertThat(content, containsString("\"display_name\":\"Encounter Inpatient\"")); - assertThat(content, containsString("\"code\":\"185463005\"")); - assertThat(content, containsString("\"code_system_name\":\"SNOMEDCT\"")); - assertThat(content, containsString("\"code_system_oid\":\"2.16.840.1.113883.6.96\"")); - assertThat(content, containsString("\"oid\":\"2.16.840.1.113883.3.464.1003.101.12.1001\"")); - } - - @Test - void testSearchQdmValueSetsWhenUnauthorized() throws Exception { - String searchCriteria = - "{\n" - + " \"profile\": \"eCQM Update 2030-05-05\",\n" - + " \"includeDraft\": true,\n" - + " \"valueSetParams\": [\n" - + " {\n" - + " \"oid\": \"2.16.840.1.113883.3.464.1003.101.12.1001\"\n" - + " }\n" - + " ]\n" - + "}"; - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); - when(vsacService.findByHarpId(anyString())).thenReturn(Optional.empty()); - MvcResult result = - mockMvc - .perform( - MockMvcRequestBuilders.put("/vsac/qdm/value-sets/searches") - .with(user(TEST_USR)) - .with(csrf()) - .content(searchCriteria) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isUnauthorized()) - .andReturn(); - assertThat(result.getResponse().getStatus(), is(equalTo(401))); - } -} diff --git a/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java b/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java index 72a4233..ccbc657 100644 --- a/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java +++ b/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java @@ -1,13 +1,16 @@ package gov.cms.madie.terminology.controller; +import ca.uhn.fhir.context.FhirContext; import gov.cms.madie.models.measure.ManifestExpansion; import gov.cms.madie.terminology.dto.*; import gov.cms.madie.terminology.exceptions.VsacUnauthorizedException; +import gov.cms.madie.terminology.helpers.TestHelpers; import gov.cms.madie.terminology.models.CodeSystem; import gov.cms.madie.terminology.models.UmlsUser; import gov.cms.madie.terminology.repositories.CodeSystemRepository; import gov.cms.madie.terminology.service.FhirTerminologyService; import gov.cms.madie.terminology.service.VsacService; +import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,10 +21,14 @@ import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockHttpServletRequest; +import java.io.IOException; import java.security.Principal; import java.time.Instant; import java.util.*; +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.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -32,8 +39,8 @@ class VsacFhirTerminologyControllerTest { private CodeSystemRepository codeSystemRepository; @Mock private VsacService vsacService; - @Mock FhirTerminologyService fhirTerminologyService; - + @Mock private FhirTerminologyService fhirTerminologyService; + @Mock private FhirContext fhirContext; @InjectMocks private VsacFhirTerminologyController vsacFhirTerminologyController; private UmlsUser umlsUser; private static final String TEST_USER = "test.user"; @@ -44,6 +51,7 @@ class VsacFhirTerminologyControllerTest { MockHttpServletRequest request; private final List mockManifests = new ArrayList<>(); private final List mockQdmValueSets = new ArrayList<>(); + private Principal principal; @BeforeEach public void setUp() { @@ -66,12 +74,12 @@ public void setUp() { .displayName("test-value-set-display-name") .build()); request = new MockHttpServletRequest(); + principal = mock(Principal.class); + when(principal.getName()).thenReturn(TEST_USER); } @Test void testGetManifestsSuccessfully() { - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.getManifests(any(UmlsUser.class))).thenReturn(mockManifests); ResponseEntity> response = @@ -82,8 +90,6 @@ void testGetManifestsSuccessfully() { @Test void testUnAuthorizedUmlsUserWhileFetchingManifests() { - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); doThrow(new VsacUnauthorizedException("Please login to UMLS before proceeding")) .when(vsacService) .verifyUmlsAccess(anyString()); @@ -94,15 +100,13 @@ void testUnAuthorizedUmlsUserWhileFetchingManifests() { @Test void testGetValueSetsExpansionsSuccessfully() { - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); ValueSetsSearchCriteria valueSetsSearchCriteria = ValueSetsSearchCriteria.builder().build(); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.getValueSetsExpansionsForQdm( any(ValueSetsSearchCriteria.class), any(UmlsUser.class))) .thenReturn(mockQdmValueSets); ResponseEntity> response = - vsacFhirTerminologyController.getValueSetsExpansions(principal, valueSetsSearchCriteria); + vsacFhirTerminologyController.getQdmValueSetsExpansions(principal, valueSetsSearchCriteria); assertEquals(response.getStatusCode(), HttpStatus.OK); assertEquals(response.getBody(), mockQdmValueSets); } @@ -121,8 +125,6 @@ void retrieveAndUpdateCodeSystemsSuccessfully() { .lastUpdated(Instant.now()) .lastUpdatedUpstream(new Date()) .build()); - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.retrieveAllCodeSystems(any())).thenReturn(mockCodeSystemsPage); @@ -135,8 +137,6 @@ void retrieveAndUpdateCodeSystemsSuccessfully() { @Test void testUnAuthorizedUmlsUserWhileFetchingValueSetsExpansions() { - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); doThrow(new VsacUnauthorizedException("Please login to UMLS before proceeding")) .when(vsacService) .verifyUmlsAccess(anyString()); @@ -147,8 +147,6 @@ void testUnAuthorizedUmlsUserWhileFetchingValueSetsExpansions() { @Test void testUnAuthorizedUmlsUserWhileretrievingAndUpdatingCodeSystems() { - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); doThrow(new VsacUnauthorizedException("Please login to UMLS before proceeding")) .when(vsacService) .verifyUmlsAccess(anyString()); @@ -173,8 +171,6 @@ void testGetAllCodeSystemsSuccessfully() { .lastUpdated(Instant.now()) .lastUpdatedUpstream(new Date()) .build()); - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); when(fhirTerminologyService.getAllCodeSystems()).thenReturn(mockCodeSystemsPage); ResponseEntity> response = @@ -188,7 +184,6 @@ void testGetCode() { String codeName = "1963-8"; String codeSystem = "LOINC"; String version = "2.40"; - Principal principal = mock(Principal.class); Code code = Code.builder() .name(codeName) @@ -198,7 +193,6 @@ void testGetCode() { .display("Bicarbonate [Moles/volume] in Serum") .codeSystemOid("2.16.840.1.113883.6.1") .build(); - when(principal.getName()).thenReturn(TEST_USER); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.retrieveCode(anyString(), anyString(), anyString(), anyString())) .thenReturn(code); @@ -214,8 +208,6 @@ void testGetCodeIfNoUmlsUserFound() { String codeName = "1963-8"; String codeSystem = "LOINC"; String version = "2.40"; - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); doThrow(new VsacUnauthorizedException("Please login to UMLS before proceeding")) .when(vsacService) .verifyUmlsAccess(anyString()); @@ -246,9 +238,6 @@ void testGetCodesList() { .codeSystemUrl("https://loinc.org") .status(CodeStatus.valueOf("ACTIVE")) .build(); - - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.retrieveCodesAndCodeSystems(any(), anyString())) .thenReturn(List.of(code)); @@ -283,8 +272,6 @@ void testSearchValueSets() { .build(); mockValueSets.add(v1); mockValueSets.add(v2); - Principal principal = mock(Principal.class); - when(principal.getName()).thenReturn(TEST_USER); when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); when(fhirTerminologyService.searchValueSets(any(), any())) .thenReturn(ValueSetSearchResult.builder().valueSets(mockValueSets).build()); @@ -295,4 +282,23 @@ void testSearchValueSets() { vsacFhirTerminologyController.searchValueSets(principal, queryParams); assertEquals(response.getStatusCode(), HttpStatus.OK); } + + @Test + void testGetFhirValueSetsExpansions() throws IOException { + ValueSet valueSet = + TestHelpers.getFhirTestResource( + "/value-sets/value_set_with_expansion_codes.json", ValueSet.class); + when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser); + when(fhirTerminologyService.getValueSetsExpansion( + any(ValueSetsSearchCriteria.class), any(UmlsUser.class))) + .thenReturn(List.of(valueSet)); + when(fhirContext.newJsonParser()).thenReturn(FhirContext.forR4().newJsonParser()); + + ResponseEntity response = + vsacFhirTerminologyController.getFhirValueSetsExpansions( + principal, ValueSetsSearchCriteria.builder().build()); + assertThat(response.getStatusCode(), is(equalTo(HttpStatus.OK))); + assertThat(response.getBody().contains("\"resourceType\":\"ValueSet\""), is(true)); + assertThat(response.getBody().contains("\"url\":\"" + valueSet.getUrl() + "\""), is(true)); + } } diff --git a/src/test/java/gov/cms/madie/terminology/service/FhirTerminologyServiceTest.java b/src/test/java/gov/cms/madie/terminology/service/FhirTerminologyServiceTest.java index 1fd0f67..ae33956 100644 --- a/src/test/java/gov/cms/madie/terminology/service/FhirTerminologyServiceTest.java +++ b/src/test/java/gov/cms/madie/terminology/service/FhirTerminologyServiceTest.java @@ -236,6 +236,22 @@ void getsValueSetsExpansionsForQdm_withNoCodes_When_ManifestExpansionIsProvided( assertEquals(0, result.get(0).getConcepts().size()); } + @Test + void getsValueSetsExpansionsForQdmIfSearchCriteriaIsEmpty() { + var valueSetsSearchCriteria = ValueSetsSearchCriteria.builder().build(); + when(mappingService.getCodeSystemEntries()).thenReturn(codeSystemEntries); + List result = + fhirTerminologyService.getValueSetsExpansionsForQdm(valueSetsSearchCriteria, umlsUser); + assertEquals(0, result.size()); + } + + @Test + void getsValueSetsExpansionsForQdmIfSearchCriteriaIsNull() { + when(mappingService.getCodeSystemEntries()).thenReturn(codeSystemEntries); + List result = fhirTerminologyService.getValueSetsExpansionsForQdm(null, umlsUser); + assertEquals(0, result.size()); + } + @Test void testRetrieveAllCodeSystems() { umlsUser = UmlsUser.builder().apiKey(TEST_API_KEY).harpId(TEST_HARP_ID).build(); diff --git a/src/test/java/gov/cms/madie/terminology/service/VsacServiceTest.java b/src/test/java/gov/cms/madie/terminology/service/VsacServiceTest.java index 45bc796..1500226 100644 --- a/src/test/java/gov/cms/madie/terminology/service/VsacServiceTest.java +++ b/src/test/java/gov/cms/madie/terminology/service/VsacServiceTest.java @@ -11,7 +11,6 @@ import gov.cms.madie.terminology.dto.Code; import gov.cms.madie.terminology.dto.CodeStatus; -import gov.cms.madie.terminology.dto.QdmValueSet; import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria; import gov.cms.madie.terminology.exceptions.VsacUnauthorizedException; import gov.cms.madie.terminology.helpers.TestHelpers; @@ -394,39 +393,6 @@ public void testValidateCodesForQDMFormat() throws JsonProcessingException { assertTrue(result.get(0).isValid()); } - @Test - public void testGetQdmValueSets() { - - when(terminologyServiceWebClient.getValueSet(any(), any(), any(), any(), any(), any())) - .thenReturn(svsValueSet); - - List valueSets = - vsacService.getValueSetsInQdmFormat(valueSetsSearchCriteria, umlsUser); - - assertThat( - valueSets.get(0).getOid(), - is(equalTo(valueSetsSearchCriteria.getValueSetParams().get(0).getOid()))); - assertThat(valueSets.get(0).getDisplayName(), is(equalTo("Office Visit"))); - assertThat(valueSets.get(0).getConcepts().size(), is(equalTo(16))); - } - - @Test - public void testGetEmptyQdmValueSets() { - - svsValueSet.getDescribedValueSet().setConceptList(null); - when(terminologyServiceWebClient.getValueSet(any(), any(), any(), any(), any(), any())) - .thenReturn(svsValueSet); - - List valueSets = - vsacService.getValueSetsInQdmFormat(valueSetsSearchCriteria, umlsUser); - - assertThat( - valueSets.get(0).getOid(), - is(equalTo(valueSetsSearchCriteria.getValueSetParams().get(0).getOid()))); - assertThat(valueSets.get(0).getDisplayName(), is(equalTo("Office Visit"))); - assertThat(valueSets.get(0).getConcepts().size(), is(equalTo(0))); - } - @Test void testVerifyUmlsAccessUmlsUserNotFound() { when(umlsUserRepository.findByHarpId(anyString())).thenReturn(Optional.empty());