import 'package:swagger_generator_flutter/core/models.dart'; import 'package:swagger_generator_flutter/pipeline/validate/core/validation_context.dart'; import 'package:swagger_generator_flutter/pipeline/validate/core/validation_result.dart'; import 'package:swagger_generator_flutter/pipeline/validate/core/validation_rule.dart'; /// 路径验证规则 class PathValidationRule extends ValidationRule { @override String get id => 'path_validation'; @override String get name => '路径验证'; @override ValidationResult validate(ValidationContext context) { final paths = context.document.paths; final errors = []; final warnings = []; if (paths.isEmpty) { errors.add( const ValidationError( path: 'paths', message: 'API 文档必须包含至少一个路径', type: ValidationErrorType.required, ), ); return ValidationResult(isValid: false, errors: errors); } paths.forEach((routeKey, path) { final pathKey = 'paths["${routeKey.pattern}"][${routeKey.method.value}]'; _validatePath(path, pathKey, errors, warnings); }); return ValidationResult( isValid: errors.isEmpty, errors: errors, warnings: warnings, ); } void _validatePath( ApiPath path, String pathKey, List errors, List warnings, ) { // 验证操作 ID if (path.operationId.isEmpty) { warnings.add( ValidationWarning( path: '$pathKey.operationId', message: '缺少操作 ID', suggestion: '建议为每个操作添加唯一的 operationId', ), ); } // 验证摘要和描述 if (path.summary.isEmpty) { warnings.add( ValidationWarning( path: '$pathKey.summary', message: '缺少操作摘要', suggestion: '建议添加简短的操作描述', ), ); } // 验证参数 for (var i = 0; i < path.parameters.length; i++) { _validateParameter( path.parameters[i], '$pathKey.parameters[$i]', errors, warnings, ); } // 验证请求体 if (path.requestBody != null) { _validateRequestBody( path.requestBody!, '$pathKey.requestBody', errors, warnings, ); } // 验证响应 if (path.responses.isEmpty) { errors.add( ValidationError( path: '$pathKey.responses', message: '操作必须定义至少一个响应', type: ValidationErrorType.required, ), ); } else { path.responses.forEach((code, response) { _validateResponse( response, '$pathKey.responses["$code"]', errors, warnings, ); }); } // 验证安全要求 // 安全要求的详细验证可能需要单独的规则或在这里简单检查 } void _validateParameter( ApiParameter parameter, String path, List errors, List warnings, ) { if (parameter.name.isEmpty) { errors.add( ValidationError( path: '$path.name', message: '参数名称不能为空', type: ValidationErrorType.required, ), ); } // 验证路径参数必须是必需的 if (parameter.location == ParameterLocation.path && !parameter.required) { errors.add( ValidationError( path: '$path.required', message: '路径参数必须是必需的', type: ValidationErrorType.constraint, ), ); } // 验证参数类型 if (parameter.type == PropertyType.unknown) { warnings.add( ValidationWarning( path: '$path.type', message: '参数类型未知', suggestion: '建议明确指定参数类型', ), ); } } void _validateRequestBody( ApiRequestBody requestBody, String path, List errors, List warnings, ) { if (requestBody.content.isEmpty) { errors.add( ValidationError( path: '$path.content', message: '请求体必须定义至少一种内容类型', type: ValidationErrorType.required, ), ); } requestBody.content.forEach((mediaType, content) { _validateMediaType( content, '$path.content["$mediaType"]', mediaType, errors, warnings, ); }); } void _validateResponse( ApiResponse response, String path, List errors, List warnings, ) { if (response.description.isEmpty) { warnings.add( ValidationWarning( path: '$path.description', message: '响应缺少描述', suggestion: '建议为响应添加描述', ), ); } response.content.forEach((mediaType, content) { _validateMediaType( content, '$path.content["$mediaType"]', mediaType, errors, warnings, ); }); } void _validateMediaType( ApiMediaType mediaType, String path, String contentType, List errors, List warnings, ) { // 验证 schema if (mediaType.schema == null) { warnings.add( ValidationWarning( path: '$path.schema', message: '媒体类型缺少 schema 定义', suggestion: '建议为媒体类型添加 schema', ), ); } // 验证编码(仅适用于 multipart 和 form data) if (contentType.startsWith('multipart/') || contentType.contains('form')) { if (mediaType.encoding.isEmpty) { warnings.add( ValidationWarning( path: '$path.encoding', message: '表单数据建议定义编码信息', suggestion: '为文件上传字段添加 contentType 等编码信息', ), ); } } } }