Skip to content

Commit

Permalink
Fixes #3944 - patient everything pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
codyebberson committed Apr 2, 2024
1 parent 7b78973 commit 5b72b90
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 70 deletions.
8 changes: 8 additions & 0 deletions packages/definitions/dist/fhir/r4/profiles-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -24099,6 +24099,14 @@
"documentation" : "See discussion below on the utility of paging through the results of the $everything operation",
"type" : "integer"
},
{
"name" : "_offset",
"use" : "in",
"min" : 0,
"max" : "1",
"documentation" : "See discussion below on the utility of paging through the results of the $everything operation",
"type" : "integer"
},
{
"name" : "return",
"use" : "out",
Expand Down
16 changes: 15 additions & 1 deletion packages/server/src/fhir/operations/patienteverything.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContentType, LOINC, createReference } from '@medplum/core';
import { Patient } from '@medplum/fhirtypes';
import { Bundle, Patient } from '@medplum/fhirtypes';
import express from 'express';
import request from 'supertest';
import { initApp, shutdownApp } from '../../app';
Expand Down Expand Up @@ -91,5 +91,19 @@ describe('Patient Everything Operation', () => {
.set('Authorization', 'Bearer ' + accessToken);
expect(res6.status).toBe(200);
expect(res6.body.entry).toHaveLength(1);

// Execute the operation with _count and _offset
const res7 = await request(app)
.get(`/fhir/R4/Patient/${res1.body.id}/$everything?_count=1&_offset=1`)
.set('Authorization', 'Bearer ' + accessToken);
expect(res7.status).toBe(200);

// Bundle should have pagination links
const bundle = res7.body as Bundle;
expect(bundle.entry).toHaveLength(1);
expect(bundle.link).toBeDefined();
expect(bundle.link?.some((link) => link.relation === 'next')).toBeTruthy();
expect(bundle.link?.some((link) => link.relation === 'first')).toBeTruthy();
expect(bundle.link?.some((link) => link.relation === 'previous')).toBeTruthy();
});
});
91 changes: 22 additions & 69 deletions packages/server/src/fhir/operations/patienteverything.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { allOk, getReferenceString, Operator, SearchRequest } from '@medplum/core';
import { allOk, getReferenceString, Operator } from '@medplum/core';
import { FhirRequest, FhirResponse } from '@medplum/fhir-router';
import {
Bundle,
BundleEntry,
CompartmentDefinitionResource,
Patient,
Resource,
ResourceType,
} from '@medplum/fhirtypes';
import { Bundle, CompartmentDefinitionResource, Patient, ResourceType } from '@medplum/fhirtypes';
import { getAuthenticatedContext } from '../../context';
import { getPatientCompartments } from '../patient';
import { Repository } from '../repo';
import { getFullUrl } from '../response';
import { getOperationDefinition } from './definitions';
import { parseInputParameters } from './utils/parameters';

const operation = getOperationDefinition('Patient', 'everything');

type PatientEverythingParameters = {
_since?: string;
_count?: number;
_offset?: number;
};

// Patient everything operation.
Expand Down Expand Up @@ -57,12 +51,19 @@ export async function getPatientEverything(
patient: Patient,
params?: PatientEverythingParameters
): Promise<Bundle> {
const patientRef = getReferenceString(patient);
const resourceList = getPatientCompartments().resource as CompartmentDefinitionResource[];
const searches: SearchRequest[] = [];
const types = resourceList.map((r) => r.code as ResourceType).filter((t) => t !== 'Binary');
types.push('Patient');
types.sort();

const filters = [
{
code: '_compartment',
operator: Operator.EQUALS,
value: getReferenceString(patient),
},
];

// Build a list of filters to apply to the searches
const filters = [];
if (params?._since) {
filters.push({
code: '_lastUpdated',
Expand All @@ -71,59 +72,11 @@ export async function getPatientEverything(
});
}

// Build a list of searches
for (const resource of resourceList) {
const searchParams = resource.param;
if (!searchParams) {
continue;
}
for (const code of searchParams) {
searches.push({
resourceType: resource.code as ResourceType,
count: 1000,
filters: [
{
code,
operator: Operator.EQUALS,
value: patientRef,
},
...filters,
],
});
}
}

// Execute all of the searches in parallel
// Some day we could do this in a single SQL query
const promises = searches.map((searchRequest) => repo.search(searchRequest));
const searchResults = await Promise.all(promises);

// Build the result bundle
const entry: BundleEntry[] = [];

if (!params?._since || (patient.meta?.lastUpdated as string) >= params?._since) {
entry.push({
fullUrl: getFullUrl('Patient', patient.id as string),
resource: patient,
});
}

const resourceSet = new Set<string>([getReferenceString(patient)]);
for (const searchResult of searchResults) {
if (searchResult.entry) {
for (const e of searchResult.entry) {
const resourceRef = getReferenceString(e.resource as Resource);
if (!resourceSet.has(resourceRef)) {
resourceSet.add(resourceRef);
entry.push(e);
}
}
}
}

return {
resourceType: 'Bundle',
type: 'searchset',
entry,
};
return repo.search({
resourceType: 'Patient',
types,
filters,
count: params?._count,
offset: params?._offset,
});
}

0 comments on commit 5b72b90

Please sign in to comment.