Skip to content

Instantly share code, notes, and snippets.

@ir4y
Created July 15, 2026 07:27
Show Gist options
  • Select an option

  • Save ir4y/018110ad20bb4e2588f696f0632cfd4e to your computer and use it in GitHub Desktop.

Select an option

Save ir4y/018110ad20bb4e2588f696f0632cfd4e to your computer and use it in GitHub Desktop.
upload-view-definitions.ts
#!/usr/bin/env bun
// Uploads ViewDefinition resources from initBundle.json into a FHIR server,
// then invokes $materialize on each uploaded ViewDefinition.
//
// Usage:
// FHIR_BASE_URL=http://localhost:8080 CLIENT_ID=root CLIENT_SECRET=secret bun run upload-view-definitions.ts [path/to/bundle.json]
const baseUrl = (process.env.FHIR_BASE_URL ?? "http://localhost:8080").replace(/\/$/, "");
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const materializeType = process.env.MATERIALIZE_TYPE ?? "view"; // view | materialized-view | table
if (!clientId || !clientSecret) {
console.error("Missing CLIENT_ID / CLIENT_SECRET environment variables.");
process.exit(1);
}
const authHeader = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`;
const bundlePath = process.argv[2] ?? new URL("./initBundle.json", import.meta.url).pathname;
const bundle = await Bun.file(bundlePath).json();
const viewDefinitions = (bundle.entry ?? [])
.map((entry: any) => entry.resource)
.filter((resource: any) => resource?.resourceType === "ViewDefinition");
if (viewDefinitions.length === 0) {
console.error(`No ViewDefinition resources found in ${bundlePath}`);
process.exit(1);
}
console.log(`Found ${viewDefinitions.length} ViewDefinition resource(s) in ${bundlePath}`);
async function fhirRequest(method: string, path: string, body?: unknown) {
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
Authorization: authHeader,
"Content-Type": "application/json",
Accept: "application/json",
},
body: body === undefined ? undefined : JSON.stringify(body),
});
const text = await response.text();
const data = text ? JSON.parse(text) : undefined;
if (!response.ok) {
throw new Error(
`${method} ${path} failed: ${response.status} ${response.statusText}\n${JSON.stringify(data, null, 2)}`
);
}
return data;
}
const uploaded: string[] = [];
const failed: { id: string; stage: string; error: string }[] = [];
for (const viewDefinition of viewDefinitions) {
const id = viewDefinition.id;
try {
await fhirRequest("PUT", `/fhir/ViewDefinition/${id}`, viewDefinition);
console.log(`Uploaded ViewDefinition/${id}`);
uploaded.push(id);
} catch (error) {
console.error(`Failed to upload ViewDefinition/${id}: ${(error as Error).message}`);
failed.push({ id, stage: "upload", error: (error as Error).message });
}
}
const materializeParameters = {
resourceType: "Parameters",
parameter: [{ name: "type", valueCode: materializeType }],
};
for (const id of uploaded) {
try {
await fhirRequest("POST", `/fhir/ViewDefinition/${id}/$materialize`, materializeParameters);
console.log(`Materialized ViewDefinition/${id}`);
} catch (error) {
console.error(`Failed to materialize ViewDefinition/${id}: ${(error as Error).message}`);
failed.push({ id, stage: "materialize", error: (error as Error).message });
}
}
if (failed.length > 0) {
console.error(`\n${failed.length} operation(s) failed:`);
for (const failure of failed) {
console.error(` - ${failure.id} (${failure.stage}): ${failure.error}`);
}
process.exit(1);
}
console.log(`\nDone. Uploaded and materialized ${uploaded.length} ViewDefinition resource(s).`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment