kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import type { GitHubConfig } from "../config";
2import { createExternalLink, findExternalLink } from "../services/link-manager";
3import {
4 findAllIntegrationsByRepo,
5 findTaskByNumber,
6 isTaskInFinalState,
7 updateTaskStatus,
8} from "../services/task-service";
9import { extractTaskNumber } from "../utils/branch-matcher";
10import { resolveTargetStatus } from "../utils/resolve-column";
11
12type PROpenedPayload = {
13 action: string;
14 pull_request: {
15 number: number;
16 title: string;
17 body: string | null;
18 html_url: string;
19 state: string;
20 draft: boolean;
21 merged: boolean;
22 head: {
23 ref: string;
24 };
25 user: { login: string } | null;
26 };
27 repository: {
28 owner: { login: string };
29 name: string;
30 };
31};
32
33export async function handlePullRequestOpened(payload: PROpenedPayload) {
34 const { pull_request, repository } = payload;
35
36 const integrations = await findAllIntegrationsByRepo(
37 repository.owner.login,
38 repository.name,
39 );
40
41 for (const integration of integrations) {
42 if (!integration.project) {
43 continue;
44 }
45
46 const config = JSON.parse(integration.config) as GitHubConfig;
47 const projectSlug = integration.project.slug;
48 const branchName = pull_request.head.ref;
49
50 const taskNumber = extractTaskNumber(
51 branchName,
52 pull_request.title,
53 pull_request.body ?? undefined,
54 config,
55 projectSlug,
56 );
57
58 if (!taskNumber) {
59 continue;
60 }
61
62 const task = await findTaskByNumber(integration.projectId, taskNumber);
63
64 if (!task) {
65 continue;
66 }
67
68 const existingLink = await findExternalLink(
69 integration.id,
70 "pull_request",
71 pull_request.number.toString(),
72 );
73
74 if (existingLink) {
75 continue;
76 }
77
78 await createExternalLink({
79 taskId: task.id,
80 integrationId: integration.id,
81 resourceType: "pull_request",
82 externalId: pull_request.number.toString(),
83 url: pull_request.html_url,
84 title: pull_request.title,
85 metadata: {
86 state: pull_request.state,
87 draft: pull_request.draft,
88 merged: pull_request.merged,
89 branch: branchName,
90 author: pull_request.user?.login,
91 },
92 });
93
94 const targetStatus = await resolveTargetStatus(
95 integration.projectId,
96 "pr_opened",
97 config.statusTransitions?.onPROpen || "in-review",
98 );
99
100 const isTaskFinal = await isTaskInFinalState(task);
101
102 if (task.status !== targetStatus && !isTaskFinal) {
103 await updateTaskStatus(task.id, targetStatus);
104 }
105
106 return;
107 }
108}