kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { and, eq } from "drizzle-orm";
2import db from "../../../database";
3import { externalLinkTable } from "../../../database/schema";
4
5export type CreateExternalLinkParams = {
6 taskId: string;
7 integrationId: string;
8 resourceType: "issue" | "pull_request" | "branch";
9 externalId: string;
10 url: string;
11 title?: string | null;
12 metadata?: Record<string, unknown>;
13};
14
15export type UpdateExternalLinkParams = {
16 title?: string | null;
17 url?: string;
18 metadata?: Record<string, unknown>;
19};
20
21export async function createExternalLink(
22 params: CreateExternalLinkParams,
23): Promise<{ id: string }> {
24 const result = await db
25 .insert(externalLinkTable)
26 .values({
27 taskId: params.taskId,
28 integrationId: params.integrationId,
29 resourceType: params.resourceType,
30 externalId: params.externalId,
31 url: params.url,
32 title: params.title ?? null,
33 metadata: params.metadata ? JSON.stringify(params.metadata) : null,
34 })
35 .returning({ id: externalLinkTable.id });
36
37 const link = result[0];
38 if (!link) {
39 throw new Error("Failed to create external link");
40 }
41
42 return link;
43}
44
45export async function findExternalLink(
46 integrationId: string,
47 resourceType: string,
48 externalId: string,
49) {
50 return db.query.externalLinkTable.findFirst({
51 where: and(
52 eq(externalLinkTable.integrationId, integrationId),
53 eq(externalLinkTable.resourceType, resourceType),
54 eq(externalLinkTable.externalId, externalId),
55 ),
56 });
57}
58
59export async function findExternalLinkByTaskAndType(
60 taskId: string,
61 integrationId: string,
62 resourceType: string,
63) {
64 return db.query.externalLinkTable.findFirst({
65 where: and(
66 eq(externalLinkTable.taskId, taskId),
67 eq(externalLinkTable.integrationId, integrationId),
68 eq(externalLinkTable.resourceType, resourceType),
69 ),
70 });
71}
72
73export async function findExternalLinksByTask(taskId: string) {
74 return db.query.externalLinkTable.findMany({
75 where: eq(externalLinkTable.taskId, taskId),
76 with: {
77 integration: true,
78 },
79 });
80}
81
82export async function updateExternalLink(
83 id: string,
84 params: UpdateExternalLinkParams,
85) {
86 const updateData: Record<string, unknown> = {};
87
88 if (params.title !== undefined) {
89 updateData.title = params.title;
90 }
91 if (params.url !== undefined) {
92 updateData.url = params.url;
93 }
94 if (params.metadata !== undefined) {
95 updateData.metadata = JSON.stringify(params.metadata);
96 }
97
98 if (Object.keys(updateData).length === 0) {
99 return;
100 }
101
102 await db
103 .update(externalLinkTable)
104 .set(updateData)
105 .where(eq(externalLinkTable.id, id));
106}
107
108export async function createOrUpdateExternalLink(
109 params: CreateExternalLinkParams,
110): Promise<{ id: string; created: boolean }> {
111 const existing = await findExternalLink(
112 params.integrationId,
113 params.resourceType,
114 params.externalId,
115 );
116
117 if (existing) {
118 await updateExternalLink(existing.id, {
119 title: params.title,
120 url: params.url,
121 metadata: params.metadata,
122 });
123 return { id: existing.id, created: false };
124 }
125
126 const link = await createExternalLink(params);
127 return { id: link.id, created: true };
128}
129
130export async function deleteExternalLink(id: string) {
131 await db.delete(externalLinkTable).where(eq(externalLinkTable.id, id));
132}
133
134export async function getExternalLinksByIntegration(integrationId: string) {
135 return db.query.externalLinkTable.findMany({
136 where: eq(externalLinkTable.integrationId, integrationId),
137 });
138}