Skip to content

Commit

Permalink
Merge branch 'develop' into MAT-6923-update-pom.xml
Browse files Browse the repository at this point in the history
  • Loading branch information
riddhi-desai authored Apr 16, 2024
2 parents 0d1279a + d2d6b98 commit 24e1e1a
Show file tree
Hide file tree
Showing 14 changed files with 564 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package gov.cms.madie.terminology.controller;

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.ValueSetsSearchCriteria;
import gov.cms.madie.terminology.exceptions.VsacUnauthorizedException;
import gov.cms.madie.terminology.models.CodeSystem;
import gov.cms.madie.terminology.models.UmlsUser;
import gov.cms.madie.terminology.service.FhirTerminologyService;
import gov.cms.madie.terminology.service.VsacService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -20,7 +19,6 @@

import java.security.Principal;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping(path = "/terminology")
Expand All @@ -34,15 +32,8 @@ public class VsacFhirTerminologyController {
public ResponseEntity<List<ManifestExpansion>> getManifests(Principal principal) {
final String username = principal.getName();
log.info("Retrieving List of available manifests, requested by HARP ID : [{}}]", username);
Optional<UmlsUser> umlsUser = vsacService.findByHarpId(username);
if (umlsUser.isPresent() && !StringUtils.isBlank(umlsUser.get().getApiKey())) {
return ResponseEntity.ok().body(fhirTerminologyService.getManifests(umlsUser.get()));
}
log.error(
"Unable to Retrieve List of available manifests, "
+ "UMLS Authentication Key Not found for user : [{}}]",
username);
throw new VsacUnauthorizedException("Please login to UMLS before proceeding");
UmlsUser umlsUser = vsacService.verifyUmlsAccess(username);
return ResponseEntity.ok().body(fhirTerminologyService.getManifests(umlsUser));
}

@PutMapping("/value-sets/expansion/qdm")
Expand All @@ -52,17 +43,10 @@ public ResponseEntity<List<QdmValueSet>> getValueSetsExpansions(
log.info(
"User [{}] is attempting to fetch value sets expansions from VSAC FHIR Terminology Server.",
username);
Optional<UmlsUser> umlsUser = vsacService.findByHarpId(username);
if (umlsUser.isPresent() && !StringUtils.isBlank(umlsUser.get().getApiKey())) {
List<QdmValueSet> qdmValueSets =
fhirTerminologyService.getValueSetsExpansionsForQdm(searchCriteria, umlsUser.get());
return ResponseEntity.ok().body(qdmValueSets);
}
log.error(
"Unable to Retrieve Value Sets Expansions, "
+ "UMLS Authentication Key Not found for user : [{}}]",
username);
throw new VsacUnauthorizedException("Please login to UMLS before proceeding");
UmlsUser umlsUser = vsacService.verifyUmlsAccess(username);
List<QdmValueSet> qdmValueSets =
fhirTerminologyService.getValueSetsExpansionsForQdm(searchCriteria, umlsUser);
return ResponseEntity.ok().body(qdmValueSets);
}

@GetMapping(path = "/update-code-systems", produces = MediaType.APPLICATION_JSON_VALUE)
Expand All @@ -73,18 +57,8 @@ public ResponseEntity<List<CodeSystem>> retrieveAndUpdateCodeSystems(
@Value("${admin-api-key}") String apiKey,
@RequestHeader("Authorization") String accessToken) {
final String username = principal.getName();
Optional<UmlsUser> umlsUser = vsacService.findByHarpId(username);

if (umlsUser.isPresent() && !StringUtils.isBlank(umlsUser.get().getApiKey())) {
return ResponseEntity.ok()
.body(fhirTerminologyService.retrieveAllCodeSystems(umlsUser.get()));
} else {
log.error(
"Unable to Retrieve List of code systems, "
+ "UMLS Authentication Key Not found for user : [{}}]",
username);
throw new VsacUnauthorizedException("Please login to UMLS before proceeding");
}
UmlsUser umlsUser = vsacService.verifyUmlsAccess(username);
return ResponseEntity.ok().body(fhirTerminologyService.retrieveAllCodeSystems(umlsUser));
}

@GetMapping(path = "/get-code-systems", produces = MediaType.APPLICATION_JSON_VALUE)
Expand All @@ -93,4 +67,22 @@ public ResponseEntity<List<CodeSystem>> getAllCodeSystems(Principal principal) {
log.info("Retrieving list of codeSystems for user: {}", username);
return ResponseEntity.ok().body(fhirTerminologyService.getAllCodeSystems());
}

@GetMapping(path = "/code", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Code> getCode(
@RequestParam() String code,
@RequestParam() String codeSystem,
@RequestParam() String version,
Principal principal) {
final String username = principal.getName();
log.info(
"User {} is performing code retrieve for code: {}, system: {} and version: {}",
username,
code,
codeSystem,
version);
UmlsUser user = vsacService.verifyUmlsAccess(username);
return ResponseEntity.ok()
.body(fhirTerminologyService.retrieveCode(code, codeSystem, version, user.getApiKey()));
}
}
15 changes: 15 additions & 0 deletions src/main/java/gov/cms/madie/terminology/dto/Code.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gov.cms.madie.terminology.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder(toBuilder = true)
public class Code {
private String name;
private String display;
private String version;
private String codeSystem;
private String codeSystemOid;
private CodeStatus status;
}
13 changes: 13 additions & 0 deletions src/main/java/gov/cms/madie/terminology/dto/CodeStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package gov.cms.madie.terminology.dto;

public enum CodeStatus {
ACTIVE("active"),
INACTIVE("inactive"),
NA("Not available");

private final String value;

CodeStatus(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

public interface CodeSystemRepository extends MongoRepository<CodeSystem, String> {
Optional<CodeSystem> findById(String id);

Optional<CodeSystem> findByNameAndVersion(String name, String version);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
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.models.CodeSystem;
Expand All @@ -13,7 +15,9 @@
import gov.cms.madie.terminology.webclient.FhirTerminologyServiceWebClient;
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.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
Expand All @@ -32,6 +36,7 @@ public class FhirTerminologyService {
private final FhirTerminologyServiceWebClient fhirTerminologyServiceWebClient;
private final MappingService mappingService;
private final CodeSystemRepository codeSystemRepository;
private final VsacService vsacService;

@Cacheable("manifest-list")
public List<ManifestExpansion> getManifests(UmlsUser umlsUser) {
Expand Down Expand Up @@ -129,6 +134,34 @@ public List<CodeSystem> retrieveAllCodeSystems(UmlsUser umlsUser) {
return allCodeSystems;
}

public Code retrieveCode(String codeName, String codeSystemName, String version, String apiKey) {
if (StringUtils.isEmpty(codeName)
|| StringUtils.isEmpty(codeSystemName)
|| StringUtils.isEmpty(version)) {
return null;
}
CodeSystem codeSystem =
codeSystemRepository.findByNameAndVersion(codeSystemName, version).orElse(null);
if (codeSystem == null) {
return null;
}
String codeJson = fhirTerminologyServiceWebClient.getCodeResource(codeName, codeSystem, apiKey);
Parameters parameters = fhirContext.newJsonParser().parseResource(Parameters.class, codeJson);
Code code =
Code.builder()
.name(codeName)
.codeSystem(codeSystemName)
.version(version)
.display(parameters.getParameter("display").getValue().toString())
.codeSystemOid(parameters.getParameter("Oid").getValue().toString())
.build();
// FHIR terminology API doesn't support code status yet. workaround is to get it from SVS.
// TODO: remove once it is supported by fhir terminology service
CodeStatus status = vsacService.getCodeStatus(code, apiKey);
code.setStatus(status);
return code;
}

private void recursiveRetrieveCodeSystems(
UmlsUser umlsUser, Integer offset, Integer count, List<CodeSystem> allCodeSystems) {
log.info("requesting page offset: {} count: {}", offset, count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
Expand All @@ -35,4 +37,15 @@ public List<CodeSystemEntry> getCodeSystemEntries() {
}
return Collections.emptyList();
}

public CodeSystemEntry getCodeSystemEntry(String codeSystemName) {
List<CodeSystemEntry> codeSystemEntries = getCodeSystemEntries();
if (CollectionUtils.isEmpty(codeSystemEntries)) {
return null;
}
return codeSystemEntries.stream()
.filter(entry -> Objects.equals(entry.getName(), codeSystemName))
.findFirst()
.orElse(null);
}
}
52 changes: 52 additions & 0 deletions src/main/java/gov/cms/madie/terminology/service/VsacService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import gov.cms.madie.models.cql.terminology.CqlCode;
import gov.cms.madie.models.cql.terminology.VsacCode;
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;
import gov.cms.madie.terminology.models.UmlsUser;
import gov.cms.madie.terminology.repositories.UmlsUserRepository;
Expand All @@ -20,6 +23,7 @@

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -156,6 +160,54 @@ public List<CqlCode> validateCodes(List<CqlCode> cqlCodes, UmlsUser umlsUser, St
return cqlCodes;
}

public CodeStatus getCodeStatus(Code code, String apiKey) {
CodeSystemEntry systemEntry = mappingService.getCodeSystemEntry(code.getCodeSystem());
// do not call SVS API to get code status if the system is not in SVS API
if (systemEntry == null
|| systemEntry.getOid().contains("NOT.IN.VSAC")
|| CollectionUtils.isEmpty(systemEntry.getVersions())) {
return CodeStatus.NA;
}

// get corresponding SVS version for given FHIR version
CodeSystemEntry.Version version =
systemEntry.getVersions().stream()
.filter(v -> Objects.equals(v.getFhir(), code.getVersion()))
.findFirst()
.orElse(null);
if (version == null || version.getVsac() == null) {
return CodeStatus.NA;
}
// prepare code path e.g. CODE:/CodeSystem/ActCode/Version/9.0.0/Code/AMB/Info
String codePath =
TerminologyServiceUtil.buildCodePath(
code.getCodeSystem(), version.getVsac(), code.getName());
VsacCode svsCode = terminologyWebClient.getCode(codePath, apiKey);
if (svsCode.getStatus().equalsIgnoreCase("ok")) {
if ("Yes".equals(svsCode.getData().getResultSet().get(0).getActive())) {
return CodeStatus.ACTIVE;
} else {
return CodeStatus.INACTIVE;
}
}
return CodeStatus.NA;
}

/**
* Verify if the user with given harp id has valid UMLS user
*
* @param harpId
* @return Instance of UmlsUser
*/
public UmlsUser verifyUmlsAccess(String harpId) {
UmlsUser user = findByHarpId(harpId).orElse(null);
if (user == null || StringUtils.isBlank(user.getApiKey())) {
log.error("UMLS API Key Not found for user : [{}}]", harpId);
throw new VsacUnauthorizedException("Please login to UMLS before proceeding");
}
return user;
}

private VsacCode validateCodeAgainstVsac(String codePath, UmlsUser umlsUser) {
return terminologyWebClient.getCode(codePath, umlsUser.getApiKey());
}
Expand Down
Loading

0 comments on commit 24e1e1a

Please sign in to comment.