chore: update src/lib/graphql/resolvers.ts
swadhinbiswas committed 8 months ago
8fa6908
1 parent 8461836
commit 8fa6908
1 file changed
+69 -7
Showing 1 changed file+69-7
▼src/lib/graphql/resolvers.ts
+69-7
| @@ -7,6 +7,11 @@ import { getDatabase, schema } from "@/db"; | ||
7 | 7 | import type { NodePgDatabase } from "drizzle-orm/node-postgres"; |
8 | 8 | import { eq, and, like, desc, sql, count } from "drizzle-orm"; |
9 | 9 | import { logger } from "@/lib/logger"; |
10 | +import { compareBranches, getCommit, mergeBranch } from "@/lib/git"; | |
11 | +import { resolveRepoPath } from "@/lib/git-storage"; | |
12 | +import { canWriteRepo } from "@/lib/permissions"; | |
13 | +import { evaluateGates } from "@/lib/ci-gates"; | |
14 | +import { closeLinkedIssuesOnMerge } from "@/lib/pr-issue-linking"; | |
10 | 15 | |
11 | 16 | // Context type for resolvers |
12 | 17 | export interface GraphQLContext { |
| @@ -209,7 +214,17 @@ export const resolvers = { | ||
209 | 214 | }; |
210 | 215 | }, |
211 | 216 | |
212 | - stargazerCount: () => 0, // Stars table not yet implemented | |
217 | + stargazerCount: async ( | |
218 | + repo: typeof schema.repositories.$inferSelect, | |
219 | + _: unknown, | |
220 | + ctx: GraphQLContext | |
221 | + ) => { | |
222 | + const result = await ctx.db | |
223 | + .select({ count: count() }) | |
224 | + .from(schema.repositoryStars) | |
225 | + .where(eq(schema.repositoryStars.repositoryId, repo.id)); | |
226 | + return result[0]?.count || 0; | |
227 | + }, | |
213 | 228 | |
214 | 229 | forkCount: async ( |
215 | 230 | repo: typeof schema.repositories.$inferSelect, |
| @@ -429,6 +444,18 @@ export const resolvers = { | ||
429 | 444 | }); |
430 | 445 | if (!repo) throw new Error("Repository not found"); |
431 | 446 | |
447 | + const repoPath = await resolveRepoPath(repo.diskPath); | |
448 | + const headCommit = await getCommit(repoPath, input.headRefName); | |
449 | + if (!headCommit) throw new Error(`Head branch ${input.headRefName} not found`); | |
450 | + | |
451 | + const baseCommit = await getCommit(repoPath, input.baseRefName); | |
452 | + if (!baseCommit) throw new Error(`Base branch ${input.baseRefName} not found`); | |
453 | + | |
454 | + const { diffs } = await compareBranches(repoPath, input.baseRefName, input.headRefName); | |
455 | + const additions = diffs.reduce((sum, diff) => sum + diff.additions, 0); | |
456 | + const deletions = diffs.reduce((sum, diff) => sum + diff.deletions, 0); | |
457 | + const changedFiles = diffs.length; | |
458 | + | |
432 | 459 | // Get next PR number |
433 | 460 | const result = await ctx.db |
434 | 461 | .select({ count: count() }) |
| @@ -447,10 +474,13 @@ export const resolvers = { | ||
447 | 474 | authorId: ctx.userId, |
448 | 475 | headBranch: input.headRefName, |
449 | 476 | baseBranch: input.baseRefName, |
450 | - headSha: "0000000000000000000000000000000000000000", // Placeholder - would fetch from git | |
451 | - baseSha: "0000000000000000000000000000000000000000", // Placeholder | |
477 | + headSha: headCommit.sha, | |
478 | + baseSha: baseCommit.sha, | |
452 | 479 | isDraft: input.draft, |
453 | 480 | state: "open", |
481 | + additions, | |
482 | + deletions, | |
483 | + changedFiles, | |
454 | 484 | createdAt: new Date(), |
455 | 485 | updatedAt: new Date(), |
456 | 486 | }); |
| @@ -476,19 +506,51 @@ export const resolvers = { | ||
476 | 506 | |
477 | 507 | if (pr.state !== "open") throw new Error("Pull request is not open"); |
478 | 508 | |
479 | - // In a real implementation, this would trigger a git merge or enqueue | |
480 | - // For now, we update the DB state | |
509 | + const repo = await ctx.db.query.repositories.findFirst({ | |
510 | + where: eq(schema.repositories.id, pr.repositoryId), | |
511 | + }); | |
512 | + if (!repo) throw new Error("Repository not found"); | |
513 | + | |
514 | + if (!(await canWriteRepo(ctx.userId, repo, { isAdmin: ctx.user?.isAdmin ?? undefined }))) { | |
515 | + throw new Error("Access denied"); | |
516 | + } | |
517 | + | |
518 | + const gateResult = await evaluateGates(pr.id); | |
519 | + if (!gateResult.canMerge) { | |
520 | + const failed = gateResult.results | |
521 | + .filter((r) => !r.passed) | |
522 | + .map((r) => r.message) | |
523 | + .join("; "); | |
524 | + throw new Error(`Merge blocked: ${failed}`); | |
525 | + } | |
526 | + | |
527 | + const commitTitle = input.commitTitle || `Merge pull request #${pr.number} from ${pr.headBranch}`; | |
528 | + const mergeMessage = input.commitBody | |
529 | + ? `${commitTitle}\n\n${input.commitBody}` | |
530 | + : commitTitle; | |
531 | + | |
532 | + const repoPath = await resolveRepoPath(repo.diskPath); | |
533 | + const mergeResult = await mergeBranch(repoPath, pr.baseBranch, pr.headBranch, mergeMessage); | |
534 | + if (!mergeResult.success) { | |
535 | + throw new Error(mergeResult.message || "Merge failed"); | |
536 | + } | |
537 | + | |
538 | + const now = new Date(); | |
481 | 539 | await ctx.db.update(schema.pullRequests) |
482 | 540 | .set({ |
483 | 541 | state: "merged", |
484 | 542 | isMerged: true, |
485 | - mergedAt: new Date(), | |
543 | + mergedAt: now, | |
486 | 544 | mergedById: ctx.userId, |
545 | + mergeCommitSha: mergeResult.sha || null, | |
546 | + mergeSha: mergeResult.sha || null, | |
487 | 547 | mergeMethod: input.mergeMethod?.toLowerCase() || "merge", |
488 | - updatedAt: new Date(), | |
548 | + updatedAt: now, | |
489 | 549 | }) |
490 | 550 | .where(eq(schema.pullRequests.id, input.pullRequestId)); |
491 | 551 | |
552 | + await closeLinkedIssuesOnMerge(pr.id, ctx.userId); | |
553 | + | |
492 | 554 | const updatedPr = await ctx.db.query.pullRequests.findFirst({ |
493 | 555 | where: eq(schema.pullRequests.id, input.pullRequestId), |
494 | 556 | }); |
495 | 557 | |