@azure-utils/storybooks
Version:
Utils to upload and manage Storybooks via Azure Functions and storage.
505 lines (501 loc) • 18.3 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_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;
}
});