Skip to main content
Glama
vital.ts5.84 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { isObject } from '@medplum/core'; import type { BotEvent, MedplumClient } from '@medplum/core'; import type { ProjectSetting, Questionnaire, QuestionnaireItem, QuestionnaireItemAnswerOption, } from '@medplum/fhirtypes'; type GetLabTestEvent = { endpoint: 'get_lab_tests'; payload?: { labID?: number; }; }; type GetLabEvent = { endpoint: 'get_labs'; }; type GetMarkersEvent = { endpoint: 'get_markers'; payload?: { labTestID: string; }; }; type GetAoEQuestionnaireEvent = { endpoint: 'get_aoe_questionnaire'; payload?: { labTestID: string; }; }; type Event = GetLabTestEvent | GetLabEvent | GetMarkersEvent | GetAoEQuestionnaireEvent; /** * Wrapper around the vital API. * * @param medplum - An instance of the Medplum client for interacting with the FHIR server. * @param event - The BotEvent containing the incoming message. * * @returns The response to the incoming message. */ export async function handler( medplum: MedplumClient, event: BotEvent<Event> ): Promise<LabTest[] | Lab[] | Marker[] | Questionnaire | undefined> { if (!isObject(event.input)) { return undefined; } const input = event.input as Event; switch (input.endpoint) { case 'get_lab_tests': return getLabTests(event.secrets, input.payload?.labID); case 'get_labs': return getLabs(event.secrets); case 'get_markers': if (!input.payload?.labTestID) { throw new Error('Missing labTestID'); } return getMarkers(event.secrets, input.payload.labTestID); case 'get_aoe_questionnaire': if (!input.payload?.labTestID) { throw new Error('Missing labTestID'); } return getAoEQuestionnaire(event.secrets, input.payload.labTestID); default: return undefined; } } async function getLabs(secrets: Record<string, ProjectSetting>): Promise<Lab[]> { const apiKey = secrets['VITAL_API_KEY'].valueString; const baseURL = secrets['VITAL_BASE_URL']?.valueString || 'https://api.dev.tryvital.io'; if (!apiKey || !baseURL) { throw new Error('VITAL_API_KEY and VITAL_BASE_URL are required'); } const url = `${baseURL}/v3/lab_tests/labs`; const resp = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'x-vital-api-key': apiKey, }, }); switch (resp.status) { case 200: { return resp.json() as Promise<Lab[]>; } default: { const data = await resp.json(); throw new Error('Vital API error: ' + JSON.stringify(data)); } } } async function getMarkers(secrets: Record<string, ProjectSetting>, labTestID: string): Promise<Marker[]> { const labTests = await fetchLabTests(secrets); return labTests.find((lt) => lt.id === labTestID)?.markers || []; } async function getAoEQuestionnaire(secrets: Record<string, ProjectSetting>, markerID: string): Promise<Questionnaire> { const markers = await getMarkers(secrets, markerID); return { resourceType: 'Questionnaire', title: 'Ask on Order Entry (AOE)', status: 'active', item: markers.map((marker) => ({ linkId: marker.id.toString(), text: marker.name, type: 'group', item: marker.aoe?.questions.map<QuestionnaireItem>((question) => ({ linkId: question.id.toString(), text: question.value, type: (question.type === 'numeric' ? 'decimal' : question.type) as QuestionnaireItem['type'], required: question.required, answerOption: question.answers?.map<QuestionnaireItemAnswerOption>((answer) => ({ valueString: answer.code, // valueString: question.type !== 'numeric' ? answer.value : undefined, // valueInteger: question.type === 'numeric' ? Number.parseFloat(answer.value) : undefined, })), })), })), }; } async function getLabTests(secrets: Record<string, ProjectSetting>, labID?: number): Promise<LabTest[]> { const labTests = await fetchLabTests(secrets); if (labID) { return labTests.filter((lt) => lt.lab.id === labID); } return labTests; } async function fetchLabTests(secrets: Record<string, ProjectSetting>): Promise<LabTest[]> { const apiKey = secrets['VITAL_API_KEY'].valueString; const baseURL = secrets['VITAL_BASE_URL']?.valueString || 'https://api.dev.tryvital.io'; if (!apiKey || !baseURL) { throw new Error('VITAL_API_KEY and VITAL_BASE_URL are required'); } const url = `${baseURL}/v3/lab_tests`; const resp = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'x-vital-api-key': apiKey, }, }); switch (resp.status) { case 200: { return resp.json() as Promise<LabTest[]>; } default: { const data = await resp.json(); throw new Error('Vital API error: ' + JSON.stringify(data)); } } } type Lab = { id: number; slug: string; name: string; first_line_address: string; city: string; zipcode: string; collection_methods: string[]; sample_types: string[]; }; export type Marker = { id: number; name: string; slug: string; description: string; lab_id: number; provider_id: string; type?: string; unit: any; price: string; aoe?: { questions: { id: number; required: boolean; code: string; value: string; type: string; sequence: number; answers: { id: number; code: string; value: string; }[]; }[]; }; }; export type LabTest = { id: string; slug: string; name: string; sample_type: string; method: string; price: number; is_active: boolean; status: string; fasting: boolean; lab: Lab; markers?: Marker[]; is_delegated: boolean; };

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