@azure-utils/storybooks
Version:
Utils to upload and manage Storybooks via Azure Functions and storage.
113 lines (101 loc) • 3.36 kB
text/typescript
import type {
HttpRequest,
HttpResponseInit,
InvocationContext,
} from "@azure/functions";
import {
createDocument,
type ZodOpenApiSecuritySchemeObject,
} from "zod-openapi";
import {
openAPIPaths,
openAPISchemas,
openAPISecuritySchemas,
openAPITags,
} from "../utils/openapi-utils";
import { CONTENT_TYPES, SUPPORTED_CONTENT_TYPES_MSG } from "../utils/constants";
import { responseError } from "../utils/response-utils";
import { getStore } from "../utils/store";
export async function openAPIHandler(
request: HttpRequest,
context: InvocationContext
): Promise<HttpResponseInit> {
const { authLevel, openapi, serviceName } = getStore();
const {
title = serviceName.toUpperCase(),
version = process.env["NODE_ENV"] || "TEST",
servers,
} = openapi || {};
context.log("Serving OpenAPI schema...", authLevel);
try {
const securitySchemes: Record<string, ZodOpenApiSecuritySchemeObject> = {};
if (authLevel) {
securitySchemes["functionsKey"] = openAPISecuritySchemas.functionsKey;
}
const openAPISpec = createDocument({
openapi: "3.1.0",
info: { title, version },
security: authLevel ? [{ functionsKey: [] }] : [],
servers,
tags: Object.values(openAPITags),
paths: openAPIPaths,
components: {
schemas: openAPISchemas,
securitySchemes,
},
});
const { searchParams } = new URL(request.url);
const isDownloadJSON = searchParams.get("download") === "json";
if (isDownloadJSON) {
const headers = new Headers({
"Content-Disposition": `attachment; filename="${title}_${version}_openapi.json"`,
});
return { status: 200, jsonBody: openAPISpec, headers };
}
const accept = request.headers.get("accept");
if (!accept || accept.includes(CONTENT_TYPES.JSON)) {
return { status: 200, jsonBody: openAPISpec };
}
if (accept.includes(CONTENT_TYPES.HTML)) {
const html = generateSwaggerUI(title, openAPISpec);
return {
status: 200,
headers: { "Content-Type": CONTENT_TYPES.HTML },
body: html,
};
}
return { status: 406, body: SUPPORTED_CONTENT_TYPES_MSG };
} catch (error) {
return responseError(error, context);
}
}
function generateSwaggerUI(title: string, openAPISpec: object) {
return /* html */ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="${title} SwaggerUI" />
<title>${title}</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" crossorigin></script>
</head>
<body style="position: relative;">
<div id="swagger-ui"></div>
<div style="position: absolute; top: 0; right: 0; padding-right: 16px; display: flex; gap: 0.5rem;">
<form>
<input type="hidden" name="download" value="json" />
<button type="submit">Download</button>
</form>
<button type="button" onClick="window.location.reload();">Refresh</button>
</div>
<script async defer>
window.swaggerUI = SwaggerUIBundle(${JSON.stringify({
dom_id: "#swagger-ui",
spec: openAPISpec,
})});
</script>
</body>
</html>`;
}