Skip to main content
Glama
references.test.ts10.5 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { WithId } from '@medplum/core'; import { createReference } from '@medplum/core'; import type { Login, Patient, Project, Questionnaire, ServiceRequest, UserConfiguration } from '@medplum/fhirtypes'; import { randomUUID } from 'crypto'; import { initAppServices, shutdownApp } from '../app'; import { registerNew } from '../auth/register'; import { loadTestConfig } from '../config/loader'; import type { AuthState } from '../oauth/middleware'; import { createTestProject, withTestContext } from '../test.setup'; import { getRepoForLogin } from './accesspolicy'; import type { ReferenceTableRow } from './lookups/reference'; import { ReferenceTable } from './lookups/reference'; import { getSystemRepo } from './repo'; describe('Reference checks', () => { beforeAll(async () => { const config = await loadTestConfig(); await initAppServices(config); }); afterAll(async () => { await shutdownApp(); }); test('Check references on write', () => withTestContext(async () => { const { membership, project } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); await getSystemRepo().updateResource({ ...project, checkReferencesOnWrite: true, }); const authState: AuthState = { login: {} as Login, membership, project, userConfig: {} as UserConfiguration, }; const repo = await getRepoForLogin(authState, true); const patient = await repo.createResource<Patient>({ resourceType: 'Patient', name: [{ given: ['Alice'], family: 'Smith' }], birthDate: '1970-01-01', }); expect(patient).toBeDefined(); // Create a valid ServiceRequest // This should succeed const sr1 = await repo.createResource<ServiceRequest>({ resourceType: 'ServiceRequest', meta: { lastUpdated: new Date().toISOString(), }, status: 'active', intent: 'order', code: { text: 'test' }, // Strong reference to patient // This will be enforced subject: createReference(patient), // Expected not to choke on keys without values replaces: undefined, }); expect(sr1).toBeDefined(); // Create a ServiceRequest with a bad reference // This should fail await expect( repo.createResource<ServiceRequest>({ resourceType: 'ServiceRequest', status: 'active', intent: 'order', code: { text: 'test' }, subject: { reference: 'Patient/' + randomUUID() }, }) ).rejects.toThrow('Invalid reference'); })); test('References to resources in linked Project', () => withTestContext(async () => { const systemRepo = getSystemRepo(); const { membership, project } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); const { membership: membership2, project: project2 } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); const updatedProject = await systemRepo.updateResource({ ...project, checkReferencesOnWrite: true, link: [{ project: createReference(project2) }], }); const repo2 = await getRepoForLogin({ login: {} as Login, membership: membership2, project: project2, userConfig: {} as UserConfiguration, }); const patient2 = await repo2.createResource({ resourceType: 'Patient', }); // Reference available into linked Project let repo = await getRepoForLogin({ login: {} as Login, membership, project: updatedProject, userConfig: {} as UserConfiguration, }); const patient = await repo.createResource({ resourceType: 'Patient', link: [{ type: 'seealso', other: createReference(patient2) }], }); expect(patient.link?.[0]?.other).toStrictEqual(createReference(patient2)); // Unlink Project and verify that access is revoked const unlinkedProject = await systemRepo.updateResource({ ...updatedProject, link: undefined, }); repo = await getRepoForLogin({ login: {} as Login, membership, project: unlinkedProject, userConfig: {} as UserConfiguration, }); await expect( repo.createResource({ resourceType: 'Patient', link: [{ type: 'seealso', other: createReference(patient2) }], }) ).rejects.toBeDefined(); })); test('Project reference validation', () => withTestContext(async () => { const { membership, project: project1 } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); const { membership: _membership2, project: project2 } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); project1.checkReferencesOnWrite = true; project1.link = [{ project: createReference(project2) }]; const systemRepo = getSystemRepo(); await systemRepo.updateResource(project1); const repo = await getRepoForLogin({ login: { resourceType: 'Login' } as Login, membership, project: project1, userConfig: {} as UserConfiguration, }); let project = await repo.readResource<Project>('Project', project1.id); // Checking the name change is ancillary; mostly confirming that the update // doesn't throw due to reference validation failure expect(project.name).not.toStrictEqual('new name'); project.name = 'new name'; project = await repo.updateResource(project); expect(project.name).toStrictEqual('new name'); })); test('ProjectMembership reference validation', () => withTestContext(async () => { let { membership, project } = await registerNew({ firstName: randomUUID(), lastName: randomUUID(), projectName: randomUUID(), email: randomUUID() + '@example.com', password: randomUUID(), }); const systemRepo = getSystemRepo(); project = await systemRepo.updateResource({ ...project, checkReferencesOnWrite: true }); const repo = await getRepoForLogin({ login: { resourceType: 'Login' } as Login, membership, project, userConfig: {} as UserConfiguration, }); // Checking the externalId change is ancillary; mostly confirming that the update // doesn't throw due to reference validation failure const id = randomUUID(); expect(membership.externalId).toBeUndefined(); membership.externalId = id; membership = await repo.updateResource(membership); expect(membership.externalId).toStrictEqual(id); })); test('Check references with non-literal reference', () => withTestContext(async () => { const { repo } = await createTestProject({ project: { checkReferencesOnWrite: true }, withRepo: true, }); const patient: Patient = { resourceType: 'Patient', link: [ { type: 'refer', other: { display: 'J. Smith' }, }, ], }; await expect(repo.createResource<Patient>(patient)).resolves.toBeDefined(); })); test('Check references with reference placeholder', () => withTestContext(async () => { const { repo } = await createTestProject({ project: { checkReferencesOnWrite: true }, withRepo: true, accessPolicy: { resourceType: 'AccessPolicy', compartment: { reference: '%patient' }, resource: [{ resourceType: '*' }], }, }); const patient: Patient = { resourceType: 'Patient', link: [ { type: 'refer', other: { reference: '%patient' }, }, ], }; const questionnaire: Questionnaire = { resourceType: 'Questionnaire', status: 'active', item: [ { linkId: 'group-id', type: 'group', extension: [ { url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-templateExtract', extension: [{ url: 'template', valueReference: { reference: '#template' } }], }, ], item: [ { linkId: 'nested-item', type: 'string', text: 'A nested question', }, ], }, ], }; await expect(repo.createResource<Patient>(patient)).resolves.toBeDefined(); await expect(repo.createResource<Questionnaire>(questionnaire)).resolves.toBeDefined(); })); test('Resources with identical references', () => { const table = new ReferenceTable(); const orgId = randomUUID(); const r1: WithId<Patient> = { resourceType: 'Patient', id: '1', managingOrganization: { reference: `Organization/${orgId}` }, name: [{ given: ['Alice'], family: 'Smith' }], }; const r2: WithId<Patient> = { resourceType: 'Patient', id: '2', managingOrganization: { reference: `Organization/${orgId}` }, name: [{ given: ['Bob'], family: 'Smith' }], }; const result: ReferenceTableRow[] = []; table.extractValues(result, r1); table.extractValues(result, r2); expect(result).toStrictEqual([ { code: 'organization', resourceId: '1', targetId: orgId, }, { code: 'organization', resourceId: '2', targetId: orgId, }, ]); }); });

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