Release 0.2.0
Complete OpenAPI 3.0/3.1 generation, diagnostics, lifecycle safety, enum and naming mappings, bilingual documentation, examples, and pub.dev release validation.
This commit is contained in:
@@ -1,26 +1,95 @@
|
||||
# koi_swagger_parser
|
||||
|
||||
`koi_swagger_parser` 是一个面向 Koi 生态的新一代 OpenAPI 解析与 Dart 代码生成器,目标不是继续修补旧式 Swagger 生成链,而是为未来的 `koi_blueprint_flutter` 提供一套更稳、更清晰、更可扩展的基础设施。
|
||||
[English](README.md) | [Chinese (Simplified)](README.zh-CN.md)
|
||||
|
||||
当前版本聚焦 Koi/FastAPI 场景,优先解决这几类问题:
|
||||
`koi_swagger_parser` is an OpenAPI 3.0/3.1 Dart and Flutter client generator
|
||||
for projects that use Dio, Retrofit, Freezed, and json_serializable. It
|
||||
separates document loading, `$ref` resolution, validation, an intermediate
|
||||
representation (IR), rendering, and safe incremental writes.
|
||||
|
||||
- OpenAPI 3.1 下 `anyOf[T, null]`、`type: [T, null]` 的 nullable 解析。
|
||||
- FastAPI `code/message/data` 成功 envelope 的识别与外部泛型包装。
|
||||
- `multipart/form-data` 请求体生成 `@MultiPart()` + `@Part()`,并按 preset 选择二进制类型。
|
||||
- `oneOf` / `anyOf` + discriminator 生成 sealed union wrapper。
|
||||
- 以 Koi 目录结构输出 `api/`、`api_models/request|result|shared|enums/`。
|
||||
- 兼容旧版 `generator_config.yaml`,平滑替换 `swagger_generator_flutter`。
|
||||
Generated code follows a Koi-oriented layout:
|
||||
|
||||
## 设计目标
|
||||
```text
|
||||
lib/src/
|
||||
api/
|
||||
api_client.dart
|
||||
index.dart
|
||||
v1/
|
||||
index.dart
|
||||
stores_api.dart
|
||||
api_models/
|
||||
api_models.dart
|
||||
request/
|
||||
result/
|
||||
shared/
|
||||
enums/
|
||||
```
|
||||
|
||||
- 解析层和 Koi 业务约束解耦:parser core 不硬编码 `BaseResult`。
|
||||
- 先产出中间表示(IR),再做 Koi 风格渲染,方便未来扩展到 `koi_blueprint_flutter`。
|
||||
- 默认以 Retrofit + Freezed 为输出基线,但 response wrapper 可配置。
|
||||
- 对 FastAPI/SQLAlchemy 风格服务端有良好适配,同时不把服务端补丁写死在生成器里。
|
||||
## Features
|
||||
|
||||
## 配置文件
|
||||
- Local JSON/YAML files, HTTP(S) documents, multiple root documents, and
|
||||
local, relative, file, and remote `$ref` values.
|
||||
- Component reference resolution, caching, cycle detection, deterministic
|
||||
conflict handling, and dependency tracking.
|
||||
- Retrofit APIs and Freezed/json_serializable request, response, shared,
|
||||
enum, and union models.
|
||||
- `enum_mappings`, legacy `enum_key_mappings`, `x-enumNames`,
|
||||
`x-enum-varnames`, `x-enum-values`, descriptions, and optional unknown enum
|
||||
fallback.
|
||||
- Model, property, parameter, operation, schema, type, and import mappings
|
||||
with collision diagnostics.
|
||||
- OpenAPI path, query, header, and cookie parameters, including supported
|
||||
style, explode, and `allowReserved` combinations.
|
||||
- JSON, text, multipart, form-urlencoded, and binary requests; byte and
|
||||
stream downloads.
|
||||
- Multiple successful responses, 204, `2XX`, `default` and error responses,
|
||||
response headers, and optional `HttpResponse<T>` results.
|
||||
- Required, nullable, and absent-value semantics; read-only/write-only
|
||||
projections; `additionalProperties`; `allOf`; and supported `oneOf`/`anyOf`
|
||||
forms.
|
||||
- Server variables and security metadata. Credentials remain the
|
||||
responsibility of the host application's Dio interceptor or provider.
|
||||
- Exact, glob, and regular-expression filters.
|
||||
- A locked manifest, SHA-256 change detection, safe stale-file cleanup,
|
||||
atomic writes, dry runs, and optional build_runner integration.
|
||||
|
||||
推荐使用 `koi_swagger_parser.yaml`:
|
||||
See the [compatibility matrix](doc/COMPATIBILITY.md) for precise boundaries.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Dart SDK `^3.11.0`.
|
||||
- An OpenAPI 3.0.x or 3.1.x document in JSON or YAML.
|
||||
- A host Dart or Flutter project configured for Retrofit, Freezed, and
|
||||
json_serializable when generating both APIs and models.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the generator as a development dependency in the host project:
|
||||
|
||||
```bash
|
||||
dart pub add --dev koi_swagger_parser
|
||||
```
|
||||
|
||||
For a typical generated client, add these host runtime dependencies:
|
||||
|
||||
```bash
|
||||
dart pub add dio retrofit freezed_annotation json_annotation
|
||||
```
|
||||
|
||||
Add the matching code-generation tools:
|
||||
|
||||
```bash
|
||||
dart pub add --dev build_runner retrofit_generator freezed json_serializable
|
||||
```
|
||||
|
||||
For Flutter projects, use `flutter pub add` in place of `dart pub add` if that
|
||||
is how the project manages dependencies.
|
||||
|
||||
## Quick start
|
||||
|
||||
1. Copy [the complete configuration template](koi_swagger_parser.example.yaml)
|
||||
to `koi_swagger_parser.yaml` in the host project.
|
||||
2. Set at least one OpenAPI source and the output directories:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
@@ -32,12 +101,6 @@ output:
|
||||
base_dir: ./lib/src
|
||||
api_dir: ./lib/src/api
|
||||
models_dir: ./lib/src/api_models
|
||||
included_tags:
|
||||
- auth
|
||||
- stores
|
||||
- materials
|
||||
split_by_tags: true
|
||||
extract_version_from_url: false
|
||||
|
||||
generation:
|
||||
preset:
|
||||
@@ -45,93 +108,265 @@ generation:
|
||||
api:
|
||||
enabled: true
|
||||
use_retrofit: true
|
||||
cancel_token_support: true
|
||||
client:
|
||||
class_name: DbaApiClient
|
||||
file_name: dba_api_client
|
||||
response_wrapper:
|
||||
mode: external_generic
|
||||
success_data_field: data
|
||||
detect_standard_envelope: true
|
||||
external_generic:
|
||||
import: package:dba_network/dba_network.dart
|
||||
class_name: BaseResult
|
||||
mode: passthrough
|
||||
models:
|
||||
enabled: true
|
||||
class_prefix: Koi
|
||||
default_non_nullable_list_to_empty: true
|
||||
json_serializable:
|
||||
checked: true
|
||||
explicit_to_json: true
|
||||
include_if_null: false
|
||||
|
||||
formatting:
|
||||
run_dart_format: true
|
||||
run_build_runner: true
|
||||
build_runner_mode: build
|
||||
```
|
||||
|
||||
也兼容旧版 `generator_config.yaml`:
|
||||
3. Preview the change set:
|
||||
|
||||
- `generation.api.base_result_import` 会自动映射为 `response_wrapper.mode = external_generic`。
|
||||
- `output.included_tags`、`generation.models.class_prefix` 等旧字段会继续生效。
|
||||
```bash
|
||||
dart run koi_swagger_parser generate --all --dry-run
|
||||
```
|
||||
|
||||
## 命令行
|
||||
4. Generate the client:
|
||||
|
||||
```bash
|
||||
dart run koi_swagger_parser generate --all
|
||||
```
|
||||
|
||||
常用参数:
|
||||
When `formatting.run_build_runner` is `false`, run build_runner separately:
|
||||
|
||||
- `--config`:指定配置文件路径。
|
||||
- `--cwd`:指定工作目录。
|
||||
- `--dry-run`:只解析与渲染,不落盘。
|
||||
- `--verbose`:打印更多诊断信息。
|
||||
|
||||
## 输出结构
|
||||
|
||||
默认会生成:
|
||||
|
||||
```text
|
||||
lib/src/
|
||||
api/
|
||||
dba_api_client.dart
|
||||
index.dart
|
||||
v1/
|
||||
index.dart
|
||||
auth_api.dart
|
||||
stores_api.dart
|
||||
api_models/
|
||||
api_models.dart
|
||||
request/
|
||||
result/
|
||||
shared/
|
||||
enums/
|
||||
```bash
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
```
|
||||
|
||||
## 当前能力
|
||||
## CLI usage
|
||||
|
||||
当前版本已经覆盖 Koi 当前最需要的能力:
|
||||
```text
|
||||
dart run koi_swagger_parser generate [options]
|
||||
|
||||
- OpenAPI 3.1 文档加载(本地 JSON/YAML、远程 URL、多 source merge)。
|
||||
- `object` / `array` / `enum` / `additionalProperties` / `allOf` 基础支持。
|
||||
- nullable query/path/body 字段解析。
|
||||
- FastAPI envelope 检测与 `BaseResult<T>` 外部包装。
|
||||
- `multipart/form-data` 生成 `MultipartFile` / `File` / `Uint8List` / `String` 等 preset 驱动参数。
|
||||
- `oneOf` / `anyOf` + discriminator 生成 sealed union wrapper,并可被其他模型继续引用。
|
||||
- Retrofit API 文件、Freezed 模型文件、分组 index 文件生成。
|
||||
--all, -a Generate all enabled resources. Kept for command compatibility.
|
||||
--config, -c Configuration file path.
|
||||
--cwd Host project working directory.
|
||||
--dry-run Parse and render without writing or deleting files.
|
||||
--watch Start build_runner watch after generation.
|
||||
--verbose, -v Print informational diagnostics and failure stack traces.
|
||||
```
|
||||
|
||||
下一阶段可以继续往前加:
|
||||
The loader searches the working directory for, in order:
|
||||
|
||||
- 更丰富的 multipart metadata(如 encoding / runtime fileName / contentType preset)。
|
||||
- 无 discriminator 的更强联合类型兜底策略。
|
||||
- 更多 preset(例如 Chopper / blueprint preset / 网络层集成 preset)。
|
||||
- 与 `koi_blueprint_flutter` 的一键联动模板。
|
||||
1. `koi_swagger_parser.yaml`
|
||||
2. `koi_swagger_parser.yml`
|
||||
3. `generator_config.yaml`
|
||||
4. `generator_config.yml`
|
||||
|
||||
## 开发与验证
|
||||
When `--config` points to a file outside the current directory and `--cwd` is
|
||||
not set, the configuration directory becomes the host project working
|
||||
directory. Relative sources and output paths are resolved against the
|
||||
configuration directory. `--cwd` changes where host-project commands run; it
|
||||
does not change those path semantics.
|
||||
|
||||
## Library usage
|
||||
|
||||
The same pipeline is available as a Dart API:
|
||||
|
||||
```dart
|
||||
import 'package:koi_swagger_parser/koi_swagger_parser.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
final result = await const KoiSwaggerParser().generate(
|
||||
configPath: 'koi_swagger_parser.yaml',
|
||||
dryRun: true,
|
||||
);
|
||||
|
||||
print('${result.operationsCount} operations');
|
||||
print('${result.modelsCount} models');
|
||||
for (final diagnostic in result.diagnostics) {
|
||||
print(diagnostic.format());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On diagnostic errors, generation throws `KoiDiagnosticException`. Automation
|
||||
should match diagnostic `code` values instead of message text. See
|
||||
[Diagnostics](doc/DIAGNOSTICS.md).
|
||||
|
||||
## Enum mappings
|
||||
|
||||
Enum wire values can be mapped to stable Dart member names in configuration:
|
||||
|
||||
```yaml
|
||||
generation:
|
||||
models:
|
||||
enum_unknown_fallback: true
|
||||
enum_mappings:
|
||||
OrderStatus:
|
||||
- value: pending
|
||||
name: waitingForPayment
|
||||
description: Waiting for payment
|
||||
- value: done
|
||||
name: completed
|
||||
description: Completed
|
||||
```
|
||||
|
||||
Map syntax is also accepted:
|
||||
|
||||
```yaml
|
||||
generation:
|
||||
models:
|
||||
enum_mappings:
|
||||
OrderStatus:
|
||||
pending: waitingForPayment
|
||||
done:
|
||||
name: completed
|
||||
description: Completed
|
||||
```
|
||||
|
||||
The legacy `enum_key_mappings` field remains supported. Name resolution uses
|
||||
this precedence:
|
||||
|
||||
1. `enum_mappings`
|
||||
2. `enum_key_mappings`
|
||||
3. `x-enumNames`, `x-enum-names`, or `x-enum-varnames`
|
||||
4. `x-enum-values`
|
||||
5. A name derived from the wire value
|
||||
|
||||
Descriptions use configuration first, then `x-enumDescriptions`,
|
||||
`x-enum-descriptions`, and `x-enum-values.description`. With
|
||||
`enum_unknown_fallback: true`, the generator adds a stable `unknown` member
|
||||
and applies `JsonKey.unknownEnumValue` to enum references.
|
||||
|
||||
## Naming and type mappings
|
||||
|
||||
```yaml
|
||||
generation:
|
||||
mappings:
|
||||
models:
|
||||
UserDto: User
|
||||
properties:
|
||||
UserDto:
|
||||
user_id: id
|
||||
parameters:
|
||||
getUser:
|
||||
user_id: id
|
||||
operations:
|
||||
getUser: fetchUser
|
||||
"DELETE /api/v1/users/{id}": removeUser
|
||||
schemas:
|
||||
Money:
|
||||
type: Money
|
||||
import: package:money2/money2.dart
|
||||
types:
|
||||
string:uuid:
|
||||
type: UuidValue
|
||||
import: package:uuid/uuid_value.dart
|
||||
integer:int64: int
|
||||
imports:
|
||||
UuidValue: package:uuid/uuid_value.dart
|
||||
```
|
||||
|
||||
Operation mapping keys accept either an `operationId` or `METHOD /path`.
|
||||
Schema mappings mark a schema as externally implemented, so no model is
|
||||
generated for it. The external type must provide the JSON conversion behavior
|
||||
required by its generated usage.
|
||||
|
||||
## Responses and transport
|
||||
|
||||
```yaml
|
||||
generation:
|
||||
api:
|
||||
cancel_token_support: true
|
||||
response_mode: http_response # body | http_response
|
||||
binary_response_mode: bytes # bytes | stream
|
||||
client:
|
||||
class_name: ApiClient
|
||||
file_name: api_client
|
||||
response_wrapper:
|
||||
mode: external_generic # passthrough | external_generic
|
||||
success_data_field: data
|
||||
detect_standard_envelope: true
|
||||
external_generic:
|
||||
import: package:your_network/your_network.dart
|
||||
class_name: BaseResult
|
||||
```
|
||||
|
||||
When Retrofit annotations cannot represent a supported matrix, label, cookie,
|
||||
deep-object, or reserved-character parameter, the generator emits a typed
|
||||
transport adapter in front of a private Retrofit delegate. A `default`
|
||||
response is always treated as an error response.
|
||||
|
||||
## Filtering and lifecycle
|
||||
|
||||
```yaml
|
||||
output:
|
||||
included_tags: [stores]
|
||||
excluded_paths: [/internal/**]
|
||||
included_operations: [getStore, "POST /api/v1/stores"]
|
||||
excluded_models: [Legacy*]
|
||||
clean_stale_files: true
|
||||
skip_if_unchanged: true
|
||||
manifest_path: ./.dart_tool/koi_swagger_parser/manifest.json
|
||||
```
|
||||
|
||||
Filters accept exact values, `*`, `?`, and `**` globs, or Dart regular
|
||||
expressions prefixed with `regex:`. Exclusions take precedence over
|
||||
inclusions. `included_models` can add otherwise unreferenced components as
|
||||
generation roots.
|
||||
|
||||
The manifest records only files owned by this generator. Stale cleanup removes
|
||||
an old generated file only when it is still inside the configured API/model
|
||||
roots and its content still matches the previous manifest hash. Modified files
|
||||
and handwritten neighbors are preserved.
|
||||
|
||||
## Limitations
|
||||
|
||||
The current release intentionally does not generate:
|
||||
|
||||
- Swagger/OpenAPI 2.0 clients.
|
||||
- Callbacks, webhooks, or Link Object call chains.
|
||||
- XML, Protobuf, or arbitrary custom codecs.
|
||||
- A complete JSON Schema 2020-12 implementation.
|
||||
- Server stubs, mock servers, or non-Dart targets.
|
||||
- A pluggable external renderer/template API.
|
||||
|
||||
Unsupported or lossy constructs produce structured warnings or errors; they
|
||||
are not silently presented as fully supported. Consult
|
||||
[Compatibility](doc/COMPATIBILITY.md) before adopting the generator for a new
|
||||
schema corpus.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Configuration reference](doc/CONFIGURATION.md)
|
||||
- [Compatibility matrix](doc/COMPATIBILITY.md)
|
||||
- [Diagnostics reference](doc/DIAGNOSTICS.md)
|
||||
- [Architecture](doc/ARCHITECTURE.md)
|
||||
- [Migration from swagger_generator_flutter](doc/MIGRATION.md)
|
||||
- [Changelog](CHANGELOG.md)
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
dart pub get
|
||||
dart format .
|
||||
dart analyze
|
||||
dart test
|
||||
dart run tool/verify_generated_package.dart
|
||||
```
|
||||
|
||||
架构设计说明见 `doc/ARCHITECTURE.md`。
|
||||
The final command creates a temporary Dart package, invokes this generator and
|
||||
its build_runner integration, and then runs analysis and runtime tests in the
|
||||
generated package.
|
||||
|
||||
## Support and contributing
|
||||
|
||||
Use [GitHub Issues](https://github.com/mrkoi-is/koi_swagger_parser/issues) for
|
||||
reproducible bug reports and feature requests. Include the generator version,
|
||||
Dart version, a minimal OpenAPI document, the relevant configuration, and
|
||||
diagnostic codes. Remove credentials and private service data before sharing a
|
||||
reproduction.
|
||||
|
||||
Contributions should include focused tests and update the compatibility matrix
|
||||
and configuration reference when behavior changes. Run the development checks
|
||||
above before opening a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
Reference in New Issue
Block a user