UNPKG

deepsource-mcp-server

Version:
211 lines (189 loc) 5.4 kB
/** * @fileoverview GraphQL response processor for run checks queries * @packageDocumentation */ import { z } from 'zod'; import { DeepSourceIssue } from '../../../models/issues.js'; import { createLogger } from '../../logging/logger.js'; const logger = createLogger('DeepSource:RunChecksProcessor'); /** * Schema for issue data in GraphQL response */ const IssueSchema = z.object({ shortcode: z.string().optional(), title: z.string().optional(), category: z.string().optional(), severity: z.string().optional(), description: z.string().optional(), tags: z.array(z.string()).optional(), }); /** * Schema for occurrence node in GraphQL response */ const OccurrenceNodeSchema = z.object({ id: z.string().optional(), issue: IssueSchema.optional(), path: z.string().optional(), beginLine: z.number().optional(), }); /** * Schema for page info */ const PageInfoSchema = z.object({ hasNextPage: z.boolean(), hasPreviousPage: z.boolean(), startCursor: z.string().optional(), endCursor: z.string().optional(), }); /** * Schema for occurrences in check node */ const OccurrencesSchema = z.object({ pageInfo: PageInfoSchema.optional(), totalCount: z.number().optional(), edges: z .array( z.object({ node: OccurrenceNodeSchema.optional(), }) ) .optional(), }); /** * Schema for check node */ const CheckNodeSchema = z.object({ occurrences: OccurrencesSchema.optional(), }); /** * Schema for run checks GraphQL response */ const RunChecksResponseSchema = z.object({ data: z.object({ data: z .object({ run: z .object({ checks: z .object({ edges: z .array( z.object({ node: CheckNodeSchema, }) ) .optional(), }) .optional(), }) .optional(), }) .optional(), }), }); /** * Result type for processed run checks */ export interface ProcessedRunChecks { issues: DeepSourceIssue[]; pageInfo: { hasNextPage: boolean; hasPreviousPage: boolean; startCursor: string | undefined; endCursor: string | undefined; }; totalCount: number; } /** * Create a DeepSourceIssue from an occurrence node * @param occurrence - Occurrence node from GraphQL response * @returns DeepSourceIssue object * @private */ function createIssueFromOccurrence( occurrence: z.infer<typeof OccurrenceNodeSchema> ): DeepSourceIssue { return { id: occurrence.id ?? 'unknown', shortcode: occurrence.issue?.shortcode ?? '', title: occurrence.issue?.title ?? 'Untitled Issue', category: occurrence.issue?.category ?? 'UNKNOWN', severity: occurrence.issue?.severity ?? 'UNKNOWN', status: 'OPEN', issue_text: occurrence.issue?.description ?? '', file_path: occurrence.path ?? 'N/A', line_number: occurrence.beginLine ?? 0, tags: occurrence.issue?.tags ?? [], }; } /** * Extract issues from validated response data * @param validated - Validated response data * @returns Processed run checks data * @private */ function extractIssues(validated: z.infer<typeof RunChecksResponseSchema>): ProcessedRunChecks { const issues: DeepSourceIssue[] = []; let pageInfo = { hasNextPage: false, hasPreviousPage: false, startCursor: undefined as string | undefined, endCursor: undefined as string | undefined, }; let totalCount = 0; const checks = validated.data.data?.run?.checks?.edges ?? []; for (const { node: check } of checks) { const occurrences = check.occurrences?.edges ?? []; const occurrencesPageInfo = check.occurrences?.pageInfo; const occurrencesTotalCount = check.occurrences?.totalCount ?? 0; // Aggregate page info (using the first check's pagination info for simplicity) if (occurrencesPageInfo) { pageInfo = { hasNextPage: occurrencesPageInfo.hasNextPage, hasPreviousPage: occurrencesPageInfo.hasPreviousPage, startCursor: occurrencesPageInfo.startCursor, endCursor: occurrencesPageInfo.endCursor, }; } // Always aggregate the total count totalCount += occurrencesTotalCount; for (const { node: occurrence } of occurrences) { if (!occurrence || !occurrence.issue) continue; issues.push(createIssueFromOccurrence(occurrence)); } } return { issues, pageInfo, totalCount }; } /** * Processor for run checks GraphQL responses */ export const RunChecksProcessor = { /** * Process a run checks GraphQL response * @param response - Raw GraphQL response * @returns Processed run checks data */ process(response: unknown): ProcessedRunChecks { try { // Validate response structure const validated = RunChecksResponseSchema.parse(response); // Extract and process data return extractIssues(validated); } catch (error) { logger.warn('Failed to parse run checks response', { error: error instanceof Error ? error.message : String(error), }); // Return empty result on parse failure return { issues: [], pageInfo: { hasNextPage: false, hasPreviousPage: false, startCursor: undefined, endCursor: undefined, }, totalCount: 0, }; } }, };