@azure-utils/storybooks
Version:
Utils to upload and manage Storybooks via Azure Functions and storage.
418 lines (414 loc) • 15.5 kB
JavaScript
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;
}
});