Skip to main content
Glama
deploy.ts3.74 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { WithId } from '@medplum/core'; import { allOk, badRequest, ContentType, getReferenceString, normalizeOperationOutcome, OperationOutcomeError, } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { Attachment, Binary, Bot } from '@medplum/fhirtypes'; import { Readable } from 'node:stream'; import { isBotEnabled } from '../../bots/utils'; import { deployLambda, getLambdaTimeoutForBot } from '../../cloud/aws/deploy'; import { deployFissionBot } from '../../cloud/fission/deploy'; import { getAuthenticatedContext } from '../../context'; import { getBinaryStorage } from '../../storage/loader'; import { readStreamToString } from '../../util/streams'; import type { Repository } from '../repo'; import { getSystemRepo } from '../repo'; export async function deployHandler(req: FhirRequest): Promise<FhirResponse> { const ctx = getAuthenticatedContext(); const { id } = req.params; // First read the bot as the user to verify access await ctx.repo.readResource<Bot>('Bot', id); // Then read the bot as system user to load extended metadata const systemRepo = getSystemRepo(); const bot = await systemRepo.readResource<Bot>('Bot', id); // Validate that the request body has a code property // Or that the Bot already has executable code attached const code = req.body.code as string | undefined; const filename = req.body.filename ?? 'index.js'; try { await deployBot(ctx.repo, bot, code, filename); return [allOk]; } catch (err) { return [normalizeOperationOutcome(err)]; } } /** * Deploys a bot to the cloud. * @param repo - The repository to use to read/write the bot. * @param bot - The bot to deploy. * @param code - The code to deploy. If not provided, the existing code will be used. * @param filename - The filename to use for the code. If not provided, 'index.js' will be used. */ export async function deployBot(repo: Repository, bot: WithId<Bot>, code?: string, filename?: string): Promise<void> { if (!code && !bot.executableCode?.url) { throw new OperationOutcomeError(badRequest('Bot missing executable code')); } if (!(await isBotEnabled(bot))) { throw new OperationOutcomeError(badRequest('Bots not enabled')); } let updatedBot: WithId<Bot> | undefined; let codeToDeploy = code; if (code) { const contentType = ContentType.JAVASCRIPT; // Create a Binary for the executable code const binary = await repo.createResource<Binary>({ resourceType: 'Binary', contentType, }); await getBinaryStorage().writeBinary(binary, filename, contentType, Readable.from(code)); // Update the bot updatedBot = await repo.updateResource<Bot>({ ...bot, executableCode: { contentType, url: getReferenceString(binary), title: filename, }, }); } else { const binary = await repo.readReference<Binary>({ reference: (bot.executableCode as Attachment).url as string, }); const stream = await getBinaryStorage().readBinary(binary); codeToDeploy = await readStreamToString(stream); } let latestBot = updatedBot ?? bot; // Deploy the bot if (latestBot.runtimeVersion === 'awslambda') { if (latestBot.timeout === undefined) { latestBot = await repo.updateResource<Bot>({ ...latestBot, timeout: await getLambdaTimeoutForBot(latestBot), }); } await deployLambda(latestBot, codeToDeploy as string); } else if (latestBot.runtimeVersion === 'fission') { await deployFissionBot(latestBot, codeToDeploy as string); } }

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