1import {
2 legacyThreadPullRequestKey,
3 normalizeThreadPullRequestKey,
4 threadPullRequestKeysEqual,
5} from "@t3tools/shared/threadPullRequests";
6import {
7 PullRequestLinkedThreadsResult,
8 PullRequestOperationError,
9 type ThreadPullRequestKey,
10} from "@t3tools/contracts";
11import * as Effect from "effect/Effect";
12import * as Schema from "effect/Schema";
13import * as SqlClient from "effect/sql/SqlClient";
14
15const decodeLinkedThreads = Schema.decodeUnknownEffect(PullRequestLinkedThreadsResult);
16
17export const listLinkedPullRequestThreads = Effect.fn("listLinkedPullRequestThreads")(
18 function* (input: ThreadPullRequestKey) {
19 const key = normalizeThreadPullRequestKey(input);
20 const sql = yield* SqlClient.SqlClient;
21 const rows = yield* sql<{
22 id: string;
23 projectId: string;
24 title: string;
25 archivedAt: string | null;
26 host: string | null;
27 repository: string;
28 number: number;
29 url: string;
30 }>`
31 SELECT t.thread_id AS id, t.project_id AS "projectId", t.title,
32 t.archived_at AS "archivedAt", json_extract(link.value, '$.host') AS host,
33 json_extract(link.value, '$.repository') AS repository,
34 json_extract(link.value, '$.number') AS number,
35 json_extract(link.value, '$.url') AS url,
36 t.updated_at AS "updatedAt"
37 FROM orchestration_v2_projection_threads AS t
38 JOIN json_each(t.payload_json, '$.pullRequests') AS link
39 WHERE json_extract(link.value, '$.number') = ${key.number}
40 AND json_extract(link.value, '$.source') != 'stack-dismissed'
41 AND t.deleted_at IS NULL
42 UNION ALL
43 SELECT t.thread_id AS id, t.project_id AS "projectId", t.title,
44 t.archived_at AS "archivedAt", NULL AS host,
45 json_extract(t.payload_json, '$.linkedPullRequest.repository') AS repository,
46 json_extract(t.payload_json, '$.linkedPullRequest.number') AS number,
47 json_extract(t.payload_json, '$.linkedPullRequest.url') AS url,
48 t.updated_at AS "updatedAt"
49 FROM orchestration_v2_projection_threads AS t
50 WHERE json_type(t.payload_json, '$.pullRequests') IS NULL
51 AND json_type(t.payload_json, '$.linkedPullRequest') = 'object'
52 AND json_extract(t.payload_json, '$.linkedPullRequest.number') = ${key.number}
53 AND t.deleted_at IS NULL
54 ORDER BY "updatedAt" DESC, id ASC
55 `;
56 const threads = rows
57 .filter((row) =>
58 threadPullRequestKeysEqual(
59 row.host === null ? legacyThreadPullRequestKey(row) : { ...row, host: row.host },
60 key,
61 ),
62 )
63 .map(({ id, projectId, title, archivedAt }) => ({ id, projectId, title, archivedAt }));
64 return yield* decodeLinkedThreads({ threads });
65 },
66 Effect.mapError(
67 (cause) =>
68 new PullRequestOperationError({
69 operation: "linkedThreads",
70 detail: "Could not load linked threads.",
71 cause,
72 }),
73 ),
74);