Skip to main content
Glama
searchparameter.test.ts13.7 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { globalSchema, indexSearchParameterBundle, indexStructureDefinitionBundle } from '@medplum/core'; import { readJson, SEARCH_PARAMETER_BUNDLE_FILES } from '@medplum/definitions'; import type { Bundle, BundleEntry, ResourceType, SearchParameter } from '@medplum/fhirtypes'; import { AddressTable } from './lookups/address'; import { HumanNameTable } from './lookups/humanname'; import type { ColumnSearchParameterImplementation, LookupTableSearchParameterImplementation, SearchParameterImplementation, TokenColumnSearchParameterImplementation, } from './searchparameter'; import { getSearchParameterImplementation, SearchStrategies } from './searchparameter'; describe('SearchParameterImplementation', () => { const indexedSearchParams: SearchParameter[] = []; beforeAll(() => { indexStructureDefinitionBundle(readJson('fhir/r4/profiles-types.json') as Bundle); indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle); indexStructureDefinitionBundle(readJson('fhir/r4/profiles-medplum.json') as Bundle); for (const filename of SEARCH_PARAMETER_BUNDLE_FILES) { const bundle = readJson(filename) as Bundle<SearchParameter>; indexSearchParameterBundle(bundle); for (const entry of bundle.entry as BundleEntry[]) { indexedSearchParams.push(entry.resource as SearchParameter); } } }); test('Get impl', () => { // expression: 'Patient.name | Person.name | Practitioner.name | RelatedPerson.name' const individualPhoneticParam = indexedSearchParams.find((e) => e.id === 'individual-phonetic') as SearchParameter; const impl = getSearchParameterImplementation('Patient', individualPhoneticParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('phonetic'); }); test('Boolean param', () => { // expression: 'Patient.active' const activeParam = indexedSearchParams.find((e) => e.id === 'Patient-active') as SearchParameter; const impl = getSearchParameterImplementation('Patient', activeParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('active'); }); test('Date param', () => { // expression: 'Patient.birthDate' const birthDateParam = indexedSearchParams.find((e) => e.id === 'individual-birthdate') as SearchParameter; const impl = getSearchParameterImplementation('Patient', birthDateParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('birthdate'); }); test('Date/Time param', () => { // expression: 'ServiceRequest.authoredOn' const authoredParam = indexedSearchParams.find((e) => e.id === 'ServiceRequest-authored') as SearchParameter; const impl = getSearchParameterImplementation('ServiceRequest', authoredParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('authored'); }); test('Get nested impl', () => { // expression: 'Patient.link.other' const missingExpressionParam = indexedSearchParams.find((e) => e.id === 'Patient-link') as SearchParameter; const impl = getSearchParameterImplementation('Patient', missingExpressionParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('link'); }); test('Missing expression for resource type', () => { const missingExpressionParam: SearchParameter = { resourceType: 'SearchParameter', code: 'test', type: 'string', expression: 'OtherType.test', base: ['Patient'], } as SearchParameter; const impl = getSearchParameterImplementation('Patient', missingExpressionParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('test'); }); test('Property not found', () => { const missingExpressionParam: SearchParameter = { resourceType: 'SearchParameter', code: 'unknown', type: 'string', expression: 'Patient.unknown', } as SearchParameter; expect(() => getSearchParameterImplementation('Patient', missingExpressionParam)).toThrow(); }); test('Subtype not found', () => { const missingExpressionParam: SearchParameter = { resourceType: 'SearchParameter', code: 'name-unknown', type: 'string', expression: 'Patient.name.select()', } as SearchParameter; expect(() => getSearchParameterImplementation('Patient', missingExpressionParam)).toThrow(); }); test('Unhandled function', () => { const missingExpressionParam: SearchParameter = { resourceType: 'SearchParameter', code: 'unhandled-function', type: 'string', expression: 'Patient.name.unknown', } as SearchParameter; expect(() => getSearchParameterImplementation('Patient', missingExpressionParam)).toThrow(); }); test('Observation-value-date', () => { // expression: '(Observation.value as dateTime) | (Observation.value as Period)', const valueDateParam = indexedSearchParams.find((e) => e.id === 'Observation-value-date') as SearchParameter; const impl = getSearchParameterImplementation('Observation', valueDateParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('valueDate'); }); test('Observation-value-quantity', () => { // expression: '(Observation.value as Quantity) | (Observation.value as SampledData)', const valueQuantityParam = indexedSearchParams.find( (e) => e.id === 'Observation-value-quantity' ) as SearchParameter; const impl = getSearchParameterImplementation('Observation', valueQuantityParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('valueQuantity'); }); test('Encounter-date', () => { // expression: 'AllergyIntolerance.recordedDate | CarePlan.period | CareTeam.period | ClinicalImpression.date | Composition.date | Consent.dateTime | DiagnosticReport.effective | Encounter.period | EpisodeOfCare.period | FamilyMemberHistory.date | Flag.period | Immunization.occurrence | List.date | Observation.effective | Procedure.performed | (RiskAssessment.occurrence as dateTime) | SupplyRequest.authoredOn', const clinicalDateParam = indexedSearchParams.find((e) => e.id === 'clinical-date') as SearchParameter; const impl = getSearchParameterImplementation('Encounter', clinicalDateParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('date'); }); test('Bundle-composition', () => { // expression: 'Bundle.entry[0].resource', const searchParam = indexedSearchParams.find((e) => e.id === 'Bundle-composition') as SearchParameter; const impl = getSearchParameterImplementation('Bundle', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('composition'); }); test('ProjectMembership-profile-type', () => { // expression: 'ProjectMembership.profile.resolve().resourceType', const searchParam = indexedSearchParams.find((e) => e.id === 'ProjectMembership-profile-type') as SearchParameter; const impl = getSearchParameterImplementation('ProjectMembership', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('profileType'); }); test('ProjectMembership-access-policy', () => { const searchParam: SearchParameter = { resourceType: 'SearchParameter', id: 'ProjectMembership-access-policy', url: 'https://medplum.com/fhir/SearchParameter/ProjectMembership-access-policy', version: '4.0.1', name: 'access-policy', status: 'draft', publisher: 'Medplum', description: 'The access policy of the user', code: 'access-policy', base: ['ProjectMembership'], type: 'reference', expression: 'ProjectMembership.accessPolicy | ProjectMembership.access.policy', target: ['AccessPolicy'], }; const impl = getSearchParameterImplementation('ProjectMembership', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('accessPolicy'); }); test('Account-patient', () => { // expression: 'Account.subject.where(resolve() is Patient)', const searchParam = indexedSearchParams.find((e) => e.id === 'Account-patient') as SearchParameter; const impl = getSearchParameterImplementation('Account', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('patient'); }); test('ActivityDefinition-composed-of', () => { // expression: 'Account.subject.where(resolve() is Patient)', const searchParam = indexedSearchParams.find((e) => e.id === 'ActivityDefinition-composed-of') as SearchParameter; const impl = getSearchParameterImplementation('ActivityDefinition', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('composedOf'); }); test('Patient-deceased', () => { // expression: 'Patient.deceased.exists() and Patient.deceased != false', const searchParam = indexedSearchParams.find((e) => e.id === 'Patient-deceased') as SearchParameter; const impl = getSearchParameterImplementation('Patient', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('deceased'); }); test('us-core-condition-asserted-date', () => { const searchParam = indexedSearchParams.find((e) => e.id === 'us-core-condition-asserted-date') as SearchParameter; const impl = getSearchParameterImplementation('Condition', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('assertedDate'); }); test('us-core-ethnicity', () => { const searchParam = indexedSearchParams.find((e) => e.id === 'us-core-ethnicity') as SearchParameter; const impl = getSearchParameterImplementation('Patient', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('ethnicity'); }); test('us-core-patient-gender-identity', () => { const searchParam = indexedSearchParams.find((e) => e.id === 'us-core-patient-gender-identity') as SearchParameter; const impl = getSearchParameterImplementation('Patient', searchParam); assertColumnImplementation(impl); expect(impl.columnName).toStrictEqual('genderIdentity'); }); test('EvidenceVariable-characteristic-type', () => { const searchParam = indexedSearchParams.find( (e) => e.id === 'EvidenceVariable-characteristic-type' ) as SearchParameter; const impl = getSearchParameterImplementation('EvidenceVariable', searchParam); expectTokenColumnImplementation(impl); }); test.each([['Patient-identifier'], ['Patient-language']])( 'token column for SearchParameter %s on Patient', (searchParamId) => { const resourceType = 'Patient'; const searchParam = indexedSearchParams.find((e) => e.id === searchParamId) as SearchParameter; const impl = getSearchParameterImplementation(resourceType, searchParam); expectTokenColumnImplementation(impl); } ); test('MedicationRequest-code legacy behavior', () => { const searchParam = indexedSearchParams.find((e) => e.id === 'clinical-code') as SearchParameter; const impl = getSearchParameterImplementation('MedicationRequest', searchParam); expectTokenColumnImplementation(impl); }); test('Observation-code excluded from legacy behavior', () => { const searchParam = indexedSearchParams.find((e) => e.id === 'clinical-code') as SearchParameter; const impl = getSearchParameterImplementation('Observation', searchParam); expectTokenColumnImplementation(impl); }); test.each([ ['individual-address-country', AddressTable, false], ['individual-given', HumanNameTable, true], ['Patient-name', HumanNameTable, true], ])('lookup table for SearchParameter %s on Patient', (searchParamId, lookupTableClass, shouldHaveSortColumn) => { const resourceType = 'Patient'; const searchParam = indexedSearchParams.find((e) => e.id === searchParamId) as SearchParameter; const impl = getSearchParameterImplementation(resourceType, searchParam); expectLookupTableImplementation(impl); expect(impl.lookupTable instanceof lookupTableClass).toBeTruthy(); expect(!!impl.sortColumnName).toBe(shouldHaveSortColumn); }); test('Everything', () => { // Make sure that getSearchParameterImplementation returns successfully for all known parameters. for (const resourceType of Object.keys(globalSchema.types)) { if (resourceType === 'Resource' || resourceType === 'DomainResource') { continue; } for (const searchParam of indexedSearchParams) { if (searchParam.base?.includes(resourceType as ResourceType)) { const impl = getSearchParameterImplementation(resourceType, searchParam); expect(impl).toBeDefined(); } } } }); }); function assertColumnImplementation( impl: SearchParameterImplementation | undefined ): asserts impl is ColumnSearchParameterImplementation { expect(impl).toBeDefined(); expect(impl?.searchStrategy).toBe(SearchStrategies.COLUMN); } function expectLookupTableImplementation( impl: SearchParameterImplementation | undefined ): asserts impl is LookupTableSearchParameterImplementation { expect(impl).toBeDefined(); expect(impl?.searchStrategy).toBe(SearchStrategies.LOOKUP_TABLE); } function expectTokenColumnImplementation( impl: SearchParameterImplementation | undefined ): asserts impl is TokenColumnSearchParameterImplementation { expect(impl).toBeDefined(); expect(impl?.searchStrategy).toBe(SearchStrategies.TOKEN_COLUMN); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server