History

Handle deleted git directories as non-repositories (#1907)

Julius Marminge committed 7 months ago

Commit 0e776321 parent 5e4bb0c7 files · +589 −167Browse files

Showing the changes to apps/server/src/git/githubPullRequests.ts (1 of 7 files). Show all

Unified · Split

apps/server/src/git/githubPullRequests.ts+128−0
@@ -0,0 +1,128 @@
1+import { Cause, Exit, Result, Schema } from "effect";
2+import { PositiveInt, TrimmedNonEmptyString } from "@t3tools/contracts";
3+import { decodeJsonResult, formatSchemaError } from "@t3tools/shared/schemaJson";
4+
5+export interface NormalizedGitHubPullRequestRecord {
6+ readonly number: number;
7+ readonly title: string;
8+ readonly url: string;
9+ readonly baseRefName: string;
10+ readonly headRefName: string;
11+ readonly state: "open" | "closed" | "merged";
12+ readonly updatedAt: string | null;
13+ readonly isCrossRepository?: boolean;
14+ readonly headRepositoryNameWithOwner?: string | null;
15+ readonly headRepositoryOwnerLogin?: string | null;
16+}
17+
18+const GitHubPullRequestSchema = Schema.Struct({
19+ number: PositiveInt,
20+ title: TrimmedNonEmptyString,
21+ url: TrimmedNonEmptyString,
22+ baseRefName: TrimmedNonEmptyString,
23+ headRefName: TrimmedNonEmptyString,
24+ state: Schema.optional(Schema.NullOr(Schema.String)),
25+ mergedAt: Schema.optional(Schema.NullOr(Schema.String)),
26+ updatedAt: Schema.optional(Schema.NullOr(Schema.String)),
27+ isCrossRepository: Schema.optional(Schema.Boolean),
28+ headRepository: Schema.optional(
29+ Schema.NullOr(
30+ Schema.Struct({
31+ nameWithOwner: Schema.String,
32+ }),
33+ ),
34+ ),
35+ headRepositoryOwner: Schema.optional(
36+ Schema.NullOr(
37+ Schema.Struct({
38+ login: Schema.String,
39+ }),
40+ ),
41+ ),
42+});
43+
44+function trimOptionalString(value: string | null | undefined): string | null {
45+ const trimmed = value?.trim() ?? "";
46+ return trimmed.length > 0 ? trimmed : null;
47+}
48+
49+function normalizeGitHubPullRequestState(input: {
50+ state?: string | null | undefined;
51+ mergedAt?: string | null | undefined;
52+}): "open" | "closed" | "merged" {
53+ const normalizedState = input.state?.trim().toUpperCase();
54+ if (
55+ (typeof input.mergedAt === "string" && input.mergedAt.trim().length > 0) ||
56+ normalizedState === "MERGED"
57+ ) {
58+ return "merged";
59+ }
60+ if (normalizedState === "CLOSED") {
61+ return "closed";
62+ }
63+ return "open";
64+}
65+
66+function normalizeGitHubPullRequestRecord(
67+ raw: Schema.Schema.Type<typeof GitHubPullRequestSchema>,
68+): NormalizedGitHubPullRequestRecord {
69+ const headRepositoryNameWithOwner = trimOptionalString(raw.headRepository?.nameWithOwner);
70+ const headRepositoryOwnerLogin =
71+ trimOptionalString(raw.headRepositoryOwner?.login) ??
72+ (typeof headRepositoryNameWithOwner === "string" && headRepositoryNameWithOwner.includes("/")
73+ ? (headRepositoryNameWithOwner.split("/")[0] ?? null)
74+ : null);
75+
76+ return {
77+ number: raw.number,
78+ title: raw.title,
79+ url: raw.url,
80+ baseRefName: raw.baseRefName,
81+ headRefName: raw.headRefName,
82+ state: normalizeGitHubPullRequestState(raw),
83+ updatedAt:
84+ typeof raw.updatedAt === "string" && raw.updatedAt.trim().length > 0 ? raw.updatedAt : null,
85+ ...(typeof raw.isCrossRepository === "boolean"
86+ ? { isCrossRepository: raw.isCrossRepository }
87+ : {}),
88+ ...(headRepositoryNameWithOwner ? { headRepositoryNameWithOwner } : {}),
89+ ...(headRepositoryOwnerLogin ? { headRepositoryOwnerLogin } : {}),
90+ };
91+}
92+
93+const decodeGitHubPullRequestList = decodeJsonResult(Schema.Array(Schema.Unknown));
94+const decodeGitHubPullRequest = decodeJsonResult(GitHubPullRequestSchema);
95+const decodeGitHubPullRequestEntry = Schema.decodeUnknownExit(GitHubPullRequestSchema);
96+
97+export const formatGitHubJsonDecodeError = formatSchemaError;
98+
99+export function decodeGitHubPullRequestListJson(
100+ raw: string,
101+): Result.Result<
102+ ReadonlyArray<NormalizedGitHubPullRequestRecord>,
103+ Cause.Cause<Schema.SchemaError>
104+> {
105+ const result = decodeGitHubPullRequestList(raw);
106+ if (Result.isSuccess(result)) {
107+ const pullRequests: NormalizedGitHubPullRequestRecord[] = [];
108+ for (const entry of result.success) {
109+ const decodedEntry = decodeGitHubPullRequestEntry(entry);
110+ if (Exit.isFailure(decodedEntry)) {
111+ continue;
112+ }
113+ pullRequests.push(normalizeGitHubPullRequestRecord(decodedEntry.value));
114+ }
115+ return Result.succeed(pullRequests);
116+ }
117+ return Result.fail(result.failure);
118+}
119+
120+export function decodeGitHubPullRequestJson(
121+ raw: string,
122+): Result.Result<NormalizedGitHubPullRequestRecord, Cause.Cause<Schema.SchemaError>> {
123+ const result = decodeGitHubPullRequest(raw);
124+ if (Result.isSuccess(result)) {
125+ return Result.succeed(normalizeGitHubPullRequestRecord(result.success));
126+ }
127+ return Result.fail(result.failure);
128+}