import { prisma } from "@codeflash-ai/common" import { Request, Response } from "express" import { Octokit } from "@octokit/core" import { githubApp } from "../github/github-app.js" import { getInstallationOctokitByOwner, isUserCollaborator } from "../github/github-utils.js" import { userNickname } from "../auth0-mgmt.js" import { posthog } from "../analytics.js" import { logger } from "../utils/logger.js" import { missingRequiredFields, unauthorized, githubNotCollaborator, validationFailure, internalServerError, conflict, githubInstallationNotFound, githubInstallationError, } from "../exceptions/index.js" const GITHUB_APP_INSTALL_LINK = "https://github.com/apps/codeflash-ai/installations/select_target" /** * Validates the installation octokit result and throws appropriate errors if it's an Error. * Uses TypeScript assertion to narrow the type to Octokit on success. */ function assertInstallationOctokit( octokit: Octokit | Error, owner: string, repo: string, req: Request, ): asserts octokit is Octokit { if (!(octokit instanceof Error)) return const repoPath = `${owner}/${repo}` const errorMessage = octokit.message if (errorMessage.includes("not installed")) { logger.warn("GitHub App not installed on repository", req, { repo: repoPath, installationLink: GITHUB_APP_INSTALL_LINK, }) throw githubInstallationNotFound(`${repoPath}. Please install the GitHub App at: ${GITHUB_APP_INSTALL_LINK}`) } else { throw githubInstallationError(errorMessage) } } export async function is_code_being_optimized_again(req: Request, res: Response) { try { const { owner, repo, pr_number, code_contexts } = req.body const userId = (req as any).userId if (!repo || !owner || !pr_number || !code_contexts) { throw missingRequiredFields("repo, owner, pr_number, code_contexts") } const nickname: string | null = await userNickname(userId) if (nickname == null) { throw unauthorized("") } const octokit = await getInstallationOctokitByOwner(githubApp, owner, repo, userId) assertInstallationOctokit(octokit, owner, repo, req) // Check collaborator status with error handling try { const isCollaborator = await isUserCollaborator(octokit, owner, repo, nickname) if (!isCollaborator) { logger.warn("User is not a collaborator on repository", req, { nickname, repo: `${owner}/${repo}`, }) throw githubNotCollaborator(`${owner}/${repo}`) } } catch (error) { if (error && typeof error === "object" && "getHttpStatus" in error) { throw error } logger.error("Error checking collaborator status", req, {}, error as Error) throw internalServerError("Failed to verify collaborator status") } // Validate pr_number is an integer above 0 const pr_number_int = parseInt(pr_number, 10) if (isNaN(pr_number_int) || pr_number_int <= 0) { throw validationFailure("pr_number must be a positive integer") } // Validate code_contexts is an array if (!Array.isArray(code_contexts)) { throw validationFailure("code_contexts must be an array of objects") } if (code_contexts.length === 0) { throw validationFailure("code_contexts cannot be empty") } // Validate each code context object has required fields for (let i = 0; i < code_contexts.length; i++) { const context = code_contexts[i] if (typeof context !== "object" || context === null) { throw validationFailure(`code_contexts[${i}] must be an object`) } const { file_path, function_name, code_hash } = context if (typeof file_path !== "string" || file_path.trim().length === 0) { throw validationFailure(`code_contexts[${i}].file_path must be a non-empty string`) } if (typeof function_name !== "string" || function_name.trim().length === 0) { throw validationFailure(`code_contexts[${i}].function_name must be a non-empty string`) } if (typeof code_hash !== "string" || !/^[a-f0-9]{64}$/.test(code_hash)) { throw validationFailure(`code_contexts[${i}].code_hash must be a valid SHA-256 hash`) } } // Build the compound keys for batch querying const compoundKeys = code_contexts.map(({ code_hash }) => ({ repo, owner, pr_number: pr_number_int, context_hash: code_hash, })) const existingRecords = await prisma.pr_code_context_hash_cache.findMany({ where: { OR: compoundKeys.map(key => ({ repo: key.repo, owner: key.owner, pr_number: key.pr_number, context_hash: key.context_hash, })), }, select: { context_hash: true, }, }) // Create a Set of existing hashes for O(1) lookup const existingHashes = new Set(existingRecords.map(record => record.context_hash)) // Identify code contexts that are already optimized (return as tuples) const alreadyOptimizedTuples = code_contexts .filter(({ code_hash }) => existingHashes.has(code_hash)) .map(({ file_path, function_name }) => [file_path, function_name]) posthog?.capture({ distinctId: userId, event: `cfapi-github-pr-optimization`, properties: { repo_owner: owner, repo_name: repo, pr_number, }, }) logger.info("Code context check completed", req, { repo: `${owner}/${repo}`, pr_number, total_contexts: code_contexts.length, already_optimized: alreadyOptimizedTuples.length, new_contexts: code_contexts.length - alreadyOptimizedTuples.length, }) res.status(200).json({ already_optimized_tuples: alreadyOptimizedTuples, total_contexts: code_contexts.length, new_contexts: code_contexts.length - alreadyOptimizedTuples.length, }) } catch (error) { if (error && typeof error === "object" && "getHttpStatus" in error) { throw error } logger.errorWithSentry("Error in is_code_being_optimized_again", req, {}, error as Error) throw internalServerError("Error checking code optimization status") } } export async function add_optimized_code_context(req: Request, res: Response) { try { const { owner, repo, pr_number, code_hash } = req.body const userId = (req as any).userId if (!repo || !owner || !pr_number || !code_hash) { throw missingRequiredFields("repo, owner, pr_number, code_hash") } const nickname: string | null = await userNickname(userId) if (nickname == null) { throw unauthorized("") } const octokit = await getInstallationOctokitByOwner(githubApp, owner, repo, userId) assertInstallationOctokit(octokit, owner, repo, req) // Check collaborator status with error handling try { const isCollaborator = await isUserCollaborator(octokit, owner, repo, nickname) if (!isCollaborator) { logger.warn("User is not a collaborator on repository", req, { nickname, repo: `${owner}/${repo}`, }) throw githubNotCollaborator(`${owner}/${repo}`) } } catch (error) { if (error && typeof error === "object" && "getHttpStatus" in error) { throw error } logger.error("Error checking collaborator status", req, {}, error as Error) throw internalServerError("Failed to verify collaborator status") } // Validate pr_number is an integer above 0 const pr_number_int = parseInt(pr_number, 10) if (isNaN(pr_number_int) || pr_number_int <= 0) { throw validationFailure("pr_number must be a positive integer") } // Validate code_hash is a valid SHA-256 hash if (typeof code_hash !== "string" || !/^[a-f0-9]{64}$/.test(code_hash)) { throw validationFailure("code_hash must be a valid SHA-256 hash") } // Create the new entry await prisma.pr_code_context_hash_cache.create({ data: { repo, owner, pr_number: pr_number_int, context_hash: code_hash, }, }) logger.info("Code context successfully added", req, { repo: `${owner}/${repo}`, pr_number, code_hash, }) res.status(201).json({ message: "Code context successfully added", }) } catch (error: any) { if (error && typeof error === "object" && "getHttpStatus" in error) { throw error } logger.errorWithSentry("Error in add_optimized_code_context", req, {}, error as Error) // Handle unique constraint violations gracefully if (error.code === "P2002") { logger.warn("Code context already exists for this PR", req, {}) throw conflict("Code context already exists for this PR") } throw internalServerError("Error adding code context") } }