Skip to main content
Glama
codesystemlookup.ts5.17 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { TypedValue, WithId } from '@medplum/core'; import { OperationOutcomeError, allOk, append, badRequest, notFound, serverError } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { CodeSystem, CodeSystemProperty, Coding } from '@medplum/fhirtypes'; import { getAuthenticatedContext } from '../../context'; import { DatabaseMode, getDatabasePool } from '../../database'; import { Column, Condition } from '../sql'; import { getOperationDefinition } from './definitions'; import { buildOutputParameters, parseInputParameters } from './utils/parameters'; import { findTerminologyResource, selectCoding } from './utils/terminology'; const operation = getOperationDefinition('CodeSystem', 'lookup'); type CodeSystemLookupParameters = { code?: string; system?: string; version?: string; coding?: Coding; property?: string[]; }; export async function codeSystemLookupHandler(req: FhirRequest): Promise<FhirResponse> { const params = parseInputParameters<CodeSystemLookupParameters>(operation, req); const repo = getAuthenticatedContext().repo; let codeSystem: WithId<CodeSystem>; if (req.params.id) { codeSystem = await getAuthenticatedContext().repo.readResource<CodeSystem>('CodeSystem', req.params.id); } else if (params.system) { codeSystem = await findTerminologyResource(repo, 'CodeSystem', params.system, { version: params.version }); } else if (params.coding?.system) { codeSystem = await findTerminologyResource(repo, 'CodeSystem', params.coding.system, { version: params.version }); } else { return [badRequest('No code system specified')]; } let coding: Coding & { code: string }; if (params.coding) { if (!params.coding.code) { return [badRequest('No code specified')]; } coding = params.coding as Coding & { code: string }; } else if (params.code) { coding = { system: params.system ?? codeSystem.url, code: params.code }; } else { return [badRequest('No coding specified')]; } const output = await lookupCoding(codeSystem, coding); return [allOk, buildOutputParameters(operation, output)]; } export type CodeSystemLookupOutput = { name: string; display: string; designation?: { language?: string; value: string }[]; property?: { code: string; description: string; value: TypedValue }[]; }; export async function lookupCoding( codeSystem: WithId<CodeSystem>, coding: Coding & { code: string } ): Promise<CodeSystemLookupOutput> { if (coding.system && coding.system !== codeSystem.url) { throw new OperationOutcomeError(notFound); } const lookup = selectCoding(codeSystem.id, coding.code); const propertyTable = lookup.getNextJoinAlias(); lookup.join( 'LEFT JOIN', 'Coding_Property', propertyTable, new Condition(new Column(propertyTable, 'coding'), '=', new Column('Coding', 'id')) ); const csPropTable = lookup.getNextJoinAlias(); lookup.join( 'LEFT JOIN', 'CodeSystem_Property', csPropTable, new Condition(new Column(propertyTable, 'property'), '=', new Column(csPropTable, 'id')) ); const target = lookup.getNextJoinAlias(); lookup.join( 'LEFT JOIN', 'Coding', target, new Condition(new Column(propertyTable, 'target'), '=', new Column(target, 'id')) ); lookup .column(new Column(csPropTable, 'code')) .column(new Column(csPropTable, 'type')) .column(new Column(csPropTable, 'description')) .column(new Column(propertyTable, 'value')) .column(new Column(target, 'display', undefined, 'targetDisplay')); const db = getDatabasePool(DatabaseMode.READER); const result = await lookup.execute(db); if (!result.length) { throw new OperationOutcomeError(notFound); } const output: CodeSystemLookupOutput = { name: codeSystem.title ?? codeSystem.name ?? (codeSystem.url as string), display: result.find((r) => !r.synonymOf).display ?? '', }; for (const property of result) { if (property.synonymOf) { output.designation = append(output.designation, { language: property.language, value: property.display, }); } else if (property.code && property.value) { output.property = append(output.property, { code: property.code, description: property.targetDisplay ?? property.description ?? undefined, value: toTypedValue(property.value, property.type), }); } } return output; } function toTypedValue(value: string, type: CodeSystemProperty['type']): TypedValue { switch (type) { case 'boolean': if (value === 'true') { return { type, value: true }; } else if (value === 'false') { return { type, value: false }; } else { throw new OperationOutcomeError(serverError(new Error('Invalid value for boolean property: ' + value))); } case 'integer': return { type, value: Number.parseInt(value, 10) }; case 'decimal': return { type, value: Number.parseFloat(value) }; default: return { type, value }; } }

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