Skip to main content
Glama
compliance-report.repository.ts13.5 kB
/** * @fileoverview ComplianceReport repository implementation * * Concrete implementation of IComplianceReportRepository using DeepSource API. */ import { IComplianceReportRepository } from '../../domain/aggregates/compliance-report/compliance-report.repository.js'; import { ComplianceReport } from '../../domain/aggregates/compliance-report/compliance-report.aggregate.js'; import { ProjectKey } from '../../types/branded.js'; import { ReportType } from '../../types/report-types.js'; import { ComplianceReportStatus, ComplianceReportId, } from '../../domain/aggregates/compliance-report/compliance-report.types.js'; import { DeepSourceClient } from '../../deepsource.js'; import { ComplianceReportMapper } from '../mappers/compliance-report.mapper.js'; import { createLogger } from '../../utils/logging/logger.js'; import { BaseDeepSourceClient } from '../../client/base-client.js'; import type { AxiosInstance } from 'axios'; const logger = createLogger('ComplianceReportRepository'); /** * Concrete implementation of IComplianceReportRepository using DeepSource API * * This repository provides access to ComplianceReport aggregates by fetching data * from the DeepSource API and mapping it to domain models. * * Note: The DeepSource API provides compliance reports for specific types * (OWASP_TOP_10, SANS_TOP_25, MISRA_C). This repository ensures fresh data * retrieval on every request as per requirements. */ export class ComplianceReportRepository implements IComplianceReportRepository { constructor(private readonly client: DeepSourceClient) { // client is stored for use in methods } /** * Helper method to get repository ID for a project * * @param projectKey - The project key * @returns The repository GraphQL ID */ private async getRepositoryId(projectKey: ProjectKey): Promise<string> { const projects = await this.client.listProjects(); const project = projects.find((p) => p.key === projectKey); if (!project) { throw new Error(`Project not found: ${projectKey}`); } // Get repository ID from runs query (similar to QualityMetricsRepository) try { const runs = await this.client.listRuns(projectKey, { first: 1 }); if (runs.items.length > 0 && runs.items[0]?.repository?.id) { return runs.items[0].repository.id; } // Fallback: Direct repository query const repoQuery = ` query($login: String!, $name: String!, $provider: VCSProvider!) { repository(login: $login, name: $name, vcsProvider: $provider) { id } } `; // Access protected client member via type assertion const clientWithAxios = this.client as unknown as BaseDeepSourceClient & { client: AxiosInstance; }; const response = await clientWithAxios.client.post('', { query: repoQuery.trim(), variables: { login: project.repository.login, name: project.name, provider: project.repository.provider, }, }); if (response.data.errors) { throw new Error( `GraphQL Errors: ${response.data.errors.map((e: { message: string }) => e.message).join(', ')}` ); } const repositoryId = response.data.data?.repository?.id; if (!repositoryId) { throw new Error(`Repository ID not found for project: ${projectKey}`); } return repositoryId; } catch (error) { logger.error('Error fetching repository ID', { projectKey, error }); throw error; } } /** * Finds a report by composite ID * * @param id - The composite ID (projectKey:reportType) * @returns The report if found, null otherwise */ async findById(id: string): Promise<ComplianceReport | null> { try { logger.debug('Finding compliance report by composite ID', { id }); // Parse composite ID const parts = id.split(':'); if (parts.length !== 2) { logger.error('Invalid composite ID format', { id }); return null; } const [projectKey, reportType] = parts; return this.findByProjectAndType(projectKey as ProjectKey, reportType as ReportType); } catch (error) { logger.error('Error finding compliance report by ID', { id, error }); throw error; } } /** * Finds a report by project and type * * @param projectKey - The project key * @param reportType - The report type * @returns The report if found, null otherwise */ async findByProjectAndType( projectKey: ProjectKey, reportType: ReportType ): Promise<ComplianceReport | null> { try { logger.debug('Finding compliance report by project and type', { projectKey, reportType, }); const repositoryId = await this.getRepositoryId(projectKey); const apiReport = await this.client.getComplianceReport(projectKey, reportType); if (!apiReport) { logger.debug('Compliance report not found', { projectKey, reportType }); return null; } const domainReport = ComplianceReportMapper.toDomain(apiReport, projectKey, repositoryId); logger.debug('Compliance report found and mapped', { projectKey, reportType, id: domainReport.id, }); return domainReport; } catch (error) { logger.error('Error finding compliance report by project and type', { projectKey, reportType, error, }); throw error; } } /** * Finds the latest reports for a project * * @param projectKey - The project key * @returns All reports for the project, sorted by generation date (newest first) */ async findLatest(projectKey: ProjectKey): Promise<ComplianceReport[]> { try { logger.debug('Finding latest compliance reports for project', { projectKey }); const repositoryId = await this.getRepositoryId(projectKey); const reportTypes = [ReportType.OWASP_TOP_10, ReportType.SANS_TOP_25, ReportType.MISRA_C]; const reports: ComplianceReport[] = []; // Fetch all available report types for (const reportType of reportTypes) { try { const apiReport = await this.client.getComplianceReport(projectKey, reportType); if (apiReport) { const domainReport = ComplianceReportMapper.toDomain( apiReport, projectKey, repositoryId ); reports.push(domainReport); } } catch (error) { // Log but don't fail - some report types may not be available logger.debug('Report type not available', { projectKey, reportType, error }); } } // Sort by generation date (newest first) reports.sort((a, b) => b.generatedAt.getTime() - a.generatedAt.getTime()); logger.debug('Latest compliance reports found', { projectKey, count: reports.length, }); return reports; } catch (error) { logger.error('Error finding latest compliance reports', { projectKey, error }); throw error; } } /** * Finds reports by status * * @param projectKey - The project key * @param status - The report status * @returns Reports matching the status */ async findByStatus( projectKey: ProjectKey, status: ComplianceReportStatus ): Promise<ComplianceReport[]> { try { logger.debug('Finding compliance reports by status', { projectKey, status }); const allReports = await this.findLatest(projectKey); const statusReports = allReports.filter((report) => report.status === status); logger.debug('Status-filtered compliance reports found', { projectKey, status, count: statusReports.length, }); return statusReports; } catch (error) { logger.error('Error finding reports by status', { projectKey, status, error }); throw error; } } /** * Finds all compliant reports for a project * * @param projectKey - The project key * @returns Reports with compliance score >= 85% */ async findCompliantReports(projectKey: ProjectKey): Promise<ComplianceReport[]> { try { logger.debug('Finding compliant reports for project', { projectKey }); const allReports = await this.findLatest(projectKey); const compliantReports = allReports.filter((report) => report.isCompliant); logger.debug('Compliant reports found', { projectKey, count: compliantReports.length, }); return compliantReports; } catch (error) { logger.error('Error finding compliant reports', { projectKey, error }); throw error; } } /** * Finds reports with critical issues * * @param projectKey - The project key * @returns Reports that have critical severity issues */ async findReportsWithCriticalIssues(projectKey: ProjectKey): Promise<ComplianceReport[]> { try { logger.debug('Finding reports with critical issues for project', { projectKey }); const allReports = await this.findLatest(projectKey); const criticalReports = allReports.filter((report) => report.hasCriticalIssues); logger.debug('Reports with critical issues found', { projectKey, count: criticalReports.length, }); return criticalReports; } catch (error) { logger.error('Error finding reports with critical issues', { projectKey, error }); throw error; } } /** * Finds a report by composite ID components * * @param id - The composite ID components * @returns The report if found, null otherwise */ async findByCompositeId(id: ComplianceReportId): Promise<ComplianceReport | null> { try { logger.debug('Finding compliance report by composite ID components', { id }); return this.findByProjectAndType(id.projectKey, id.reportType); } catch (error) { logger.error('Error finding report by composite ID', { id, error }); throw error; } } /** * Counts total reports for a project * * @param projectKey - The project key * @returns The total number of reports */ async countByProject(projectKey: ProjectKey): Promise<number> { try { logger.debug('Counting compliance reports for project', { projectKey }); const reports = await this.findLatest(projectKey); const count = reports.length; logger.debug('Compliance report count for project', { projectKey, count }); return count; } catch (error) { logger.error('Error counting reports', { projectKey, error }); throw error; } } /** * Counts reports by type for a project * * @param projectKey - The project key * @param reportType - The report type * @returns The number of reports of the given type */ async countByType(projectKey: ProjectKey, reportType: ReportType): Promise<number> { try { logger.debug('Counting compliance reports by type', { projectKey, reportType }); const report = await this.findByProjectAndType(projectKey, reportType); const count = report ? 1 : 0; logger.debug('Report count by type', { projectKey, reportType, count }); return count; } catch (error) { logger.error('Error counting reports by type', { projectKey, reportType, error }); throw error; } } /** * Checks if a report exists for a project and type * * @param projectKey - The project key * @param reportType - The report type * @returns True if a report exists, false otherwise */ async exists(projectKey: ProjectKey, reportType: ReportType): Promise<boolean> { try { logger.debug('Checking if compliance report exists', { projectKey, reportType }); const report = await this.findByProjectAndType(projectKey, reportType); const exists = report !== null; logger.debug('Compliance report existence check', { projectKey, reportType, exists }); return exists; } catch (error) { logger.error('Error checking report existence', { projectKey, reportType, error }); throw error; } } /** * Saves compliance report * * Note: The DeepSource API doesn't support creating or updating compliance reports. * Reports are generated automatically based on repository analysis. * * @param report - The report to save * @throws Error indicating the operation is not supported */ // skipcq: JS-0105 - Repository method required by interface contract async save(report: ComplianceReport): Promise<void> { logger.warn('Attempted to save compliance report', { id: report.id }); throw new Error( 'Save operation is not supported by DeepSource API. ' + 'Compliance reports are generated automatically based on repository analysis.' ); } /** * Deletes compliance report * * Note: The DeepSource API doesn't support deleting compliance reports. * Reports are managed automatically based on repository configuration. * * @param id - The report ID to delete * @throws Error indicating the operation is not supported */ // skipcq: JS-0105 - Repository method required by interface contract async delete(id: string): Promise<void> { logger.warn('Attempted to delete compliance report', { id }); throw new Error( 'Delete operation is not supported by DeepSource API. ' + 'Compliance reports are managed automatically based on repository configuration.' ); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sapientpants/deepsource-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server