Skip to main content
Glama
plandefinitionapply.test.ts31.4 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { WithId } from '@medplum/core'; import { ContentType, createReference, getReferenceString } from '@medplum/core'; import type { ActivityDefinition, CarePlan, Encounter, OperationOutcome, Patient, PlanDefinition, Questionnaire, RequestGroup, Task, } from '@medplum/fhirtypes'; import express from 'express'; import { randomUUID } from 'node:crypto'; import request from 'supertest'; import { initApp, shutdownApp } from '../../app'; import { loadTestConfig } from '../../config/loader'; import { initTestAuth } from '../../test.setup'; const app = express(); let accessToken: string; describe('PlanDefinition apply', () => { beforeAll(async () => { const config = await loadTestConfig(); await initApp(app, config); accessToken = await initTestAuth(); }); afterAll(async () => { await shutdownApp(); }); test('Happy path', async () => { // 1. Create a Questionnaire // 2. Create a PlanDefinition // 3. Create a Patient // 4. Apply the PlanDefinition to create the Task and RequestGroup // 5. Verify the RequestGroup // 6. Verify the Task // 1. Create a Questionnaire const res1 = await request(app) .post(`/fhir/R4/Questionnaire`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Questionnaire', url: 'http://example.com/Questionnaire/patient-reg-' + randomUUID(), status: 'active', name: 'Patient Registration', title: 'Patient Registration', subjectType: ['Patient'], item: [ { linkId: '1', text: 'First question', type: 'string', }, ], }); expect(res1.status).toBe(201); // 2. Create a PlanDefinition const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', action: [ { title: res1.body.title, definitionCanonical: (res1.body as Questionnaire).url, }, ], }); expect(res2.status).toBe(201); // 3. Create a Patient const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Workflow'], family: 'Demo' }], }); expect(res3.status).toBe(201); // 4. Apply the PlanDefinition to create the Task and RequestGroup const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, ], }); expect(res4.status).toBe(200); expect(res4.body.resourceType).toStrictEqual('CarePlan'); const carePlan = res4.body as WithId<CarePlan>; // 5. Verify the RequestGroup const res5 = await request(app) .get(`/fhir/R4/${carePlan.activity?.[0]?.reference?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res5.status).toBe(200); const requestGroup = res5.body as RequestGroup; expect(requestGroup.action).toHaveLength(1); const taskReference = requestGroup.action?.[0]?.resource?.reference; expect(taskReference).toContain('Task/'); // 6. Verify the Task const res6 = await request(app) .get(`/fhir/R4/${taskReference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res6.status).toBe(200); expect(res6.body.resourceType).toStrictEqual('Task'); const resultTask = res6.body as Task; expect(resultTask.code?.text).toStrictEqual(res1.body.title); expect(resultTask.for).toMatchObject(createReference(res3.body as Patient)); expect(resultTask.focus).toMatchObject(createReference(res1.body as Questionnaire)); expect(resultTask.input).toHaveLength(1); expect(resultTask.input?.[0]?.valueReference?.reference).toStrictEqual( getReferenceString(res1.body as Questionnaire) ); }); test('Happy path - Encounter', async () => { // 1. Create a Questionnaire // 2. Create a PlanDefinition // 3. Create a Patient // 4. Create an Encounter // 5. Apply the PlanDefinition to create the Task and RequestGroup // 6. Verify the Task // 1. Create a Questionnaire const res1 = await request(app) .post(`/fhir/R4/Questionnaire`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Questionnaire', url: 'http://example.com/Questionnaire/patient-reg-' + randomUUID(), status: 'active', name: 'Patient Registration', title: 'Patient Registration', subjectType: ['Patient'], item: [ { linkId: '1', text: 'First question', type: 'string', }, ], }); expect(res1.status).toBe(201); // 2. Create a PlanDefinition const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', action: [ { title: res1.body.title, definitionCanonical: (res1.body as Questionnaire).url, }, ], }); expect(res2.status).toBe(201); // 3. Create a Patient const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Workflow'], family: 'Test' }], }); expect(res3.status).toBe(201); // 4. Create an Encounter const res4 = await request(app) .post(`/fhir/R4/Encounter`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Encounter', status: 'active', class: { system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'EMER', display: 'emergency', }, }); expect(res4.status).toBe(201); // 5. Apply the PlanDefinition to create the Task and RequestGroup const res5 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, { name: 'encounter', valueString: getReferenceString(res4.body as Encounter), }, ], }); expect(res5.status).toBe(200); expect(res5.body.resourceType).toStrictEqual('CarePlan'); const carePlan = res5.body as CarePlan; const resRG = await request(app) .get(`/fhir/R4/${carePlan.activity?.[0]?.reference?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(resRG.status).toBe(200); expect(resRG.body.resourceType).toBe('RequestGroup'); const requestGroup = resRG.body as RequestGroup; expect(requestGroup.action).toHaveLength(1); const taskReference = requestGroup.action?.[0]?.resource?.reference; expect(taskReference).toContain('Task/'); // 6. Verify the Task const res6 = await request(app) .get(`/fhir/R4/${taskReference}`) .set('Authorization', 'Bearer ' + accessToken); const resultTask = res6.body as Task; expect(resultTask.for).toMatchObject(createReference(res3.body as Patient)); expect(resultTask.focus).toMatchObject(createReference(res1.body as Questionnaire)); expect(resultTask.encounter).toMatchObject(createReference(res4.body as Encounter)); expect(resultTask.input).toHaveLength(1); expect(resultTask.input?.[0]?.valueReference?.reference).toStrictEqual( getReferenceString(res1.body as Questionnaire) ); expect(resultTask.basedOn).toHaveLength(1); expect(resultTask.basedOn?.[0]?.reference).toStrictEqual(getReferenceString(res2.body as PlanDefinition)); // 7. Verify the encounter was updated const res7 = await request(app) .get(`/fhir/R4/Encounter/${res4.body.id}`) .set('Authorization', 'Bearer ' + accessToken); expect(res7.status).toBe(200); }); test('Unsupported content type', async () => { const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', }); expect(res2.status).toBe(201); const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.TEXT) .send('hello'); expect(res4.status).toBe(400); expect((res4.body as OperationOutcome).issue?.[0]?.details?.text).toStrictEqual( "Expected at least 1 value(s) for required input parameter 'subject'" ); }); test('Incorrect parameters type', async () => { const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', }); expect(res2.status).toBe(201); const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', }); expect(res4.status).toBe(400); expect((res4.body as OperationOutcome).issue?.[0]?.details?.text).toStrictEqual( "Expected at least 1 value(s) for required input parameter 'subject'" ); }); test('Missing subject', async () => { const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', }); expect(res2.status).toBe(201); const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [], }); expect(res4.status).toBe(400); expect((res4.body as OperationOutcome).issue?.[0]?.details?.text).toStrictEqual( 'Expected 1..* value(s) for input parameter subject, but 0 provided' ); }); test('General task', async () => { const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', action: [ { description: 'do the thing', }, ], }); expect(res2.status).toBe(201); const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Workflow'], family: 'Demo' }], }); expect(res3.status).toBe(201); const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, ], }); expect(res4.status).toBe(200); }); test('ActivityDefinition ServiceRequest', async () => { const res1 = await request(app) .post(`/fhir/R4/ActivityDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'ActivityDefinition', url: 'http://example.com/ActivityDefinition/' + randomUUID(), status: 'active', kind: 'ServiceRequest', name: 'CompleteBloodCountOrder', title: 'Complete Blood Count Order', description: 'Order for a complete blood count', code: { coding: [ { system: 'http://snomed.info/sct', code: '26604007', display: 'Complete blood count', }, { system: 'http://www.ama-assn.org/go/cpt', code: '85025', display: 'Complete CBC with automated differential WBC', }, ], }, intent: 'order', priority: 'routine', participant: [ { type: 'practitioner', role: { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/practitioner-role', code: 'doctor', display: 'Doctor', }, ], }, }, ], }); expect(res1.status).toBe(201); expect(res1.body.resourceType).toBe('ActivityDefinition'); const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', status: 'active', action: [ { title: 'Order a CBC', definitionCanonical: (res1.body as ActivityDefinition).url, }, ], }); expect(res2.status).toBe(201); expect(res2.body.resourceType).toBe('PlanDefinition'); expect(res2.body.id).toBeDefined(); const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Workflow'], family: 'Demo' }], }); expect(res3.status).toBe(201); const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, ], }); expect(res4.status).toBe(200); expect(res4.body.resourceType).toBe('CarePlan'); const carePlan = res4.body as WithId<CarePlan>; const res5 = await request(app) .get(`/fhir/R4/${carePlan.activity?.[0]?.reference?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res5.status).toBe(200); expect(res5.body.resourceType).toBe('RequestGroup'); const res6 = await request(app) .get(`/fhir/R4/${(res5.body as RequestGroup).action?.[0]?.resource?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res6.status).toBe(200); const resultTask = res6.body as Task; const res7 = await request(app) .get(`/fhir/R4/${resultTask.focus?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res7.status).toBe(200); expect(res7.body.resourceType).toBe('ServiceRequest'); }); test('Dynamic and static assigned owner and performerType', async () => { // 1. Create two ActivityDefinitions with different kinds // 2. Create a PlanDefinition // 3. Create a Patient // 4. Create a Practitioner // 5. Create an Organization // 6. Create an Encounter // 7. Apply the PlanDefinition with all parameters // 8. Verify both Tasks include all references // 1a. Create an ActivityDefinition with kind 'ServiceRequest' const res1a = await request(app) .post(`/fhir/R4/ActivityDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'ActivityDefinition', url: 'http://example.com/ActivityDefinition/bp-' + randomUUID(), status: 'active', kind: 'ServiceRequest', name: 'BloodPressureCheck', title: 'Blood Pressure Check', description: 'Check patient blood pressure', extension: [ { url: 'https://medplum.com/fhir/StructureDefinition/task-elements', extension: [ { url: 'owner', valueExpression: { language: 'text/fhirpath', expression: '%practitioner', }, }, { url: 'performerType', valueCodeableConcept: { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', code: 'ATND', display: 'attender', }, ], }, }, ], }, ], code: { coding: [ { system: 'http://snomed.info/sct', code: '75367002', display: 'Blood pressure monitoring', }, ], }, intent: 'order', priority: 'routine', }); expect(res1a.status).toBe(201); // 1b. Create a second ActivityDefinition with kind 'Task' to test task-elements extension works for non-ServiceRequest kinds const res1b = await request(app) .post(`/fhir/R4/ActivityDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'ActivityDefinition', url: 'http://example.com/ActivityDefinition/task-' + randomUUID(), status: 'active', kind: 'Task', // Different kind to test the fix name: 'GeneralTaskWithOwner', title: 'General Task with Dynamic Owner', description: 'A general task that should also respect task-elements extension', extension: [ { url: 'https://medplum.com/fhir/StructureDefinition/task-elements', extension: [ { url: 'owner', valueExpression: { language: 'text/fhirpath', expression: '%organization', }, }, { url: 'performerType', valueCodeableConcept: { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', code: 'SPRF', display: 'secondary performer', }, ], }, }, ], }, ], intent: 'order', priority: 'routine', }); expect(res1b.status).toBe(201); // 2. Create a PlanDefinition with both ActivityDefinitions const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition with Multiple ActivityDefinitions', status: 'active', action: [ { title: res1a.body.title, definitionCanonical: (res1a.body as ActivityDefinition).url, }, { title: res1b.body.title, definitionCanonical: (res1b.body as ActivityDefinition).url, }, ], }); expect(res2.status).toBe(201); // 3. Create a Patient const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Alice'], family: 'Smith' }], gender: 'female', birthDate: '1990-01-01', }); expect(res3.status).toBe(201); // 4. Create a Practitioner const res4 = await request(app) .post(`/fhir/R4/Practitioner`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Practitioner', name: [{ given: ['Dr. John'], family: 'Doe' }], identifier: [ { system: 'http://example.org/practitioner-ids', value: 'PRACT001', }, ], qualification: [ { code: { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/v2-0360', code: 'MD', display: 'Doctor of Medicine', }, ], }, }, ], }); expect(res4.status).toBe(201); // 5. Create an Organization const res5 = await request(app) .post(`/fhir/R4/Organization`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Organization', name: 'Example Healthcare Organization', identifier: [ { system: 'http://example.org/organization-ids', value: 'ORG001', }, ], type: [ { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/organization-type', code: 'prov', display: 'Healthcare Provider', }, ], }, ], }); expect(res5.status).toBe(201); // 6. Create an Encounter const res6 = await request(app) .post(`/fhir/R4/Encounter`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Encounter', status: 'active', class: { system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'AMB', display: 'ambulatory', }, subject: createReference(res3.body as Patient), serviceProvider: createReference(res5.body), participant: [ { individual: createReference(res4.body), type: [ { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', code: 'ATND', display: 'attender', }, ], }, ], }, ], }); expect(res6.status).toBe(201); // 7. Apply the PlanDefinition with all parameters const res7 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, { name: 'practitioner', valueString: getReferenceString(res4.body), }, { name: 'organization', valueString: getReferenceString(res5.body), }, { name: 'encounter', valueString: getReferenceString(res6.body), }, ], }); expect(res7.status).toBe(200); expect(res7.body.resourceType).toStrictEqual('CarePlan'); const carePlan = res7.body as CarePlan; const resRG = await request(app) .get(`/fhir/R4/${carePlan.activity?.[0]?.reference?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(resRG.status).toBe(200); expect(resRG.body.resourceType).toBe('RequestGroup'); const requestGroup = resRG.body as RequestGroup; expect(requestGroup.action).toHaveLength(2); const taskRef1 = requestGroup.action?.[0]?.resource?.reference; expect(taskRef1).toContain('Task/'); const taskRef2 = requestGroup.action?.[1]?.resource?.reference; expect(taskRef2).toContain('Task/'); // 8a. Verify the first Task (ServiceRequest kind) includes all references const res8a = await request(app) .get(`/fhir/R4/${taskRef1}`) .set('Authorization', 'Bearer ' + accessToken); expect(res8a.status).toBe(200); expect(res8a.body.resourceType).toStrictEqual('Task'); const resultTask1 = res8a.body as Task; expect(resultTask1.for).toMatchObject(createReference(res3.body as Patient)); expect(resultTask1.encounter).toMatchObject(createReference(res6.body)); expect(resultTask1.input).toHaveLength(1); expect(resultTask1.input?.[0]?.type?.text).toStrictEqual('ServiceRequest'); expect(resultTask1.basedOn).toHaveLength(1); expect(resultTask1.basedOn?.[0]?.reference).toStrictEqual(getReferenceString(res2.body as PlanDefinition)); expect(resultTask1.owner).toMatchObject(createReference(res4.body)); // %practitioner expect(resultTask1.performerType).toHaveLength(1); expect(resultTask1.performerType?.[0]?.coding?.[0]?.code).toStrictEqual('ATND'); // 8b. Verify the second Task (Task kind) also includes task-elements extension values const res8b = await request(app) .get(`/fhir/R4/${taskRef2}`) .set('Authorization', 'Bearer ' + accessToken); expect(res8b.status).toBe(200); expect(res8b.body.resourceType).toStrictEqual('Task'); const resultTask2 = res8b.body as Task; expect(resultTask2.for).toMatchObject(createReference(res3.body as Patient)); expect(resultTask2.encounter).toMatchObject(createReference(res6.body)); expect(resultTask2.basedOn).toHaveLength(1); expect(resultTask2.basedOn?.[0]?.reference).toStrictEqual(getReferenceString(res2.body as PlanDefinition)); expect(resultTask2.owner).toMatchObject(createReference(res5.body)); // %organization (from the second ActivityDefinition) expect(resultTask2.performerType).toHaveLength(1); expect(resultTask2.performerType?.[0]?.coding?.[0]?.code).toStrictEqual('SPRF'); expect(resultTask2.code?.text).toStrictEqual(res1b.body.title); // 9. Verify the ServiceRequest was created for the first task const serviceRequestRef = resultTask1.focus?.reference; expect(serviceRequestRef).toBeDefined(); const res9 = await request(app) .get(`/fhir/R4/${serviceRequestRef}`) .set('Authorization', 'Bearer ' + accessToken); expect(res9.status).toBe(200); expect(res9.body.resourceType).toBe('ServiceRequest'); expect(res9.body.subject).toMatchObject(createReference(res3.body as Patient)); expect(res9.body.encounter).toMatchObject(createReference(res6.body)); // 10. Verify the encounter was updated with the plan definition reference const res10 = await request(app) .get(`/fhir/R4/Encounter/${res6.body.id}`) .set('Authorization', 'Bearer ' + accessToken); expect(res10.status).toBe(200); }); test('Populates instantiates link when PlanDefinition has url', async () => { // 1. Create a Questionnaire const res1 = await request(app) .post(`/fhir/R4/Questionnaire`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Questionnaire', url: 'http://example.com/Questionnaire/patient-reg-' + randomUUID(), status: 'active', name: 'Patient Registration', title: 'Patient Registration', subjectType: ['Patient'], item: [ { linkId: '1', text: 'First question', type: 'string', }, ], }); expect(res1.status).toBe(201); // 2. Create a PlanDefinition const res2 = await request(app) .post(`/fhir/R4/PlanDefinition`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'PlanDefinition', title: 'Example Plan Definition', url: 'http://example.com/PlanDefinition/test-' + randomUUID(), status: 'active', action: [ { title: res1.body.title, definitionCanonical: (res1.body as Questionnaire).url, }, ], }); expect(res2.status).toBe(201); const planDefinition = res2.body as PlanDefinition; // 3. Create a Patient const res3 = await request(app) .post(`/fhir/R4/Patient`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Patient', name: [{ given: ['Workflow'], family: 'Demo' }], }); expect(res3.status).toBe(201); // 4. Apply the PlanDefinition to create the Task and RequestGroup const res4 = await request(app) .post(`/fhir/R4/PlanDefinition/${res2.body.id}/$apply`) .set('Authorization', 'Bearer ' + accessToken) .set('Content-Type', ContentType.FHIR_JSON) .send({ resourceType: 'Parameters', parameter: [ { name: 'subject', valueString: getReferenceString(res3.body as Patient), }, ], }); expect(res4.status).toBe(200); expect(res4.body.resourceType).toStrictEqual('CarePlan'); const carePlan = res4.body as WithId<CarePlan>; expect(carePlan.instantiatesCanonical).toStrictEqual([planDefinition.url]); // 5. Verify the RequestGroup const res5 = await request(app) .get(`/fhir/R4/${carePlan.activity?.[0]?.reference?.reference}`) .set('Authorization', 'Bearer ' + accessToken); expect(res5.status).toBe(200); const requestGroup = res5.body as RequestGroup; expect(requestGroup.instantiatesCanonical).toStrictEqual([planDefinition.url]); }); });

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