Skip to main content
Glama
structuredefinitionexpandprofile.ts3.32 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { allOk, badRequest, Operator } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { Bundle, BundleEntry, StructureDefinition } from '@medplum/fhirtypes'; import { getAuthenticatedContext } from '../../context'; import type { Repository } from '../repo'; import { getFullUrl } from '../response'; /** * Handles a StructureDefinition profile expansion request. * Searches for all extensions related to the profile. * @param req - The FHIR request. * @returns The FHIR response. */ export async function structureDefinitionExpandProfileHandler(req: FhirRequest): Promise<FhirResponse> { const ctx = getAuthenticatedContext(); const { url } = req.query; if (!url || typeof url !== 'string') { return [badRequest('StructureDefinition profile url not specified')]; } const profile = await fetchProfileByUrl(ctx.repo, url); if (!profile) { return [badRequest(`StructureDefinition profile with URL ${url} not found`)]; } const sds = await loadNestedStructureDefinitions(ctx.repo, profile, new Set([url]), 1); const bundle = bundleResults([profile, ...sds]); return [allOk, bundle]; } async function fetchProfileByUrl(repo: Repository, url: string): Promise<StructureDefinition | undefined> { return repo.searchOne<StructureDefinition>({ resourceType: 'StructureDefinition', filters: [ { code: 'url', operator: Operator.EQUALS, value: url, }, ], sortRules: [ { code: 'version', descending: true, }, ], }); } async function loadNestedStructureDefinitions( repo: Repository, profile: StructureDefinition, searchedProfiles: Set<string>, depth: number ): Promise<StructureDefinition[]> { // Recurse at most 10 levels deep if (depth > 10) { return []; } const profilesUrlsToLoad: string[] = []; profile.snapshot?.element?.forEach((element) => { const profileUrls: string[] | undefined = element.type ?.map((t) => t.profile) .flat() .filter((p): p is NonNullable<string> => p !== undefined); profileUrls?.forEach((p) => { if (!searchedProfiles.has(p)) { profilesUrlsToLoad.push(p); searchedProfiles.add(p); } }); }); const promises: Promise<StructureDefinition | undefined>[] = profilesUrlsToLoad.map((url) => fetchProfileByUrl(repo, url) ); const response = []; const sds = await Promise.all(promises); for (const result of sds) { if (result === undefined) { continue; } response.push(result); // recursive loop const nested = await loadNestedStructureDefinitions(repo, result, searchedProfiles, depth + 1); response.push(...nested); } return response; } function bundleResults(profiles: StructureDefinition[]): Bundle<StructureDefinition> { const entry: BundleEntry<StructureDefinition>[] = []; for (const profile of profiles) { if (profile.id !== undefined) { entry.push({ fullUrl: getFullUrl('StructureDefinition', profile.id), resource: profile, }); } } return { resourceType: 'Bundle', type: 'searchset', entry, }; }

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