| 1 | import { legacyThreadPullRequestKey } from "@t3tools/shared/threadPullRequests"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as SqlClient from "effect/sql/SqlClient"; |
| 4 | |
| 5 | interface LegacyLinkedThreadRow { |
| 6 | readonly threadId: string; |
| 7 | readonly updatedAt: string; |
| 8 | readonly linkedPullRequestJson: string; |
| 9 | } |
| 10 | |
| 11 | interface LegacyLinkedPullRequest { |
| 12 | readonly repository: string; |
| 13 | readonly number: number; |
| 14 | readonly url: string; |
| 15 | } |
| 16 | |
| 17 | function parseLegacyLinkedPullRequest(json: string): LegacyLinkedPullRequest | null { |
| 18 | try { |
| 19 | const value: unknown = JSON.parse(json); |
| 20 | if (typeof value !== "object" || value === null) return null; |
| 21 | const { repository, number, url } = value as Record<string, unknown>; |
| 22 | if (typeof repository !== "string" || repository.trim().length === 0) return null; |
| 23 | if (typeof number !== "number" || !Number.isInteger(number) || number < 1) return null; |
| 24 | if (typeof url !== "string" || url.trim().length === 0) return null; |
| 25 | return { repository, number, url }; |
| 26 | } catch { |
| 27 | return null; |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | export default Effect.gen(function* () { |
| 32 | const sql = yield* SqlClient.SqlClient; |
| 33 | |
| 34 | yield* sql` |
| 35 | CREATE TABLE IF NOT EXISTS projection_thread_pull_requests ( |
| 36 | thread_id TEXT NOT NULL, |
| 37 | host TEXT NOT NULL, |
| 38 | repository TEXT NOT NULL, |
| 39 | number INTEGER NOT NULL, |
| 40 | url TEXT NOT NULL, |
| 41 | source TEXT NOT NULL, |
| 42 | linked_at TEXT NOT NULL, |
| 43 | snapshot_json TEXT, |
| 44 | stack_json TEXT, |
| 45 | PRIMARY KEY (thread_id, host, repository, number) |
| 46 | ) |
| 47 | `; |
| 48 | |
| 49 | yield* sql` |
| 50 | CREATE INDEX IF NOT EXISTS idx_projection_thread_pull_requests_pr |
| 51 | ON projection_thread_pull_requests(host, repository, number) |
| 52 | `; |
| 53 | |
| 54 | const legacyRows = yield* sql<LegacyLinkedThreadRow>` |
| 55 | SELECT |
| 56 | thread_id AS "threadId", |
| 57 | updated_at AS "updatedAt", |
| 58 | linked_pull_request_json AS "linkedPullRequestJson" |
| 59 | FROM projection_threads |
| 60 | WHERE linked_pull_request_json IS NOT NULL |
| 61 | `; |
| 62 | |
| 63 | for (const row of legacyRows) { |
| 64 | const linked = parseLegacyLinkedPullRequest(row.linkedPullRequestJson); |
| 65 | if (linked === null) continue; |
| 66 | const key = legacyThreadPullRequestKey(linked); |
| 67 | yield* sql` |
| 68 | INSERT OR IGNORE INTO projection_thread_pull_requests ( |
| 69 | thread_id, |
| 70 | host, |
| 71 | repository, |
| 72 | number, |
| 73 | url, |
| 74 | source, |
| 75 | linked_at, |
| 76 | snapshot_json, |
| 77 | stack_json |
| 78 | ) |
| 79 | VALUES ( |
| 80 | ${row.threadId}, |
| 81 | ${key.host}, |
| 82 | ${key.repository}, |
| 83 | ${linked.number}, |
| 84 | ${linked.url}, |
| 85 | 'manual', |
| 86 | ${row.updatedAt}, |
| 87 | NULL, |
| 88 | NULL |
| 89 | ) |
| 90 | `; |
| 91 | } |
| 92 | }); |