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 {
4 columnTable,
5 integrationTable,
6 taskTable,
7} from "../../../database/schema";
8
9const NON_COLUMN_STATUSES = new Set(["planned", "archived"]);
10
11export async function findTaskByNumber(projectId: string, taskNumber: number) {
12 return db.query.taskTable.findFirst({
13 where: and(
14 eq(taskTable.projectId, projectId),
15 eq(taskTable.number, taskNumber),
16 ),
17 });
18}
19
20export async function findTaskById(taskId: string) {
21 return db.query.taskTable.findFirst({
22 where: eq(taskTable.id, taskId),
23 });
24}
25
26export async function updateTaskStatus(taskId: string, newStatus: string) {
27 const task = await db.query.taskTable.findFirst({
28 where: eq(taskTable.id, taskId),
29 });
30
31 if (!task) {
32 return;
33 }
34
35 let columnId: string | null = null;
36
37 const column = await db.query.columnTable.findFirst({
38 where: and(
39 eq(columnTable.projectId, task.projectId),
40 eq(columnTable.slug, newStatus),
41 ),
42 });
43
44 if (column) {
45 columnId = column.id;
46 } else if (!NON_COLUMN_STATUSES.has(newStatus)) {
47 console.warn(
48 `[GitHub] Skipping status update for task ${taskId}: column "${newStatus}" not found in project ${task.projectId}`,
49 );
50 return;
51 }
52
53 await db
54 .update(taskTable)
55 .set({ status: newStatus, columnId })
56 .where(eq(taskTable.id, taskId));
57}
58
59export async function isTaskInFinalState(task: {
60 projectId: string;
61 status: string;
62 columnId: string | null;
63}): Promise<boolean> {
64 if (task.columnId) {
65 const columnById = await db.query.columnTable.findFirst({
66 where: and(
67 eq(columnTable.id, task.columnId),
68 eq(columnTable.projectId, task.projectId),
69 ),
70 });
71
72 if (columnById) {
73 return columnById.isFinal;
74 }
75 }
76
77 const columnByStatus = await db.query.columnTable.findFirst({
78 where: and(
79 eq(columnTable.projectId, task.projectId),
80 eq(columnTable.slug, task.status),
81 ),
82 });
83
84 if (columnByStatus) {
85 return columnByStatus.isFinal;
86 }
87
88 return task.status === "done";
89}
90
91export async function getIntegrationWithProject(integrationId: string) {
92 return db.query.integrationTable.findFirst({
93 where: eq(integrationTable.id, integrationId),
94 with: {
95 project: true,
96 },
97 });
98}
99
100export async function findIntegrationByRepo(owner: string, repo: string) {
101 const integrations = await findAllIntegrationsByRepo(owner, repo);
102 return integrations[0] || null;
103}
104
105export async function findAllIntegrationsByRepo(owner: string, repo: string) {
106 const integrations = await db.query.integrationTable.findMany({
107 where: and(
108 eq(integrationTable.type, "github"),
109 eq(integrationTable.isActive, true),
110 ),
111 with: {
112 project: true,
113 },
114 });
115
116 return integrations.filter((integration) => {
117 try {
118 const config = JSON.parse(integration.config);
119 return config.repositoryOwner === owner && config.repositoryName === repo;
120 } catch {
121 return false;
122 }
123 });
124}