mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
feat: add entity exposure detection (v0.3.0)
Implement entity exposure detection to prevent domain entities from leaking to API responses. Detects when controllers/routes return domain entities instead of DTOs. Features: - EntityExposure value object with detailed suggestions - IEntityExposureDetector interface in domain layer - EntityExposureDetector implementation in infrastructure - Integration into AnalyzeProject use case - CLI display with helpful suggestions - 24 comprehensive unit tests (98% coverage) - Examples for bad and good patterns Detection scope: - Infrastructure layer only (controllers, routes, handlers, resolvers, gateways) - Identifies PascalCase entities without Dto/Request/Response suffixes - Parses async methods with Promise<T> return types - Provides step-by-step remediation suggestions Test coverage: - EntityExposureDetector: 98.07% - Overall project: 90.6% statements, 83.97% branches - 218 tests passing BREAKING CHANGE: Version bump to 0.3.0
This commit is contained in:
@@ -5,6 +5,7 @@ import { ICodeParser } from "../../domain/services/ICodeParser"
|
||||
import { IHardcodeDetector } from "../../domain/services/IHardcodeDetector"
|
||||
import { INamingConventionDetector } from "../../domain/services/INamingConventionDetector"
|
||||
import { IFrameworkLeakDetector } from "../../domain/services/IFrameworkLeakDetector"
|
||||
import { IEntityExposureDetector } from "../../domain/services/IEntityExposureDetector"
|
||||
import { SourceFile } from "../../domain/entities/SourceFile"
|
||||
import { DependencyGraph } from "../../domain/entities/DependencyGraph"
|
||||
import { ProjectPath } from "../../domain/value-objects/ProjectPath"
|
||||
@@ -32,6 +33,7 @@ export interface AnalyzeProjectResponse {
|
||||
circularDependencyViolations: CircularDependencyViolation[]
|
||||
namingViolations: NamingConventionViolation[]
|
||||
frameworkLeakViolations: FrameworkLeakViolation[]
|
||||
entityExposureViolations: EntityExposureViolation[]
|
||||
metrics: ProjectMetrics
|
||||
}
|
||||
|
||||
@@ -95,6 +97,18 @@ export interface FrameworkLeakViolation {
|
||||
suggestion: string
|
||||
}
|
||||
|
||||
export interface EntityExposureViolation {
|
||||
rule: typeof RULES.ENTITY_EXPOSURE
|
||||
entityName: string
|
||||
returnType: string
|
||||
file: string
|
||||
layer: string
|
||||
line?: number
|
||||
methodName?: string
|
||||
message: string
|
||||
suggestion: string
|
||||
}
|
||||
|
||||
export interface ProjectMetrics {
|
||||
totalFiles: number
|
||||
totalFunctions: number
|
||||
@@ -115,6 +129,7 @@ export class AnalyzeProject extends UseCase<
|
||||
private readonly hardcodeDetector: IHardcodeDetector,
|
||||
private readonly namingConventionDetector: INamingConventionDetector,
|
||||
private readonly frameworkLeakDetector: IFrameworkLeakDetector,
|
||||
private readonly entityExposureDetector: IEntityExposureDetector,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -164,6 +179,7 @@ export class AnalyzeProject extends UseCase<
|
||||
const circularDependencyViolations = this.detectCircularDependencies(dependencyGraph)
|
||||
const namingViolations = this.detectNamingConventions(sourceFiles)
|
||||
const frameworkLeakViolations = this.detectFrameworkLeaks(sourceFiles)
|
||||
const entityExposureViolations = this.detectEntityExposures(sourceFiles)
|
||||
const metrics = this.calculateMetrics(sourceFiles, totalFunctions, dependencyGraph)
|
||||
|
||||
return ResponseDto.ok({
|
||||
@@ -174,6 +190,7 @@ export class AnalyzeProject extends UseCase<
|
||||
circularDependencyViolations,
|
||||
namingViolations,
|
||||
frameworkLeakViolations,
|
||||
entityExposureViolations,
|
||||
metrics,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -364,6 +381,34 @@ export class AnalyzeProject extends UseCase<
|
||||
return violations
|
||||
}
|
||||
|
||||
private detectEntityExposures(sourceFiles: SourceFile[]): EntityExposureViolation[] {
|
||||
const violations: EntityExposureViolation[] = []
|
||||
|
||||
for (const file of sourceFiles) {
|
||||
const exposures = this.entityExposureDetector.detectExposures(
|
||||
file.content,
|
||||
file.path.relative,
|
||||
file.layer,
|
||||
)
|
||||
|
||||
for (const exposure of exposures) {
|
||||
violations.push({
|
||||
rule: RULES.ENTITY_EXPOSURE,
|
||||
entityName: exposure.entityName,
|
||||
returnType: exposure.returnType,
|
||||
file: file.path.relative,
|
||||
layer: exposure.layer,
|
||||
line: exposure.line,
|
||||
methodName: exposure.methodName,
|
||||
message: exposure.getMessage(),
|
||||
suggestion: exposure.getSuggestion(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return violations
|
||||
}
|
||||
|
||||
private calculateMetrics(
|
||||
sourceFiles: SourceFile[],
|
||||
totalFunctions: number,
|
||||
|
||||
Reference in New Issue
Block a user