Search in sources :

Example 1 with PATIENT

use of org.hl7.fhir.r4.model.codesystems.ResourceTypes.PATIENT in project beneficiary-fhir-data by CMSgov.

the class TransformerUtils method addResourcesToBundle.

/**
 * @param bundle a {@link Bundle} to add the list of {@link ExplanationOfBenefit} resources to.
 * @param resources a list of either {@link ExplanationOfBenefit}s, {@link Coverage}s, or {@link
 *     Patient}s, of which a portion will be added to the bundle based on the paging values
 * @return Returns a {@link Bundle} of {@link ExplanationOfBenefit}s, {@link Coverage}s, or {@link
 *     Patient}s, which may contain multiple matching resources, or may also be empty.
 */
public static Bundle addResourcesToBundle(Bundle bundle, List<IBaseResource> resources) {
    Set<String> beneIds = new HashSet<String>();
    for (IBaseResource res : resources) {
        BundleEntryComponent entry = bundle.addEntry();
        entry.setResource((Resource) res);
        if (entry.getResource().getResourceType() == ResourceType.ExplanationOfBenefit) {
            ExplanationOfBenefit eob = ((ExplanationOfBenefit) entry.getResource());
            if (eob != null && eob.getPatient() != null && !Strings.isNullOrEmpty(eob.getPatient().getReference())) {
                String reference = eob.getPatient().getReference().replace("Patient/", "");
                if (!Strings.isNullOrEmpty(reference)) {
                    beneIds.add(reference);
                }
            }
        } else if (entry.getResource().getResourceType() == ResourceType.Patient) {
            Patient patient = ((Patient) entry.getResource());
            if (patient != null && !Strings.isNullOrEmpty(patient.getId())) {
                beneIds.add(patient.getId());
            }
        } else if (entry.getResource().getResourceType() == ResourceType.Coverage) {
            Coverage coverage = ((Coverage) entry.getResource());
            if (coverage != null && coverage.getBeneficiary() != null && !Strings.isNullOrEmpty(coverage.getBeneficiary().getReference())) {
                String reference = coverage.getBeneficiary().getReference().replace("Patient/", "");
                if (!Strings.isNullOrEmpty(reference)) {
                    beneIds.add(reference);
                }
            }
        }
    }
    logBeneIdToMdc(beneIds);
    return bundle;
}
Also used : BundleEntryComponent(org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent) Patient(org.hl7.fhir.dstu3.model.Patient) Coverage(org.hl7.fhir.dstu3.model.Coverage) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) ExplanationOfBenefit(org.hl7.fhir.dstu3.model.ExplanationOfBenefit) HashSet(java.util.HashSet)

Example 2 with PATIENT

use of org.hl7.fhir.r4.model.codesystems.ResourceTypes.PATIENT in project beneficiary-fhir-data by CMSgov.

the class ExplanationOfBenefitResourceProvider method findByPatient.

/**
 * Adds support for the FHIR "search" operation for {@link ExplanationOfBenefit}s, allowing users
 * to search by {@link ExplanationOfBenefit#getPatient()}.
 *
 * <p>The {@link Search} annotation indicates that this method supports the search operation.
 * There may be many different methods annotated with this {@link Search} annotation, to support
 * many different search criteria.
 *
 * @param patient a {@link ReferenceParam} for the {@link ExplanationOfBenefit#getPatient()} to
 *     try and find matches for {@link ExplanationOfBenefit}s
 * @param type a list of {@link ClaimType} to include in the result. Defaults to all types.
 * @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
 *     pagination
 * @param excludeSamhsa an {@link OptionalParam} that, if <code>"true"</code>, will use {@link
 *     Stu3EobSamhsaMatcher} to filter out all SAMHSA-related claims from the results
 * @param lastUpdated an {@link OptionalParam} that specifies a date range for the lastUpdated
 *     field.
 * @param serviceDate an {@link OptionalParam} that specifies a date range for {@link
 *     ExplanationOfBenefit}s that completed
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a {@link Bundle} of {@link ExplanationOfBenefit}s, which may contain multiple
 *     matching resources, or may also be empty.
 */
@Search
@Trace
public Bundle findByPatient(@RequiredParam(name = ExplanationOfBenefit.SP_PATIENT) @Description(shortDefinition = "The patient identifier to search for") ReferenceParam patient, @OptionalParam(name = "type") @Description(shortDefinition = "A list of claim types to include") TokenAndListParam type, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "excludeSAMHSA") @Description(shortDefinition = "If true, exclude all SAMHSA-related resources") String excludeSamhsa, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, @OptionalParam(name = "service-date") @Description(shortDefinition = "Include resources that completed in the given range") DateRangeParam serviceDate, RequestDetails requestDetails) {
    /*
     * startIndex is an optional parameter here because it must be declared in the
     * event it is passed in. However, it is not being used here because it is also
     * contained within requestDetails and parsed out along with other parameters
     * later.
     */
    String beneficiaryId = patient.getIdPart();
    Set<ClaimType> claimTypes = parseTypeParam(type);
    Boolean includeTaxNumbers = returnIncludeTaxNumbers(requestDetails);
    OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/ExplanationOfBenefit?");
    Operation operation = new Operation(Operation.Endpoint.V1_EOB);
    operation.setOption("by", "patient");
    operation.setOption("types", (claimTypes.size() == ClaimType.values().length) ? "*" : claimTypes.stream().sorted(Comparator.comparing(ClaimType::name)).collect(Collectors.toList()).toString());
    operation.setOption("IncludeTaxNumbers", "" + includeTaxNumbers);
    operation.setOption("pageSize", paging.isPagingRequested() ? "" + paging.getPageSize() : "*");
    operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
    operation.setOption("service-date", Boolean.toString(serviceDate != null && !serviceDate.isEmpty()));
    operation.publishOperationName();
    List<IBaseResource> eobs = new ArrayList<IBaseResource>();
    // Optimize when the lastUpdated parameter is specified and result set is empty
    if (loadedFilterManager.isResultSetEmpty(beneficiaryId, lastUpdated)) {
        return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
    }
    /*
     * The way our JPA/SQL schema is setup, we have to run a separate search for
     * each claim type, then combine the results. It's not super efficient, but it's
     * also not so inefficient that it's worth fixing.
     */
    if (claimTypes.contains(ClaimType.CARRIER))
        eobs.addAll(transformToEobs(ClaimType.CARRIER, findClaimTypeByPatient(ClaimType.CARRIER, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.DME))
        eobs.addAll(transformToEobs(ClaimType.DME, findClaimTypeByPatient(ClaimType.DME, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.HHA))
        eobs.addAll(transformToEobs(ClaimType.HHA, findClaimTypeByPatient(ClaimType.HHA, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.HOSPICE))
        eobs.addAll(transformToEobs(ClaimType.HOSPICE, findClaimTypeByPatient(ClaimType.HOSPICE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.INPATIENT))
        eobs.addAll(transformToEobs(ClaimType.INPATIENT, findClaimTypeByPatient(ClaimType.INPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.OUTPATIENT))
        eobs.addAll(transformToEobs(ClaimType.OUTPATIENT, findClaimTypeByPatient(ClaimType.OUTPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.PDE))
        eobs.addAll(transformToEobs(ClaimType.PDE, findClaimTypeByPatient(ClaimType.PDE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.SNF))
        eobs.addAll(transformToEobs(ClaimType.SNF, findClaimTypeByPatient(ClaimType.SNF, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (Boolean.parseBoolean(excludeSamhsa))
        filterSamhsa(eobs);
    eobs.sort(ExplanationOfBenefitResourceProvider::compareByClaimIdThenClaimType);
    // Add bene_id to MDC logs
    TransformerUtils.logBeneIdToMdc(Arrays.asList(beneficiaryId));
    return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
}
Also used : OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) ArrayList(java.util.ArrayList) Operation(gov.cms.bfd.server.war.Operation) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) Trace(com.newrelic.api.agent.Trace) Search(ca.uhn.fhir.rest.annotation.Search)

Example 3 with PATIENT

use of org.hl7.fhir.r4.model.codesystems.ResourceTypes.PATIENT in project beneficiary-fhir-data by CMSgov.

the class PatientResourceProvider method queryDatabaseByHash.

/**
 * @param hash the {@link Beneficiary} hash value to match
 * @param hashType a string to represent the hash type (used for logging purposes)
 * @param requestHeader the {@link #RequestHeaders} where resource request headers are
 *     encapsulated
 * @param beneficiaryHashField the JPA location of the beneficiary hash field
 * @param beneficiaryHistoryHashField the JPA location of the beneficiary history hash field
 * @return a FHIR {@link Patient} for the CCW {@link Beneficiary} that matches the specified
 *     {@link Beneficiary} hash value
 * @throws NoResultException A {@link NoResultException} will be thrown if no matching {@link
 *     Beneficiary} can be found
 */
@Trace
private Patient queryDatabaseByHash(String hash, String hashType, SingularAttribute<Beneficiary, String> beneficiaryHashField, SingularAttribute<BeneficiaryHistory, String> beneficiaryHistoryHashField, RequestHeaders requestHeader) {
    if (hash == null || hash.trim().isEmpty())
        throw new IllegalArgumentException();
    /*
     * Beneficiaries' HICN/MBIs can change over time and those past HICN/MBIs may land in
     * BeneficiaryHistory records. Accordingly, we need to search for matching HICN/MBIs in both the
     * Beneficiary and the BeneficiaryHistory records.
     *
     * There's no sane way to do this in a single query with JPA 2.1, it appears: JPA doesn't
     * support UNIONs and it doesn't support subqueries in FROM clauses. That said, the ideal query
     * would look like this:
     *
     * SELECT * FROM ( SELECT DISTINCT "beneficiaryId" FROM "Beneficiaries" WHERE "hicn" =
     * :'hicn_hash' UNION SELECT DISTINCT "beneficiaryId" FROM "BeneficiariesHistory" WHERE "hicn" =
     * :'hicn_hash') AS matching_benes INNER JOIN "Beneficiaries" ON matching_benes."beneficiaryId"
     * = "Beneficiaries"."beneficiaryId" LEFT JOIN "BeneficiariesHistory" ON
     * "Beneficiaries"."beneficiaryId" = "BeneficiariesHistory"."beneficiaryId" LEFT JOIN
     * "MedicareBeneficiaryIdHistory" ON "Beneficiaries"."beneficiaryId" =
     * "MedicareBeneficiaryIdHistory"."beneficiaryId";
     *
     * ... with the returned columns and JOINs being dynamic, depending on IncludeIdentifiers.
     *
     * In lieu of that, we run two queries: one to find HICN/MBI matches in BeneficiariesHistory,
     * and a second to find BENE_ID or HICN/MBI matches in Beneficiaries (with all of their data, so
     * we're ready to return the result). This is bad and dumb but I can't find a better working
     * alternative.
     *
     * (I'll just note that I did also try JPA/Hibernate native SQL queries but couldn't get the
     * joins or fetch groups to work with them.)
     *
     * If we want to fix this, we need to move identifiers out entirely to separate tables:
     * BeneficiaryHicns and BeneficiaryMbis. We could then safely query these tables and join them
     * back to Beneficiaries (and hopefully the optimizer will play nice, too).
     */
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    // First, find all matching hashes from BeneficiariesHistory.
    CriteriaQuery<String> beneHistoryMatches = builder.createQuery(String.class);
    Root<BeneficiaryHistory> beneHistoryMatchesRoot = beneHistoryMatches.from(BeneficiaryHistory.class);
    beneHistoryMatches.select(beneHistoryMatchesRoot.get(BeneficiaryHistory_.beneficiaryId));
    beneHistoryMatches.where(builder.equal(beneHistoryMatchesRoot.get(beneficiaryHistoryHashField), hash));
    List<String> matchingIdsFromBeneHistory = null;
    Long hicnsFromHistoryQueryNanoSeconds = null;
    Timer.Context beneHistoryMatchesTimer = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_" + hashType, hashType + "s_from_beneficiarieshistory")).time();
    try {
        matchingIdsFromBeneHistory = entityManager.createQuery(beneHistoryMatches).getResultList();
    } finally {
        hicnsFromHistoryQueryNanoSeconds = beneHistoryMatchesTimer.stop();
        TransformerUtils.recordQueryInMdc("bene_by_" + hashType + "." + hashType + "s_from_beneficiarieshistory", hicnsFromHistoryQueryNanoSeconds, matchingIdsFromBeneHistory == null ? 0 : matchingIdsFromBeneHistory.size());
    }
    // Then, find all Beneficiary records that match the hash or those BENE_IDs.
    CriteriaQuery<Beneficiary> beneMatches = builder.createQuery(Beneficiary.class);
    Root<Beneficiary> beneMatchesRoot = beneMatches.from(Beneficiary.class);
    beneMatchesRoot.fetch(Beneficiary_.skippedRifRecords, JoinType.LEFT);
    if (requestHeader.isHICNinIncludeIdentifiers())
        beneMatchesRoot.fetch(Beneficiary_.beneficiaryHistories, JoinType.LEFT);
    if (requestHeader.isMBIinIncludeIdentifiers())
        beneMatchesRoot.fetch(Beneficiary_.medicareBeneficiaryIdHistories, JoinType.LEFT);
    beneMatches.select(beneMatchesRoot);
    Predicate beneHashMatches = builder.equal(beneMatchesRoot.get(beneficiaryHashField), hash);
    if (matchingIdsFromBeneHistory != null && !matchingIdsFromBeneHistory.isEmpty()) {
        Predicate beneHistoryHashMatches = beneMatchesRoot.get(Beneficiary_.beneficiaryId).in(matchingIdsFromBeneHistory);
        beneMatches.where(builder.or(beneHashMatches, beneHistoryHashMatches));
    } else {
        beneMatches.where(beneHashMatches);
    }
    List<Beneficiary> matchingBenes = Collections.emptyList();
    Long benesByHashOrIdQueryNanoSeconds = null;
    Timer.Context timerHicnQuery = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_" + hashType, "bene_by_" + hashType + "_or_id")).time();
    try {
        matchingBenes = entityManager.createQuery(beneMatches).getResultList();
    } finally {
        benesByHashOrIdQueryNanoSeconds = timerHicnQuery.stop();
        TransformerUtils.recordQueryInMdc(String.format("bene_by_" + hashType + ".bene_by_" + hashType + "_or_id.include_%s", String.join("_", (List<String>) requestHeader.getValue(HEADER_NAME_INCLUDE_IDENTIFIERS))), benesByHashOrIdQueryNanoSeconds, matchingBenes.size());
    }
    // Then, if we found more than one distinct BENE_ID, or none, throw an error.
    long distinctBeneIds = matchingBenes.stream().map(Beneficiary::getBeneficiaryId).filter(Objects::nonNull).distinct().count();
    Beneficiary beneficiary = null;
    if (distinctBeneIds <= 0) {
        throw new NoResultException();
    } else if (distinctBeneIds > 1) {
        MDC.put("database_query.by_hash.collision.distinct_bene_ids", Long.toString(distinctBeneIds));
        throw new ResourceNotFoundException("By hash query found more than one distinct BENE_ID: " + Long.toString(distinctBeneIds));
    } else if (distinctBeneIds == 1) {
        beneficiary = matchingBenes.get(0);
    }
    // Null out the unhashed HICNs if we're not supposed to be returning them
    if (!requestHeader.isHICNinIncludeIdentifiers()) {
        beneficiary.setHicnUnhashed(Optional.empty());
    }
    // Null out the unhashed MBIs if we're not supposed to be returning
    if (!requestHeader.isMBIinIncludeIdentifiers()) {
        beneficiary.setMedicareBeneficiaryId(Optional.empty());
    }
    Patient patient = BeneficiaryTransformer.transform(metricRegistry, beneficiary, requestHeader);
    return patient;
}
Also used : CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder) BeneficiaryHistory(gov.cms.bfd.model.rif.BeneficiaryHistory) Patient(org.hl7.fhir.dstu3.model.Patient) NoResultException(javax.persistence.NoResultException) Predicate(javax.persistence.criteria.Predicate) Timer(com.codahale.metrics.Timer) Objects(java.util.Objects) ResourceNotFoundException(ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException) Beneficiary(gov.cms.bfd.model.rif.Beneficiary) Trace(com.newrelic.api.agent.Trace)

Example 4 with PATIENT

use of org.hl7.fhir.r4.model.codesystems.ResourceTypes.PATIENT in project beneficiary-fhir-data by CMSgov.

the class PatientResourceProvider method read.

/**
 * Adds support for the FHIR "read" operation, for {@link Patient}s. The {@link Read} annotation
 * indicates that this method supports the read operation.
 *
 * <p>Read operations take a single parameter annotated with {@link IdParam}, and should return a
 * single resource instance.
 *
 * @param patientId The read operation takes one parameter, which must be of type {@link IdType}
 *     and must be annotated with the {@link IdParam} annotation.
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a resource matching the specified {@link IdDt}, or <code>null</code> if none
 *     exists.
 */
@Read(version = false)
@Trace
public Patient read(@IdParam IdType patientId, RequestDetails requestDetails) {
    if (patientId == null)
        throw new IllegalArgumentException();
    if (patientId.getVersionIdPartAsLong() != null)
        throw new IllegalArgumentException();
    String beneIdText = patientId.getIdPart();
    if (beneIdText == null || beneIdText.trim().isEmpty())
        throw new IllegalArgumentException();
    RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
    Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
    operation.setOption("by", "id");
    // there is another method with exclude list: requestHeader.getNVPairs(<excludeHeaders>)
    requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
    operation.publishOperationName();
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Beneficiary> criteria = builder.createQuery(Beneficiary.class);
    Root<Beneficiary> root = criteria.from(Beneficiary.class);
    root.fetch(Beneficiary_.skippedRifRecords, JoinType.LEFT);
    if (requestHeader.isHICNinIncludeIdentifiers())
        root.fetch(Beneficiary_.beneficiaryHistories, JoinType.LEFT);
    if (requestHeader.isMBIinIncludeIdentifiers())
        root.fetch(Beneficiary_.medicareBeneficiaryIdHistories, JoinType.LEFT);
    criteria.select(root);
    criteria.where(builder.equal(root.get(Beneficiary_.beneficiaryId), beneIdText));
    Beneficiary beneficiary = null;
    Long beneByIdQueryNanoSeconds = null;
    Timer.Context timerBeneQuery = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_id")).time();
    try {
        beneficiary = entityManager.createQuery(criteria).getSingleResult();
    } catch (NoResultException e) {
        throw new ResourceNotFoundException(patientId);
    } finally {
        beneByIdQueryNanoSeconds = timerBeneQuery.stop();
        TransformerUtils.recordQueryInMdc(String.format("bene_by_id.include_%s", String.join("_", (List<String>) requestHeader.getValue(HEADER_NAME_INCLUDE_IDENTIFIERS))), beneByIdQueryNanoSeconds, beneficiary == null ? 0 : 1);
    }
    // Null out the unhashed HICNs if we're not supposed to be returning them
    if (!requestHeader.isHICNinIncludeIdentifiers()) {
        beneficiary.setHicnUnhashed(Optional.empty());
    }
    // Null out the unhashed MBIs if we're not supposed to be returning
    if (!requestHeader.isMBIinIncludeIdentifiers()) {
        beneficiary.setMedicareBeneficiaryId(Optional.empty());
    }
    // Add bene_id to MDC logs
    TransformerUtils.logBeneIdToMdc(Arrays.asList(beneIdText));
    Patient patient = BeneficiaryTransformer.transform(metricRegistry, beneficiary, requestHeader);
    return patient;
}
Also used : CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder) Patient(org.hl7.fhir.dstu3.model.Patient) Operation(gov.cms.bfd.server.war.Operation) NoResultException(javax.persistence.NoResultException) Timer(com.codahale.metrics.Timer) RequestHeaders(gov.cms.bfd.server.war.commons.RequestHeaders) ResourceNotFoundException(ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException) Beneficiary(gov.cms.bfd.model.rif.Beneficiary) Read(ca.uhn.fhir.rest.annotation.Read) Trace(com.newrelic.api.agent.Trace)

Example 5 with PATIENT

use of org.hl7.fhir.r4.model.codesystems.ResourceTypes.PATIENT in project beneficiary-fhir-data by CMSgov.

the class PatientResourceProvider method searchByIdentifier.

/**
 * Adds support for the FHIR "search" operation for {@link Patient}s, allowing users to search by
 * {@link Patient#getIdentifier()}. Specifically, the following criteria are supported:
 *
 * <ul>
 *   <li>Matching a {@link Beneficiary#getHicn()} hash value: when {@link TokenParam#getSystem()}
 *       matches one of the {@link #SUPPORTED_HASH_IDENTIFIER_SYSTEMS} entries.
 * </ul>
 *
 * <p>Searches that don't match one of the above forms are not supported.
 *
 * <p>The {@link Search} annotation indicates that this method supports the search operation.
 * There may be many different methods annotated with this {@link Search} annotation, to support
 * many different search criteria.
 *
 * @param identifier an {@link Identifier} {@link TokenParam} for the {@link
 *     Patient#getIdentifier()} to try and find a matching {@link Patient} for
 * @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
 *     pagination
 * @param lastUpdated an {@link OptionalParam} to filter the results based on the passed date
 *     range
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a {@link List} of {@link Patient}s, which may contain multiple matching
 *     resources, or may also be empty.
 */
@Search
@Trace
public Bundle searchByIdentifier(@RequiredParam(name = Patient.SP_IDENTIFIER) @Description(shortDefinition = "The patient identifier to search for") TokenParam identifier, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, RequestDetails requestDetails) {
    if (identifier.getQueryParameterQualifier() != null)
        throw new InvalidRequestException("Unsupported query parameter qualifier: " + identifier.getQueryParameterQualifier());
    if (!SUPPORTED_HASH_IDENTIFIER_SYSTEMS.contains(identifier.getSystem()))
        throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
    RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
    Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
    operation.setOption("by", "identifier");
    requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
    operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
    operation.publishOperationName();
    List<IBaseResource> patients;
    try {
        Patient patient;
        switch(identifier.getSystem()) {
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH:
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH_OLD:
                patient = queryDatabaseByHicnHash(identifier.getValue(), requestHeader);
                break;
            case TransformerConstants.CODING_BBAPI_BENE_MBI_HASH:
                patient = queryDatabaseByMbiHash(identifier.getValue(), requestHeader);
                break;
            default:
                throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
        }
        patients = QueryUtils.isInRange(patient.getMeta().getLastUpdated().toInstant(), lastUpdated) ? Collections.singletonList(patient) : Collections.emptyList();
    } catch (NoResultException e) {
        patients = new LinkedList<>();
    }
    OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/Patient?");
    Bundle bundle = TransformerUtils.createBundle(paging, patients, loadedFilterManager.getTransactionTime());
    return bundle;
}
Also used : OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) Bundle(org.hl7.fhir.dstu3.model.Bundle) Patient(org.hl7.fhir.dstu3.model.Patient) InvalidRequestException(ca.uhn.fhir.rest.server.exceptions.InvalidRequestException) Operation(gov.cms.bfd.server.war.Operation) NoResultException(javax.persistence.NoResultException) RequestHeaders(gov.cms.bfd.server.war.commons.RequestHeaders) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) LinkedList(java.util.LinkedList) Trace(com.newrelic.api.agent.Trace) Search(ca.uhn.fhir.rest.annotation.Search)

Aggregations

Test (org.junit.Test)576 Test (org.junit.jupiter.api.Test)442 Patient (org.hl7.fhir.r4.model.Patient)437 IBaseResource (org.hl7.fhir.instance.model.api.IBaseResource)255 HashMap (java.util.HashMap)251 Patient (org.hl7.fhir.dstu3.model.Patient)249 IBundleProvider (ca.uhn.fhir.rest.api.server.IBundleProvider)228 Bundle (org.hl7.fhir.r4.model.Bundle)203 Path (javax.ws.rs.Path)188 Date (java.util.Date)171 Produces (javax.ws.rs.Produces)163 ArrayList (java.util.ArrayList)156 JsonObject (javax.json.JsonObject)141 SearchParameterMap (org.openmrs.module.fhir2.api.search.param.SearchParameterMap)140 MockHttpServletResponse (org.springframework.mock.web.MockHttpServletResponse)138 ReferenceParam (ca.uhn.fhir.rest.param.ReferenceParam)137 ReferenceAndListParam (ca.uhn.fhir.rest.param.ReferenceAndListParam)128 ReferenceOrListParam (ca.uhn.fhir.rest.param.ReferenceOrListParam)128 BaseModuleContextSensitiveTest (org.openmrs.test.BaseModuleContextSensitiveTest)125 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)120