UNPKG

deepsource-mcp-server

Version:
383 lines 12.3 kB
/** * @fileoverview ComplianceReport aggregate root * * This module defines the ComplianceReport aggregate which represents security * and code quality compliance reports for a project. */ import { AggregateRoot } from '../../shared/aggregate-root.js'; import { IssueCount } from '../../value-objects/issue-count.js'; import { createLogger } from '../../../utils/logging/logger.js'; const logger = createLogger('ComplianceReport'); /** * ComplianceReport aggregate root * * Represents compliance reports (OWASP, SANS, MISRA-C) for a project. * Tracks compliance categories, issues, and trends over time. * * @example * ```typescript * const report = ComplianceReport.create({ * projectKey: asProjectKey('my-project'), * repositoryId: asGraphQLNodeId('repo123'), * reportType: ReportType.OWASP_TOP_10, * categories: [ * { * name: 'A1: Injection', * compliant: IssueCount.create(45), * nonCompliant: IssueCount.create(5), * issueCount: IssueCount.create(50), * severity: 'CRITICAL' * } * ] * }); * * console.log(report.summary.complianceScore.value); // 90 * console.log(report.isCompliant); // true * ``` */ export class ComplianceReport extends AggregateRoot { _projectKey; _repositoryId; _reportType; _status; _categories; _summary; _trend; _generatedAt; _lastUpdated; constructor(id, projectKey, repositoryId, reportType, status, categories, summary, trend, generatedAt, lastUpdated) { super(id); this._projectKey = projectKey; this._repositoryId = repositoryId; this._reportType = reportType; this._status = status; this._categories = categories; this._summary = summary; this._trend = trend; this._generatedAt = generatedAt; this._lastUpdated = lastUpdated; } /** * Creates a new ComplianceReport aggregate * * @param params - Creation parameters * @returns A new ComplianceReport instance */ static create(params) { const { projectKey, repositoryId, reportType, status, categories, trend } = params; // Create composite ID const id = ComplianceReport.createId({ projectKey, reportType }); const now = new Date(); const initialCategories = categories || []; const summary = ComplianceReport.calculateSummary(initialCategories); const report = new ComplianceReport(id, projectKey, repositoryId, reportType, status || 'PENDING', initialCategories, summary, trend || null, now, now); report.addDomainEvent({ aggregateId: id, eventType: 'ComplianceReportCreated', occurredAt: now, payload: { projectKey, reportType, complianceScore: summary.complianceScore.value, }, }); return report; } /** * Creates a composite ID for the aggregate */ static createId(id) { return `${id.projectKey}:${id.reportType}`; } /** * Calculates summary statistics from categories */ static calculateSummary(categories) { let totalCompliant = IssueCount.zero(); let totalNonCompliant = IssueCount.zero(); let totalIssues = IssueCount.zero(); const severityDistribution = { critical: IssueCount.zero('critical'), major: IssueCount.zero('major'), minor: IssueCount.zero('minor'), info: IssueCount.zero('info'), }; for (const category of categories) { totalCompliant = totalCompliant.add(category.compliant); totalNonCompliant = totalNonCompliant.add(category.nonCompliant); totalIssues = totalIssues.add(category.issueCount); // Update severity distribution switch (category.severity) { case 'CRITICAL': severityDistribution.critical = severityDistribution.critical.add(category.nonCompliant); break; case 'MAJOR': severityDistribution.major = severityDistribution.major.add(category.nonCompliant); break; case 'MINOR': severityDistribution.minor = severityDistribution.minor.add(category.nonCompliant); break; case 'INFO': severityDistribution.info = severityDistribution.info.add(category.nonCompliant); break; default: // Log unexpected severity level but continue processing logger.warn(`Unexpected severity level: ${category.severity}`); break; } } // Calculate compliance score const total = totalCompliant.count + totalNonCompliant.count; const scoreValue = total > 0 ? (totalCompliant.count / total) * 100 : 100; const complianceScore = { value: Math.round(scoreValue * 10) / 10, // Round to 1 decimal level: ComplianceReport.getComplianceLevel(scoreValue), }; return { totalCompliant, totalNonCompliant, totalIssues, complianceScore, severityDistribution, }; } /** * Determines compliance level based on score */ static getComplianceLevel(score) { if (score >= 95) return 'EXCELLENT'; if (score >= 85) return 'GOOD'; if (score >= 70) return 'FAIR'; return 'POOR'; } /** * Gets the project key */ get projectKey() { return this._projectKey; } /** * Gets the repository ID */ get repositoryId() { return this._repositoryId; } /** * Gets the report type */ get reportType() { return this._reportType; } /** * Gets the report status */ get status() { return this._status; } /** * Gets the compliance categories */ get categories() { return [...this._categories]; } /** * Gets the summary statistics */ get summary() { return { ...this._summary, severityDistribution: { ...this._summary.severityDistribution }, }; } /** * Gets the trend information */ get trend() { return this._trend ? { ...this._trend } : null; } /** * Gets the generation timestamp */ get generatedAt() { return this._generatedAt; } /** * Gets the last update timestamp */ get lastUpdated() { return this._lastUpdated; } /** * Checks if the report meets compliance standards */ get isCompliant() { return this._summary.complianceScore.value >= 85; // 85% is the default threshold } /** * Checks if the report has critical issues */ get hasCriticalIssues() { return this._summary.severityDistribution.critical.isPositive; } /** * Generates/regenerates the report */ generate() { if (this._status === 'GENERATING') { throw new Error('Report is already being generated'); } this._status = 'GENERATING'; this._lastUpdated = new Date(); this.addDomainEvent({ aggregateId: this._id, eventType: 'ComplianceReportGenerationStarted', occurredAt: this._lastUpdated, payload: {}, }); this.markAsModified(); } /** * Completes report generation */ complete() { if (this._status !== 'GENERATING') { throw new Error('Report must be in GENERATING status to complete'); } this._status = 'READY'; this._generatedAt = new Date(); this._lastUpdated = this._generatedAt; this.addDomainEvent({ aggregateId: this._id, eventType: 'ComplianceReportCompleted', occurredAt: this._lastUpdated, payload: { complianceScore: this._summary.complianceScore.value, hasCriticalIssues: this.hasCriticalIssues, }, }); this.markAsModified(); } /** * Fails report generation * * @param reason - The reason for failure */ fail(reason) { if (this._status !== 'GENERATING') { throw new Error('Report must be in GENERATING status to fail'); } this._status = 'ERROR'; this._lastUpdated = new Date(); this.addDomainEvent({ aggregateId: this._id, eventType: 'ComplianceReportFailed', occurredAt: this._lastUpdated, payload: { reason }, }); this.markAsModified(); } /** * Updates the report categories * * @param params - Category update parameters */ updateCategories(params) { const { categories } = params; if (categories.length === 0) { throw new Error('Report must have at least one category'); } this._categories = categories; this._summary = ComplianceReport.calculateSummary(categories); this._lastUpdated = new Date(); this.addDomainEvent({ aggregateId: this._id, eventType: 'ComplianceReportCategoriesUpdated', occurredAt: this._lastUpdated, payload: { categoryCount: categories.length, complianceScore: this._summary.complianceScore.value, }, }); this.markAsModified(); } /** * Updates the report trend * * @param params - Trend update parameters */ updateTrend(params) { const { trend } = params; this._trend = trend; this._lastUpdated = new Date(); this.addDomainEvent({ aggregateId: this._id, eventType: 'ComplianceReportTrendUpdated', occurredAt: this._lastUpdated, payload: { trendDirection: trend.direction, changePercentage: trend.changePercentage, }, }); this.markAsModified(); } /** * Compares this report with a previous report * * @param previous - The previous report to compare with * @returns Trend information */ compareWithPrevious(previous) { if (previous._reportType !== this._reportType) { throw new Error('Cannot compare reports of different types'); } const currentScore = this._summary.complianceScore.value; const previousScore = previous._summary.complianceScore.value; const changePercentage = currentScore - previousScore; let direction; if (Math.abs(changePercentage) < 0.1) { direction = 'STABLE'; } else if (changePercentage > 0) { direction = 'IMPROVING'; } else { direction = 'DEGRADING'; } return { label: 'Compared to previous report', value: currentScore, changePercentage, direction, }; } /** * Reconstructs ComplianceReport from persisted data * * @param data - Persisted report data * @returns A reconstructed ComplianceReport instance */ static fromPersistence(data) { return new ComplianceReport(data.id, data.projectKey, data.repositoryId, data.reportType, data.status, data.categories, data.summary, data.trend, data.generatedAt, data.lastUpdated); } /** * Converts the report to a persistence-friendly format */ toPersistence() { return { id: this._id, projectKey: this._projectKey, repositoryId: this._repositoryId, reportType: this._reportType, status: this._status, categories: [...this._categories], summary: this.summary, trend: this._trend, generatedAt: this._generatedAt, lastUpdated: this._lastUpdated, }; } } //# sourceMappingURL=compliance-report.aggregate.js.map