feat: init swagger project
This commit is contained in:
parent
696d789686
commit
b7c67fb9e6
|
@ -30,4 +30,5 @@ Thumbs.db
|
|||
|
||||
# Output files
|
||||
example/test_output/
|
||||
test_output_verification/
|
||||
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -5,12 +5,30 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.1.0] - 2025-09-30
|
||||
|
||||
### Added
|
||||
- **OpenAPI 3.0 Support** - Generate both Swagger 2.0 and OpenAPI 3.0 specifications
|
||||
- New `--spec-version` (`-s`) flag to choose between `swagger2.0` and `openapi3.0`
|
||||
- Automatic conversion from Swagger 2.0 to OpenAPI 3.0 format
|
||||
- Support for OpenAPI 3.0 features:
|
||||
- Server objects with complete URLs (instead of host/basePath)
|
||||
- Components/schemas (instead of definitions)
|
||||
- Components/securitySchemes (instead of securityDefinitions)
|
||||
- RequestBody separated from parameters
|
||||
- Integration with `github.com/getkin/kin-openapi` library for OpenAPI 3.0
|
||||
|
||||
### Changed
|
||||
- Updated CLI help text to reflect dual specification support
|
||||
- Enhanced Makefile examples to generate both Swagger 2.0 and OpenAPI 3.0
|
||||
- Version bumped to 1.1.0
|
||||
|
||||
## [1.0.0] - 2025-09-30
|
||||
|
||||
### Added
|
||||
- Initial release as standalone tool
|
||||
- Extracted from go-zero project and made independent
|
||||
- Support for converting go-zero `.api` files to OpenAPI 2.0 (Swagger) specification
|
||||
- Support for converting go-zero `.api` files to Swagger 2.0 specification
|
||||
- JSON and YAML output formats
|
||||
- Command-line interface using cobra
|
||||
- Support for all go-zero API features:
|
||||
|
|
16
Makefile
16
Makefile
|
@ -67,10 +67,20 @@ build-all: ## Build for all platforms
|
|||
GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe $(MAIN_PATH)
|
||||
@echo "✅ Multi-platform build complete"
|
||||
|
||||
example: build ## Generate example swagger files
|
||||
example: build ## Generate example swagger and openapi files
|
||||
@echo "Generating examples..."
|
||||
@mkdir -p example/test_output
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example -y
|
||||
@echo " - Swagger 2.0 (JSON)..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example_swagger2
|
||||
@echo " - Swagger 2.0 (YAML)..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example_swagger2 -y
|
||||
@echo " - OpenAPI 3.0 (JSON)..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example_openapi3 -s openapi3.0
|
||||
@echo " - OpenAPI 3.0 (YAML)..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example.api -d example/test_output -f example_openapi3 -s openapi3.0 -y
|
||||
@echo " - Chinese Swagger 2.0..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example_cn.api -d example/test_output -f example_cn_swagger2
|
||||
@echo " - Chinese OpenAPI 3.0..."
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) -a example/example_cn.api -d example/test_output -f example_cn_openapi3 -s openapi3.0
|
||||
@echo "✅ Examples generated in example/test_output/"
|
||||
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
# OpenAPI 3.0 使用指南
|
||||
|
||||
## 📚 概述
|
||||
|
||||
從 v1.1.0 開始,`go-doc` 支援生成 **Swagger 2.0** 和 **OpenAPI 3.0** 兩種規格。
|
||||
|
||||
## 🚀 快速開始
|
||||
|
||||
### 生成 Swagger 2.0(預設)
|
||||
```bash
|
||||
go-doc -a example/example.api -d output
|
||||
```
|
||||
|
||||
### 生成 OpenAPI 3.0
|
||||
```bash
|
||||
go-doc -a example/example.api -d output -s openapi3.0
|
||||
```
|
||||
|
||||
### 生成 OpenAPI 3.0 YAML
|
||||
```bash
|
||||
go-doc -a example/example.api -d output -s openapi3.0 -y
|
||||
```
|
||||
|
||||
## 🔍 兩種規格的差異
|
||||
|
||||
### 1. 版本宣告
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"swagger": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"openapi": "3.0.3"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 伺服器定義
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"host": "example.com",
|
||||
"basePath": "/v1",
|
||||
"schemes": ["http", "https"]
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"servers": [
|
||||
{"url": "http://example.com/v1"},
|
||||
{"url": "https://example.com/v1"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 資料模型定義
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"definitions": {
|
||||
"User": {
|
||||
"type": "object",
|
||||
"properties": {...}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
"User": {
|
||||
"type": "object",
|
||||
"properties": {...}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 安全定義
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"securityDefinitions": {
|
||||
"apiKey": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"apiKey": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 請求內容
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "body",
|
||||
"schema": {"$ref": "#/definitions/User"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/User"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 回應內容
|
||||
**Swagger 2.0:**
|
||||
```json
|
||||
{
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"schema": {"$ref": "#/definitions/User"}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OpenAPI 3.0:**
|
||||
```json
|
||||
{
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/User"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 使用範例
|
||||
|
||||
### 完整範例:生成多種格式
|
||||
|
||||
```bash
|
||||
# Swagger 2.0 JSON
|
||||
go-doc -a example/example.api -d output -f api-swagger2
|
||||
|
||||
# Swagger 2.0 YAML
|
||||
go-doc -a example/example.api -d output -f api-swagger2 -y
|
||||
|
||||
# OpenAPI 3.0 JSON
|
||||
go-doc -a example/example.api -d output -f api-openapi3 -s openapi3.0
|
||||
|
||||
# OpenAPI 3.0 YAML
|
||||
go-doc -a example/example.api -d output -f api-openapi3 -s openapi3.0 -y
|
||||
```
|
||||
|
||||
### 使用 Makefile
|
||||
|
||||
```bash
|
||||
# 生成所有範例(包含 Swagger 2.0 和 OpenAPI 3.0)
|
||||
make example
|
||||
|
||||
# 僅編譯
|
||||
make build
|
||||
|
||||
# 測試
|
||||
make test
|
||||
```
|
||||
|
||||
## ⚙️ 轉換邏輯
|
||||
|
||||
`go-doc` 的內部轉換流程:
|
||||
|
||||
1. **解析 `.api` 檔案** → 使用 go-zero 的 API parser
|
||||
2. **生成 Swagger 2.0 結構** → 基礎的 OpenAPI 2.0 規格
|
||||
3. **轉換為 OpenAPI 3.0**(如果指定)→ 自動轉換:
|
||||
- `definitions` → `components/schemas`
|
||||
- `securityDefinitions` → `components/securitySchemes`
|
||||
- `host/basePath/schemes` → `servers`
|
||||
- body parameters → `requestBody`
|
||||
4. **序列化輸出** → JSON 或 YAML 格式
|
||||
|
||||
## 🔧 技術細節
|
||||
|
||||
### 使用的函式庫
|
||||
- **Swagger 2.0**: `github.com/go-openapi/spec`
|
||||
- **OpenAPI 3.0**: `github.com/getkin/kin-openapi`
|
||||
|
||||
### 轉換函數
|
||||
- `spec2Swagger()`: 將 go-zero API 轉為 Swagger 2.0
|
||||
- `convertSwagger2ToOpenAPI3()`: 將 Swagger 2.0 轉為 OpenAPI 3.0
|
||||
|
||||
### 型別轉換
|
||||
```go
|
||||
// Swagger 2.0 Type
|
||||
type: "string"
|
||||
|
||||
// OpenAPI 3.0 Type
|
||||
type: &Types{"string"}
|
||||
```
|
||||
|
||||
## 📖 參考資源
|
||||
|
||||
- [OpenAPI 3.0 Specification](https://spec.openapis.org/oas/v3.0.3)
|
||||
- [Swagger 2.0 Specification](https://swagger.io/specification/v2/)
|
||||
- [go-zero API 規範](https://go-zero.dev/docs/tutorials)
|
||||
|
||||
## ❓ 常見問題
|
||||
|
||||
### Q: 應該使用哪個版本?
|
||||
**A:**
|
||||
- **Swagger 2.0**: 如果需要與舊工具或系統相容
|
||||
- **OpenAPI 3.0**: 推薦使用,功能更強大且為現代標準
|
||||
|
||||
### Q: 可以同時生成兩種格式嗎?
|
||||
**A:** 可以!執行兩次命令即可:
|
||||
```bash
|
||||
go-doc -a api.api -d out -f api-v2
|
||||
go-doc -a api.api -d out -f api-v3 -s openapi3.0
|
||||
```
|
||||
|
||||
### Q: 轉換有什麼限制嗎?
|
||||
**A:** 大部分功能都能完整轉換。唯一差異是:
|
||||
- OpenAPI 3.0 的 server URLs 會根據 schemes 自動生成多個
|
||||
- 某些 Swagger 2.0 特定的擴展可能不會轉換
|
||||
|
||||
### Q: 如何驗證生成的文檔?
|
||||
**A:** 可以使用以下工具:
|
||||
- **Swagger Editor**: https://editor.swagger.io/
|
||||
- **Swagger UI**: 本地運行或線上版本
|
||||
- **OpenAPI Generator**: 生成客戶端/伺服器代碼來驗證
|
||||
|
||||
## 🎉 總結
|
||||
|
||||
`go-doc` 現在支援:
|
||||
- ✅ Swagger 2.0 (OpenAPI 2.0)
|
||||
- ✅ OpenAPI 3.0
|
||||
- ✅ JSON 和 YAML 輸出
|
||||
- ✅ 自動轉換
|
||||
- ✅ 完整的 go-zero API 特性支援
|
||||
|
||||
開始使用:
|
||||
```bash
|
||||
go-doc -a your-api.api -d output -s openapi3.0
|
||||
```
|
|
@ -0,0 +1,103 @@
|
|||
# 🚀 Quick Start Guide
|
||||
|
||||
## 安裝
|
||||
|
||||
### 從原始碼編譯
|
||||
```bash
|
||||
git clone <your-repo>
|
||||
cd go-doc
|
||||
make build
|
||||
```
|
||||
|
||||
編譯完成後,執行檔位於 `bin/go-doc`
|
||||
|
||||
## 基本使用
|
||||
|
||||
### 1️⃣ 生成 Swagger 2.0(預設)
|
||||
```bash
|
||||
./bin/go-doc -a example/example.api -d output
|
||||
```
|
||||
|
||||
生成檔案:`output/example.json`
|
||||
|
||||
### 2️⃣ 生成 OpenAPI 3.0
|
||||
```bash
|
||||
./bin/go-doc -a example/example.api -d output -s openapi3.0
|
||||
```
|
||||
|
||||
### 3️⃣ 生成 YAML 格式
|
||||
```bash
|
||||
./bin/go-doc -a example/example.api -d output -y
|
||||
```
|
||||
|
||||
### 4️⃣ 自訂檔名
|
||||
```bash
|
||||
./bin/go-doc -a example/example.api -d output -f my-api
|
||||
```
|
||||
|
||||
## 常用命令組合
|
||||
|
||||
```bash
|
||||
# Swagger 2.0 JSON
|
||||
./bin/go-doc -a api.api -d docs
|
||||
|
||||
# Swagger 2.0 YAML
|
||||
./bin/go-doc -a api.api -d docs -y
|
||||
|
||||
# OpenAPI 3.0 JSON
|
||||
./bin/go-doc -a api.api -d docs -s openapi3.0
|
||||
|
||||
# OpenAPI 3.0 YAML
|
||||
./bin/go-doc -a api.api -d docs -s openapi3.0 -y
|
||||
```
|
||||
|
||||
## 完整參數
|
||||
|
||||
```
|
||||
-a, --api string API 檔案路徑(必要)
|
||||
-d, --dir string 輸出目錄(必要)
|
||||
-f, --filename string 輸出檔名(不含副檔名)
|
||||
-s, --spec-version string 規格版本:swagger2.0 或 openapi3.0(預設:swagger2.0)
|
||||
-y, --yaml 生成 YAML 格式(預設:JSON)
|
||||
-h, --help 顯示說明
|
||||
-v, --version 顯示版本
|
||||
```
|
||||
|
||||
## 使用 Makefile
|
||||
|
||||
```bash
|
||||
# 編譯
|
||||
make build
|
||||
|
||||
# 生成範例
|
||||
make example
|
||||
|
||||
# 清理
|
||||
make clean
|
||||
|
||||
# 運行測試
|
||||
make test
|
||||
|
||||
# 查看所有命令
|
||||
make help
|
||||
```
|
||||
|
||||
## 測試所有格式
|
||||
|
||||
```bash
|
||||
./test_all_formats.sh
|
||||
```
|
||||
|
||||
## 查看範例
|
||||
|
||||
生成的範例檔案位於:
|
||||
- `example/test_output/example_swagger2.json` - Swagger 2.0 JSON
|
||||
- `example/test_output/example_swagger2.yaml` - Swagger 2.0 YAML
|
||||
- `example/test_output/example_openapi3.json` - OpenAPI 3.0 JSON
|
||||
- `example/test_output/example_openapi3.yaml` - OpenAPI 3.0 YAML
|
||||
|
||||
## 更多資訊
|
||||
|
||||
- 完整文檔:`README.md`
|
||||
- OpenAPI 3.0 指南:`OPENAPI3_GUIDE.md`
|
||||
- 版本變更:`CHANGELOG.md`
|
55
README.md
55
README.md
|
@ -3,20 +3,21 @@
|
|||
[](https://go.dev/)
|
||||
[](LICENSE)
|
||||
|
||||
**go-doc** is a standalone tool that converts [go-zero](https://github.com/zeromicro/go-zero) `.api` files into OpenAPI 2.0 (Swagger) specification.
|
||||
**go-doc** is a standalone tool that converts [go-zero](https://github.com/zeromicro/go-zero) `.api` files into OpenAPI specifications.
|
||||
|
||||
Originally part of the go-zero project, go-doc is now an independent, easy-to-use command-line tool for generating API documentation.
|
||||
Originally part of the go-zero project, go-doc is now an independent, easy-to-use command-line tool for generating API documentation in both **Swagger 2.0** and **OpenAPI 3.0** formats.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🚀 **Standalone Binary** - No dependencies on go-zero runtime
|
||||
- 📝 **Full Swagger 2.0 Support** - Complete OpenAPI specification generation
|
||||
- 📝 **Dual Specification Support** - Generate both **Swagger 2.0** and **OpenAPI 3.0** formats
|
||||
- 🎯 **Rich Type Support** - Handles structs, arrays, maps, pointers, and nested types
|
||||
- 🏷️ **Tag-based Configuration** - Support for `json`, `form`, `path`, `header` tags
|
||||
- 📊 **Advanced Validations** - Range, enum, default, example values
|
||||
- 🔐 **Security Definitions** - Custom authentication configurations
|
||||
- 📦 **Multiple Output Formats** - JSON or YAML output
|
||||
- 🎨 **Definition References** - Optional use of Swagger definitions for cleaner output
|
||||
- 🎨 **Definition References** - Optional use of definitions/schemas for cleaner output
|
||||
- 🔄 **Automatic Conversion** - Seamless conversion from Swagger 2.0 to OpenAPI 3.0
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
|
@ -39,12 +40,18 @@ go install github.com/danielchan-25/go-doc/cmd/go-doc@latest
|
|||
### Basic Usage
|
||||
|
||||
```bash
|
||||
# Generate JSON swagger file
|
||||
# Generate Swagger 2.0 (default)
|
||||
go-doc -a example/example.api -d output
|
||||
|
||||
# Generate YAML swagger file
|
||||
# Generate OpenAPI 3.0
|
||||
go-doc -a example/example.api -d output -s openapi3.0
|
||||
|
||||
# Generate YAML format
|
||||
go-doc -a example/example.api -d output -y
|
||||
|
||||
# Generate OpenAPI 3.0 in YAML
|
||||
go-doc -a example/example.api -d output -s openapi3.0 -y
|
||||
|
||||
# Specify custom filename
|
||||
go-doc -a example/example.api -d output -f my-api
|
||||
```
|
||||
|
@ -53,14 +60,38 @@ go-doc -a example/example.api -d output -f my-api
|
|||
|
||||
```
|
||||
Flags:
|
||||
-a, --api string API file path (required)
|
||||
-d, --dir string Output directory (required)
|
||||
-f, --filename string Output filename without extension (optional, defaults to API filename)
|
||||
-h, --help help for go-doc
|
||||
-v, --version version for go-doc
|
||||
-y, --yaml Generate YAML format instead of JSON
|
||||
-a, --api string API file path (required)
|
||||
-d, --dir string Output directory (required)
|
||||
-f, --filename string Output filename without extension (optional, defaults to API filename)
|
||||
-h, --help help for go-doc
|
||||
-s, --spec-version string OpenAPI specification version: swagger2.0 or openapi3.0 (default "swagger2.0")
|
||||
-v, --version version for go-doc
|
||||
-y, --yaml Generate YAML format instead of JSON
|
||||
```
|
||||
|
||||
### Specification Versions
|
||||
|
||||
#### Swagger 2.0 (Default)
|
||||
```bash
|
||||
go-doc -a example/example.api -d output
|
||||
# or explicitly
|
||||
go-doc -a example/example.api -d output -s swagger2.0
|
||||
```
|
||||
|
||||
#### OpenAPI 3.0
|
||||
```bash
|
||||
go-doc -a example/example.api -d output -s openapi3.0
|
||||
```
|
||||
|
||||
**Key Differences:**
|
||||
- **Swagger 2.0**: Uses `host`, `basePath`, `schemes` at root level
|
||||
- **OpenAPI 3.0**: Uses `servers` array with complete URLs
|
||||
- **Swagger 2.0**: Definitions under `definitions`
|
||||
- **OpenAPI 3.0**: Schemas under `components/schemas`
|
||||
- **Swagger 2.0**: Security definitions under `securityDefinitions`
|
||||
- **OpenAPI 3.0**: Security schemes under `components/securitySchemes`
|
||||
- **OpenAPI 3.0**: Request bodies are separated from parameters
|
||||
|
||||
## 📖 API File Format
|
||||
|
||||
### Basic Structure
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
commit = "dev"
|
||||
date = "unknown"
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ func main() {
|
|||
rootCmd := &cobra.Command{
|
||||
Use: "go-doc",
|
||||
Short: "Generate Swagger documentation from go-zero API files",
|
||||
Long: `go-doc is a tool that converts go-zero .api files into OpenAPI 2.0 (Swagger) specification.`,
|
||||
Long: `go-doc is a tool that converts go-zero .api files into OpenAPI specifications (Swagger 2.0 or OpenAPI 3.0).`,
|
||||
Version: fmt.Sprintf("%s (commit: %s, built at: %s)", version, commit, date),
|
||||
RunE: swagger.Command,
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ func main() {
|
|||
rootCmd.Flags().StringVarP(&swagger.VarStringDir, "dir", "d", "", "Output directory (required)")
|
||||
rootCmd.Flags().StringVarP(&swagger.VarStringFilename, "filename", "f", "", "Output filename without extension (optional, defaults to API filename)")
|
||||
rootCmd.Flags().BoolVarP(&swagger.VarBoolYaml, "yaml", "y", false, "Generate YAML format instead of JSON")
|
||||
rootCmd.Flags().StringVarP(&swagger.VarStringSpecVersion, "spec-version", "s", "swagger2.0", "OpenAPI specification version: swagger2.0 or openapi3.0")
|
||||
|
||||
if err := rootCmd.MarkFlagRequired("api"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "演示 API" // 对应 swagger 的 title
|
||||
description: "演示 api 生成 swagger 文件的 api 完整写法" // 对应 swagger 的 description
|
||||
version: "v1" // 对应 swagger 的 version
|
||||
termsOfService: "https://github.com/zeromicro/go-zero" // 对应 swagger 的 termsOfService
|
||||
contactName: "keson.an" // 对应 swagger 的 contactName
|
||||
contactURL: "https://github.com/zeromicro/go-zero" // 对应 swagger 的 contactURL
|
||||
contactEmail: "example@gmail.com" // 对应 swagger 的 contactEmail
|
||||
licenseName: "MIT" // 对应 swagger 的 licenseName
|
||||
licenseURL: "https://github.com/zeromicro/go-zero" // 对应 swagger 的 licenseURL
|
||||
consumes: "application/json" // 对应 swagger 的 consumes,不填默认为 application/json
|
||||
produces: "application/json" // 对应 swagger 的 produces,不填默认为 application/json
|
||||
schemes: "http,https" // 对应 swagger 的 schemes,不填默认为 https
|
||||
host: "example.com" // 对应 swagger 的 host,不填默认为 127.0.0.1
|
||||
basePath: "/v1" // 对应 swagger 的 basePath,不填默认为 /
|
||||
wrapCodeMsg: true // 是否用 code-msg 通用响应体,如果开启,则以格式 {"code":0,"msg":"OK","data":$data} 包括响应体
|
||||
bizCodeEnumDescription: "1001-未登录<br>1002-无权限操作" // 全局业务错误码枚举描述,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 时生效
|
||||
// securityDefinitionsFromJson 为自定义鉴权配置,json 内容将直接放入 swagger 的 securityDefinitions 中,
|
||||
// 格式参考 https://swagger.io/specification/v2/#security-definitions-object
|
||||
// 在 api 的 @server 中可声明 authType 来指定其路由使用的鉴权类型
|
||||
securityDefinitionsFromJson: `{"apiKey":{"description":"apiKey 类型鉴权自定义","type":"apiKey","name":"x-api-key","in":"header"}}`
|
||||
useDefinitions: true// 开启声明将生成models 进行关联,definitions 仅对响应体和 json 请求体生效
|
||||
)
|
||||
|
||||
type (
|
||||
QueryReq {
|
||||
Id int `form:"id,range=[1:10000],example=10"`
|
||||
Name string `form:"name,example=keson.an"`
|
||||
Avatar string `form:"avatar,optional,example=https://example.com/avatar.png"`
|
||||
}
|
||||
QueryResp {
|
||||
Id int `json:"id,example=10"`
|
||||
Name string `json:"name,example=keson.an"`
|
||||
}
|
||||
// 錯誤響應
|
||||
ErrorResponse struct {
|
||||
Code string `json:"code" swagger:"description:錯誤代碼"`
|
||||
Message string `json:"message" swagger:"description:錯誤訊息"`
|
||||
Details string `json:"details,omitempty" swagger:"description:詳細信息"`
|
||||
}
|
||||
|
||||
// 具體錯誤類型
|
||||
ValidationError struct {
|
||||
Code string `json:"code" swagger:"description:驗證錯誤代碼"`
|
||||
Message string `json:"message" swagger:"description:驗證錯誤訊息"`
|
||||
Fields []string `json:"fields" swagger:"description:錯誤字段列表"`
|
||||
}
|
||||
|
||||
InsufficientStockError struct {
|
||||
Code string `json:"code" swagger:"description:庫存不足錯誤代碼"`
|
||||
Message string `json:"message" swagger:"description:庫存不足錯誤訊息"`
|
||||
ProductID string `json:"product_id" swagger:"description:商品ID"`
|
||||
Required int `json:"required" swagger:"description:需要數量"`
|
||||
Available int `json:"available" swagger:"description:可用數量"`
|
||||
}
|
||||
|
||||
InvalidPaymentError struct {
|
||||
Code string `json:"code" swagger:"description:支付錯誤代碼"`
|
||||
Message string `json:"message" swagger:"description:支付錯誤訊息"`
|
||||
Method string `json:"method" swagger:"description:支付方式"`
|
||||
}
|
||||
|
||||
UnauthorizedError struct {
|
||||
Code string `json:"code" swagger:"description:未授權錯誤代碼"`
|
||||
Message string `json:"message" swagger:"description:未授權錯誤訊息"`
|
||||
Reason string `json:"reason" swagger:"description:未授權原因"`
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
tags: "query 演示" // 对应 swagger 的 tags,可以对 swagger 中的 api 进行分组
|
||||
summary: "query 类型接口集合" // 对应 swagger 的 summary
|
||||
prefix: v1
|
||||
authType: apiKey // 指定该路由使用的鉴权类型,值为 securityDefinitionsFromJson 中定义的名称
|
||||
group:"demo"
|
||||
)
|
||||
service Swagger {
|
||||
@doc (
|
||||
description: "query 接口"
|
||||
bizCodeEnumDescription: " 1003-用不存在<br>1004-非法操作" // 接口级别业务错误码枚举描述,会覆盖全局的业务错误码,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 且 useDefinitions 为 false 时生效
|
||||
)
|
||||
/*
|
||||
@respdoc-201 (QueryResp) // 創建成功
|
||||
@respdoc-400 (
|
||||
300101: (ValidationError) 參數驗證失敗
|
||||
300102: (InsufficientStockError) 庫存不足
|
||||
300103: (InvalidPaymentError) 支付方式無效
|
||||
) // 客戶端錯誤
|
||||
@respdoc-401 (UnauthorizedError) // 未授權
|
||||
@respdoc-500 (ErrorResponse) // 服務器錯誤
|
||||
*/
|
||||
@handler query
|
||||
get /query (QueryReq) returns (QueryResp)
|
||||
}
|
4
go.mod
4
go.mod
|
@ -3,6 +3,7 @@ module go-doc
|
|||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/getkin/kin-openapi v0.128.0
|
||||
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
|
@ -18,8 +19,11 @@ require (
|
|||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/gookit/color v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/invopop/yaml v0.3.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
|
|
16
go.sum
16
go.sum
|
@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
|
||||
github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
|
@ -11,12 +13,16 @@ github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e h1:auobAirzhPsL
|
|||
github.com/go-openapi/spec v0.21.1-0.20250328170532-a3928469592e/go.mod h1:NAKTe9SplQBxIUlHlsuId1jk1I7bWTVV/2q/GtdRi6g=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
|
||||
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
|
||||
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
|
||||
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
|
||||
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
@ -25,10 +31,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
|
@ -37,6 +47,8 @@ github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
|||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/zeromicro/go-zero v1.9.0 h1:hlVtQCSHPszQdcwZTawzGwTej1G2mhHybYzMRLuwCt4=
|
||||
|
|
|
@ -25,6 +25,9 @@ var (
|
|||
|
||||
// VarBoolYaml specifies whether to generate a YAML file.
|
||||
VarBoolYaml bool
|
||||
|
||||
// VarStringSpecVersion specifies the OpenAPI specification version (swagger2.0 or openapi3.0).
|
||||
VarStringSpecVersion string
|
||||
)
|
||||
|
||||
func Command(_ *cobra.Command, _ []string) error {
|
||||
|
@ -36,6 +39,11 @@ func Command(_ *cobra.Command, _ []string) error {
|
|||
return errors.New("missing -dir")
|
||||
}
|
||||
|
||||
// Validate spec version
|
||||
if VarStringSpecVersion != "swagger2.0" && VarStringSpecVersion != "openapi3.0" {
|
||||
return errors.New("spec-version must be either 'swagger2.0' or 'openapi3.0'")
|
||||
}
|
||||
|
||||
api, err := parser.Parse(VarStringAPI, "")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -46,13 +54,30 @@ func Command(_ *cobra.Command, _ []string) error {
|
|||
if err := api.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
swagger, err := spec2Swagger(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.MarshalIndent(swagger, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
var data []byte
|
||||
|
||||
if VarStringSpecVersion == "openapi3.0" {
|
||||
// Generate OpenAPI 3.0
|
||||
swagger2, err := spec2Swagger(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
openapi3Doc := convertSwagger2ToOpenAPI3(swagger2)
|
||||
data, err = json.MarshalIndent(openapi3Doc, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Generate Swagger 2.0 (default)
|
||||
swagger, err := spec2Swagger(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err = json.MarshalIndent(swagger, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = util.MkdirIfNotExist(VarStringDir)
|
||||
|
@ -81,7 +106,7 @@ func Command(_ *cobra.Command, _ []string) error {
|
|||
return os.WriteFile(filePath, data, 0644)
|
||||
}
|
||||
|
||||
// generate json swagger file
|
||||
// generate json file
|
||||
filePath := filepath.Join(VarStringDir, filename+".json")
|
||||
return os.WriteFile(filePath, data, 0644)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
package swagger
|
||||
|
||||
import (
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// convertSwagger2ToOpenAPI3 converts a Swagger 2.0 spec to OpenAPI 3.0
|
||||
func convertSwagger2ToOpenAPI3(swagger2 *spec.Swagger) *openapi3.T {
|
||||
openapi3Doc := &openapi3.T{
|
||||
OpenAPI: "3.0.3",
|
||||
Info: &openapi3.Info{
|
||||
Title: swagger2.Info.Title,
|
||||
Description: swagger2.Info.Description,
|
||||
TermsOfService: swagger2.Info.TermsOfService,
|
||||
Version: swagger2.Info.Version,
|
||||
},
|
||||
Servers: openapi3.Servers{},
|
||||
Paths: &openapi3.Paths{},
|
||||
}
|
||||
|
||||
// Convert extensions
|
||||
if swagger2.Info.VendorExtensible.Extensions != nil {
|
||||
openapi3Doc.Extensions = make(map[string]interface{})
|
||||
for k, v := range swagger2.Info.VendorExtensible.Extensions {
|
||||
openapi3Doc.Extensions[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Add extensions from swagger root
|
||||
if swagger2.Extensions != nil {
|
||||
if openapi3Doc.Extensions == nil {
|
||||
openapi3Doc.Extensions = make(map[string]interface{})
|
||||
}
|
||||
for k, v := range swagger2.Extensions {
|
||||
openapi3Doc.Extensions[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Contact
|
||||
if swagger2.Info.Contact != nil {
|
||||
openapi3Doc.Info.Contact = &openapi3.Contact{
|
||||
Name: swagger2.Info.Contact.Name,
|
||||
URL: swagger2.Info.Contact.URL,
|
||||
Email: swagger2.Info.Contact.Email,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert License
|
||||
if swagger2.Info.License != nil {
|
||||
openapi3Doc.Info.License = &openapi3.License{
|
||||
Name: swagger2.Info.License.Name,
|
||||
URL: swagger2.Info.License.URL,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Servers from host, basePath, and schemes
|
||||
if len(swagger2.Host) > 0 || len(swagger2.BasePath) > 0 {
|
||||
schemes := swagger2.Schemes
|
||||
if len(schemes) == 0 {
|
||||
schemes = []string{"https"}
|
||||
}
|
||||
for _, scheme := range schemes {
|
||||
serverURL := scheme + "://"
|
||||
if len(swagger2.Host) > 0 {
|
||||
serverURL += swagger2.Host
|
||||
} else {
|
||||
serverURL += "localhost"
|
||||
}
|
||||
if len(swagger2.BasePath) > 0 && swagger2.BasePath != "/" {
|
||||
serverURL += swagger2.BasePath
|
||||
}
|
||||
openapi3Doc.Servers = append(openapi3Doc.Servers, &openapi3.Server{
|
||||
URL: serverURL,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Paths
|
||||
openapi3Doc.Paths = openapi3.NewPaths()
|
||||
for path, pathItem := range swagger2.Paths.Paths {
|
||||
newPathItem := &openapi3.PathItem{}
|
||||
|
||||
if pathItem.Get != nil {
|
||||
newPathItem.Get = convertOperation(pathItem.Get)
|
||||
}
|
||||
if pathItem.Post != nil {
|
||||
newPathItem.Post = convertOperation(pathItem.Post)
|
||||
}
|
||||
if pathItem.Put != nil {
|
||||
newPathItem.Put = convertOperation(pathItem.Put)
|
||||
}
|
||||
if pathItem.Delete != nil {
|
||||
newPathItem.Delete = convertOperation(pathItem.Delete)
|
||||
}
|
||||
if pathItem.Patch != nil {
|
||||
newPathItem.Patch = convertOperation(pathItem.Patch)
|
||||
}
|
||||
if pathItem.Head != nil {
|
||||
newPathItem.Head = convertOperation(pathItem.Head)
|
||||
}
|
||||
if pathItem.Options != nil {
|
||||
newPathItem.Options = convertOperation(pathItem.Options)
|
||||
}
|
||||
|
||||
openapi3Doc.Paths.Set(path, newPathItem)
|
||||
}
|
||||
|
||||
// Convert Definitions to Components/Schemas
|
||||
if len(swagger2.Definitions) > 0 {
|
||||
openapi3Doc.Components = &openapi3.Components{
|
||||
Schemas: make(openapi3.Schemas),
|
||||
}
|
||||
for name, schema := range swagger2.Definitions {
|
||||
openapi3Doc.Components.Schemas[name] = convertSchemaToSchemaRef(&schema)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert SecurityDefinitions to Components/SecuritySchemes
|
||||
if len(swagger2.SecurityDefinitions) > 0 {
|
||||
if openapi3Doc.Components == nil {
|
||||
openapi3Doc.Components = &openapi3.Components{}
|
||||
}
|
||||
openapi3Doc.Components.SecuritySchemes = make(openapi3.SecuritySchemes)
|
||||
for name, secDef := range swagger2.SecurityDefinitions {
|
||||
openapi3Doc.Components.SecuritySchemes[name] = convertSecurityScheme(secDef)
|
||||
}
|
||||
}
|
||||
|
||||
return openapi3Doc
|
||||
}
|
||||
|
||||
// convertOperation converts a Swagger 2.0 operation to OpenAPI 3.0
|
||||
func convertOperation(op *spec.Operation) *openapi3.Operation {
|
||||
newOp := &openapi3.Operation{
|
||||
Tags: op.Tags,
|
||||
Summary: op.Summary,
|
||||
Description: op.Description,
|
||||
OperationID: op.ID,
|
||||
Parameters: openapi3.Parameters{},
|
||||
Responses: &openapi3.Responses{},
|
||||
Deprecated: op.Deprecated,
|
||||
}
|
||||
|
||||
// Convert ExternalDocs
|
||||
if op.ExternalDocs != nil {
|
||||
newOp.ExternalDocs = &openapi3.ExternalDocs{
|
||||
Description: op.ExternalDocs.Description,
|
||||
URL: op.ExternalDocs.URL,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Parameters and RequestBody
|
||||
var bodyParam *spec.Parameter
|
||||
for _, param := range op.Parameters {
|
||||
if param.In == "body" {
|
||||
bodyParam = ¶m
|
||||
} else {
|
||||
newOp.Parameters = append(newOp.Parameters, convertParameter(¶m))
|
||||
}
|
||||
}
|
||||
|
||||
// Convert body parameter to requestBody
|
||||
if bodyParam != nil {
|
||||
newOp.RequestBody = &openapi3.RequestBodyRef{
|
||||
Value: convertBodyParameter(bodyParam, op.Consumes),
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Responses
|
||||
for code, response := range op.Responses.StatusCodeResponses {
|
||||
newOp.Responses.Set(intToString(code), convertResponse(&response, op.Produces))
|
||||
}
|
||||
|
||||
// Convert Security
|
||||
if len(op.Security) > 0 {
|
||||
newOp.Security = &openapi3.SecurityRequirements{}
|
||||
for _, sec := range op.Security {
|
||||
secReq := openapi3.SecurityRequirement{}
|
||||
for key, scopes := range sec {
|
||||
secReq[key] = scopes
|
||||
}
|
||||
*newOp.Security = append(*newOp.Security, secReq)
|
||||
}
|
||||
}
|
||||
|
||||
return newOp
|
||||
}
|
||||
|
||||
// convertParameter converts a Swagger 2.0 parameter to OpenAPI 3.0
|
||||
func convertParameter(param *spec.Parameter) *openapi3.ParameterRef {
|
||||
schema := &openapi3.Schema{
|
||||
Format: param.Format,
|
||||
Default: param.Default,
|
||||
}
|
||||
if param.Type != "" {
|
||||
schema.Type = &openapi3.Types{param.Type}
|
||||
}
|
||||
|
||||
newParam := &openapi3.Parameter{
|
||||
Name: param.Name,
|
||||
In: param.In,
|
||||
Description: param.Description,
|
||||
Required: param.Required,
|
||||
Schema: &openapi3.SchemaRef{Value: schema},
|
||||
}
|
||||
|
||||
// Convert validation properties
|
||||
if param.Maximum != nil {
|
||||
max := *param.Maximum
|
||||
newParam.Schema.Value.Max = &max
|
||||
}
|
||||
if param.Minimum != nil {
|
||||
min := *param.Minimum
|
||||
newParam.Schema.Value.Min = &min
|
||||
}
|
||||
if param.ExclusiveMaximum {
|
||||
newParam.Schema.Value.ExclusiveMax = true
|
||||
}
|
||||
if param.ExclusiveMinimum {
|
||||
newParam.Schema.Value.ExclusiveMin = true
|
||||
}
|
||||
if len(param.Enum) > 0 {
|
||||
newParam.Schema.Value.Enum = param.Enum
|
||||
}
|
||||
|
||||
// Handle array items
|
||||
if param.Items != nil {
|
||||
newParam.Schema.Value.Items = convertItemsToSchemaRef(param.Items)
|
||||
}
|
||||
|
||||
return &openapi3.ParameterRef{Value: newParam}
|
||||
}
|
||||
|
||||
// convertBodyParameter converts a body parameter to RequestBody
|
||||
func convertBodyParameter(param *spec.Parameter, consumes []string) *openapi3.RequestBody {
|
||||
if len(consumes) == 0 {
|
||||
consumes = []string{"application/json"}
|
||||
}
|
||||
|
||||
content := make(openapi3.Content)
|
||||
for _, contentType := range consumes {
|
||||
mediaType := &openapi3.MediaType{}
|
||||
if param.Schema != nil {
|
||||
mediaType.Schema = convertSchemaToSchemaRef(param.Schema)
|
||||
}
|
||||
content[contentType] = mediaType
|
||||
}
|
||||
|
||||
return &openapi3.RequestBody{
|
||||
Description: param.Description,
|
||||
Required: param.Required,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
// convertResponse converts a Swagger 2.0 response to OpenAPI 3.0
|
||||
func convertResponse(response *spec.Response, produces []string) *openapi3.ResponseRef {
|
||||
if len(produces) == 0 {
|
||||
produces = []string{"application/json"}
|
||||
}
|
||||
|
||||
newResp := &openapi3.Response{
|
||||
Description: &response.Description,
|
||||
}
|
||||
|
||||
if response.Schema != nil {
|
||||
newResp.Content = make(openapi3.Content)
|
||||
for _, contentType := range produces {
|
||||
mediaType := &openapi3.MediaType{
|
||||
Schema: convertSchemaToSchemaRef(response.Schema),
|
||||
}
|
||||
newResp.Content[contentType] = mediaType
|
||||
}
|
||||
}
|
||||
|
||||
return &openapi3.ResponseRef{Value: newResp}
|
||||
}
|
||||
|
||||
// convertSchemaToSchemaRef converts a Swagger 2.0 schema to OpenAPI 3.0 SchemaRef
|
||||
func convertSchemaToSchemaRef(schema *spec.Schema) *openapi3.SchemaRef {
|
||||
if schema == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle $ref
|
||||
if schema.Ref.String() != "" {
|
||||
ref := schema.Ref.String()
|
||||
// Convert #/definitions/ to #/components/schemas/
|
||||
if len(ref) > 14 && ref[:14] == "#/definitions/" {
|
||||
ref = "#/components/schemas/" + ref[14:]
|
||||
}
|
||||
return &openapi3.SchemaRef{Ref: ref}
|
||||
}
|
||||
|
||||
newSchema := &openapi3.Schema{
|
||||
Format: schema.Format,
|
||||
Description: schema.Description,
|
||||
Default: schema.Default,
|
||||
Example: schema.Example,
|
||||
Required: schema.Required,
|
||||
}
|
||||
|
||||
// Convert Type from StringOrArray to *Types
|
||||
if len(schema.Type) > 0 {
|
||||
types := openapi3.Types(schema.Type)
|
||||
newSchema.Type = &types
|
||||
}
|
||||
|
||||
// Convert validation properties
|
||||
if schema.Maximum != nil {
|
||||
max := *schema.Maximum
|
||||
newSchema.Max = &max
|
||||
}
|
||||
if schema.Minimum != nil {
|
||||
min := *schema.Minimum
|
||||
newSchema.Min = &min
|
||||
}
|
||||
if schema.ExclusiveMaximum {
|
||||
newSchema.ExclusiveMax = true
|
||||
}
|
||||
if schema.ExclusiveMinimum {
|
||||
newSchema.ExclusiveMin = true
|
||||
}
|
||||
if len(schema.Enum) > 0 {
|
||||
newSchema.Enum = schema.Enum
|
||||
}
|
||||
|
||||
// Convert properties
|
||||
if len(schema.Properties) > 0 {
|
||||
newSchema.Properties = make(openapi3.Schemas)
|
||||
for name, prop := range schema.Properties {
|
||||
newSchema.Properties[name] = convertSchemaToSchemaRef(&prop)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert items
|
||||
if schema.Items != nil && schema.Items.Schema != nil {
|
||||
newSchema.Items = convertSchemaToSchemaRef(schema.Items.Schema)
|
||||
}
|
||||
|
||||
// Convert additionalProperties
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
newSchema.AdditionalProperties = openapi3.AdditionalProperties{
|
||||
Schema: convertSchemaToSchemaRef(schema.AdditionalProperties.Schema),
|
||||
}
|
||||
}
|
||||
|
||||
return &openapi3.SchemaRef{Value: newSchema}
|
||||
}
|
||||
|
||||
// convertItemsToSchemaRef converts Swagger 2.0 items to OpenAPI 3.0 SchemaRef
|
||||
func convertItemsToSchemaRef(items *spec.Items) *openapi3.SchemaRef {
|
||||
if items == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
schema := &openapi3.Schema{
|
||||
Format: items.Format,
|
||||
}
|
||||
|
||||
if items.Type != "" {
|
||||
schema.Type = &openapi3.Types{items.Type}
|
||||
}
|
||||
|
||||
if items.Items != nil {
|
||||
schema.Items = convertItemsToSchemaRef(items.Items)
|
||||
}
|
||||
|
||||
return &openapi3.SchemaRef{Value: schema}
|
||||
}
|
||||
|
||||
// convertSecurityScheme converts a Swagger 2.0 security definition to OpenAPI 3.0
|
||||
func convertSecurityScheme(secDef *spec.SecurityScheme) *openapi3.SecuritySchemeRef {
|
||||
newScheme := &openapi3.SecurityScheme{
|
||||
Type: secDef.Type,
|
||||
Description: secDef.Description,
|
||||
Name: secDef.Name,
|
||||
}
|
||||
|
||||
// Convert In to openapi3 format
|
||||
switch secDef.In {
|
||||
case "header":
|
||||
newScheme.In = "header"
|
||||
case "query":
|
||||
newScheme.In = "query"
|
||||
case "cookie":
|
||||
newScheme.In = "cookie"
|
||||
}
|
||||
|
||||
// Handle OAuth2
|
||||
if secDef.Type == "oauth2" {
|
||||
newScheme.Flows = &openapi3.OAuthFlows{}
|
||||
|
||||
switch secDef.Flow {
|
||||
case "implicit":
|
||||
newScheme.Flows.Implicit = &openapi3.OAuthFlow{
|
||||
AuthorizationURL: secDef.AuthorizationURL,
|
||||
Scopes: secDef.Scopes,
|
||||
}
|
||||
case "password":
|
||||
newScheme.Flows.Password = &openapi3.OAuthFlow{
|
||||
TokenURL: secDef.TokenURL,
|
||||
Scopes: secDef.Scopes,
|
||||
}
|
||||
case "application":
|
||||
newScheme.Flows.ClientCredentials = &openapi3.OAuthFlow{
|
||||
TokenURL: secDef.TokenURL,
|
||||
Scopes: secDef.Scopes,
|
||||
}
|
||||
case "accessCode":
|
||||
newScheme.Flows.AuthorizationCode = &openapi3.OAuthFlow{
|
||||
AuthorizationURL: secDef.AuthorizationURL,
|
||||
TokenURL: secDef.TokenURL,
|
||||
Scopes: secDef.Scopes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &openapi3.SecuritySchemeRef{Value: newScheme}
|
||||
}
|
||||
|
||||
// intToString converts int to string for response codes
|
||||
func intToString(code int) string {
|
||||
if code < 0 || code > 999 {
|
||||
return "200" // fallback to 200
|
||||
}
|
||||
hundreds := code / 100
|
||||
tens := (code / 10) % 10
|
||||
ones := code % 10
|
||||
return string(rune('0'+hundreds)) + string(rune('0'+tens)) + string(rune('0'+ones))
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
#!/bin/bash
|
||||
# 測試所有格式生成
|
||||
|
||||
set -e
|
||||
|
||||
echo "🧪 測試 go-doc 所有格式生成"
|
||||
echo "================================"
|
||||
|
||||
# 建立測試目錄
|
||||
TEST_DIR="test_output_verification"
|
||||
mkdir -p "$TEST_DIR"
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 1: Swagger 2.0 (JSON)"
|
||||
./bin/go-doc -a example/example.api -d "$TEST_DIR" -f test1_swagger2
|
||||
if [ -f "$TEST_DIR/test1_swagger2.json" ]; then
|
||||
VERSION=$(jq -r '.swagger' "$TEST_DIR/test1_swagger2.json")
|
||||
echo "✅ 成功生成 Swagger $VERSION"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 2: Swagger 2.0 (YAML)"
|
||||
./bin/go-doc -a example/example.api -d "$TEST_DIR" -f test2_swagger2 -y
|
||||
if [ -f "$TEST_DIR/test2_swagger2.yaml" ]; then
|
||||
echo "✅ 成功生成 YAML 格式"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 3: OpenAPI 3.0 (JSON)"
|
||||
./bin/go-doc -a example/example.api -d "$TEST_DIR" -f test3_openapi3 -s openapi3.0
|
||||
if [ -f "$TEST_DIR/test3_openapi3.json" ]; then
|
||||
VERSION=$(jq -r '.openapi' "$TEST_DIR/test3_openapi3.json")
|
||||
echo "✅ 成功生成 OpenAPI $VERSION"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 4: OpenAPI 3.0 (YAML)"
|
||||
./bin/go-doc -a example/example.api -d "$TEST_DIR" -f test4_openapi3 -s openapi3.0 -y
|
||||
if [ -f "$TEST_DIR/test4_openapi3.yaml" ]; then
|
||||
echo "✅ 成功生成 OpenAPI YAML"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 5: 中文範例 (Swagger 2.0)"
|
||||
./bin/go-doc -a example/example_cn.api -d "$TEST_DIR" -f test5_cn_swagger2
|
||||
if [ -f "$TEST_DIR/test5_cn_swagger2.json" ]; then
|
||||
TITLE=$(jq -r '.info.title' "$TEST_DIR/test5_cn_swagger2.json")
|
||||
echo "✅ 成功生成中文範例: $TITLE"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 6: 中文範例 (OpenAPI 3.0)"
|
||||
./bin/go-doc -a example/example_cn.api -d "$TEST_DIR" -f test6_cn_openapi3 -s openapi3.0
|
||||
if [ -f "$TEST_DIR/test6_cn_openapi3.json" ]; then
|
||||
TITLE=$(jq -r '.info.title' "$TEST_DIR/test6_cn_openapi3.json")
|
||||
echo "✅ 成功生成中文 OpenAPI 3.0: $TITLE"
|
||||
else
|
||||
echo "❌ 失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 測試 7: 錯誤處理(無效的 spec-version)"
|
||||
if ./bin/go-doc -a example/example.api -d "$TEST_DIR" -s invalid 2>&1 | grep -q "spec-version must be"; then
|
||||
echo "✅ 錯誤處理正常"
|
||||
else
|
||||
echo "❌ 錯誤處理失敗"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📊 生成檔案統計"
|
||||
echo "================================"
|
||||
ls -lh "$TEST_DIR"
|
||||
|
||||
echo ""
|
||||
echo "📈 檔案大小比較"
|
||||
echo "================================"
|
||||
echo "Swagger 2.0 vs OpenAPI 3.0:"
|
||||
S2_SIZE=$(stat -f%z "$TEST_DIR/test1_swagger2.json" 2>/dev/null || stat -c%s "$TEST_DIR/test1_swagger2.json")
|
||||
O3_SIZE=$(stat -f%z "$TEST_DIR/test3_openapi3.json" 2>/dev/null || stat -c%s "$TEST_DIR/test3_openapi3.json")
|
||||
echo " Swagger 2.0: $S2_SIZE bytes"
|
||||
echo " OpenAPI 3.0: $O3_SIZE bytes"
|
||||
|
||||
echo ""
|
||||
echo "🎉 所有測試通過!"
|
||||
echo ""
|
||||
echo "生成的檔案位於: $TEST_DIR/"
|
Loading…
Reference in New Issue