swagger_generator_flutter/lib/validators/enhanced_validator.dart

159 lines
4.8 KiB
Dart

/// 增强的 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();
}
}