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 @@
1import { Cause, Exit, Result, Schema } from "effect";
2import { PositiveInt, TrimmedNonEmptyString } from "@t3tools/contracts";
3import { decodeJsonResult, formatSchemaError } from "@t3tools/shared/schemaJson";
4
5export 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
18const 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
44function trimOptionalString(value: string | null | undefined): string | null {
45 const trimmed = value?.trim() ?? "";
46 return trimmed.length > 0 ? trimmed : null;
47}
48
49function 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
66function 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
93const decodeGitHubPullRequestList = decodeJsonResult(Schema.Array(Schema.Unknown));
94const decodeGitHubPullRequest = decodeJsonResult(GitHubPullRequestSchema);
95const decodeGitHubPullRequestEntry = Schema.decodeUnknownExit(GitHubPullRequestSchema);
96
97export const formatGitHubJsonDecodeError = formatSchemaError;
98
99export 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
120export 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}