mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
Major internal refactoring to eliminate hardcoded values and improve maintainability. Guardian now fully passes its own quality checks! Changes: - Extract all RepositoryViolation messages to domain constants - Extract all framework leak template strings to centralized constants - Extract all layer paths to infrastructure constants - Extract all regex patterns to IMPORT_PATTERNS constant - Add 30+ new constants for better maintainability New files: - src/infrastructure/constants/paths.ts (layer paths, patterns) - src/domain/constants/Messages.ts (25+ repository messages) - src/domain/constants/FrameworkCategories.ts (framework categories) - src/shared/constants/layers.ts (layer names) Impact: - Reduced hardcoded values from 37 to 1 (97% improvement) - Guardian passes its own src/ directory checks with 0 violations - All 292 tests still passing (100% pass rate) - No breaking changes - fully backwards compatible Test results: - 292 tests passing (100% pass rate) - 96.77% statement coverage - 83.82% branch coverage
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
import { ValueObject } from "./ValueObject"
|
|
import { ENTITY_EXPOSURE_MESSAGES } from "../constants/Messages"
|
|
|
|
interface EntityExposureProps {
|
|
readonly entityName: string
|
|
readonly returnType: string
|
|
readonly filePath: string
|
|
readonly layer: string
|
|
readonly line?: number
|
|
readonly methodName?: string
|
|
}
|
|
|
|
/**
|
|
* Represents an entity exposure violation in the codebase
|
|
*
|
|
* Entity exposure occurs when a domain entity is directly exposed in API responses
|
|
* instead of using DTOs (Data Transfer Objects). This violates the separation of concerns
|
|
* and can lead to exposing internal domain logic to external clients.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Bad: Controller returning domain entity
|
|
* const exposure = EntityExposure.create(
|
|
* 'User',
|
|
* 'User',
|
|
* 'src/infrastructure/controllers/UserController.ts',
|
|
* 'infrastructure',
|
|
* 25,
|
|
* 'getUser'
|
|
* )
|
|
*
|
|
* console.log(exposure.getMessage())
|
|
* // "Method 'getUser' returns domain entity 'User' instead of DTO"
|
|
* ```
|
|
*/
|
|
export class EntityExposure extends ValueObject<EntityExposureProps> {
|
|
private constructor(props: EntityExposureProps) {
|
|
super(props)
|
|
}
|
|
|
|
public static create(
|
|
entityName: string,
|
|
returnType: string,
|
|
filePath: string,
|
|
layer: string,
|
|
line?: number,
|
|
methodName?: string,
|
|
): EntityExposure {
|
|
return new EntityExposure({
|
|
entityName,
|
|
returnType,
|
|
filePath,
|
|
layer,
|
|
line,
|
|
methodName,
|
|
})
|
|
}
|
|
|
|
public get entityName(): string {
|
|
return this.props.entityName
|
|
}
|
|
|
|
public get returnType(): string {
|
|
return this.props.returnType
|
|
}
|
|
|
|
public get filePath(): string {
|
|
return this.props.filePath
|
|
}
|
|
|
|
public get layer(): string {
|
|
return this.props.layer
|
|
}
|
|
|
|
public get line(): number | undefined {
|
|
return this.props.line
|
|
}
|
|
|
|
public get methodName(): string | undefined {
|
|
return this.props.methodName
|
|
}
|
|
|
|
public getMessage(): string {
|
|
const method = this.props.methodName
|
|
? `Method '${this.props.methodName}'`
|
|
: ENTITY_EXPOSURE_MESSAGES.METHOD_DEFAULT
|
|
return `${method} returns domain entity '${this.props.entityName}' instead of DTO`
|
|
}
|
|
|
|
public getSuggestion(): string {
|
|
const suggestions = [
|
|
`Create a DTO class (e.g., ${this.props.entityName}ResponseDto) in the application layer`,
|
|
`Create a mapper to convert ${this.props.entityName} to ${this.props.entityName}ResponseDto`,
|
|
`Update the method to return ${this.props.entityName}ResponseDto instead of ${this.props.entityName}`,
|
|
]
|
|
return suggestions.join("\n")
|
|
}
|
|
|
|
public getExampleFix(): string {
|
|
return `
|
|
// ❌ Bad: Exposing domain entity
|
|
async ${this.props.methodName || ENTITY_EXPOSURE_MESSAGES.METHOD_DEFAULT_NAME}(): Promise<${this.props.entityName}> {
|
|
return await this.service.find()
|
|
}
|
|
|
|
// ✅ Good: Using DTO
|
|
async ${this.props.methodName || ENTITY_EXPOSURE_MESSAGES.METHOD_DEFAULT_NAME}(): Promise<${this.props.entityName}ResponseDto> {
|
|
const entity = await this.service.find()
|
|
return ${this.props.entityName}Mapper.toDto(entity)
|
|
}`
|
|
}
|
|
}
|