From 48e2a7773699151c614af4bf2c6cf133d8881a43 Mon Sep 17 00:00:00 2001 From: mcmcphillips Date: Mon, 13 May 2024 11:14:42 -0700 Subject: [PATCH] MAT-6393: Add search functionality --- .../VsacFhirTerminologyController.java | 12 +++++ .../terminology/dto/ValueSetForSearch.java | 18 +++++++ .../service/FhirTerminologyService.java | 49 ++++++++++++++++--- .../FhirTerminologyServiceWebClient.java | 28 ++++++++++- .../TerminologyServiceWebClient.java | 1 - .../VsacFhirTerminologyControllerTest.java | 1 - .../task/UpdateCodeSystemTaskTest.java | 1 + .../FhirTerminologyServiceWebClientTest.java | 8 ++- 8 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 src/main/java/gov/cms/madie/terminology/dto/ValueSetForSearch.java 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 fffc99b..cc21e34 100644 --- a/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java +++ b/src/main/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyController.java @@ -3,6 +3,7 @@ import gov.cms.madie.models.measure.ManifestExpansion; import gov.cms.madie.terminology.dto.Code; import gov.cms.madie.terminology.dto.QdmValueSet; +import gov.cms.madie.terminology.dto.ValueSetForSearch; import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria; import gov.cms.madie.terminology.models.CodeSystem; import gov.cms.madie.terminology.models.UmlsUser; @@ -19,6 +20,7 @@ import java.security.Principal; import java.util.List; +import java.util.Map; @RestController @RequestMapping(path = "/terminology") @@ -68,6 +70,16 @@ public ResponseEntity> getAllCodeSystems(Principal principal) { return ResponseEntity.ok().body(fhirTerminologyService.getAllCodeSystems()); } + @GetMapping(path = "/search-value-sets", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ResponseEntity> searchValueSets( + Principal principal, @RequestParam Map queryParams) { + final String username = principal.getName(); + UmlsUser umlsUser = vsacService.verifyUmlsAccess(username); + var result = fhirTerminologyService.searchValueSets(umlsUser.getApiKey(), queryParams); + return ResponseEntity.ok().body(result); + } + @GetMapping(path = "/code", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getCode( @RequestParam() String code, diff --git a/src/main/java/gov/cms/madie/terminology/dto/ValueSetForSearch.java b/src/main/java/gov/cms/madie/terminology/dto/ValueSetForSearch.java new file mode 100644 index 0000000..f0adff7 --- /dev/null +++ b/src/main/java/gov/cms/madie/terminology/dto/ValueSetForSearch.java @@ -0,0 +1,18 @@ +package gov.cms.madie.terminology.dto; + +import lombok.Builder; +import lombok.Data; +import org.hl7.fhir.r4.model.Enumerations; + +@Data +@Builder +public class ValueSetForSearch { + private String title; + private String name; + private String url; + private String oid; + private String steward; + private String version; + private String codeSystem; + private Enumerations.PublicationStatus status; +} 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 689bf74..79d067d 100644 --- a/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java +++ b/src/main/java/gov/cms/madie/terminology/service/FhirTerminologyService.java @@ -4,10 +4,7 @@ import ca.uhn.fhir.parser.IParser; import gov.cms.madie.models.mapping.CodeSystemEntry; import gov.cms.madie.models.measure.ManifestExpansion; -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.dto.*; import gov.cms.madie.terminology.models.CodeSystem; import gov.cms.madie.terminology.models.UmlsUser; import gov.cms.madie.terminology.repositories.CodeSystemRepository; @@ -16,9 +13,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r4.model.*; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.web.util.UriComponentsBuilder; @@ -26,6 +21,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; @Service @@ -121,6 +117,43 @@ private List getValueSetConcepts( return List.of(); } + public List searchValueSets(String apiKey, Map queryParams) { + IParser parser = fhirContext.newJsonParser(); + String responseString = fhirTerminologyServiceWebClient.searchValueSets(apiKey, queryParams); + Bundle bundle = parser.parseResource(Bundle.class, responseString); + List valueSetList = new ArrayList<>(); + bundle + .getEntry() + .forEach( + entry -> { + Resource resource = entry.getResource(); + if (resource instanceof ValueSet) { + String oid = ""; + for (Identifier identifier : ((ValueSet) resource).getIdentifier()) { + if (identifier.getValue() != null && !identifier.getValue().isEmpty()) { + oid = identifier.getValue(); + } + } + ValueSetForSearch valueSet = + ValueSetForSearch.builder() + .title(((ValueSet) resource).getTitle()) + .name(((ValueSet) resource).getName()) + .url(((ValueSet) resource).getUrl()) + .version(((ValueSet) resource).getVersion()) + // .codeSystem(codeSystem) // + // Theres a way to get this but its very complicated + .status(((ValueSet) resource).getStatus()) + .steward(((ValueSet) resource).getPublisher()) + .oid(oid) + .build(); + valueSetList.add(valueSet); + } + log.info("valueSetList {}", valueSetList); + }); + + return valueSetList; + } + public List getAllCodeSystems() { return codeSystemRepository.findAll(); } @@ -173,7 +206,7 @@ private void recursiveRetrieveCodeSystems( entry -> { var codeSystem = (org.hl7.fhir.r4.model.CodeSystem) entry.getResource(); String codeSystemValue = ""; - for (org.hl7.fhir.r4.model.Identifier identifier : codeSystem.getIdentifier()) { + for (Identifier identifier : codeSystem.getIdentifier()) { if (identifier.getValue() != null && !identifier.getValue().isEmpty()) { codeSystemValue = identifier.getValue(); break; diff --git a/src/main/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClient.java b/src/main/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClient.java index dba948a..8d6d29b 100644 --- a/src/main/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClient.java +++ b/src/main/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClient.java @@ -11,6 +11,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; @@ -28,13 +30,15 @@ public class FhirTerminologyServiceWebClient { private final String codeSystemPath; private final String codeLookupsUrl; private final String defaultProfile; + private final String searchValueSetEndpoint; public FhirTerminologyServiceWebClient( @Value("${client.fhir-terminology-service.base-url}") String fhirTerminologyServiceBaseUrl, @Value("${client.fhir-terminology-service.manifests-urn}") String manifestUrn, @Value("${client.fhir-terminology-service.code-system-urn}") String codeSystemUrn, @Value("${client.fhir-terminology-service.code-lookups}") String codeLookupsUrl, - @Value("${client.default_profile}") String defaultProfile) { + @Value("${client.default_profile}") String defaultProfile, + @Value("${client.search_value_set_endpoint}") String searchValueSetEndpoint) { fhirTerminologyWebClient = WebClient.builder() .baseUrl(fhirTerminologyServiceBaseUrl) @@ -46,6 +50,7 @@ public FhirTerminologyServiceWebClient( this.codeSystemPath = codeSystemUrn; this.codeLookupsUrl = codeLookupsUrl; this.defaultProfile = defaultProfile; + this.searchValueSetEndpoint = searchValueSetEndpoint; } public String getManifestBundle(String apiKey) { @@ -59,6 +64,27 @@ public String getCodeSystemsPage(Integer offset, Integer count, String apiKey) { return fetchResourceFromVsac(codeUri.toString(), apiKey, "CodeSystem"); } + public String searchValueSets(String apiKey, Map queryParams) { + if (queryParams.containsKey("url")) { + String urlValue = queryParams.get("url"); + // if user didnt add htpp:// we do + if (!urlValue.startsWith("http://")) { + urlValue = "http://" + urlValue; + } + queryParams.put("url", urlValue); + } + MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); + multiValueMap.setAll(queryParams); + URI uri = + UriComponentsBuilder.fromUriString(searchValueSetEndpoint) + .queryParams(multiValueMap) + .encode() + .build() + .toUri(); + log.info("value set search url is: {}", uri.toString()); + return fetchResourceFromVsac(uri.toString(), apiKey, "bundle"); + } + public String getValueSetResource( String apiKey, ValueSetsSearchCriteria.ValueSetParams valueSetParams, diff --git a/src/main/java/gov/cms/madie/terminology/webclient/TerminologyServiceWebClient.java b/src/main/java/gov/cms/madie/terminology/webclient/TerminologyServiceWebClient.java index 5a8dd1a..352b4c9 100644 --- a/src/main/java/gov/cms/madie/terminology/webclient/TerminologyServiceWebClient.java +++ b/src/main/java/gov/cms/madie/terminology/webclient/TerminologyServiceWebClient.java @@ -29,7 +29,6 @@ public TerminologyServiceWebClient( @Value("${client.vsac_base_url}") String baseUrl, @Value("${client.valueset_endpoint}") String valueSetEndpoint, @Value("${client.default_profile}") String defaultProfile) { - this.terminologyClient = webClientBuilder.baseUrl(baseUrl).build(); this.baseUrl = baseUrl; this.valueSetEndpoint = valueSetEndpoint; 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 b3b9b4c..72cefc0 100644 --- a/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java +++ b/src/test/java/gov/cms/madie/terminology/controller/VsacFhirTerminologyControllerTest.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; diff --git a/src/test/java/gov/cms/madie/terminology/task/UpdateCodeSystemTaskTest.java b/src/test/java/gov/cms/madie/terminology/task/UpdateCodeSystemTaskTest.java index e1ef2ae..5ab28dc 100644 --- a/src/test/java/gov/cms/madie/terminology/task/UpdateCodeSystemTaskTest.java +++ b/src/test/java/gov/cms/madie/terminology/task/UpdateCodeSystemTaskTest.java @@ -17,6 +17,7 @@ public class UpdateCodeSystemTaskTest { @Mock private FhirTerminologyService fhirTerminologyService; @InjectMocks UpdateCodeSystemTask updateCodeSystemTask; + @Test void updateCodeSystemTaskTest() { UmlsUser umlsUser = new UmlsUser(); diff --git a/src/test/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClientTest.java b/src/test/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClientTest.java index 8fb177d..14b6606 100644 --- a/src/test/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClientTest.java +++ b/src/test/java/gov/cms/madie/terminology/webclient/FhirTerminologyServiceWebClientTest.java @@ -27,6 +27,7 @@ class FhirTerminologyServiceWebClientTest { private static final String MOCK_CODE_LOOKUP = "/CodeSystem/$lookup"; private static final String DEFAULT_PROFILE = "Most Recent Code System Versions in VSAC"; public static MockWebServer mockBackEnd; + private static final String SEARCH_VALUE_SET_ENDPOINT = "https://cts.nlm.nih.gov/fhir/ValueSet"; private ValueSetsSearchCriteria.ValueSetParams testValueSetParams; @@ -44,7 +45,12 @@ void initialize() { String baseUrl = String.format("http://localhost:%s", mockBackEnd.getPort()); fhirTerminologyServiceWebClient = new FhirTerminologyServiceWebClient( - baseUrl, MOCK_MANIFEST_URN, MOCK_CODE_SYSTEM_URN, MOCK_CODE_LOOKUP, DEFAULT_PROFILE); + baseUrl, + MOCK_MANIFEST_URN, + MOCK_CODE_SYSTEM_URN, + MOCK_CODE_LOOKUP, + DEFAULT_PROFILE, + SEARCH_VALUE_SET_ENDPOINT); } @AfterAll