Skip to content

Commit

Permalink
MAT-6393: Add search functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmcphillips committed May 13, 2024
1 parent faac06a commit b854cc6
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -69,6 +70,16 @@ public ResponseEntity<List<CodeSystem>> getAllCodeSystems(Principal principal) {
return ResponseEntity.ok().body(fhirTerminologyService.getAllCodeSystems());
}

@GetMapping(path = "/search-value-sets", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<List<ValueSetForSearch>> searchValueSets(
Principal principal, @RequestParam Map<String, String> 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<Code> getCode(
@RequestParam() String code,
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/gov/cms/madie/terminology/dto/ValueSetForSearch.java
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,16 +13,18 @@
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;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
Expand Down Expand Up @@ -120,6 +119,43 @@ private List<QdmValueSet.Concept> getValueSetConcepts(
return List.of();
}

public List<ValueSetForSearch> searchValueSets(String apiKey, Map<String, String> queryParams) {
IParser parser = fhirContext.newJsonParser();
String responseString = fhirTerminologyServiceWebClient.searchValueSets(apiKey, queryParams);
Bundle bundle = parser.parseResource(Bundle.class, responseString);
List<ValueSetForSearch> 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<CodeSystem> getAllCodeSystems() {
// remove items that are marked as not present in vsac to cut expense
List<CodeSystemEntry> codeSystemMappingEntries =
Expand Down Expand Up @@ -211,7 +247,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -46,6 +50,7 @@ public FhirTerminologyServiceWebClient(
this.codeSystemPath = codeSystemUrn;
this.codeLookupsUrl = codeLookupsUrl;
this.defaultProfile = defaultProfile;
this.searchValueSetEndpoint = searchValueSetEndpoint;
}

public String getManifestBundle(String apiKey) {
Expand All @@ -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<String, String> 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<String, String> 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.security.Principal;
import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down

0 comments on commit b854cc6

Please sign in to comment.