@medplum/mock
Version:
Medplum Mock Client
4 lines • 1.3 MB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/index.ts", "../../src/client.ts", "../../src/mocks/accesspolicy.ts", "../../src/mocks/alice.ts", "../../src/mocks/bot.ts", "../../src/mocks/questionnaire.ts", "../../src/mocks/simpsons.ts", "../../src/mocks/subscription.ts", "../../src/mocks/thread.ts", "../../src/mocks/uscore/index.ts", "../../src/mocks/uscore/uscore-v5.0.1-structuredefinitions.json", "../../src/mocks/valueset.ts", "../../src/mocks/workflow.ts", "../../src/mocks/project.ts", "../../src/mocks/searchparameters.json", "../../src/mocks/smart.ts", "../../src/mocks/structuredefinitions.json", "../../src/subscription-manager.ts", "../../src/test-resources/fish-patient.ts"],
"sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nexport * from './client';\nexport * from './mocks';\nexport * from './subscription-manager';\nexport * from './test-resources/fish-patient';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport {\n BinarySource,\n ContentType,\n CreateBinaryOptions,\n LoginState,\n MedplumClient,\n MedplumClientOptions,\n MedplumRequestOptions,\n OperationOutcomeError,\n ProfileResource,\n SubscriptionEmitter,\n WithId,\n allOk,\n badRequest,\n generateId,\n getReferenceString,\n getStatus,\n indexSearchParameter,\n loadDataType,\n normalizeCreateBinaryOptions,\n} from '@medplum/core';\nimport { FhirRequest, FhirRouter, HttpMethod, MemoryRepository } from '@medplum/fhir-router';\nimport {\n Agent,\n Binary,\n Bot,\n Device,\n Reference,\n Resource,\n SearchParameter,\n StructureDefinition,\n Subscription,\n UserConfiguration,\n} from '@medplum/fhirtypes';\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n/** @ts-ignore */\nimport type { CustomTableLayout, TDocumentDefinitions, TFontDictionary } from 'pdfmake/interfaces';\nimport {\n BartSimpson,\n DifferentOrganization,\n DrAliceSmith,\n DrAliceSmithPreviousVersion,\n DrAliceSmithSchedule,\n ExampleBot,\n ExampleClient,\n ExampleQuestionnaire,\n ExampleQuestionnaireResponse,\n ExampleSubscription,\n ExampleThreadHeader,\n ExampleThreadMessages,\n HomerCommunication,\n HomerDiagnosticReport,\n HomerEncounter,\n HomerMedia,\n HomerObservation1,\n HomerObservation2,\n HomerObservation3,\n HomerObservation4,\n HomerObservation5,\n HomerObservation6,\n HomerObservation7,\n HomerObservation8,\n HomerServiceRequest,\n HomerSimpson,\n HomerSimpsonPreviousVersion,\n HomerSimpsonSpecimen,\n TestOrganization,\n exampleValueSet,\n makeDrAliceSmithSlots,\n} from './mocks';\nimport { ExampleAccessPolicy, ExampleStatusValueSet, ExampleUserConfiguration } from './mocks/accesspolicy';\nimport { TestProject, TestProjectMembership } from './mocks/project';\nimport SearchParameterList from './mocks/searchparameters.json';\nimport { ExampleSmartClientApplication } from './mocks/smart';\nimport StructureDefinitionList from './mocks/structuredefinitions.json';\nimport {\n ExampleWorkflowPlanDefinition,\n ExampleWorkflowQuestionnaire1,\n ExampleWorkflowQuestionnaire2,\n ExampleWorkflowQuestionnaire3,\n ExampleWorkflowQuestionnaireResponse1,\n ExampleWorkflowRequestGroup,\n ExampleWorkflowTask1,\n ExampleWorkflowTask2,\n ExampleWorkflowTask3,\n} from './mocks/workflow';\nimport { MockSubscriptionManager } from './subscription-manager';\n\nexport interface MockClientOptions\n extends Pick<MedplumClientOptions, 'baseUrl' | 'clientId' | 'storage' | 'cacheTime' | 'fetch'> {\n readonly debug?: boolean;\n /**\n * Override currently logged in user. Specifying null results in\n * MedplumContext.profile returning undefined as if no one were logged in.\n */\n readonly profile?: ReturnType<MedplumClient['getProfile']> | null;\n /**\n * Override the `MockFetchClient` used by this `MockClient`.\n */\n readonly mockFetchOverride?: MockFetchOverrideOptions;\n}\n\n/**\n * Override must contain all of `router`, `repo`, and `client`.\n */\nexport type MockFetchOverrideOptions = {\n client: MockFetchClient;\n router: FhirRouter;\n repo: MemoryRepository;\n};\n\nexport class MockClient extends MedplumClient {\n readonly router: FhirRouter;\n readonly repo: MemoryRepository;\n readonly client: MockFetchClient;\n readonly debug: boolean;\n activeLoginOverride?: LoginState;\n private agentAvailable = true;\n private profile: ReturnType<MedplumClient['getProfile']>;\n subManager: MockSubscriptionManager | undefined;\n\n constructor(clientOptions?: MockClientOptions) {\n const baseUrl = clientOptions?.baseUrl ?? 'https://example.com/';\n\n let router: FhirRouter;\n let repo: MemoryRepository;\n let client: MockFetchClient;\n\n if (clientOptions?.mockFetchOverride) {\n if (\n !(\n clientOptions.mockFetchOverride?.router &&\n clientOptions.mockFetchOverride?.repo &&\n clientOptions.mockFetchOverride?.client\n )\n ) {\n throw new Error('mockFetchOverride must specify all fields: client, repo, router');\n }\n router = clientOptions.mockFetchOverride.router;\n repo = clientOptions.mockFetchOverride.repo;\n client = clientOptions.mockFetchOverride.client;\n } else {\n router = new FhirRouter();\n repo = new MemoryRepository();\n client = new MockFetchClient(router, repo, baseUrl, clientOptions?.debug);\n }\n\n super({\n baseUrl,\n clientId: clientOptions?.clientId,\n storage: clientOptions?.storage,\n cacheTime: clientOptions?.cacheTime,\n createPdf: (\n docDefinition: TDocumentDefinitions,\n tableLayouts?: { [name: string]: CustomTableLayout },\n fonts?: TFontDictionary\n ) => client.mockCreatePdf(docDefinition, tableLayouts, fonts),\n fetch: clientOptions?.fetch\n ? clientOptions.fetch\n : (url: string, options: any) => {\n return client.mockFetch(url, options);\n },\n });\n\n this.router = router;\n this.repo = repo;\n this.client = client;\n // if null is specified, treat it as if no one is logged in\n this.profile = clientOptions?.profile === null ? undefined : (clientOptions?.profile ?? DrAliceSmith);\n this.debug = !!clientOptions?.debug;\n }\n\n clear(): void {\n super.clear();\n this.activeLoginOverride = undefined;\n }\n\n getProfile(): ProfileResource | undefined {\n return this.profile;\n }\n\n getUserConfiguration(): WithId<UserConfiguration> | undefined {\n return {\n resourceType: 'UserConfiguration',\n id: 'mock-user-config',\n menu: [\n {\n title: 'Favorites',\n link: [\n { name: 'Patients', target: '/Patient' },\n { name: 'Active Orders', target: '/ServiceRequest?status=active' },\n { name: 'Completed Orders', target: '/ServiceRequest?status=completed' },\n ],\n },\n {\n title: 'Admin',\n link: [\n { name: 'Project', target: '/admin/project' },\n { name: 'Batch', target: '/batch' },\n ],\n },\n ],\n };\n }\n\n setActiveLoginOverride(activeLoginOverride: LoginState): void {\n this.activeLoginOverride = activeLoginOverride;\n }\n\n setProfile(profile: ProfileResource | undefined): void {\n this.profile = profile;\n this.dispatchEvent({ type: 'change' });\n }\n\n getActiveLogin(): LoginState | undefined {\n if (this.activeLoginOverride !== undefined) {\n return this.activeLoginOverride;\n }\n return super.getActiveLogin();\n }\n\n getLogins(): LoginState[] {\n if (this.activeLoginOverride !== undefined) {\n return [this.activeLoginOverride];\n }\n return super.getLogins();\n }\n\n /**\n * Creates a FHIR `Binary` resource with the provided data content.\n *\n * The return value is the newly created resource, including the ID and meta.\n *\n * The `data` parameter can be a string or a `File` object.\n *\n * A `File` object often comes from a `<input type=\"file\">` element.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createBinary(myFile, 'test.jpg', 'image/jpeg');\n * console.log(result.id);\n * ```\n *\n * See the FHIR \"create\" operation for full details: https://www.hl7.org/fhir/http.html#create\n *\n * @category Create\n * @param createBinaryOptions -The binary options. See `CreateBinaryOptions` for full details.\n * @param requestOptions - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n */\n createBinary(\n createBinaryOptions: CreateBinaryOptions,\n requestOptions?: MedplumRequestOptions\n ): Promise<WithId<Binary>>;\n\n /**\n * @category Create\n * @param data - The binary data to upload.\n * @param filename - Optional filename for the binary.\n * @param contentType - Content type for the binary.\n * @param onProgress - Optional callback for progress events. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @param options - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n * @deprecated Use `createBinary` with `CreateBinaryOptions` instead. To be removed in a future version.\n */\n createBinary(\n data: BinarySource,\n filename: string | undefined,\n contentType: string,\n onProgress?: (e: ProgressEvent) => void,\n options?: MedplumRequestOptions\n ): Promise<WithId<Binary>>;\n\n async createBinary(\n arg1: BinarySource | CreateBinaryOptions,\n arg2: string | undefined | MedplumRequestOptions,\n arg3?: string,\n arg4?: (e: ProgressEvent) => void\n ): Promise<WithId<Binary>> {\n const createBinaryOptions = normalizeCreateBinaryOptions(arg1, arg2, arg3, arg4);\n const { filename, contentType, onProgress, securityContext } = createBinaryOptions;\n\n if (filename?.endsWith('.exe')) {\n throw new OperationOutcomeError(badRequest('Invalid file type'));\n }\n\n if (onProgress) {\n onProgress({ loaded: 0, lengthComputable: false } as ProgressEvent);\n onProgress({ loaded: 0, total: 100, lengthComputable: true } as ProgressEvent);\n onProgress({ loaded: 100, total: 100, lengthComputable: true } as ProgressEvent);\n }\n\n let data: string | undefined;\n if (typeof createBinaryOptions.data === 'string') {\n data = base64Encode(createBinaryOptions.data);\n }\n\n const binary = await this.repo.createResource<Binary>({\n resourceType: 'Binary',\n contentType,\n data,\n securityContext,\n });\n\n return {\n ...binary,\n url: `https://example.com/binary/${binary.id}`,\n };\n }\n\n async pushToAgent(\n agent: Agent | Reference<Agent>,\n destination: Device | Reference<Device> | string,\n body: any,\n contentType?: string,\n _waitForResponse?: boolean,\n _options?: MedplumRequestOptions\n ): Promise<any> {\n if (contentType === ContentType.PING) {\n if (!this.agentAvailable) {\n throw new OperationOutcomeError(badRequest('Timeout'));\n }\n if (typeof destination !== 'string' || (destination !== '8.8.8.8' && destination !== 'localhost')) {\n // Exception for test case\n if (destination !== 'abc123') {\n console.warn(\n 'IPs other than 8.8.8.8 and hostnames other than `localhost` will always throw an error in MockClient'\n );\n }\n throw new OperationOutcomeError(badRequest('Destination device not found'));\n }\n const ip = destination === 'localhost' ? '127.0.0.1' : destination;\n return `PING ${destination} (${ip}): 56 data bytes\n64 bytes from ${ip}: icmp_seq=0 ttl=115 time=10.977 ms\n64 bytes from ${ip}: icmp_seq=1 ttl=115 time=13.037 ms\n64 bytes from ${ip}: icmp_seq=2 ttl=115 time=23.159 ms\n64 bytes from ${ip}: icmp_seq=3 ttl=115 time=12.725 ms\n\n--- ${destination} ping statistics ---\n4 packets transmitted, 4 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 10.977/14.975/23.159/4.790 ms\n`;\n }\n return undefined;\n }\n\n setAgentAvailable(value: boolean): void {\n this.agentAvailable = value;\n }\n\n getSubscriptionManager(): MockSubscriptionManager {\n if (!this.subManager) {\n this.subManager = new MockSubscriptionManager(this, 'wss://example.com/ws/subscriptions-r4', {\n mockReconnectingWebSocket: true,\n });\n }\n return this.subManager;\n }\n\n setSubscriptionManager(subManager: MockSubscriptionManager): void {\n if (this.subManager) {\n this.subManager.closeWebSocket();\n }\n this.subManager = subManager;\n }\n\n subscribeToCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): SubscriptionEmitter {\n return this.getSubscriptionManager().addCriteria(criteria, subscriptionProps);\n }\n\n unsubscribeFromCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): void {\n this.getSubscriptionManager().removeCriteria(criteria, subscriptionProps);\n }\n\n getMasterSubscriptionEmitter(): SubscriptionEmitter {\n return this.getSubscriptionManager().getMasterEmitter();\n }\n}\n\nexport class MockFetchClient {\n readonly router: FhirRouter;\n readonly repo: MemoryRepository;\n readonly baseUrl: string;\n readonly debug: boolean;\n initialized = false;\n initPromise?: Promise<void>;\n\n constructor(router: FhirRouter, repo: MemoryRepository, baseUrl: string, debug = false) {\n this.router = router;\n this.repo = repo;\n this.baseUrl = baseUrl;\n this.debug = debug;\n }\n\n async mockFetch(url: string, options: any): Promise<Partial<Response>> {\n if (!this.initialized) {\n if (!this.initPromise) {\n this.initPromise = this.initMockRepo();\n }\n await this.initPromise;\n this.initialized = true;\n }\n\n const method = options.method ?? 'GET';\n const path = url.replace(this.baseUrl, '');\n\n if (this.debug) {\n console.log('MockClient', method, path);\n }\n\n const response = await this.mockHandler(method, path, options);\n\n if (this.debug && !response) {\n console.log('MockClient: not found', method, path);\n }\n\n if (this.debug) {\n console.log('MockClient', JSON.stringify(response, null, 2));\n }\n\n return Promise.resolve({\n ok: true,\n status: response?.resourceType === 'OperationOutcome' ? getStatus(response) : 200,\n headers: new Headers({\n 'content-type': ContentType.FHIR_JSON,\n }),\n blob: () => Promise.resolve(response),\n json: () => Promise.resolve(response),\n text: () => Promise.resolve(response),\n });\n }\n\n mockCreatePdf(\n docDefinition: TDocumentDefinitions,\n tableLayouts?: { [name: string]: CustomTableLayout },\n fonts?: TFontDictionary\n ): Promise<any> {\n if (this.debug) {\n console.log(`Mock Client: createPdf(`);\n console.log(` ${JSON.stringify(docDefinition, null, 2)},`);\n console.log(` ${JSON.stringify(tableLayouts, null, 2)},`);\n console.log(` ${JSON.stringify(fonts, null, 2)});`);\n }\n return Promise.resolve({});\n }\n\n private async mockHandler(method: HttpMethod, path: string, options: any): Promise<any> {\n if (path.startsWith('admin/')) {\n return this.mockAdminHandler(method, path, options);\n } else if (path.startsWith('auth/')) {\n return this.mockAuthHandler(method, path, options);\n } else if (path.startsWith('oauth2/')) {\n return this.mockOAuthHandler(method, path, options);\n } else if (path.startsWith('fhir/R4')) {\n return this.mockFhirHandler(method, path, options);\n } else {\n return null;\n }\n }\n\n private async mockAdminHandler(method: string, path: string, options: RequestInit): Promise<any> {\n if (path === 'admin/projects/setpassword' && method.toUpperCase() === 'POST') {\n return { ok: true };\n }\n\n // Create new bot\n const botCreateMatch = /^admin\\/projects\\/([\\w(-)?]+)\\/bot$/.exec(path);\n if (botCreateMatch && method.toUpperCase() === 'POST') {\n const body = options.body;\n let jsonBody: Record<string, any> | undefined;\n if (body) {\n jsonBody = JSON.parse(body as string);\n }\n\n const binary = await this.repo.createResource<Binary>({\n id: generateId(),\n resourceType: 'Binary',\n contentType: ContentType.TYPESCRIPT,\n });\n\n const projectId = botCreateMatch[1];\n return this.repo.createResource<Bot>({\n meta: {\n project: projectId,\n },\n id: generateId(),\n resourceType: 'Bot',\n name: jsonBody?.name,\n description: jsonBody?.description,\n runtimeVersion: jsonBody?.runtimeVersion ?? 'awslambda',\n sourceCode: {\n contentType: ContentType.TYPESCRIPT,\n title: 'index.ts',\n url: getReferenceString(binary),\n },\n });\n }\n\n const projectMatch = /^admin\\/projects\\/([\\w(-)?]+)$/.exec(path);\n if (projectMatch) {\n return {\n project: await this.repo.readResource('Project', projectMatch[1]),\n };\n }\n\n const membershipMatch = /^admin\\/projects\\/([\\w(-)?]+)\\/members\\/([\\w(-)?]+)$/.exec(path);\n if (membershipMatch) {\n return this.repo.readResource('ProjectMembership', membershipMatch[2]);\n }\n\n return { ok: true };\n }\n\n private mockAuthHandler(method: HttpMethod, path: string, options: any): any {\n if (path.startsWith('auth/method')) {\n return {};\n }\n\n if (path.startsWith('auth/changepassword')) {\n return this.mockChangePasswordHandler(method, path, options);\n }\n\n if (path.startsWith('auth/login')) {\n return this.mockLoginHandler(method, path, options);\n }\n\n if (path.startsWith('auth/setpassword')) {\n return this.mockSetPasswordHandler(method, path, options);\n }\n\n if (path.startsWith('auth/newuser')) {\n return this.mockNewUserHandler(method, path, options);\n }\n\n if (path.startsWith('auth/newproject') || path.startsWith('auth/newpatient') || path.startsWith('auth/scope')) {\n return {\n login: '123',\n code: 'xyz',\n };\n }\n\n if (path.startsWith('auth/resetpassword')) {\n return this.mockResetPasswordHandler(method, path, options);\n }\n\n if (path.startsWith('auth/me')) {\n return {\n profile: DrAliceSmith,\n security: {\n mfaEnrolled: false,\n sessions: [\n {\n id: '123',\n lastUpdated: new Date().toISOString(),\n authMethod: 'password',\n remoteAddress: '5.5.5.5',\n browser: 'Chrome',\n os: 'Linux',\n },\n {\n id: '456',\n lastUpdated: new Date().toISOString(),\n authMethod: 'password',\n remoteAddress: '6.6.6.6',\n browser: 'Chrome',\n os: 'Android',\n },\n ],\n },\n };\n }\n\n if (path.startsWith('auth/mfa/status')) {\n return {\n enrolled: false,\n enrollUri: 'otpauth://totp/medplum.com:alice.smith%40example',\n };\n }\n\n if (path.startsWith('auth/mfa/enroll')) {\n return allOk;\n }\n\n if (path.startsWith('auth/mfa/verify')) {\n return {\n login: '123',\n code: 'xyz',\n };\n }\n\n if (path.startsWith('auth/mfa/disable')) {\n if (options.body && JSON.parse(options.body)?.token !== 'INVALID_TOKEN') {\n return allOk;\n }\n return badRequest('Invalid token');\n }\n\n return null;\n }\n\n private mockChangePasswordHandler(_method: string, _path: string, options: any): any {\n const { body } = options;\n const { oldPassword } = JSON.parse(body);\n if (oldPassword === 'orange') {\n return allOk;\n } else {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n expression: ['oldPassword'],\n details: {\n text: 'Incorrect password',\n },\n },\n ],\n };\n }\n }\n\n private mockLoginHandler(_method: string, _path: string, options: any): any {\n const { body } = options;\n const { password } = JSON.parse(body);\n if (password === 'password') {\n return {\n login: '123',\n code: 'xyz',\n };\n } else {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n expression: ['password'],\n details: {\n text: 'Invalid password',\n },\n },\n ],\n };\n }\n }\n\n private mockSetPasswordHandler(_method: string, _path: string, options: any): any {\n const { body } = options;\n const { password } = JSON.parse(body);\n if (password === 'orange') {\n return allOk;\n } else {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n expression: ['password'],\n details: {\n text: 'Invalid password',\n },\n },\n ],\n };\n }\n }\n\n private mockNewUserHandler(_method: string, _path: string, options: any): any {\n const { body } = options;\n const { email, password } = JSON.parse(body);\n if (email === 'george@example.com' && password === 'password') {\n return allOk;\n } else {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n details: {\n text: 'Invalid',\n },\n },\n ],\n };\n }\n }\n\n private mockResetPasswordHandler(_method: string, _path: string, options: any): any {\n const { body } = options;\n const { email } = JSON.parse(body);\n if (email === 'admin@example.com') {\n return allOk;\n } else {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n expression: ['email'],\n details: {\n text: 'Email not found',\n },\n },\n ],\n };\n }\n }\n\n private mockOAuthHandler(_method: string, path: string, options: any): any {\n if (path.startsWith('oauth2/token')) {\n const formBody = new URLSearchParams(options.body);\n const clientId = formBody.get('client_id') ?? 'my-client-id';\n return {\n access_token: createFakeJwt({\n sub: '1234567890',\n iat: Math.ceil(Date.now() / 1000),\n exp: Math.ceil(Date.now() / 1000) + 60 * 60, // adding one hour in seconds\n client_id: clientId,\n login_id: '123',\n }),\n refresh_token: createFakeJwt({ client_id: 123 }),\n profile: { reference: 'Practitioner/123' },\n };\n }\n\n return null;\n }\n\n private async initMockRepo(): Promise<void> {\n const defaultResources = [\n HomerSimpsonPreviousVersion,\n HomerSimpson,\n ExampleAccessPolicy,\n ExampleStatusValueSet,\n ExampleUserConfiguration,\n ExampleBot,\n ExampleClient,\n HomerDiagnosticReport,\n HomerEncounter,\n HomerCommunication,\n HomerMedia,\n HomerObservation1,\n HomerObservation2,\n HomerObservation3,\n HomerObservation4,\n HomerObservation5,\n HomerObservation6,\n HomerObservation7,\n HomerObservation8,\n HomerSimpsonSpecimen,\n TestOrganization,\n DifferentOrganization,\n ExampleQuestionnaire,\n ExampleQuestionnaireResponse,\n HomerServiceRequest,\n ExampleSubscription,\n BartSimpson,\n DrAliceSmithPreviousVersion,\n DrAliceSmith,\n DrAliceSmithSchedule,\n ExampleWorkflowQuestionnaire1,\n ExampleWorkflowQuestionnaire2,\n ExampleWorkflowQuestionnaire3,\n ExampleWorkflowPlanDefinition,\n ExampleWorkflowQuestionnaireResponse1,\n ExampleWorkflowTask1,\n ExampleWorkflowTask2,\n ExampleWorkflowTask3,\n ExampleWorkflowRequestGroup,\n ExampleSmartClientApplication,\n TestProject,\n TestProjectMembership,\n ExampleThreadHeader,\n ...ExampleThreadMessages,\n ] satisfies Resource[];\n\n for (const resource of defaultResources) {\n await this.repo.createResource(resource);\n }\n\n for (const structureDefinition of StructureDefinitionList as StructureDefinition[]) {\n structureDefinition.kind = 'resource';\n structureDefinition.url = 'http://hl7.org/fhir/StructureDefinition/' + structureDefinition.name;\n loadDataType(structureDefinition);\n await this.repo.createResource(structureDefinition);\n }\n\n for (const searchParameter of SearchParameterList) {\n indexSearchParameter(searchParameter as SearchParameter);\n await this.repo.createResource(searchParameter as SearchParameter);\n }\n\n makeDrAliceSmithSlots().forEach((slot) => this.repo.createResource(slot));\n }\n\n private async mockFhirHandler(method: HttpMethod, url: string, options: any): Promise<Resource> {\n if (url.startsWith('fhir/R4/ValueSet/$expand')) {\n return exampleValueSet;\n }\n\n if (url.includes('fhir/R4')) {\n url = url.substring(url.indexOf('fhir/R4') + 7);\n }\n\n let body = undefined;\n if (options.body) {\n try {\n body = JSON.parse(options.body);\n } catch (_err) {\n body = options.body;\n }\n }\n\n const request: FhirRequest = {\n method,\n url,\n pathname: '',\n body,\n params: Object.create(null),\n query: Object.create(null),\n headers: toIncomingHttpHeaders(options.headers),\n };\n\n const result = await this.router.handleRequest(request, this.repo);\n if (result.length === 1) {\n return result[0];\n } else {\n return result[1];\n }\n }\n}\n\n/**\n * Creates a fake JWT token with the provided claims for testing.\n *\n * **NOTE: This function does not create a real signed JWT. Attempting to read the header or signature will fail.**\n *\n * @param claims - The claims to encode in the body of the fake JWT.\n * @returns A stringified fake JWT token.\n */\nexport function createFakeJwt(claims: Record<string, string | number>): string {\n return 'header.' + base64Encode(JSON.stringify(claims)) + '.signature';\n}\n\nfunction base64Encode(str: string): string {\n return typeof window !== 'undefined' ? window.btoa(str) : Buffer.from(str).toString('base64');\n}\n\n// even though it's just a type, avoid importing IncomingHttpHeaders from node:http\n// since MockClient needs to work in the browser. Use a reasonable approximation instead\ninterface PseudoIncomingHttpHeaders {\n [key: string]: string | undefined;\n}\nfunction toIncomingHttpHeaders(headers: HeadersInit | undefined): PseudoIncomingHttpHeaders {\n const result: PseudoIncomingHttpHeaders = {};\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n const lowerKey = key.toLowerCase();\n if (typeof value === 'string') {\n result[lowerKey] = value;\n } else {\n console.warn(`Ignoring non-string value ${value} for header ${lowerKey}`);\n }\n }\n }\n\n return result;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { WithId } from '@medplum/core';\nimport { AccessPolicy, UserConfiguration, ValueSet } from '@medplum/fhirtypes';\n\nexport const ExampleAccessPolicy: WithId<AccessPolicy> = {\n resourceType: 'AccessPolicy',\n id: '123',\n name: 'Example Access Policy',\n};\n\nexport const ExampleStatusValueSet: WithId<ValueSet> = {\n resourceType: 'ValueSet',\n id: 'example-statuses',\n status: 'active',\n compose: {\n include: [\n {\n concept: [\n { code: 'ORDERED' },\n { code: 'SENT_TO_CUSTOMER' },\n { code: 'SENT_BACK_TO_LAB' },\n { code: 'LAB_PROCESSED' },\n { code: 'LAB_COMPLETE' },\n ],\n },\n ],\n },\n};\n\nexport const ExampleUserConfiguration: WithId<UserConfiguration> = {\n resourceType: 'UserConfiguration',\n id: '123',\n name: 'Example User Configuration',\n option: [\n {\n id: 'statusValueSet',\n valueString: 'ValueSet/example-statuses',\n },\n ],\n};\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { ContentType, createReference, lazy, WithId } from '@medplum/core';\nimport { Organization, Practitioner, Schedule, Slot } from '@medplum/fhirtypes';\n\nexport const TestOrganization: WithId<Organization> = {\n resourceType: 'Organization',\n id: '123',\n meta: {\n versionId: '1',\n },\n name: 'Test Organization',\n};\n\nexport const DifferentOrganization: WithId<Organization> = {\n resourceType: 'Organization',\n id: '456',\n meta: {\n versionId: '1',\n },\n name: 'Different',\n};\n\nexport const DrAliceSmith: WithId<Practitioner> = {\n resourceType: 'Practitioner',\n id: '123',\n meta: {\n versionId: '2',\n lastUpdated: '2021-01-02T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n name: [\n {\n given: ['Alice'],\n family: 'Smith',\n },\n ],\n photo: [\n {\n contentType: ContentType.PNG,\n url: 'https://www.medplum.com/img/cdc-femaledoc.png',\n },\n ],\n};\n\nexport const DrAliceSmithPreviousVersion: WithId<Practitioner> = {\n resourceType: 'Practitioner',\n id: '123',\n meta: {\n versionId: '1',\n lastUpdated: '2021-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n name: [{ given: ['Medplum'], family: 'Admin' }],\n};\n\nexport const DrAliceSmithSchedule: WithId<Schedule> = {\n resourceType: 'Schedule',\n id: 'alice-smith-schedule',\n actor: [\n {\n reference: 'Practitioner/123',\n display: 'Dr. Alice Smith',\n },\n ],\n};\n\nexport const makeDrAliceSmithSlots = lazy((): WithId<Slot>[] => {\n const schedule = createReference(DrAliceSmithSchedule);\n const result: (Slot & { id: string })[] = [];\n const slotDate = new Date();\n for (let day = 0; day < 60; day++) {\n for (const hour of [9, 10, 11, 13, 14, 15]) {\n slotDate.setHours(hour, 0, 0, 0);\n result.push({\n resourceType: 'Slot',\n id: `slot-${day}-${hour}`,\n status: 'free',\n start: slotDate.toISOString(),\n end: new Date(slotDate.getTime() + 60 * 60 * 1000).toISOString(),\n schedule,\n });\n }\n slotDate.setDate(slotDate.getDate() + 1);\n }\n return result;\n});\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Binary, Bot, ClientApplication } from '@medplum/fhirtypes';\n\nexport const ExampleBotSourceCode: Binary = {\n resourceType: 'Binary',\n id: 'bot-source-code',\n contentType: 'application/javascript',\n};\n\nexport const ExampleBot: Bot = {\n resourceType: 'Bot',\n id: '123',\n name: 'Test Bot',\n sourceCode: {\n contentType: 'application/javascript',\n title: 'index.js',\n url: 'Binary/bot-source-code',\n },\n};\n\nexport const ExampleClient: ClientApplication = {\n resourceType: 'ClientApplication',\n id: '123',\n name: 'Test Client',\n};\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Questionnaire, QuestionnaireResponse } from '@medplum/fhirtypes';\n\nexport const ExampleQuestionnaire: Questionnaire = {\n resourceType: 'Questionnaire',\n id: '123',\n url: 'https://example.com/example-questionnaire',\n status: 'active',\n name: 'Vitals',\n title: 'Vitals',\n subjectType: ['Patient'],\n item: [\n {\n linkId: '1',\n text: 'First question',\n type: 'string',\n },\n ],\n};\n\nexport const ExampleQuestionnaireResponse: QuestionnaireResponse = {\n resourceType: 'QuestionnaireResponse',\n id: '123',\n status: 'completed',\n questionnaire: 'https://example.com/example-questionnaire',\n subject: {\n reference: 'Patient/123',\n },\n source: {\n reference: 'Practitioner/123',\n },\n};\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { ContentType, SNOMED, UCUM, createReference } from '@medplum/core';\nimport {\n Address,\n Communication,\n DiagnosticReport,\n Encounter,\n Group,\n Media,\n Observation,\n Patient,\n RelatedPerson,\n ServiceRequest,\n Specimen,\n} from '@medplum/fhirtypes';\nimport { DrAliceSmith } from './alice';\n\nconst SIMPSONS_ADDRESS: Address = {\n use: 'home',\n line: ['742 Evergreen Terrace'],\n city: 'Springfield',\n state: 'IL',\n postalCode: '12345',\n};\n\nexport const LisaSimpson: Patient = {\n resourceType: 'Patient',\n id: 'lisa-simpson',\n meta: {\n versionId: '1',\n lastUpdated: '2020-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n birthDate: '1981-05-09',\n name: [\n {\n given: ['Lisa'],\n family: 'Simpson',\n },\n ],\n photo: [\n {\n contentType: 'image/jpeg',\n url: 'https://example.com/picture.jpg',\n },\n ],\n contact: [\n {\n name: { given: ['Homer'], family: 'Simpson' },\n address: {\n use: 'home',\n line: ['742 Evergreen Terrace'],\n city: 'Springfield',\n state: 'IL',\n postalCode: '12345',\n },\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-1239',\n },\n {\n system: 'email',\n use: 'home',\n value: 'homer@thesimpsons.com',\n },\n ],\n },\n {\n name: { given: ['Marge'], family: 'Simpson' },\n address: {\n use: 'home',\n line: ['742 Evergreen Terrace'],\n city: 'Springfield',\n state: 'IL',\n postalCode: '12345',\n },\n telecom: [\n {\n system: 'phone',\n use: 'mobile',\n value: '139-1928',\n },\n {\n system: 'email',\n use: 'home',\n value: 'marge@thesimpsons.com',\n },\n ],\n },\n ],\n\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-1239',\n },\n {\n system: 'email',\n use: 'home',\n value: 'lisa@thesimpsons.com',\n },\n ],\n};\n\nexport const BartSimpson: Patient = {\n resourceType: 'Patient',\n id: '555',\n meta: {\n versionId: '1',\n lastUpdated: '2020-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n birthDate: '1979-12-17',\n name: [\n {\n given: ['Bart'],\n family: 'Simpson',\n },\n ],\n photo: [\n {\n contentType: 'image/jpeg',\n url: 'https://example.com/picture.jpg',\n },\n ],\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-1239',\n },\n {\n system: 'email',\n use: 'home',\n value: 'bart@thesimpsons.com',\n },\n ],\n};\n\nexport const HomerLisaRelatedPerson: RelatedPerson = {\n resourceType: 'RelatedPerson',\n id: 'homer-lisa-related-person',\n patient: createReference(LisaSimpson),\n address: [SIMPSONS_ADDRESS],\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-7334',\n },\n {\n system: 'email',\n use: 'home',\n value: 'chunkylover53@aol.com',\n },\n ],\n relationship: [\n {\n text: 'father',\n coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode', code: 'FTH', display: 'father' }],\n },\n ],\n};\n\nexport const HomerBartRelatedPerson: RelatedPerson = {\n resourceType: 'RelatedPerson',\n id: 'homer-bart-related-person',\n patient: createReference(BartSimpson),\n address: [SIMPSONS_ADDRESS],\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-7334',\n },\n {\n system: 'email',\n use: 'home',\n value: 'chunkylover53@aol.com',\n },\n ],\n relationship: [\n {\n text: 'father',\n coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode', code: 'FTH', display: 'father' }],\n },\n ],\n};\n\nexport const HomerSimpson: Patient = {\n resourceType: 'Patient',\n id: '123',\n gender: 'male',\n meta: {\n versionId: '2',\n lastUpdated: '2020-01-02T00:00:00.000Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n identifier: [\n { system: 'abc', value: '123' },\n { system: 'def', value: '456' },\n ],\n active: true,\n birthDate: '1956-05-12',\n name: [\n {\n given: ['Homer'],\n family: 'Simpson',\n },\n ],\n photo: [\n {\n contentType: ContentType.PNG,\n url: 'https://www.medplum.com/img/homer-simpson.png',\n },\n ],\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-7334',\n },\n {\n system: 'email',\n use: 'home',\n value: 'chunkylover53@aol.com',\n },\n ],\n address: [\n {\n use: 'home',\n line: ['742 Evergreen Terrace'],\n city: 'Springfield',\n state: 'IL',\n postalCode: '12345',\n },\n ],\n link: [\n { other: createReference(HomerLisaRelatedPerson), type: 'seealso' },\n { other: createReference(HomerBartRelatedPerson), type: 'seealso' },\n ],\n};\n\nexport const MargeSimpson: Patient = {\n resourceType: 'Patient',\n id: 'marge-simpson',\n gender: 'female',\n\n active: true,\n birthDate: '1961-08-23',\n name: [\n {\n given: ['Marge'],\n family: 'Simpson',\n },\n ],\n telecom: [\n {\n system: 'phone',\n use: 'home',\n value: '555-7334',\n },\n {\n system: 'email',\n use: 'home',\n value: 'margesimpson@aol.com',\n },\n ],\n address: [SIMPSONS_ADDRESS],\n};\n\nexport const HomerSimpsonPreviousVersion: Patient = {\n resourceType: 'Patient',\n id: '123',\n meta: {\n versionId: '1',\n lastUpdated: '2020-01-01T00:00:00.000Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n name: [\n {\n given: ['Homer'],\n family: 'Simpson',\n },\n ],\n photo: [\n {\n contentType: ContentType.PNG,\n url: 'https://www.medplum.com/img/homer-simpson.png',\n },\n ],\n};\n\nexport const HomerEncounter: Encounter = {\n resourceType: 'Encounter',\n id: '123',\n meta: {\n versionId: '456',\n lastUpdated: '2020-01-01T00:00:00.000Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n status: 'finished',\n class: { code: 'AMB', display: 'ambulatory' },\n};\n\nexport const HomerCommunication: Communication = {\n resourceType: 'Communication',\n id: '123',\n meta: {\n lastUpdated: '2020-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n status: 'completed',\n encounter: createReference(HomerEncounter),\n payload: [\n {\n contentString: 'Hello world',\n },\n ],\n};\n\nexport const HomerMedia: Media = {\n resourceType: 'Media',\n id: '123',\n meta: {\n lastUpdated: '2020-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n status: 'completed',\n encounter: createReference(HomerEncounter),\n content: {\n contentType: ContentType.TEXT,\n url: 'data:text/plain,This%20is%20a%20text/plain%20data%20URL',\n },\n};\n\nexport const HomerObservation1: Observation = {\n resourceType: 'Observation',\n id: '1',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n coding: [{ code: 'test', system: 'http://example.com' }],\n text: 'Test 1',\n },\n valueString: 'test',\n};\n\nexport const HomerObservation2: Observation = {\n resourceType: 'Observation',\n id: '2',\n status: 'corrected',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 2',\n },\n valueQuantity: {\n value: 20,\n unit: 'x',\n },\n referenceRange: [\n {\n low: {\n value: 10,\n },\n },\n ],\n};\n\nexport const HomerObservation3: Observation = {\n resourceType: 'Observation',\n id: '3',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 3',\n },\n valueQuantity: {\n value: 30,\n unit: 'x',\n },\n referenceRange: [\n {\n high: {\n value: 50,\n },\n },\n ],\n};\n\nexport const HomerObservation4: Observation = {\n resourceType: 'Observation',\n id: '4',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 4',\n },\n valueQuantity: {\n value: 50,\n unit: 'x',\n comparator: '>',\n },\n referenceRange: [\n {\n low: {\n value: 10,\n unit: 'x',\n },\n high: {\n value: 50,\n unit: 'x',\n },\n },\n ],\n interpretation: [\n {\n text: 'HIGH',\n },\n ],\n};\n\nexport const HomerObservation5: Observation = {\n resourceType: 'Observation',\n id: '5',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 5',\n },\n valueQuantity: {\n value: 100,\n unit: 'x',\n },\n referenceRange: [{}],\n interpretation: [{}],\n};\n\nexport const HomerObservation6: Observation = {\n resourceType: 'Observation',\n id: '6',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 6',\n },\n component: [\n {\n code: { text: 'Systolic' },\n valueQuantity: {\n value: 110,\n unit: 'mmHg',\n system: UCUM,\n },\n },\n {\n code: { text: 'Diastolic' },\n valueQuantity: {\n value: 75,\n unit: 'mmHg',\n system: UCUM,\n },\n },\n ],\n};\n\nexport const HomerObservation7: Observation = {\n resourceType: 'Observation',\n id: '7',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 7',\n },\n component: [\n {\n code: { text: 'Glucose' },\n valueQuantity: {\n value: 1000,\n unit: 'mg/dL',\n system: UCUM,\n },\n },\n ],\n interpretation: [\n {\n coding: [\n {\n system: 'http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation',\n code: 'HH',\n display: 'Critical high',\n },\n ],\n },\n ],\n};\n\nexport const HomerObservation8: Observation = {\n resourceType: 'Observation',\n id: '8',\n status: 'final',\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n code: {\n text: 'Test 8',\n },\n component: [\n {\n code: { text: 'HIV' },\n valueString: 'REACTIVE',\n },\n ],\n referenceRange: [\n {\n text: 'NEGATIVE',\n },\n ],\n interpretation: [\n {\n coding: [\n {\n system: 'http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation',\n code: 'RR',\n display: 'Reactive',\n },\n ],\n },\n ],\n};\n\nexport const HomerSimpsonSpecimen: Specimen = {\n id: '123',\n resourceType: 'Specimen',\n subject: createReference(HomerSimpson),\n collection: {\n collectedDateTime: '2020-01-01T12:00:00Z',\n },\n note: [\n { text: 'Specimen hemolyzed. Results may be affected.' },\n { text: 'Specimen lipemic. Results may be affected.' },\n ],\n};\n\nexport const HomerServiceRequest: ServiceRequest = {\n resourceType: 'ServiceRequest',\n id: '123',\n meta: {\n versionId: '1',\n lastUpdated: '2020-01-01T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n identifier: [\n {\n system: 'https://example.com',\n value: '9001',\n },\n ],\n code: {\n coding: [\n {\n system: SNOMED,\n code: 'SERVICE_REQUEST_CODE',\n },\n ],\n },\n subject: {\n reference: 'Patient/123',\n display: 'Homer Simpson',\n },\n status: 'active',\n intent: 'order',\n orderDetail: [\n {\n text: 'ORDERED',\n },\n ],\n specimen: [createReference(HomerSimpsonSpecimen)],\n authoredOn: '2020-01-01T12:00:00Z',\n};\n\nexport const HomerDiagnosticReport: DiagnosticReport = {\n resourceType: 'DiagnosticReport',\n id: '123',\n meta: {\n versionId: '1',\n lastUpdated: '2020-01-02T12:00:00Z',\n author: {\n reference: 'Practitioner/123',\n },\n },\n status: 'final',\n code: { text: 'Test Report' },\n subject: createReference(HomerSimpson),\n basedOn: [createReference(HomerServiceRequest)],\n specimen: [createReference(HomerSimpsonSpecimen)],\n resultsInterpreter: [createReference(DrAliceSmith)],\n result: [\n createReference(HomerObservation1),\n createReference(HomerObservation2),\n createReference(HomerObservation3),\n createReference(HomerObservation4),\n createReference(HomerObservation5),\n createReference(HomerObservation6),\n createReference(HomerObservation7),\n createReference(HomerObservation8),\n ],\n};\n\nexport const SimpsonsFamily: Group = {\n resourceType: 'Group',\n id: 'simpsons-family',\n type: 'person',\n actual: true,\n member: [\n { entity: createReference(HomerSimpson) },\n { entity: createReference(MargeSimpson) },\n { entity: createReference(BartSimpson) },\n { entity: createReference(LisaSimpson) },\n ],\n};\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { AuditEvent, Subscription } from '@medplum/fhirtypes';\n\nexport const ExampleSubscription: Subscription = {\n resourceType: 'Subscription',\n id: '123',\n meta: {\n versionId: '456',\n },\n status: 'active',\n reason: 'Reason',\n criteria: 'Criteria',\n channel: {\n type: 'rest-hook',\n endpoint: 'https://example.com',\n },\n};\n\nexport const ExampleAuditEvent: AuditEvent = {\n resourceType: 'AuditEvent',\n id: '123',\n meta: {\n lastUpdated: new Date().toISOString(),\n versionId: '456',\n author: {\n reference: 'Practitioner/123',\n },\n },\n type: {\n system: 'http://terminology.hl7.org/CodeSystem/audit-event-type',\n code: 'rest-hook',\n },\n recorded: new Date().toISOString(),\n agent: [\n {\n requestor: true,\n who: {\n reference: 'Practitioner/123',\n },\n },\n ],\n source: {\n observer: {\n reference: 'Device/123',\n },\n },\n};\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { createReference } from '@medplum/core';\nimport { Communication } from '@medplum/fhirtypes';\nimport { DrAliceSmith } from './alice';\nimport { HomerSimpson } from './simpsons';\n\nexport const ExampleThreadHeader = {\n id: 'message-header-123',\n resourceType: 'Communication',\n topic: { text: 'Example Thread' },\n sender: createReference(DrAliceSmith),\n recipient: [createReference(DrAliceSmith), createReference(HomerSimpson)],\n sent: '2024-03-27T06:31:35Z',\n status: 'in-progress',\n} satisfies Communication;\n\nconst baseExampleThreadMessage = {\n resourceType: 'Communication',\n status: 'completed',\n partOf: [createReference(ExampleThreadHeader)],\n} satisfies Communication;\n\nconst baseExampleDrAliceMessage = {\n ...baseExampleThreadMessage,\n sender: createReference(DrAliceSmith),\n recipient: [createReference(HomerSimpson)],\n} satisfies Communication;\n\nconst baseExampleHomerMessage = {\n ...baseExampleThreadMessage,\n sender: createReference(HomerSimpson),\n recipient: [createReference(DrAliceSmith)],\n} satisfies Communication;\n\nexport const ExampleThreadMessages = [\n {\n ...baseExampleDrAliceMessage,\n id: 'message-1',\n payload: [{ contentString: 'Hi, Homer. Can you come in to discuss treatment for your radiation poisoning?' }],\n sent: '2024-03-27T06:31:35Z',\n received: '2024-03-27T06:31:39Z',\n },\n {\n ...baseExampleHomerMessage,\n id: 'message-2',\n payload: [{ contentString: 'Aww, not again... Doh!' }],\n sent: '2024-03-27T06:32:35Z',\n received: '2024-03-27T06:32:39Z',\n },\n {\n ...baseExampleDrAliceMessage,\n id: 'message-3',\n payload: [{ contentString: \"Homer, I haven't received your labs yet. Did you go for your lab work?\" }],\n sent: '2024-03-27T06:35:35Z',\n received: '2024-03-27T06:36:35Z',\n },\n {\n ...baseExampleHomerMessage,\n payload: [{ contentString: 'Of course I did! Must be in the mail' }],\n sent: '2024-03-27T06:36:39Z',\n received: '2024-03-27T06:37:39Z',\n },\n {\n ...baseExampleDrAliceMessage,\n id: 'message-5',\n payload: [{ contentString: 'Homer, this is for your own wellbeing. You need to take this seriously.' }],\n sent: '2024-03-27T06:37:39Z',\n received: '2024-03-27T06:38:39Z',\n },\n {\n ...baseExampleHomerMessage,\n id: 'message-6',\n status: 'in-progress',\n payload: [{ contentString: \"Well I stopped eating donuts didn't I? Sometimes...\" }],\n sent: '2024-03-27T06:38:42Z',\n },\n] satisfies Communication[];\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { HTTP_HL7_ORG, deepClone } from '@medplum/