swagger_generator_flutter/lib/core/models.dart

2454 lines
65 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// Swagger数据模型定义
/// 提供类型安全的数据结构
library;
/// HTTP方法枚举
/// 表示常见的 RESTful API 方法。
enum HttpMethod {
/// GET 方法
get('GET'),
/// POST 方法
post('POST'),
/// PUT 方法
put('PUT'),
/// DELETE 方法
delete('DELETE'),
/// PATCH 方法
patch('PATCH'),
/// HEAD 方法
head('HEAD'),
/// OPTIONS 方法
options('OPTIONS');
/// 枚举值对应的字符串
const HttpMethod(this.value);
final String value;
/// 通过字符串获取 HttpMethod 枚举
static HttpMethod fromString(String value) {
return HttpMethod.values.firstWhere(
(method) => method.value == value.toUpperCase(),
orElse: () => HttpMethod.get,
);
}
}
/// 模型用途类型
/// 用于标识 API 模型在实际使用中的角色
enum ModelUsageType {
/// 请求模型 - 用于 requestBody 或 parameters
/// 此类模型不应添加 defaultValue以确保数据的明确性
request,
/// 响应模型 - 用于 response
/// 此类模型应添加 defaultValue以提高安全性和容错性
response,
/// 通用模型 - 既用于请求又用于响应
/// 此类模型的处理策略可配置,默认添加 defaultValue
common,
/// 未知 - 未被任何 API 使用,或无法确定用途
/// 此类模型的处理策略可配置,默认添加 defaultValue
unknown,
}
/// API服务器信息 (OpenAPI 3.0)
class ApiServer {
const ApiServer({
required this.url,
this.description = '',
this.variables = const {},
});
/// 从JSON创建ApiServer
factory ApiServer.fromJson(Map<String, dynamic> json) {
final variablesJson = json['variables'] as Map<String, dynamic>? ?? {};
final variables = <String, ApiServerVariable>{};
variablesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
variables[key] = ApiServerVariable.fromJson(value);
}
});
return ApiServer(
url: json['url'] as String? ?? '',
description: json['description'] as String? ?? '',
variables: variables,
);
}
/// 服务器URL
final String url;
/// 服务器描述
final String description;
/// 服务器变量
final Map<String, ApiServerVariable> variables;
}
/// API服务器变量 (OpenAPI 3.0)
class ApiServerVariable {
const ApiServerVariable({
required this.defaultValue,
this.enumValues = const [],
this.description = '',
});
/// 从JSON创建ApiServerVariable
factory ApiServerVariable.fromJson(Map<String, dynamic> json) {
return ApiServerVariable(
enumValues:
(json['enum'] as List<dynamic>?)?.map((e) => e.toString()).toList() ??
[],
defaultValue: json['default'] as String? ?? '',
description: json['description'] as String? ?? '',
);
}
/// 变量的可选值
final List<String> enumValues;
/// 默认值
final String defaultValue;
/// 变量描述
final String description;
}
/// 属性类型枚举
/// 用于描述 API 属性的数据类型。
enum PropertyType {
/// 字符串类型
string('string'),
/// 整数类型
integer('integer'),
/// 浮点数类型
number('number'),
/// 布尔类型
boolean('boolean'),
/// 数组类型
array('array'),
/// 对象类型
object('object'),
/// 文件类型
file('file'),
/// 日期类型
date('date'),
/// 日期时间类型
dateTime('date-time'),
/// 引用类型
reference('reference'),
/// 枚举类型
enumType('enum'),
/// 未知类型
unknown('unknown');
/// 枚举值对应的字符串
const PropertyType(this.value);
final String value;
/// 通过字符串获取 PropertyType 枚举
static PropertyType fromString(String value) {
return PropertyType.values.firstWhere(
(type) => type.value == value.toLowerCase(),
orElse: () => PropertyType.unknown,
);
}
}
/// 参数位置枚举
/// 用于描述 API 参数在请求中的位置。
enum ParameterLocation {
/// 查询参数
query('query'),
/// 路径参数
path('path'),
/// 请求头参数
header('header'),
/// 请求体参数
body('body'),
/// 表单参数
form('formData'),
/// Cookie 参数
cookie('cookie');
/// 枚举值对应的字符串
const ParameterLocation(this.value);
final String value;
/// 通过字符串获取 ParameterLocation 枚举
static ParameterLocation fromString(String value) {
return ParameterLocation.values.firstWhere(
(location) => location.value == value.toLowerCase(),
orElse: () => ParameterLocation.query,
);
}
}
/// OpenAPI 3.0 文档信息
/// 描述整个 API 的元数据和结构。
class SwaggerDocument {
/// 构造函数
const SwaggerDocument({
required this.title,
required this.version,
required this.description,
required this.paths,
required this.models,
required this.controllers,
this.servers = const [],
this.components = const ApiComponents(),
this.security = const [],
});
/// 从JSON创建SwaggerDocument
factory SwaggerDocument.fromJson(Map<String, dynamic> json) {
final info = json['info'] as Map<String, dynamic>? ?? {};
// 解析 servers (OpenAPI 3.0)
final serversJson = json['servers'] as List<dynamic>? ?? [];
final servers = serversJson
.map((server) => ApiServer.fromJson(server as Map<String, dynamic>))
.toList();
// 如果没有 servers 配置,提供默认值
if (servers.isEmpty) {
servers.add(const ApiServer(url: '/'));
}
// 解析 components (OpenAPI 3.0)
final componentsJson = json['components'] as Map<String, dynamic>?;
final components = componentsJson != null
? ApiComponents.fromJson(componentsJson)
: const ApiComponents();
// 解析全局安全要求
final securityJson = json['security'] as List<dynamic>? ?? [];
final security = securityJson
.map((s) => ApiSecurityRequirement.fromJson(s as Map<String, dynamic>))
.toList();
return SwaggerDocument(
title: info['title'] as String? ?? 'API',
version: info['version'] as String? ?? '1.0.0',
description: info['description'] as String? ?? '',
servers: servers,
components: components,
paths: _parsePaths(json['paths'] as Map<String, dynamic>? ?? {}),
models: components.schemas, // 从 components 中提取 schemas
controllers: {},
security: security,
);
}
/// 文档标题
final String title;
/// 版本号
final String version;
/// 文档描述
final String description;
/// 服务器配置 (OpenAPI 3.0)
final List<ApiServer> servers;
/// 可重用组件 (OpenAPI 3.0)
final ApiComponents components;
/// 路径定义
final Map<String, ApiPath> paths;
/// 数据模型定义 (从 components.schemas 提取)
final Map<String, ApiModel> models;
/// 控制器定义
final Map<String, ApiController> controllers;
/// 全局安全要求
final List<ApiSecurityRequirement> security;
/// 从JSON解析API路径 (静态辅助方法)
static Map<String, ApiPath> _parsePaths(Map<String, dynamic> pathsJson) {
final paths = <String, ApiPath>{};
pathsJson.forEach((path, pathJson) {
final pathData = pathJson as Map<String, dynamic>;
pathData.forEach((method, methodJson) {
if (HttpMethod.values.any((m) => m.name == method)) {
final httpMethod =
HttpMethod.values.firstWhere((m) => m.name == method);
// This is a simplified parser for tests. It might overwrite paths if a path has multiple methods.
// The main parser in SwaggerDataParser handles this by creating unique keys.
paths[path] = ApiPath.fromJson(
path,
httpMethod,
methodJson as Map<String, dynamic>,
);
}
});
});
return paths;
}
}
/// API路径信息
class ApiPath {
const ApiPath({
required this.path,
required this.method,
required this.summary,
required this.description,
required this.operationId,
required this.tags,
required this.parameters,
required this.responses,
this.requestBody,
this.deprecated = false,
this.security = const [],
});
/// 从JSON创建ApiPath
factory ApiPath.fromJson(
String path,
HttpMethod method,
Map<String, dynamic> json,
) {
return ApiPath(
path: path,
method: method,
summary: json['summary'] as String? ?? '',
description: json['description'] as String? ?? '',
operationId: json['operationId'] as String? ?? '',
tags:
(json['tags'] as List<dynamic>?)?.map((e) => e.toString()).toList() ??
[],
parameters: (json['parameters'] as List?)
?.map((p) => ApiParameter.fromJson(p as Map<String, dynamic>))
.toList() ??
[],
responses: (json['responses'] as Map<String, dynamic>?)?.map(
(code, response) => MapEntry(
code,
ApiResponse.fromJson(code, response as Map<String, dynamic>),
),
) ??
{},
requestBody: json['requestBody'] != null
? ApiRequestBody.fromJson(json['requestBody'] as Map<String, dynamic>)
: null,
deprecated: json['deprecated'] as bool? ?? false,
security: (json['security'] as List?)
?.map(
(s) =>
ApiSecurityRequirement.fromJson(s as Map<String, dynamic>),
)
.toList() ??
[],
);
}
final String path;
final HttpMethod method;
final String summary;
final String description;
final String operationId;
final List<String> tags;
final List<ApiParameter> parameters;
final Map<String, ApiResponse> responses;
final ApiRequestBody? requestBody;
final bool deprecated;
/// 安全要求
final List<ApiSecurityRequirement> security;
}
/// API参数信息
class ApiParameter {
const ApiParameter({
required this.name,
required this.location,
required this.required,
required this.type,
required this.description,
this.format,
this.example,
this.defaultValue,
});
/// 从JSON创建ApiParameter
factory ApiParameter.fromJson(Map<String, dynamic> json) {
final schema = json['schema'] as Map<String, dynamic>?;
final type =
schema?['type'] as String? ?? json['type'] as String? ?? 'string';
return ApiParameter(
name: json['name'] as String? ?? '',
location: ParameterLocation.fromString(json['in'] as String? ?? 'query'),
required: json['required'] as bool? ?? false,
type: PropertyType.fromString(type),
description: json['description'] as String? ?? '',
format: schema?['format'] as String? ?? json['format'] as String?,
example: json['example'],
defaultValue: schema?['default'] ?? json['default'],
);
}
final String name;
final ParameterLocation location;
final bool required;
final PropertyType type;
final String description;
final String? format;
final dynamic example;
final dynamic defaultValue;
}
/// API响应信息 (OpenAPI 3.0)
class ApiResponse {
const ApiResponse({
required this.code,
required this.description,
this.headers = const {},
this.content = const {},
this.links = const {},
@Deprecated('Use content instead') this.schema,
});
/// 从JSON创建ApiResponse
factory ApiResponse.fromJson(String code, Map<String, dynamic> json) {
// 解析 headers
final headersJson = json['headers'] as Map<String, dynamic>? ?? {};
final headers = <String, ApiHeader>{};
headersJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
headers[key] = ApiHeader.fromJson(value);
}
});
// 解析 content
final contentJson = json['content'] as Map<String, dynamic>? ?? {};
final content = <String, ApiMediaType>{};
contentJson.forEach((mediaType, mediaTypeData) {
if (mediaTypeData is Map<String, dynamic>) {
content[mediaType] = ApiMediaType.fromJson(mediaTypeData, mediaType);
}
});
// 解析 links
final linksJson = json['links'] as Map<String, dynamic>? ?? {};
final links = <String, ApiLink>{};
linksJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
links[key] = ApiLink.fromJson(value);
}
});
return ApiResponse(
code: code,
description: json['description'] as String? ?? '',
headers: headers,
content: content,
links: links,
// 保留 schema 字段以兼容 Swagger 2.0
schema: json['schema'] as Map<String, dynamic>?,
);
}
/// 响应状态码
final String code;
/// 响应描述
final String description;
/// 响应头定义
final Map<String, ApiHeader> headers;
/// 内容类型映射 (media type -> MediaTypeObject)
final Map<String, ApiMediaType> content;
/// 响应链接
final Map<String, ApiLink> links;
/// Schema 定义 (Swagger 2.0 兼容,已弃用)
@Deprecated(
'Use content instead. This field is for Swagger 2.0 compatibility only.',
)
final Map<String, dynamic>? schema;
/// 获取支持的媒体类型列表
List<String> get supportedMediaTypes => content.keys.toList();
/// 检查是否支持指定的媒体类型
bool supportsMediaType(String mediaType) => content.containsKey(mediaType);
/// 获取指定媒体类型的 schema
Map<String, dynamic>? getSchemaForMediaType(String mediaType) {
return content[mediaType]?.schema;
}
/// 检查是否有响应头定义
bool get hasHeaders => headers.isNotEmpty;
/// 检查是否有响应链接
bool get hasLinks => links.isNotEmpty;
}
/// API请求体信息 (OpenAPI 3.0)
class ApiRequestBody {
const ApiRequestBody({
this.description = '',
this.required = false,
this.content = const {},
});
/// 从JSON创建ApiRequestBody
factory ApiRequestBody.fromJson(Map<String, dynamic> json) {
final contentJson = json['content'] as Map<String, dynamic>? ?? {};
final content = <String, ApiMediaType>{};
contentJson.forEach((mediaType, mediaTypeData) {
if (mediaTypeData is Map<String, dynamic>) {
content[mediaType] = ApiMediaType.fromJson(mediaTypeData, mediaType);
}
});
return ApiRequestBody(
description: json['description'] as String? ?? '',
required: json['required'] as bool? ?? false,
content: content,
);
}
/// 请求体描述
final String description;
/// 是否必需
final bool required;
/// 内容类型映射 (media type -> MediaTypeObject)
final Map<String, ApiMediaType> content;
/// 获取支持的媒体类型列表
List<String> get supportedMediaTypes => content.keys.toList();
/// 检查是否支持指定的媒体类型
bool supportsMediaType(String mediaType) => content.containsKey(mediaType);
/// 获取指定媒体类型的 schema
Map<String, dynamic>? getSchemaForMediaType(String mediaType) {
return content[mediaType]?.schema;
}
}
/// 媒体类型枚举
enum MediaType {
json,
xml,
formData,
formUrlEncoded,
multipartFormData,
textPlain,
textHtml,
textCsv,
applicationOctetStream,
applicationPdf,
imagePng,
imageJpeg,
imageGif,
imageSvg,
audioMp3,
videoMp4,
custom,
}
extension MediaTypeExtension on MediaType {
String get value {
switch (this) {
case MediaType.json:
return 'application/json';
case MediaType.xml:
return 'application/xml';
case MediaType.formData:
return 'multipart/form-data';
case MediaType.formUrlEncoded:
return 'application/x-www-form-urlencoded';
case MediaType.multipartFormData:
return 'multipart/form-data';
case MediaType.textPlain:
return 'text/plain';
case MediaType.textHtml:
return 'text/html';
case MediaType.textCsv:
return 'text/csv';
case MediaType.applicationOctetStream:
return 'application/octet-stream';
case MediaType.applicationPdf:
return 'application/pdf';
case MediaType.imagePng:
return 'image/png';
case MediaType.imageJpeg:
return 'image/jpeg';
case MediaType.imageGif:
return 'image/gif';
case MediaType.imageSvg:
return 'image/svg+xml';
case MediaType.audioMp3:
return 'audio/mpeg';
case MediaType.videoMp4:
return 'video/mp4';
case MediaType.custom:
return 'custom';
}
}
static MediaType fromString(String value) {
final lowerValue = value.toLowerCase();
switch (lowerValue) {
case 'application/json':
return MediaType.json;
case 'application/xml':
case 'text/xml':
return MediaType.xml;
case 'multipart/form-data':
return MediaType.multipartFormData;
case 'application/x-www-form-urlencoded':
return MediaType.formUrlEncoded;
case 'text/plain':
return MediaType.textPlain;
case 'text/html':
return MediaType.textHtml;
case 'text/csv':
return MediaType.textCsv;
case 'application/octet-stream':
return MediaType.applicationOctetStream;
case 'application/pdf':
return MediaType.applicationPdf;
case 'image/png':
return MediaType.imagePng;
case 'image/jpeg':
case 'image/jpg':
return MediaType.imageJpeg;
case 'image/gif':
return MediaType.imageGif;
case 'image/svg+xml':
return MediaType.imageSvg;
case 'audio/mpeg':
case 'audio/mp3':
return MediaType.audioMp3;
case 'video/mp4':
return MediaType.videoMp4;
default:
return MediaType.custom;
}
}
/// 检查是否是文本类型
bool get isText {
switch (this) {
case MediaType.textPlain:
case MediaType.textHtml:
case MediaType.textCsv:
case MediaType.json:
case MediaType.xml:
return true;
default:
return false;
}
}
/// 检查是否是二进制类型
bool get isBinary {
switch (this) {
case MediaType.applicationOctetStream:
case MediaType.applicationPdf:
case MediaType.imagePng:
case MediaType.imageJpeg:
case MediaType.imageGif:
case MediaType.imageSvg:
case MediaType.audioMp3:
case MediaType.videoMp4:
return true;
default:
return false;
}
}
/// 检查是否是表单类型
bool get isForm {
switch (this) {
case MediaType.formData:
case MediaType.formUrlEncoded:
case MediaType.multipartFormData:
return true;
default:
return false;
}
}
/// 检查是否是图片类型
bool get isImage {
switch (this) {
case MediaType.imagePng:
case MediaType.imageJpeg:
case MediaType.imageGif:
case MediaType.imageSvg:
return true;
default:
return false;
}
}
/// 检查是否是音频类型
bool get isAudio {
switch (this) {
case MediaType.audioMp3:
return true;
default:
return false;
}
}
/// 检查是否是视频类型
bool get isVideo {
switch (this) {
case MediaType.videoMp4:
return true;
default:
return false;
}
}
/// 获取适合的 Dart 类型
String get dartType {
switch (this) {
case MediaType.json:
return 'Map<String, dynamic>';
case MediaType.xml:
return 'String';
case MediaType.formData:
case MediaType.formUrlEncoded:
case MediaType.multipartFormData:
return 'FormData';
case MediaType.textPlain:
case MediaType.textHtml:
case MediaType.textCsv:
return 'String';
case MediaType.applicationOctetStream:
case MediaType.applicationPdf:
case MediaType.imagePng:
case MediaType.imageJpeg:
case MediaType.imageGif:
case MediaType.audioMp3:
case MediaType.videoMp4:
return 'List<int>';
case MediaType.imageSvg:
return 'String';
case MediaType.custom:
return 'dynamic';
}
}
}
/// API媒体类型信息 (OpenAPI 3.0)
class ApiMediaType {
const ApiMediaType({
this.schema,
this.example,
this.examples = const {},
this.encoding = const {},
this.mediaType = MediaType.json,
this.rawMediaType = 'application/json',
});
/// 从JSON创建ApiMediaType
factory ApiMediaType.fromJson(
Map<String, dynamic> json, [
String? contentType,
]) {
final examplesJson = json['examples'] as Map<String, dynamic>? ?? {};
final examples = <String, ApiExample>{};
examplesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
examples[key] = ApiExample.fromJson(value);
}
});
final encodingJson = json['encoding'] as Map<String, dynamic>? ?? {};
final encoding = <String, ApiEncoding>{};
encodingJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
encoding[key] = ApiEncoding.fromJson(value);
}
});
// 确定媒体类型
final rawType = contentType ?? 'application/json';
final mediaType = MediaTypeExtension.fromString(rawType);
return ApiMediaType(
schema: json['schema'] as Map<String, dynamic>?,
example: json['example'],
examples: examples,
encoding: encoding,
mediaType: mediaType,
rawMediaType: rawType,
);
}
/// Schema 定义
final Map<String, dynamic>? schema;
/// 示例数据
final dynamic example;
/// 多个示例数据
final Map<String, ApiExample> examples;
/// 编码信息 (用于 multipart 和 form data)
final Map<String, ApiEncoding> encoding;
/// 媒体类型
final MediaType mediaType;
/// 原始媒体类型字符串
final String rawMediaType;
/// 检查是否是 JSON 类型
bool get isJson => mediaType == MediaType.json;
/// 检查是否是 XML 类型
bool get isXml => mediaType == MediaType.xml;
/// 检查是否是表单类型
bool get isForm => mediaType.isForm;
/// 检查是否是文件上传类型
bool get isFileUpload => mediaType == MediaType.multipartFormData;
/// 检查是否是二进制类型
bool get isBinary => mediaType.isBinary;
/// 检查是否是文本类型
bool get isText => mediaType.isText;
/// 检查是否是图片类型
bool get isImage => mediaType.isImage;
/// 获取适合的 Dart 类型
String get dartType => mediaType.dartType;
}
/// API示例信息 (OpenAPI 3.0)
class ApiExample {
const ApiExample({
this.summary = '',
this.description = '',
this.value,
this.externalValue,
});
/// 从JSON创建ApiExample
factory ApiExample.fromJson(Map<String, dynamic> json) {
return ApiExample(
summary: json['summary'] as String? ?? '',
description: json['description'] as String? ?? '',
value: json['value'],
externalValue: json['externalValue'] as String?,
);
}
/// 示例摘要
final String summary;
/// 示例描述
final String description;
/// 示例值
final dynamic value;
/// 外部示例URL
final String? externalValue;
}
/// API编码信息 (OpenAPI 3.0)
class ApiEncoding {
const ApiEncoding({
this.contentType,
this.headers = const {},
this.style,
this.explode = false,
this.allowReserved = false,
});
/// 从JSON创建ApiEncoding
factory ApiEncoding.fromJson(Map<String, dynamic> json) {
final headersJson = json['headers'] as Map<String, dynamic>? ?? {};
final headers = <String, ApiHeader>{};
headersJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
headers[key] = ApiHeader.fromJson(value);
}
});
return ApiEncoding(
contentType: json['contentType'] as String?,
headers: headers,
style: json['style'] as String?,
explode: json['explode'] as bool? ?? false,
allowReserved: json['allowReserved'] as bool? ?? false,
);
}
/// 内容类型
final String? contentType;
/// 头部信息
final Map<String, ApiHeader> headers;
/// 样式
final String? style;
/// 是否展开
final bool explode;
/// 是否允许保留字符
final bool allowReserved;
/// 检查是否是文件类型
bool get isFile {
if (contentType == null) return false;
final type = contentType!.toLowerCase();
return type.startsWith('image/') ||
type.startsWith('audio/') ||
type.startsWith('video/') ||
type == 'application/octet-stream' ||
type == 'application/pdf' ||
type.contains('binary');
}
/// 检查是否是图片文件
bool get isImage {
if (contentType == null) return false;
return contentType!.toLowerCase().startsWith('image/');
}
/// 检查是否是音频文件
bool get isAudio {
if (contentType == null) return false;
return contentType!.toLowerCase().startsWith('audio/');
}
/// 检查是否是视频文件
bool get isVideo {
if (contentType == null) return false;
return contentType!.toLowerCase().startsWith('video/');
}
/// 检查是否有自定义头部
bool get hasHeaders => headers.isNotEmpty;
}
/// API头部信息 (OpenAPI 3.0)
class ApiHeader {
const ApiHeader({
this.description = '',
this.required = false,
this.deprecated = false,
this.schema,
this.example,
});
/// 从JSON创建ApiHeader
factory ApiHeader.fromJson(Map<String, dynamic> json) {
return ApiHeader(
description: json['description'] as String? ?? '',
required: json['required'] as bool? ?? false,
deprecated: json['deprecated'] as bool? ?? false,
schema: json['schema'] as Map<String, dynamic>?,
example: json['example'],
);
}
/// 头部描述
final String description;
/// 是否必需
final bool required;
/// 是否已弃用
final bool deprecated;
/// Schema 定义
final Map<String, dynamic>? schema;
/// 示例值
final dynamic example;
}
/// API链接信息 (OpenAPI 3.0)
class ApiLink {
const ApiLink({
this.description = '',
this.operationRef,
this.operationId,
this.parameters = const {},
this.requestBody,
this.server,
});
/// 从JSON创建ApiLink
factory ApiLink.fromJson(Map<String, dynamic> json) {
final serverJson = json['server'] as Map<String, dynamic>?;
final server = serverJson != null ? ApiServer.fromJson(serverJson) : null;
return ApiLink(
description: json['description'] as String? ?? '',
operationRef: json['operationRef'] as String?,
operationId: json['operationId'] as String?,
parameters: json['parameters'] as Map<String, dynamic>? ?? {},
requestBody: json['requestBody'],
server: server,
);
}
/// 链接描述
final String description;
/// 操作引用
final String? operationRef;
/// 操作ID
final String? operationId;
/// 参数映射
final Map<String, dynamic> parameters;
/// 请求体映射
final dynamic requestBody;
/// 服务器信息
final ApiServer? server;
}
/// API组件信息 (OpenAPI 3.0)
/// 包含可重用的组件定义
class ApiComponents {
const ApiComponents({
this.schemas = const {},
this.responses = const {},
this.parameters = const {},
this.examples = const {},
this.requestBodies = const {},
this.headers = const {},
this.securitySchemes = const {},
this.links = const {},
this.callbacks = const {},
});
/// 从JSON创建ApiComponents
factory ApiComponents.fromJson(Map<String, dynamic> json) {
// 解析 schemas
final schemasJson = json['schemas'] as Map<String, dynamic>? ?? {};
final schemas = <String, ApiModel>{};
schemasJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
schemas[key] = ApiModel.fromJson(key, value);
}
});
// 解析 responses
final responsesJson = json['responses'] as Map<String, dynamic>? ?? {};
final responses = <String, ApiResponse>{};
responsesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
responses[key] = ApiResponse.fromJson(key, value);
}
});
// 解析 parameters
final parametersJson = json['parameters'] as Map<String, dynamic>? ?? {};
final parameters = <String, ApiParameter>{};
parametersJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
parameters[key] = ApiParameter.fromJson(value);
}
});
// 解析 examples
final examplesJson = json['examples'] as Map<String, dynamic>? ?? {};
final examples = <String, ApiExample>{};
examplesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
examples[key] = ApiExample.fromJson(value);
}
});
// 解析 requestBodies
final requestBodiesJson =
json['requestBodies'] as Map<String, dynamic>? ?? {};
final requestBodies = <String, ApiRequestBody>{};
requestBodiesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
requestBodies[key] = ApiRequestBody.fromJson(value);
}
});
// 解析 headers
final headersJson = json['headers'] as Map<String, dynamic>? ?? {};
final headers = <String, ApiHeader>{};
headersJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
headers[key] = ApiHeader.fromJson(value);
}
});
// 解析 securitySchemes
final securitySchemesJson =
json['securitySchemes'] as Map<String, dynamic>? ?? {};
final securitySchemes = <String, ApiSecurityScheme>{};
securitySchemesJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
securitySchemes[key] = ApiSecurityScheme.fromJson(value);
}
});
// 解析 links
final linksJson = json['links'] as Map<String, dynamic>? ?? {};
final links = <String, ApiLink>{};
linksJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
links[key] = ApiLink.fromJson(value);
}
});
// 解析 callbacks
final callbacksJson = json['callbacks'] as Map<String, dynamic>? ?? {};
final callbacks = <String, ApiCallback>{};
callbacksJson.forEach((key, value) {
if (value is Map<String, dynamic>) {
callbacks[key] = ApiCallback.fromJson(value);
}
});
return ApiComponents(
schemas: schemas,
responses: responses,
parameters: parameters,
examples: examples,
requestBodies: requestBodies,
headers: headers,
securitySchemes: securitySchemes,
links: links,
callbacks: callbacks,
);
}
/// Schema 定义
final Map<String, ApiModel> schemas;
/// 响应定义
final Map<String, ApiResponse> responses;
/// 参数定义
final Map<String, ApiParameter> parameters;
/// 示例定义
final Map<String, ApiExample> examples;
/// 请求体定义
final Map<String, ApiRequestBody> requestBodies;
/// 头部定义
final Map<String, ApiHeader> headers;
/// 安全方案定义
final Map<String, ApiSecurityScheme> securitySchemes;
/// 链接定义
final Map<String, ApiLink> links;
/// 回调定义
final Map<String, ApiCallback> callbacks;
}
/// API安全方案信息 (OpenAPI 3.0)
class ApiSecurityScheme {
const ApiSecurityScheme({
required this.type,
this.description = '',
this.name,
this.location,
this.scheme,
this.bearerFormat,
this.flows,
this.openIdConnectUrl,
});
/// 从JSON创建ApiSecurityScheme
factory ApiSecurityScheme.fromJson(Map<String, dynamic> json) {
final type = SecuritySchemeTypeExtension.fromString(
json['type'] as String? ?? 'apiKey',
);
return ApiSecurityScheme(
type: type,
description: json['description'] as String? ?? '',
name: json['name'] as String?,
location: json['in'] != null
? ApiKeyLocationExtension.fromString(json['in'] as String)
: null,
scheme: json['scheme'] as String?,
bearerFormat: json['bearerFormat'] as String?,
flows: json['flows'] != null
? OAuth2Flows.fromJson(json['flows'] as Map<String, dynamic>)
: null,
openIdConnectUrl: json['openIdConnectUrl'] as String?,
);
}
/// 安全方案类型
final SecuritySchemeType type;
/// 描述
final String description;
/// 名称 (用于 apiKey)
final String? name;
/// 位置 (用于 apiKey)
final ApiKeyLocation? location;
/// 方案 (用于 http)
final String? scheme;
/// Bearer 格式 (用于 http bearer)
final String? bearerFormat;
/// OAuth2 流程信息 (用于 oauth2)
final OAuth2Flows? flows;
/// OpenID Connect URL (用于 openIdConnect)
final String? openIdConnectUrl;
/// 检查是否是 API Key 认证
bool get isApiKey => type == SecuritySchemeType.apiKey;
/// 检查是否是 HTTP 认证
bool get isHttp => type == SecuritySchemeType.http;
/// 检查是否是 OAuth2 认证
bool get isOAuth2 => type == SecuritySchemeType.oauth2;
/// 检查是否是 OpenID Connect 认证
bool get isOpenIdConnect => type == SecuritySchemeType.openIdConnect;
/// 检查是否是 Bearer 认证
bool get isBearer => isHttp && scheme?.toLowerCase() == 'bearer';
/// 检查是否是 Basic 认证
bool get isBasic => isHttp && scheme?.toLowerCase() == 'basic';
/// 检查是否有 OAuth2 流程
bool get hasOAuth2Flows => flows?.hasAnyFlow ?? false;
/// 获取 API Key 的完整配置信息
String get apiKeyInfo {
if (!isApiKey || name == null || location == null) return '';
return '${location!.value}:$name';
}
/// 获取 HTTP 认证的完整配置信息
String get httpAuthInfo {
if (!isHttp || scheme == null) return '';
if (bearerFormat != null) {
return '$scheme ($bearerFormat)';
}
return scheme!;
}
}
/// API回调信息 (OpenAPI 3.0)
class ApiCallback {
const ApiCallback({
this.expressions = const {},
});
/// 从JSON创建ApiCallback
factory ApiCallback.fromJson(Map<String, dynamic> json) {
final expressions = <String, ApiPathItem>{};
json.forEach((expression, pathItemData) {
if (pathItemData is Map<String, dynamic>) {
expressions[expression] = ApiPathItem.fromJson(pathItemData);
}
});
return ApiCallback(
expressions: expressions,
);
}
/// 回调表达式映射
final Map<String, ApiPathItem> expressions;
}
/// API路径项信息 (OpenAPI 3.0)
class ApiPathItem {
const ApiPathItem({
this.summary = '',
this.description = '',
this.operations = const {},
this.servers = const [],
this.parameters = const [],
});
/// 从JSON创建ApiPathItem
factory ApiPathItem.fromJson(Map<String, dynamic> json) {
final operations = <String, ApiOperation>{};
final httpMethods = [
'get',
'put',
'post',
'delete',
'options',
'head',
'patch',
'trace',
];
for (final method in httpMethods) {
if (json[method] != null) {
operations[method] =
ApiOperation.fromJson(json[method] as Map<String, dynamic>);
}
}
final serversJson = json['servers'] as List<dynamic>? ?? [];
final servers = serversJson
.map((server) => ApiServer.fromJson(server as Map<String, dynamic>))
.toList();
final parametersJson = json['parameters'] as List<dynamic>? ?? [];
final parameters = parametersJson
.map((param) => ApiParameter.fromJson(param as Map<String, dynamic>))
.toList();
return ApiPathItem(
summary: json['summary'] as String? ?? '',
description: json['description'] as String? ?? '',
operations: operations,
servers: servers,
parameters: parameters,
);
}
/// 路径摘要
final String summary;
/// 路径描述
final String description;
/// 操作映射 (HTTP方法 -> 操作)
final Map<String, ApiOperation> operations;
/// 服务器信息
final List<ApiServer> servers;
/// 参数信息
final List<ApiParameter> parameters;
}
/// API操作信息 (OpenAPI 3.0)
class ApiOperation {
const ApiOperation({
this.summary = '',
this.description = '',
this.operationId,
this.tags = const [],
this.parameters = const [],
this.requestBody,
this.responses = const {},
this.security = const [],
});
/// 从JSON创建ApiOperation
factory ApiOperation.fromJson(Map<String, dynamic> json) {
final parametersJson = json['parameters'] as List<dynamic>? ?? [];
final parameters = parametersJson
.map((param) => ApiParameter.fromJson(param as Map<String, dynamic>))
.toList();
final requestBodyJson = json['requestBody'] as Map<String, dynamic>?;
final requestBody = requestBodyJson != null
? ApiRequestBody.fromJson(requestBodyJson)
: null;
final responsesJson = json['responses'] as Map<String, dynamic>? ?? {};
final responses = <String, ApiResponse>{};
responsesJson.forEach((code, responseData) {
if (responseData is Map<String, dynamic>) {
responses[code] = ApiResponse.fromJson(code, responseData);
}
});
final securityJson = json['security'] as List<dynamic>? ?? [];
final security = securityJson
.map((s) => ApiSecurityRequirement.fromJson(s as Map<String, dynamic>))
.toList();
return ApiOperation(
summary: json['summary'] as String? ?? '',
description: json['description'] as String? ?? '',
operationId: json['operationId'] as String?,
tags:
(json['tags'] as List<dynamic>?)?.map((e) => e.toString()).toList() ??
[],
parameters: parameters,
requestBody: requestBody,
responses: responses,
security: security,
);
}
/// 操作摘要
final String summary;
/// 操作描述
final String description;
/// 操作ID
final String? operationId;
/// 标签
final List<String> tags;
/// 参数
final List<ApiParameter> parameters;
/// 请求体
final ApiRequestBody? requestBody;
/// 响应
final Map<String, ApiResponse> responses;
/// 安全要求
final List<ApiSecurityRequirement> security;
}
/// API 判别器信息 (OpenAPI 3.0)
/// 用于多态类型的判别
class ApiDiscriminator {
const ApiDiscriminator({
required this.propertyName,
this.mapping = const {},
});
/// 从JSON创建ApiDiscriminator
factory ApiDiscriminator.fromJson(Map<String, dynamic> json) {
final mappingJson = json['mapping'] as Map<String, dynamic>? ?? {};
final mapping = <String, String>{};
mappingJson.forEach((key, value) {
if (value is String) {
mapping[key] = value;
}
});
return ApiDiscriminator(
propertyName: json['propertyName'] as String? ?? '',
mapping: mapping,
);
}
/// 判别器属性名
final String propertyName;
/// 映射表 (值 -> schema 引用)
final Map<String, String> mapping;
/// 检查是否有映射表
bool get hasMapping => mapping.isNotEmpty;
/// 根据值获取对应的 schema 引用
String? getSchemaForValue(String value) => mapping[value];
}
/// API Schema 信息 (OpenAPI 3.0)
/// 表示一个 JSON Schema 对象,支持组合模式
class ApiSchema {
const ApiSchema({
this.type,
this.format,
this.description = '',
this.properties = const {},
this.required = const [],
this.items,
this.reference,
this.enumValues = const [],
this.allOf = const [],
this.oneOf = const [],
this.anyOf = const [],
this.not,
this.discriminator,
this.additionalProperties,
this.patternProperties = const {},
this.propertyNames,
this.dependencies = const {},
this.constValue,
this.ifSchema,
this.thenSchema,
this.elseSchema,
this.minimum,
this.maximum,
this.exclusiveMinimum,
this.exclusiveMaximum,
this.minLength,
this.maxLength,
this.pattern,
this.minItems,
this.maxItems,
this.uniqueItems,
this.nullable = false,
this.example,
this.defaultValue,
});
/// 从JSON创建ApiSchema
factory ApiSchema.fromJson(Map<String, dynamic> json) {
// 解析 properties
final propertiesJson = json['properties'] as Map<String, dynamic>? ?? {};
final properties = <String, ApiProperty>{};
final requiredFields = (json['required'] as List<dynamic>?)
?.map((e) => e.toString())
.toList() ??
[];
propertiesJson.forEach((propName, propData) {
if (propData is Map<String, dynamic>) {
properties[propName] = ApiProperty.fromJson(
propName,
propData,
requiredFields,
);
}
});
// 解析 items (用于数组类型)
final itemsJson = json['items'] as Map<String, dynamic>?;
final items = itemsJson != null ? ApiSchema.fromJson(itemsJson) : null;
// 解析组合模式
final allOfJson = json['allOf'] as List<dynamic>? ?? [];
final allOf = allOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final oneOfJson = json['oneOf'] as List<dynamic>? ?? [];
final oneOf = oneOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final anyOfJson = json['anyOf'] as List<dynamic>? ?? [];
final anyOf = anyOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final notJson = json['not'] as Map<String, dynamic>?;
final not = notJson != null ? ApiSchema.fromJson(notJson) : null;
// 解析 discriminator
final discriminatorJson = json['discriminator'] as Map<String, dynamic>?;
final discriminator = discriminatorJson != null
? ApiDiscriminator.fromJson(discriminatorJson)
: null;
// 解析 patternProperties
final patternPropertiesJson =
json['patternProperties'] as Map<String, dynamic>? ?? {};
final patternProperties = <String, ApiSchema>{};
patternPropertiesJson.forEach((pattern, schemaData) {
if (schemaData is Map<String, dynamic>) {
patternProperties[pattern] = ApiSchema.fromJson(schemaData);
}
});
// 解析 propertyNames
final propertyNamesJson = json['propertyNames'] as Map<String, dynamic>?;
final propertyNames = propertyNamesJson != null
? ApiSchema.fromJson(propertyNamesJson)
: null;
// 解析 dependencies
final dependencies = json['dependencies'] as Map<String, dynamic>? ?? {};
// 解析条件 Schema (if/then/else)
final ifJson = json['if'] as Map<String, dynamic>?;
final ifSchema = ifJson != null ? ApiSchema.fromJson(ifJson) : null;
final thenJson = json['then'] as Map<String, dynamic>?;
final thenSchema = thenJson != null ? ApiSchema.fromJson(thenJson) : null;
final elseJson = json['else'] as Map<String, dynamic>?;
final elseSchema = elseJson != null ? ApiSchema.fromJson(elseJson) : null;
// 处理引用
String? reference;
if (json[r'$ref'] != null) {
final ref = json[r'$ref'] as String;
reference = ref.split('/').last;
}
return ApiSchema(
type: json['type'] as String?,
format: json['format'] as String?,
description: json['description'] as String? ?? '',
properties: properties,
required: requiredFields,
items: items,
reference: reference,
enumValues: (json['enum'] as List<dynamic>?) ?? [],
allOf: allOf,
oneOf: oneOf,
anyOf: anyOf,
not: not,
discriminator: discriminator,
additionalProperties: json['additionalProperties'],
patternProperties: patternProperties,
propertyNames: propertyNames,
dependencies: dependencies,
constValue: json['const'],
ifSchema: ifSchema,
thenSchema: thenSchema,
elseSchema: elseSchema,
minimum: json['minimum'] as num?,
maximum: json['maximum'] as num?,
exclusiveMinimum: json['exclusiveMinimum'] as bool?,
exclusiveMaximum: json['exclusiveMaximum'] as bool?,
minLength: json['minLength'] as int?,
maxLength: json['maxLength'] as int?,
pattern: json['pattern'] as String?,
minItems: json['minItems'] as int?,
maxItems: json['maxItems'] as int?,
uniqueItems: json['uniqueItems'] as bool?,
nullable: json['nullable'] as bool? ?? false,
example: json['example'],
defaultValue: json['default'],
);
}
/// Schema 类型
final String? type;
/// Schema 格式
final String? format;
/// Schema 描述
final String description;
/// 属性定义 (用于 object 类型)
final Map<String, ApiProperty> properties;
/// 必需字段
final List<String> required;
/// 数组项定义 (用于 array 类型)
final ApiSchema? items;
/// 引用 ($ref)
final String? reference;
/// 枚举值
final List<dynamic> enumValues;
/// 组合模式
final List<ApiSchema> allOf;
final List<ApiSchema> oneOf;
final List<ApiSchema> anyOf;
final ApiSchema? not;
/// 多态类型判别器 (OpenAPI 3.0)
final ApiDiscriminator? discriminator;
/// 额外属性 (可以是 boolean 或 Schema)
final dynamic additionalProperties;
/// 模式属性 (patternProperties)
final Map<String, ApiSchema> patternProperties;
/// 属性名称约束
final ApiSchema? propertyNames;
/// 属性依赖关系
final Map<String, dynamic> dependencies;
/// 常量值
final dynamic constValue;
/// 条件 Schema (if/then/else)
final ApiSchema? ifSchema;
final ApiSchema? thenSchema;
final ApiSchema? elseSchema;
/// 最小值/最大值 (用于数值类型)
final num? minimum;
final num? maximum;
final bool? exclusiveMinimum;
final bool? exclusiveMaximum;
/// 字符串长度限制
final int? minLength;
final int? maxLength;
final String? pattern;
/// 数组长度限制
final int? minItems;
final int? maxItems;
final bool? uniqueItems;
/// 可空性
final bool nullable;
/// 示例值
final dynamic example;
/// 默认值
final dynamic defaultValue;
/// 检查是否是组合模式
bool get isComposition =>
allOf.isNotEmpty || oneOf.isNotEmpty || anyOf.isNotEmpty;
/// 检查是否是 allOf 组合
bool get isAllOf => allOf.isNotEmpty;
/// 检查是否是 oneOf 组合
bool get isOneOf => oneOf.isNotEmpty;
/// 检查是否是 anyOf 组合
bool get isAnyOf => anyOf.isNotEmpty;
/// 检查是否有 not 约束
bool get hasNot => not != null;
/// 检查是否有判别器
bool get hasDiscriminator => discriminator != null;
/// 检查是否是引用类型
bool get isReference => reference != null;
/// 检查是否是枚举类型
bool get isEnum => enumValues.isNotEmpty;
/// 检查是否是数组类型
bool get isArray => type == 'array';
/// 检查是否是对象类型
bool get isObject => type == 'object' || properties.isNotEmpty;
/// 检查是否有模式属性
bool get hasPatternProperties => patternProperties.isNotEmpty;
/// 检查是否有属性名称约束
bool get hasPropertyNames => propertyNames != null;
/// 检查是否有属性依赖
bool get hasDependencies => dependencies.isNotEmpty;
/// 检查是否有常量值
bool get hasConstValue => constValue != null;
/// 检查是否有条件 Schema
bool get hasConditionalSchema =>
ifSchema != null || thenSchema != null || elseSchema != null;
/// 检查是否允许额外属性
bool get allowsAdditionalProperties {
if (additionalProperties == null) return true; // 默认允许
if (additionalProperties is bool) return additionalProperties as bool;
return true; // 如果是 Schema 对象,表示允许但有约束
}
/// 获取额外属性的 Schema如果 additionalProperties 是 Schema 对象)
ApiSchema? get additionalPropertiesSchema {
if (additionalProperties is Map<String, dynamic>) {
return ApiSchema.fromJson(additionalProperties as Map<String, dynamic>);
}
return null;
}
}
/// API模型信息
class ApiModel {
const ApiModel({
required this.name,
required this.description,
required this.properties,
required this.required,
this.isEnum = false,
this.enumValues = const [],
this.enumType,
this.allOf = const [],
this.oneOf = const [],
this.anyOf = const [],
this.not,
this.discriminator,
this.usageType = ModelUsageType.unknown,
});
/// 从JSON创建ApiModel
factory ApiModel.fromJson(
String name,
Map<String, dynamic> json, {
ModelUsageType usageType = ModelUsageType.unknown,
}) {
final isEnum = json['enum'] != null;
final enumValues =
isEnum ? (json['enum'] as List<dynamic>?) ?? <dynamic>[] : <dynamic>[];
final properties = json['properties'] as Map<String, dynamic>? ?? {};
List<String> required;
if (json.containsKey('required')) {
required = (json['required'] as List<dynamic>?)
?.map((e) => e.toString())
.toList() ??
[];
} else {
// 没有 required 字段时,凡 nullable != true 的都视为 required
required = properties.entries
.where((e) => !(e.value['nullable'] as bool? ?? false))
.map((e) => e.key)
.toList();
}
// 解析组合模式
final allOfJson = json['allOf'] as List<dynamic>? ?? [];
final allOf = allOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final oneOfJson = json['oneOf'] as List<dynamic>? ?? [];
final oneOf = oneOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final anyOfJson = json['anyOf'] as List<dynamic>? ?? [];
final anyOf = anyOfJson
.map((schema) => ApiSchema.fromJson(schema as Map<String, dynamic>))
.toList();
final notJson = json['not'] as Map<String, dynamic>?;
final not = notJson != null ? ApiSchema.fromJson(notJson) : null;
// 解析 discriminator
final discriminatorJson = json['discriminator'] as Map<String, dynamic>?;
final discriminator = discriminatorJson != null
? ApiDiscriminator.fromJson(discriminatorJson)
: null;
return ApiModel(
name: name,
description: json['description'] as String? ?? '',
required: required,
isEnum: isEnum,
enumValues: enumValues,
enumType: isEnum
? PropertyType.fromString(json['type'] as String? ?? 'string')
: null,
allOf: allOf,
oneOf: oneOf,
anyOf: anyOf,
not: not,
discriminator: discriminator,
usageType: usageType,
properties: properties.map(
(propName, propData) => MapEntry(
propName,
ApiProperty.fromJson(
propName,
propData as Map<String, dynamic>,
required,
),
),
),
);
}
final String name;
final String description;
final Map<String, ApiProperty> properties;
final List<String> required;
final bool isEnum;
final List<dynamic> enumValues;
final PropertyType? enumType;
/// 组合模式支持 (OpenAPI 3.0)
final List<ApiSchema> allOf;
final List<ApiSchema> oneOf;
final List<ApiSchema> anyOf;
final ApiSchema? not;
/// 多态类型判别器 (OpenAPI 3.0)
final ApiDiscriminator? discriminator;
/// 模型用途类型
/// 标识该模型在 API 中的实际用途(请求/响应/通用/未知)
final ModelUsageType usageType;
/// 检查是否使用了组合模式
bool get isComposition =>
allOf.isNotEmpty || oneOf.isNotEmpty || anyOf.isNotEmpty;
/// 检查是否是 allOf 组合
bool get isAllOf => allOf.isNotEmpty;
/// 检查是否是 oneOf 组合
bool get isOneOf => oneOf.isNotEmpty;
/// 检查是否是 anyOf 组合
bool get isAnyOf => anyOf.isNotEmpty;
/// 检查是否有 not 约束
bool get hasNot => not != null;
/// 检查是否有判别器
bool get hasDiscriminator => discriminator != null;
/// 创建副本并更新用途类型
/// 用于在分析 schema 使用情况后更新模型的用途类型
ApiModel copyWithUsageType(ModelUsageType newUsageType) {
return ApiModel(
name: name,
description: description,
properties: properties,
required: required,
isEnum: isEnum,
enumValues: enumValues,
enumType: enumType,
allOf: allOf,
oneOf: oneOf,
anyOf: anyOf,
not: not,
discriminator: discriminator,
usageType: newUsageType,
);
}
}
/// API属性信息
class ApiProperty {
const ApiProperty({
required this.name,
required this.type,
required this.description,
required this.required,
this.format,
this.nullable = false,
this.example,
this.defaultValue,
this.reference,
this.items,
this.nestedProperties = const {},
this.nestedRequired = const [],
this.schema,
});
/// 从JSON创建ApiProperty
factory ApiProperty.fromJson(
String name,
Map<String, dynamic> json,
List<String> requiredFields, {
int maxDepth = 10,
int currentDepth = 0,
}) {
// 防止过深的嵌套
if (currentDepth >= maxDepth) {
return ApiProperty(
name: name,
type: PropertyType.object,
description: '达到最大嵌套深度的对象',
required: requiredFields.contains(name),
);
}
final type = PropertyType.fromString(json['type'] as String? ?? 'string');
String? reference;
ApiModel? items;
final nestedProperties = <String, ApiProperty>{};
var nestedRequired = <String>[];
ApiSchema? schema;
// 处理引用类型
if (json[r'$ref'] != null) {
final ref = json[r'$ref'] as String;
reference = ref.split('/').last;
}
// 处理复杂 schema组合模式等
if (json['allOf'] != null ||
json['oneOf'] != null ||
json['anyOf'] != null) {
schema = ApiSchema.fromJson(json);
}
// 处理嵌套对象类型
if (type == PropertyType.object && json['properties'] != null) {
final propertiesJson = json['properties'] as Map<String, dynamic>;
nestedRequired = (json['required'] as List<dynamic>?)
?.map((e) => e.toString())
.toList() ??
[];
propertiesJson.forEach((propName, propData) {
if (propData is Map<String, dynamic>) {
try {
final nestedProperty = ApiProperty.fromJson(
propName,
propData,
nestedRequired,
maxDepth: maxDepth,
currentDepth: currentDepth + 1,
);
nestedProperties[propName] = nestedProperty;
} catch (e) {
// 如果解析嵌套属性失败,创建一个基本属性
nestedProperties[propName] = ApiProperty(
name: propName,
type: PropertyType.string,
description: '解析失败的嵌套属性',
required: nestedRequired.contains(propName),
);
}
}
});
}
// 处理数组类型的 items
if (type == PropertyType.array && json['items'] != null) {
final itemsJson = json['items'] as Map<String, dynamic>;
// 如果 items 是引用类型
if (itemsJson[r'$ref'] != null) {
final itemRef = itemsJson[r'$ref'] as String;
final itemRefName = itemRef.split('/').last;
items = ApiModel(
name: itemRefName,
description: '',
properties: {},
required: [],
);
} else if (itemsJson['type'] == 'object' &&
itemsJson['properties'] != null) {
// 如果 items 是嵌套对象
final itemProperties = <String, ApiProperty>{};
final itemRequired = (itemsJson['required'] as List<dynamic>?)
?.map((e) => e.toString())
.toList() ??
[];
final itemPropertiesJson =
itemsJson['properties'] as Map<String, dynamic>;
itemPropertiesJson.forEach((propName, propData) {
if (propData is Map<String, dynamic>) {
try {
final itemProperty = ApiProperty.fromJson(
propName,
propData,
itemRequired,
maxDepth: maxDepth,
currentDepth: currentDepth + 1,
);
itemProperties[propName] = itemProperty;
} catch (e) {
// 创建基本属性作为后备
itemProperties[propName] = ApiProperty(
name: propName,
type: PropertyType.string,
description: '解析失败的数组项属性',
required: itemRequired.contains(propName),
);
}
}
});
items = ApiModel(
name: '${name}Item',
description: itemsJson['description'] as String? ?? '',
properties: itemProperties,
required: itemRequired,
);
} else {
// 如果 items 是基本类型
final itemType =
PropertyType.fromString(itemsJson['type'] as String? ?? 'string');
items = ApiModel(
name: itemType.value,
description: '',
properties: {},
required: [],
);
}
}
return ApiProperty(
name: name,
type: reference != null ? PropertyType.reference : type,
format: json['format'] as String?,
description: json['description'] as String? ?? '',
required: requiredFields.contains(name),
nullable: json['nullable'] as bool? ?? false,
example: json['example'],
defaultValue: json['default'],
reference: reference,
items: items,
nestedProperties: nestedProperties,
nestedRequired: nestedRequired,
schema: schema,
);
}
final String name;
final PropertyType type;
final String? format;
final String description;
final bool required;
final bool nullable;
final dynamic example;
final dynamic defaultValue;
final String? reference;
final ApiModel? items; // 用于数组类型
/// 嵌套对象属性 (用于 object 类型)
final Map<String, ApiProperty> nestedProperties;
/// 嵌套对象的必需字段
final List<String> nestedRequired;
/// Schema 定义 (用于复杂类型)
final ApiSchema? schema;
/// 检查是否有嵌套属性
bool get hasNestedProperties => nestedProperties.isNotEmpty;
/// 检查是否有复杂 schema
bool get hasComplexSchema => schema != null;
}
/// API控制器信息
class ApiController {
const ApiController({
required this.name,
required this.description,
required this.paths,
});
/// 从路径列表创建ApiController
factory ApiController.fromPaths(String name, List<ApiPath> paths) {
return ApiController(name: name, description: name, paths: paths);
}
final String name;
final String description;
final List<ApiPath> paths;
}
/// 安全方案类型
enum SecuritySchemeType {
apiKey,
http,
oauth2,
openIdConnect,
}
extension SecuritySchemeTypeExtension on SecuritySchemeType {
String get value {
switch (this) {
case SecuritySchemeType.apiKey:
return 'apiKey';
case SecuritySchemeType.http:
return 'http';
case SecuritySchemeType.oauth2:
return 'oauth2';
case SecuritySchemeType.openIdConnect:
return 'openIdConnect';
}
}
static SecuritySchemeType fromString(String value) {
switch (value.toLowerCase()) {
case 'apikey':
return SecuritySchemeType.apiKey;
case 'http':
return SecuritySchemeType.http;
case 'oauth2':
return SecuritySchemeType.oauth2;
case 'openidconnect':
return SecuritySchemeType.openIdConnect;
default:
return SecuritySchemeType.apiKey;
}
}
}
/// API Key 位置
enum ApiKeyLocation {
query,
header,
cookie,
}
extension ApiKeyLocationExtension on ApiKeyLocation {
String get value {
switch (this) {
case ApiKeyLocation.query:
return 'query';
case ApiKeyLocation.header:
return 'header';
case ApiKeyLocation.cookie:
return 'cookie';
}
}
static ApiKeyLocation fromString(String value) {
switch (value.toLowerCase()) {
case 'query':
return ApiKeyLocation.query;
case 'header':
return ApiKeyLocation.header;
case 'cookie':
return ApiKeyLocation.cookie;
default:
return ApiKeyLocation.header;
}
}
}
/// OAuth2 流程类型
enum OAuth2FlowType {
authorizationCode,
implicit,
password,
clientCredentials,
}
extension OAuth2FlowTypeExtension on OAuth2FlowType {
String get value {
switch (this) {
case OAuth2FlowType.authorizationCode:
return 'authorizationCode';
case OAuth2FlowType.implicit:
return 'implicit';
case OAuth2FlowType.password:
return 'password';
case OAuth2FlowType.clientCredentials:
return 'clientCredentials';
}
}
static OAuth2FlowType fromString(String value) {
switch (value.toLowerCase()) {
case 'authorizationcode':
return OAuth2FlowType.authorizationCode;
case 'implicit':
return OAuth2FlowType.implicit;
case 'password':
return OAuth2FlowType.password;
case 'clientcredentials':
return OAuth2FlowType.clientCredentials;
default:
return OAuth2FlowType.authorizationCode;
}
}
}
/// OAuth2 流程配置
class OAuth2Flow {
const OAuth2Flow({
this.authorizationUrl,
this.tokenUrl,
this.refreshUrl,
this.scopes = const {},
});
/// 从 JSON 创建 OAuth2Flow
factory OAuth2Flow.fromJson(Map<String, dynamic> json) {
final scopesData = json['scopes'];
final Map<String, String> scopes;
if (scopesData is Map) {
scopes = scopesData
.map((key, value) => MapEntry(key.toString(), value.toString()));
} else {
scopes = {};
}
return OAuth2Flow(
authorizationUrl: json['authorizationUrl'] as String?,
tokenUrl: json['tokenUrl'] as String?,
refreshUrl: json['refreshUrl'] as String?,
scopes: scopes,
);
}
/// 授权 URL (用于 authorizationCode 和 implicit 流程)
final String? authorizationUrl;
/// 令牌 URL (用于 authorizationCode, password 和 clientCredentials 流程)
final String? tokenUrl;
/// 刷新 URL (可选)
final String? refreshUrl;
/// 可用的作用域
final Map<String, String> scopes;
/// 检查是否有授权 URL
bool get hasAuthorizationUrl =>
authorizationUrl != null && authorizationUrl!.isNotEmpty;
/// 检查是否有令牌 URL
bool get hasTokenUrl => tokenUrl != null && tokenUrl!.isNotEmpty;
/// 检查是否有刷新 URL
bool get hasRefreshUrl => refreshUrl != null && refreshUrl!.isNotEmpty;
/// 检查是否有作用域
bool get hasScopes => scopes.isNotEmpty;
}
/// OAuth2 流程集合
class OAuth2Flows {
const OAuth2Flows({
this.authorizationCode,
this.implicit,
this.password,
this.clientCredentials,
});
/// 从 JSON 创建 OAuth2Flows
factory OAuth2Flows.fromJson(Map<String, dynamic> json) {
return OAuth2Flows(
authorizationCode: json['authorizationCode'] != null
? OAuth2Flow.fromJson(
json['authorizationCode'] as Map<String, dynamic>,
)
: null,
implicit: json['implicit'] != null
? OAuth2Flow.fromJson(json['implicit'] as Map<String, dynamic>)
: null,
password: json['password'] != null
? OAuth2Flow.fromJson(json['password'] as Map<String, dynamic>)
: null,
clientCredentials: json['clientCredentials'] != null
? OAuth2Flow.fromJson(
json['clientCredentials'] as Map<String, dynamic>,
)
: null,
);
}
/// 授权码流程
final OAuth2Flow? authorizationCode;
/// 隐式流程
final OAuth2Flow? implicit;
/// 密码流程
final OAuth2Flow? password;
/// 客户端凭证流程
final OAuth2Flow? clientCredentials;
/// 获取所有可用的流程
List<OAuth2FlowType> get availableFlows {
final flows = <OAuth2FlowType>[];
if (authorizationCode != null) flows.add(OAuth2FlowType.authorizationCode);
if (implicit != null) flows.add(OAuth2FlowType.implicit);
if (password != null) flows.add(OAuth2FlowType.password);
if (clientCredentials != null) flows.add(OAuth2FlowType.clientCredentials);
return flows;
}
/// 检查是否有任何流程
bool get hasAnyFlow => availableFlows.isNotEmpty;
/// 获取指定类型的流程
OAuth2Flow? getFlow(OAuth2FlowType type) {
switch (type) {
case OAuth2FlowType.authorizationCode:
return authorizationCode;
case OAuth2FlowType.implicit:
return implicit;
case OAuth2FlowType.password:
return password;
case OAuth2FlowType.clientCredentials:
return clientCredentials;
}
}
}
/// 安全要求 (单个安全方案的要求)
class ApiSecurityRequirement {
const ApiSecurityRequirement({
this.requirements = const {},
});
/// 从 JSON 创建 ApiSecurityRequirement
factory ApiSecurityRequirement.fromJson(Map<String, dynamic> json) {
final requirements = <String, List<String>>{};
json.forEach((schemeName, scopes) {
if (scopes is List) {
requirements[schemeName] = List<String>.from(scopes);
} else {
requirements[schemeName] = [];
}
});
return ApiSecurityRequirement(requirements: requirements);
}
/// 安全方案名称到作用域列表的映射
final Map<String, List<String>> requirements;
/// 检查是否为空
bool get isEmpty => requirements.isEmpty;
/// 检查是否非空
bool get isNotEmpty => requirements.isNotEmpty;
/// 获取所有安全方案名称
List<String> get schemeNames => requirements.keys.toList();
/// 获取指定方案的作用域
List<String> getScopesForScheme(String schemeName) {
return requirements[schemeName] ?? [];
}
/// 检查是否包含指定的安全方案
bool hasScheme(String schemeName) {
return requirements.containsKey(schemeName);
}
}