UNPKG

@azure-utils/storybooks

Version:

Utils to upload and manage Storybooks via Azure Functions and storage.

418 lines (414 loc) 15.5 kB
const require_chunk = require('./chunk-DWy1uDak.cjs'); const require_constants = require('./constants-94H7Co6A.cjs'); const require_shared = require('./shared-BSQDPNdH.cjs'); const require_projects = require('./projects-Dq7sJTc7.cjs'); const require_openapi_utils = require('./openapi-utils-CMVFCUk1.cjs'); const require_store = require('./store-CYxr095K.cjs'); const require_url_utils = require('./url-utils-Dy7KiQmB.cjs'); const require_builds_table = require('./builds-table-C0WHumRL.cjs'); const require_validators = require('./validators-XghOucvl.cjs'); const require_upload_utils = require('./upload-utils-WCE_dx3_.cjs'); const __azure_functions = require_chunk.__toESM(require("@azure/functions")); //#region src/components/build-form.tsx var import_jsx_runtime$1 = require_chunk.__toESM(require_store.require_jsx_runtime()); async function BuildForm({ projectId }) { return /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("form", { "hx-ext": "response-targets", "hx-post": require_store.urlBuilder.allBuilds(projectId), "hx-target-error": "#form-error", style: { maxWidth: "60ch" }, enctype: require_constants.CONTENT_TYPES.FORM_MULTIPART, children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "Details" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "sha", children: "SHA" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "sha", name: "sha", required: true })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "message", children: "Message" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "message", name: "message" })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "zipFile", children: "Zip file" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "zipFile", name: "zipFile", type: "file", accept: require_constants.CONTENT_TYPES.ZIP, required: true })] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "Author" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "authorName", children: "Name" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "authorName", name: "authorName", required: true })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "authorEmail", children: "Email" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "authorEmail", name: "authorEmail", required: true })] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "Labels" }), Array.from({ length: 4 }).map((_, i) => { const id = `label-${i}`; return /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("label", { for: id, children: ["Label ", i + 1] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id, name: "labels", required: i === 0 }), i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { class: "description", children: "Required" }) : null ] }); })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { style: { display: "flex", gap: "1rem" }, children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("button", { type: "submit", children: "Upload build" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("button", { type: "reset", children: "Reset" })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)(require_store.ErrorMessage, { id: "form-error" }) ] }); } //#endregion //#region src/handlers/build-handlers.tsx var import_jsx_runtime = require_chunk.__toESM(require_store.require_jsx_runtime()); async function listBuilds(request, context) { const { projectId = "" } = request.params; try { if (require_store.checkIsNewMode()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: "Upload Build", breadcrumbs: [{ label: projectId, href: require_store.urlBuilder.projectId(projectId) }, { label: "Builds", href: require_store.urlBuilder.allBuilds(projectId) }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuildForm, { projectId }) })); const { connectionString } = require_store.getStore(); const buildModel = new require_projects.BuildModel(context, connectionString, projectId); const builds = await buildModel.list(); if (require_store.checkIsHTMLRequest()) { const projectModel = buildModel.projectModel; const project = await projectModel.get(projectId); const labels = await projectModel.labelModel(projectId).list(); return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: "All Builds", breadcrumbs: [projectId], toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.buildUpload(projectId), children: "Upload" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_builds_table.BuildTable, { builds, labels, project, caption: `Builds (${builds.length})` }) })); } return { status: 200, jsonBody: builds }; } catch (error) { return require_store.responseError(error, context); } } async function getBuild(request, context) { const { projectId = "", buildSHA = "" } = request.params; const labelSlug = request.query.get(require_constants.QUERY_PARAMS.labelSlug) || void 0; try { const { connectionString } = require_store.getStore(); const buildModel = new require_projects.BuildModel(context, connectionString, projectId); const build = await buildModel.get(buildSHA, labelSlug); if (require_store.checkIsHTMLRequest()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { breadcrumbs: [projectId, "Builds"], title: build.message ? `[${build.sha.slice(0, 7)}] ${build.message}` : buildSHA.slice(0, 7), toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { "hx-delete": request.url, "hx-confirm": "Are you sure about deleting the build?", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { children: "Delete" }) }) }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_builds_table.RawDataPreview, { data: build, summary: "Build details", open: true }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "1rem", display: "flex", gap: "1rem" }, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.storybookIndexHtml(projectId, buildSHA), target: "_blank", children: "View Storybook" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.storybookTestReport(projectId, buildSHA), target: "_blank", children: "View Test Report" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.storybookCoverage(projectId, buildSHA), target: "_blank", children: "View Coverage" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.storybookZip(projectId, buildSHA), download: `storybook-${projectId}-${buildSHA}.zip`, target: "_blank", children: "Download Storybook" }) ] })] }) })); return { status: 200, jsonBody: build }; } catch (error) { return require_store.responseError(error, context); } } async function deleteBuild(request, context) { const { projectId = "", buildSHA = "" } = request.params; try { const { connectionString } = require_store.getStore(); const buildModel = new require_projects.BuildModel(context, connectionString, projectId); await buildModel.delete(buildSHA); const buildsUrl = require_store.urlBuilder.allBuilds(projectId); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(buildsUrl, 303); return { status: 204, headers: { Location: buildsUrl } }; } catch (error) { return require_store.responseError(error, context); } } async function uploadBuild(request, context) { try { const { projectId = "" } = request.params; const { connectionString } = require_store.getStore(); const buildModel = new require_projects.BuildModel(context, connectionString, projectId); if (!await buildModel.projectModel.has(projectId)) return require_store.responseError(`The project '${projectId}' does not exist.`, context, 404); const contentType = request.headers.get("content-type"); if (contentType?.includes(require_constants.CONTENT_TYPES.ZIP)) { const buildUploadData = require_projects.BuildUploadSchema.parse(require_url_utils.urlSearchParamsToObject(request.query)); const bodyValidationResponse = require_validators.validateBuildUploadZipBody(request, context); if (bodyValidationResponse) return bodyValidationResponse; const uploadResponse = await require_upload_utils.uploadZipWithDecompressed(projectId, buildUploadData.sha); if (uploadResponse) return uploadResponse; await buildModel.create(buildUploadData); const buildUrl = require_store.urlBuilder.buildSHA(projectId, buildUploadData.sha); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(buildUrl, 303); return { status: 202 }; } if (contentType?.includes(require_constants.CONTENT_TYPES.FORM_MULTIPART)) { const { zipFile,...buildUploadData } = require_projects.BuildUploadFormSchema.parse(require_url_utils.urlSearchParamsToObject(await request.formData())); const uploadResponse = await require_upload_utils.uploadZipWithDecompressed(projectId, buildUploadData.sha, zipFile); if (uploadResponse) return uploadResponse; await buildModel.create(buildUploadData); const buildUrl = require_store.urlBuilder.buildSHA(projectId, buildUploadData.sha); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(buildUrl, 303); return { status: 202 }; } return require_store.responseError(`Invalid content type, expected ${require_constants.CONTENT_TYPES.ZIP} or ${require_constants.CONTENT_TYPES.FORM_MULTIPART}.`, context, 415); } catch (error) { return require_store.responseError(error, context); } } //#endregion //#region src/routers/builds-router.ts const TAG = require_openapi_utils.openAPITags.builds.name; function registerBuildsRouter(options) { const { authLevel, baseRoute, basePathParamsSchema, handlerWrapper, openAPIEnabled, serviceName } = options; const routeWithBuildSHA = require_url_utils.joinUrl(baseRoute, "{buildSHA}"); __azure_functions.app.get(`${serviceName}-builds-list`, { authLevel, route: baseRoute, handler: handlerWrapper(listBuilds, [{ resource: "build", action: "read" }, { resource: "ui", action: "read" }]) }); __azure_functions.app.post(`${serviceName}-build-upload`, { authLevel, route: baseRoute, handler: handlerWrapper(uploadBuild, [{ resource: "build", action: "create" }]) }); __azure_functions.app.get(`${serviceName}-build-get`, { authLevel, route: routeWithBuildSHA, handler: handlerWrapper(getBuild, [{ resource: "build", action: "read" }, { resource: "ui", action: "read" }]) }); __azure_functions.app.deleteRequest(`${serviceName}-build-delete`, { authLevel, route: routeWithBuildSHA, handler: handlerWrapper(deleteBuild, [{ resource: "build", action: "delete" }]) }); if (openAPIEnabled) { const buildPathParameterSchema = basePathParamsSchema.extend({ buildSHA: require_shared.BuildSHASchema }); require_openapi_utils.registerOpenAPIPath(baseRoute, { get: { tags: [TAG], summary: "List all builds for the project.", description: "Retrieves a list of builds.", requestParams: { path: basePathParamsSchema }, responses: { ...require_constants.commonErrorResponses, 200: { description: "A list of builds.", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: require_projects.BuildSchema.array(), example: [{ project: "project-id", sha: "s123s14" }] }, [require_constants.CONTENT_TYPES.HTML]: { example: "<!DOCTYPE html>" } } } } }, post: { tags: [TAG], summary: "Upload a new build", description: "Uploads a new build with the provided metadata.", requestParams: { path: basePathParamsSchema, query: require_projects.BuildUploadSchema }, requestBody: { required: true, description: "Compressed zip containing storybook and test results.", content: { [require_constants.CONTENT_TYPES.ZIP]: { schema: { type: "string", format: "binary" }, example: "storybook.zip" } } }, responses: { ...require_constants.commonErrorResponses, 202: { description: "Build uploaded successfully" } } } }); require_openapi_utils.registerOpenAPIPath(routeWithBuildSHA, { get: { tags: [TAG], summary: "Get build details", description: "Retrieves the details of a specific build.", requestParams: { path: buildPathParameterSchema }, responses: { ...require_constants.commonErrorResponses, 200: { description: "Build details retrieved successfully", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: require_projects.BuildSchema }, [require_constants.CONTENT_TYPES.HTML]: { example: "<!DOCTYPE html>" } } }, 404: { description: "Matching build not found." } } }, delete: { tags: [TAG], summary: "Delete a build", description: "Deletes a specific build.", requestParams: { path: buildPathParameterSchema }, responses: { ...require_constants.commonErrorResponses, 204: { description: "Build deleted successfully" }, 404: { description: "Matching build not found." } } } }); } } //#endregion Object.defineProperty(exports, 'registerBuildsRouter', { enumerable: true, get: function () { return registerBuildsRouter; } });