240 lines
6.0 KiB
Dart
240 lines
6.0 KiB
Dart
import 'package:swagger_generator_flutter/core/models.dart';
|
||
import 'package:swagger_generator_flutter/validators/core/validation_context.dart';
|
||
import 'package:swagger_generator_flutter/validators/core/validation_result.dart';
|
||
import 'package:swagger_generator_flutter/validators/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 = <ValidationError>[];
|
||
final warnings = <ValidationWarning>[];
|
||
|
||
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<ValidationError> errors,
|
||
List<ValidationWarning> 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<ValidationError> errors,
|
||
List<ValidationWarning> 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<ValidationError> errors,
|
||
List<ValidationWarning> 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<ValidationError> errors,
|
||
List<ValidationWarning> 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<ValidationError> errors,
|
||
List<ValidationWarning> 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 等编码信息',
|
||
),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|