UNPKG

@azure-utils/storybooks

Version:

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

505 lines (501 loc) 18.3 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_projects_table = require('./projects-table-BIdoDclZ.cjs'); const require_builds_table = require('./builds-table-C0WHumRL.cjs'); const require_labels_table = require('./labels-table-C6Zj73wz.cjs'); const __azure_functions = require_chunk.__toESM(require("@azure/functions")); const zod = require_chunk.__toESM(require("zod")); //#region src/components/project-form.tsx var import_jsx_runtime$1 = require_chunk.__toESM(require_store.require_jsx_runtime()); async function ProjectForm({ project }) { return /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("form", { "hx-ext": "response-targets", "hx-patch": project ? require_store.urlBuilder.projectId(project.id) : void 0, "hx-post": project ? void 0 : require_store.urlBuilder.allProjects(), "hx-target-error": "#form-error", style: { maxWidth: "60ch" }, children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "Details" }), project?.id ? null : /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "id", children: "Project ID" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "id", name: "id", pattern: require_constants.PATTERNS.projectId.pattern, required: true }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { class: "description", children: "Only lowercase alphabets, numbers and hyphen (-) allowed. Max length: 60 chars" }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "name", children: "Project Name" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "name", name: "name", required: true, value: project?.name })] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "GitHub" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "gitHubRepo", children: "Repo name" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "gitHubRepo", name: "gitHubRepo", placeholder: "owner/repo", required: true, pattern: "^.+\\/.+$", value: project?.gitHubRepo })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "gitHubPath", children: "Instance path" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "gitHubPath", name: "gitHubPath", placeholder: "packages/ui", value: project?.gitHubPath }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { class: "description", children: "Optional. If the Storybook is not is the root of repo." }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "gitHubDefaultBranch", children: "Default branch" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "gitHubDefaultBranch", name: "gitHubDefaultBranch", placeholder: "main", value: project?.gitHubDefaultBranch }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { class: "description", children: "Optional. If the default branch is not 'main'." }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("fieldset", { children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("legend", { children: "Purge" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { class: "field", children: [ /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("label", { for: "purgeBuildsAfterDays", children: "Purge builds after days" }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("input", { id: "purgeBuildsAfterDays", name: "purgeBuildsAfterDays", required: true, type: "number", inputmode: "numeric", value: (project?.purgeBuildsAfterDays || require_constants.DEFAULT_PURGE_AFTER_DAYS).toString() }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsx)("span", { class: "description", safe: true, children: require_projects.ProjectSchema.def.shape.purgeBuildsAfterDays.description || "" }) ] })] }), /* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("div", { style: { display: "flex", gap: "1rem" }, children: [/* @__PURE__ */ (0, import_jsx_runtime$1.jsxs)("button", { type: "submit", children: [project ? "Update" : "Create", " Project"] }), /* @__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/project-handlers.tsx var import_jsx_runtime = require_chunk.__toESM(require_store.require_jsx_runtime()); async function listProjects(_request, context) { try { if (require_store.checkIsNewMode()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: "Create Project", breadcrumbs: [{ label: "Projects", href: require_store.urlBuilder.allProjects() }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectForm, { project: void 0 }) })); const { connectionString } = require_store.getStore(); const projectModel = new require_projects.ProjectModel(context, connectionString); const projects = await projectModel.list(); if (require_store.checkIsHTMLRequest()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: "All Projects", toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.projectCreate(), children: "+ Create" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_projects_table.ProjectsTable, { projects }) })); return { status: 200, jsonBody: projects }; } catch (error) { return require_store.responseError(error, context, 500); } } async function getProject(request, context) { const { projectId } = request.params; const { connectionString } = require_store.getStore(); if (!projectId) return { status: 400, body: "Missing project ID" }; try { const projectModel = new require_projects.ProjectModel(context, connectionString); const project = await projectModel.get(projectId); if (require_store.checkIsEditMode()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: "Edit Project", breadcrumbs: [{ label: "Projects", href: require_store.urlBuilder.allProjects() }, { label: projectId, href: require_store.urlBuilder.projectId(projectId) }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectForm, { project }) })); const builds = await projectModel.buildModel(projectId).list({ limit: 25 }); const labels = await projectModel.labelModel(projectId).list({ limit: 25 }); if (require_store.checkIsHTMLRequest()) return require_store.responseHTML(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_store.DocumentLayout, { title: project.name, breadcrumbs: [{ label: "Projects", href: require_store.urlBuilder.allProjects() }], toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.projectIdEdit(projectId), children: "Edit" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { "hx-delete": request.url, "hx-confirm": "Are you sure about deleting the project?", 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: project, summary: "Project details" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_labels_table.LabelsTable, { caption: "Latest labels", toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.allLabels(projectId), children: "View all" }), labels, projectId }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(require_builds_table.BuildTable, { toolbar: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: require_store.urlBuilder.allBuilds(projectId), children: "View all" }), caption: "Latest builds", project, builds, labels }) ] }) })); return { status: 200, jsonBody: { ...project, latestBuilds: builds, labels } }; } catch (error) { return require_store.responseError(error, context, 404); } } async function createProject(request, context) { try { const { connectionString } = require_store.getStore(); const contentType = request.headers.get("content-type"); if (!contentType) return require_store.responseError("Content-Type header is required", context, 400); if (!contentType.includes(require_constants.CONTENT_TYPES.FORM_ENCODED)) return require_store.responseError(`Invalid Content-Type, expected ${require_constants.CONTENT_TYPES.FORM_ENCODED}`, context, 415); const data = require_projects.ProjectCreateSchema.parse(require_url_utils.urlSearchParamsToObject(await request.formData())); const projectModel = new require_projects.ProjectModel(context, connectionString); await projectModel.create(data); const projectUrl = require_store.urlBuilder.projectId(data.id); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(projectUrl, 303); return { status: 201, headers: { Location: projectUrl }, jsonBody: { data, links: { self: projectUrl } } }; } catch (error) { return require_store.responseError(error, context); } } async function updateProject(request, context) { try { const { projectId } = request.params; const { connectionString } = require_store.getStore(); if (!projectId) return { status: 400, body: "Missing project ID" }; const contentType = request.headers.get("content-type"); if (!contentType) return require_store.responseError("Content-Type header is required", context, 400); if (!contentType.includes(require_constants.CONTENT_TYPES.FORM_ENCODED)) return require_store.responseError(`Invalid Content-Type, expected ${require_constants.CONTENT_TYPES.FORM_ENCODED}`, context, 415); const data = require_projects.ProjectSchema.partial().parse(require_url_utils.urlSearchParamsToObject(await request.formData())); const model = new require_projects.ProjectModel(context, connectionString); await model.update(projectId, data); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(request.url, 303); return { status: 202, headers: { Location: request.url }, jsonBody: { data: await model.get(projectId), links: { self: request.url } } }; } catch (error) { return require_store.responseError(error, context, 404); } } async function deleteProject(request, context) { try { const { projectId } = request.params; if (!projectId) return { status: 400, body: "Missing project ID" }; const { connectionString } = require_store.getStore(); const model = new require_projects.ProjectModel(context, connectionString); await model.delete(projectId); const projectsUrl = require_store.urlBuilder.allProjects(); if (require_store.checkIsHTMLRequest() || require_store.checkIsHXRequest()) return require_store.responseRedirect(projectsUrl, 303); return { status: 204, headers: { Location: projectsUrl } }; } catch (error) { return require_store.responseError(error, context, 404); } } //#endregion //#region src/routers/projects-router.ts const TAG = require_openapi_utils.openAPITags.projects.name; function registerProjectsRouter(options) { const { authLevel, baseRoute, basePathParamsSchema, handlerWrapper, openAPIEnabled, serviceName } = options; const projectIdRoute = require_url_utils.joinUrl(baseRoute, "{projectId}"); __azure_functions.app.get(`${serviceName}-projects-list`, { authLevel, route: baseRoute, handler: handlerWrapper(listProjects, [{ resource: "project", action: "read" }, { resource: "ui", action: "read" }]) }); __azure_functions.app.post(`${serviceName}-project-create`, { authLevel, route: baseRoute, handler: handlerWrapper(createProject, [{ resource: "project", action: "create" }]) }); __azure_functions.app.get(`${serviceName}-project-get`, { authLevel, route: projectIdRoute, handler: handlerWrapper(getProject, [{ resource: "project", action: "read" }, { resource: "ui", action: "read" }]) }); __azure_functions.app.patch(`${serviceName}-project-update`, { authLevel, route: projectIdRoute, handler: handlerWrapper(updateProject, [{ resource: "project", action: "update" }]) }); __azure_functions.app.deleteRequest(`${serviceName}-project-delete`, { authLevel, route: projectIdRoute, handler: handlerWrapper(deleteProject, [{ resource: "project", action: "delete" }]) }); if (openAPIEnabled) { const projectPathParameterSchema = basePathParamsSchema.extend({ projectId: require_shared.ProjectIdSchema }); require_openapi_utils.registerOpenAPIPath(baseRoute, { get: { tags: [TAG], summary: "List all projects", description: "Retrieves a list of projects.", requestParams: { path: basePathParamsSchema }, responses: { ...require_constants.commonErrorResponses, 200: { description: "A list of projects.", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: require_projects.ProjectSchema.array(), example: [{ project: "project-id" }] }, [require_constants.CONTENT_TYPES.HTML]: { example: "<!DOCTYPE html>" } } } } }, post: { tags: [TAG], summary: "Create a new project", description: "Creates a new project with the provided metadata.", requestBody: { required: true, description: "Data about the project", content: { [require_constants.CONTENT_TYPES.FORM_ENCODED]: { schema: require_projects.ProjectCreateSchema } } }, requestParams: { path: basePathParamsSchema }, responses: { ...require_constants.commonErrorResponses, 201: { description: "Project created successfully", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: zod.default.object({ data: require_projects.ProjectSchema, links: zod.default.object({ self: zod.default.url() }) }) } } }, 303: { description: "Project created, redirecting...", headers: { Location: zod.default.url() } }, 415: { description: "Unsupported Media Type" } } } }); require_openapi_utils.registerOpenAPIPath(projectIdRoute, { get: { tags: [TAG], summary: "Get project details", description: "Retrieves the details of a specific project.", requestParams: { path: projectPathParameterSchema }, responses: { ...require_constants.commonErrorResponses, 200: { description: "Project details retrieved successfully", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: require_projects.ProjectSchema }, [require_constants.CONTENT_TYPES.HTML]: { example: "<!DOCTYPE html>" } } }, 404: { description: "Matching project not found." } } }, patch: { tags: [TAG], summary: "Update project details", description: "Updates the details of a specific project.", requestParams: { path: projectPathParameterSchema }, requestBody: { required: true, description: "Updated project data", content: { [require_constants.CONTENT_TYPES.FORM_ENCODED]: { schema: require_projects.ProjectSchema.partial() } } }, responses: { ...require_constants.commonErrorResponses, 202: { description: "Project updated successfully", content: { [require_constants.CONTENT_TYPES.JSON]: { schema: zod.default.object({ data: require_projects.ProjectSchema, links: zod.default.object({ self: zod.default.url() }) }) } } }, 303: { description: "Project updated, redirecting...", headers: { Location: zod.default.url() } }, 404: { description: "Matching project not found." }, 415: { description: "Unsupported Media Type" } } }, delete: { tags: [TAG], summary: "Delete a project", description: "Deletes a specific project.", requestParams: { path: projectPathParameterSchema }, responses: { ...require_constants.commonErrorResponses, 204: { description: "Project deleted successfully" }, 404: { description: "Matching project not found." } } } }); } } //#endregion Object.defineProperty(exports, 'registerProjectsRouter', { enumerable: true, get: function () { return registerProjectsRouter; } });