Skip to content
File

Blob: src/worker/presentation/serializers.ts

typescript227 lines
1import {
2 BranchName,
3 CommitSha,
4 type LogEvent,
5 OwnerSlug,
6 type PendingRunSummary,
7 type ProjectConfigSummary,
8 ProjectId,
9 type ProjectDetail,
10 ProjectSlug,
11 type ProjectSummary,
12 type RunDetail,
13 type RunExecutionState,
14 RunId,
15 RunStatus,
16 type RunStep,
17 type RunSummary,
18 TriggerType,
19 type UserSummary,
20 UserId,
21 type WebhookRecentDelivery,
22 type WebhookSummary,
23 WebhookDeliveryOutcome,
24 WebhookEventKind,
25 WebhookId,
26 WebhookProvider,
27} from "@/contracts";
28import {
29 expectTrusted,
30 isoDateTimeFromTimestamp,
31 nullableIsoDateTimeFromTimestamp,
32 nullableTrusted,
33 type PendingRunState,
34 type RunLogRecord,
35 type RunMetaState,
36 type RunStepState,
37} from "@/worker/contracts";
38import type { RunIndexRow, UserRow } from "@/worker/db/d1/repositories";
39import type { ParsedWebhookDeliveryRow, StoredProjectWebhook } from "@/worker/durable/project-do/webhooks/types";
40 
41interface ProjectSummarySource {
42 id: string;
43 ownerUserId: string;
44 ownerSlug: string;
45 projectSlug: string;
46 name: string;
47 repoUrl: string;
48 defaultBranch: string;
49 configPath: string;
50 createdAt: number;
51 updatedAt: number;
52}
53 
54interface ProjectConfigSummarySource extends ProjectSummarySource {
55 dispatchMode: "queue" | "workflows";
56}
57 
58const toIso = (value: number | null) => nullableIsoDateTimeFromTimestamp(value);
59 
60export const serializeUserSummary = (user: UserRow): UserSummary => ({
61 id: expectTrusted(UserId, user.id, "UserId"),
62 slug: expectTrusted(OwnerSlug, user.slug, "OwnerSlug"),
63 email: user.email,
64 displayName: user.displayName,
65 createdAt: isoDateTimeFromTimestamp(user.createdAt),
66 disabledAt: toIso(user.disabledAt),
67});
68 
69export const serializeProjectSummary = (
70 project: ProjectSummarySource,
71 lastRunStatus: RunStatus | null,
72): ProjectSummary => ({
73 id: expectTrusted(ProjectId, project.id, "ProjectId"),
74 ownerUserId: expectTrusted(UserId, project.ownerUserId, "UserId"),
75 ownerSlug: expectTrusted(OwnerSlug, project.ownerSlug, "OwnerSlug"),
76 projectSlug: expectTrusted(ProjectSlug, project.projectSlug, "ProjectSlug"),
77 name: project.name,
78 repoUrl: project.repoUrl,
79 defaultBranch: expectTrusted(BranchName, project.defaultBranch, "BranchName"),
80 configPath: project.configPath,
81 createdAt: isoDateTimeFromTimestamp(project.createdAt),
82 updatedAt: isoDateTimeFromTimestamp(project.updatedAt),
83 lastRunStatus,
84});
85 
86export const serializeProjectConfigSummary = (
87 project: ProjectConfigSummarySource,
88 lastRunStatus: RunStatus | null,
89): ProjectConfigSummary => ({
90 ...serializeProjectSummary(project, lastRunStatus),
91 dispatchMode: project.dispatchMode,
92});
93 
94export const serializePendingRunSummary = (pendingRun: PendingRunState): PendingRunSummary => ({
95 runId: pendingRun.runId,
96 branch: pendingRun.branch,
97 queuedAt: isoDateTimeFromTimestamp(pendingRun.queuedAt),
98});
99 
100export const serializeRunSummary = (
101 run: Pick<
102 RunIndexRow,
103 | "id"
104 | "projectId"
105 | "triggeredByUserId"
106 | "triggerType"
107 | "branch"
108 | "commitSha"
109 | "status"
110 | "queuedAt"
111 | "startedAt"
112 | "finishedAt"
113 | "exitCode"
114 >,
115): RunSummary => ({
116 id: expectTrusted(RunId, run.id, "RunId"),
117 projectId: expectTrusted(ProjectId, run.projectId, "ProjectId"),
118 triggeredByUserId: nullableTrusted(UserId, run.triggeredByUserId, "UserId"),
119 triggerType: expectTrusted(TriggerType, run.triggerType, "TriggerType"),
120 branch: expectTrusted(BranchName, run.branch, "BranchName"),
121 commitSha: nullableTrusted(CommitSha, run.commitSha, "CommitSha"),
122 status: expectTrusted(RunStatus, run.status, "RunStatus"),
123 queuedAt: isoDateTimeFromTimestamp(run.queuedAt),
124 startedAt: toIso(run.startedAt),
125 finishedAt: toIso(run.finishedAt),
126 exitCode: run.exitCode,
127});
128 
129export const serializeRunStep = (step: RunStepState): RunStep => ({
130 id: step.id,
131 runId: step.runId,
132 position: step.position,
133 name: step.name,
134 command: step.command,
135 status: step.status,
136 startedAt: toIso(step.startedAt),
137 finishedAt: toIso(step.finishedAt),
138 exitCode: step.exitCode,
139});
140 
141export const serializeRunExecutionState = (meta: RunMetaState): RunExecutionState => ({
142 status: meta.status,
143 currentStep: meta.currentStep,
144 startedAt: toIso(meta.startedAt),
145 finishedAt: toIso(meta.finishedAt),
146 exitCode: meta.exitCode,
147 errorMessage: meta.errorMessage,
148});
149 
150export const serializeLogEvent = (event: RunLogRecord): LogEvent => ({
151 id: event.id,
152 runId: event.runId,
153 seq: event.seq,
154 stream: event.stream,
155 chunk: event.chunk,
156 createdAt: isoDateTimeFromTimestamp(event.createdAt),
157});
158 
159export const serializeProjectDetail = (detail: {
160 project: ProjectConfigSummarySource;
161 lastRunStatus: RunStatus | null;
162 activeRun: RunSummary | null;
163 pendingRuns: PendingRunSummary[];
164}): ProjectDetail => ({
165 project: serializeProjectConfigSummary(detail.project, detail.lastRunStatus),
166 activeRun: detail.activeRun,
167 pendingRuns: detail.pendingRuns,
168});
169 
170export const serializeRunDetail = (detail: {
171 run: RunSummary;
172 currentStep: number | null;
173 errorMessage: string | null;
174 steps: RunStep[];
175 recentLogs: LogEvent[];
176 detailAvailable: boolean;
177}): RunDetail => ({
178 run: detail.run,
179 currentStep: detail.currentStep,
180 errorMessage: detail.errorMessage,
181 steps: detail.steps,
182 recentLogs: detail.recentLogs,
183 detailAvailable: detail.detailAvailable,
184});
185 
186export const serializeWebhookRecentDelivery = (
187 delivery: Pick<
188 ParsedWebhookDeliveryRow,
189 | "provider"
190 | "deliveryId"
191 | "eventKind"
192 | "eventName"
193 | "outcome"
194 | "repoUrl"
195 | "ref"
196 | "branch"
197 | "commitSha"
198 | "beforeSha"
199 | "runId"
200 | "receivedAt"
201 >,
202): WebhookRecentDelivery => ({
203 provider: expectTrusted(WebhookProvider, delivery.provider, "WebhookProvider"),
204 deliveryId: delivery.deliveryId,
205 eventKind: expectTrusted(WebhookEventKind, delivery.eventKind, "WebhookEventKind"),
206 eventName: delivery.eventName,
207 outcome: expectTrusted(WebhookDeliveryOutcome, delivery.outcome, "WebhookDeliveryOutcome"),
208 repoUrl: delivery.repoUrl,
209 ref: delivery.ref,
210 branch: nullableTrusted(BranchName, delivery.branch, "BranchName"),
211 commitSha: nullableTrusted(CommitSha, delivery.commitSha, "CommitSha"),
212 beforeSha: nullableTrusted(CommitSha, delivery.beforeSha, "CommitSha"),
213 runId: nullableTrusted(RunId, delivery.runId, "RunId"),
214 receivedAt: isoDateTimeFromTimestamp(delivery.receivedAt),
215});
216 
217export const serializeWebhookSummary = (webhook: StoredProjectWebhook): WebhookSummary => ({
218 id: expectTrusted(WebhookId, webhook.id, "WebhookId"),
219 projectId: expectTrusted(ProjectId, webhook.projectId, "ProjectId"),
220 provider: expectTrusted(WebhookProvider, webhook.provider, "WebhookProvider"),
221 enabled: webhook.enabled,
222 config: webhook.config,
223 createdAt: isoDateTimeFromTimestamp(webhook.createdAt),
224 updatedAt: isoDateTimeFromTimestamp(webhook.updatedAt),
225 recentDeliveries: webhook.recentDeliveries.map(serializeWebhookRecentDelivery),
226});