/// 增强的 OpenAPI 验证器 /// 集成详细的错误报告和修复建议 library; import 'package:swagger_generator_flutter/core/error_reporter.dart'; import 'package:swagger_generator_flutter/core/models.dart'; import 'package:swagger_generator_flutter/validators/schema_validator.dart'; /// 增强的 OpenAPI 验证器 class EnhancedValidator { EnhancedValidator({ SchemaValidator? schemaValidator, bool includeWarnings = true, }) : _schemaValidator = schemaValidator ?? SchemaValidator(), _errorReporter = ErrorReporter(), _includeWarnings = includeWarnings; final SchemaValidator _schemaValidator; final ErrorReporter _errorReporter; final bool _includeWarnings; /// 获取错误报告器 ErrorReporter get errorReporter => _errorReporter; /// 验证 OpenAPI 文档 bool validateDocument(SwaggerDocument document) { _errorReporter.clear(); // 使用基础验证器进行验证 final result = _schemaValidator.validateDocument(document); // 转换错误 for (final error in result.errors) { _errorReporter.reportError( id: 'VALIDATION_ERROR', title: 'Validation Error', description: error.message, severity: _mapSeverity(error.type), category: ErrorCategory.validation, jsonPath: error.path, suggestions: error.suggestion != null ? [FixSuggestion(description: error.suggestion!)] : [], ); } // 转换警告 if (_includeWarnings) { for (final warning in result.warnings) { _errorReporter.reportError( id: 'VALIDATION_WARNING', title: 'Validation Warning', description: warning.message, severity: ErrorSeverity.warning, category: ErrorCategory.bestPractice, jsonPath: warning.path, suggestions: warning.suggestion != null ? [FixSuggestion(description: warning.suggestion!)] : [], ); } // 额外的最佳实践检查 _checkBestPractices(document); } return !_errorReporter.hasErrorsOrCritical; } ErrorSeverity _mapSeverity(ValidationErrorType type) { switch (type) { case ValidationErrorType.required: case ValidationErrorType.type: case ValidationErrorType.format: case ValidationErrorType.reference: case ValidationErrorType.constraint: case ValidationErrorType.security: case ValidationErrorType.compatibility: return ErrorSeverity.error; } } /// 检查最佳实践 void _checkBestPractices(SwaggerDocument document) { // 检查是否使用了标签 final hasTaggedOperations = document.paths.values.any((path) => path.tags.isNotEmpty); if (!hasTaggedOperations) { _errorReporter.reportError( id: 'NO_OPERATION_TAGS', title: 'No Operation Tags', description: 'Consider using tags to organize your API operations.', severity: ErrorSeverity.info, category: ErrorCategory.bestPractice, jsonPath: 'paths', suggestions: [ const FixSuggestion( description: 'Add tags to operations', codeExample: '"tags": ["users"]', ), ], ); } // 检查操作 ID document.paths.forEach((routeKey, path) { if (path.operationId.isEmpty) { final pathPattern = routeKey.pattern; final method = path.method; _errorReporter.reportError( id: 'MISSING_OPERATION_ID', title: 'Missing Operation ID', description: 'Operation should have an operationId for ' 'better code generation.', severity: ErrorSeverity.warning, category: ErrorCategory.bestPractice, jsonPath: 'paths["$pathPattern"][${method.value}].operationId', suggestions: [ FixSuggestion( description: 'Add a unique operationId', codeExample: '"operationId": ' '"${_generateOperationId(pathPattern, method)}"', ), ], ); } }); } /// 生成操作 ID String _generateOperationId(String path, HttpMethod method) { final pathParts = path .split('/') .where((part) => part.isNotEmpty && !part.startsWith('{')) .toList(); final methodPrefix = method.value.toLowerCase(); if (pathParts.length >= 3) { // 移除 api/v1 前缀 pathParts.removeRange(0, 2); } final nameParts = pathParts.map(_toPascalCase).join(); return '$methodPrefix$nameParts'; } /// 转换为 PascalCase String _toPascalCase(String input) { return input .split('_') .map( (word) => word.isEmpty ? '' : word[0].toUpperCase() + word.substring(1).toLowerCase(), ) .join(); } }