14个文本转图像AI API
使用 oasdiff 检测 API 中的重大变化
API 版本控制越来越普遍,而且不仅限于非常流行或商业化的 API。即使是像 Sportradar 这样的业余和特殊兴趣 API也具有多个版本。这会给开发人员和用户带来问题。
版本控制往往会破坏事物。现有代码可能不会随 API 一起更新。oasdiff是一个用于检测OpenAPI规范变化的开源工具,旨在防止偏差给您的开发人员和客户带来问题。
oasdiff 可以作为 Golang 包或命令行实用程序运行。该工具比较 OpenAPI 规范(通常为 JSON 或 YAML 格式),并返回突出显示差异的报告。oasdiff 分析从端点到请求/响应参数的所有内容以进行更新和修订,特别是寻找可能导致中断集成的更改。它是CI/CD 管道的宝贵工具。
下面,我们将向您展示如何开始使用 oasdiff,以防止在更新 API 时出现任何中断或服务中断。
1.安装oasdiff
oasdiff 存储库中提到了许多安装 oasdiff 的选项,包括 macOS、Windows 和 Linux 安装程序以及使用 Go 加载。在本教程中,我们将使用 Go 方法,因为 Go 包可以轻松集成到 CI/CD 管道中。
首先,如果您还没有安装Go,您需要安装它。
安装 Go 后,在终端中输入以下命令。
go install github.com/tufin/oasdiff@latest
请注意,如果您刚刚安装了 Go,并且在运行该命令时出现错误,请打开一个新的终端实例,这应该可以解决问题。
如果您使用的是 macOS,您也可以使用以下命令使用 Brew 安装 oasdiff:
brew tap tufin/homebrew-tufin
brew install oasdiff
oasdiff 包装器
如果您想在不安装任何东西的情况下尝试一下,oasdiff 也有几个包装器。
- GitHub 动作
- 云服务
- OpenAPI 同步
2. 尝试 oasdiff
安装 oasdiff 后,您可以测试它以了解其工作原理。首先,确保您已克隆GitHub 存储库。完成后,您可以运行oasdiff diff
以查看两个本地 YAML 文件之间的差异。
输入以下命令。
oasdiff diff data/openapi-test1.yaml data/openapi-test2.yaml
当你这样做时,你应该得到以下输出。
info:
contact:
added: true
version:
from: 1.0.0
to: 1.0.1
paths:
deleted:
- /subscribe
- /api/{domain}/{project}/install-command
- /register
modified:
/api/{domain}/{project}/badges/security-score:
operations:
added:
- POST
modified:
GET:
tags:
deleted:
- security
operationID:
from: GetSecurityScores
to: ""
parameters:
deleted:
cookie:
- test
header:
- user
- X-Auth-Name
modified:
path:
domain:
schema:
type:
from: string
to: integer
format:
from: hyphen-separated list
to: non-negative integer
description:
from: Hyphen-separated list of lowercase string
to: Non-negative integers (including zero)
example:
from: generic-bank
to: "100"
min:
from: null
to: 7
pattern:
from: ^(?:([a-z]+-)*([a-z]+)?)$
to: ^(?:\d+)$
query:
filter:
content:
mediaTypeModified:
application/json:
schema:
properties:
modified:
color:
type:
from: string
to: number
image:
explode:
from: null
to: true
schema:
description:
from: alphanumeric
to: alphanumeric with underscore, dash, period, slash and colon
examples:
deleted:
- "0"
token:
schema:
anyOf:
added:
- RevisionSchema[0]
- RevisionSchema[1]
type:
from: string
to: ""
format:
from: uuid
to: ""
description:
from: RFC 4122 UUID
to: ""
example:
from: 26734565-dbcc-449a-a370-0beaaf04b0e8
to: null
maxLength:
from: 29
to: null
pattern:
from: ^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12})$
to: ""
responses:
added:
- default
deleted:
- "200"
- "201"
- "400"
parameters:
deleted:
path:
- domain
endpoints:
added:
- method: POST
path: /api/{domain}/{project}/badges/security-score
deleted:
- method: POST
path: /register
- method: POST
path: /subscribe
- method: GET
path: /api/{domain}/{project}/install-command
modified:
? method: GET
path: /api/{domain}/{project}/badges/security-score
: tags:
deleted:
- security
operationID:
from: GetSecurityScores
to: ""
parameters:
deleted:
cookie:
- test
header:
- user
- X-Auth-Name
modified:
path:
domain:
schema:
type:
from: string
to: integer
format:
from: hyphen-separated list
to: non-negative integer
description:
from: Hyphen-separated list of lowercase string
to: Non-negative integers (including zero)
example:
from: generic-bank
to: "100"
min:
from: null
to: 7
pattern:
from: ^(?:([a-z]+-)*([a-z]+)?)$
to: ^(?:\d+)$
query:
filter:
content:
mediaTypeModified:
application/json:
schema:
properties:
modified:
color:
type:
from: string
to: number
image:
explode:
from: null
to: true
schema:
description:
from: alphanumeric
to: alphanumeric with underscore, dash, period, slash and colon
examples:
deleted:
- "0"
token:
schema:
anyOf:
added:
- RevisionSchema[0]
- RevisionSchema[1]
type:
from: string
to: ""
format:
from: uuid
to: ""
description:
from: RFC 4122 UUID
to: ""
example:
from: 26734565-dbcc-449a-a370-0beaaf04b0e8
to: null
maxLength:
from: 29
to: null
pattern:
from: ^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12})$
to: ""
responses:
added:
- default
deleted:
- "200"
- "201"
- "400"
security:
deleted:
- bearerAuth
servers:
deleted:
- tufin.com
tags:
deleted:
- security
- reuven
externalDocs:
deleted: true
components:
schemas:
deleted:
- network-policies
- rules
parameters:
deleted:
- network-policies
headers:
deleted:
- testc
- new
- test
requestBodies:
deleted:
- reuven
responses:
deleted:
- OK
securitySchemes:
deleted:
- AccessToken
- OAuth
- bearerAuth
如您所见,oasdiff 的输出非常详细地描述了两个版本之间发生的所有修订。例如,本例中删除了以下端点:/subscribe
、/api/{domain}/{project}/install-command
和/register
。
在该modified
部分下,您可以看到端点/api/{domain}/{project}/badges/security-score:
已添加 POST 命令。此外,许多功能也已被弃用,这些功能也已详细说明。
如果您想要更整洁的列表,也可以将结果返回为 HTML。不要使用-f text
字符串末尾的命令,而是使用-f html
,如下所示。
oasdiff diff data/openapi-test1.yaml data/openapi-test2.yaml -f html
Oasdiff 不仅适用于本地文件。您还可以使用 HTTP/s 轻松查看远程 API 之间的差异。
首先输入以下内容:
oasdiff diff https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test1.yaml https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test3.yaml -f text
结果显示,四个端点已被修改:security-score
、install-command
、register
和subscribe
。
如果您想查看两个版本之间的任何重大变化,请尝试以下步骤:
oasdiff breaking https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test1.yaml https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test3.yaml
这些结果表明,将端点中的成功状态替换为200
或会导致中断。201
security-score
甚至还有一个专门的命令用于评估/API
路径内的端点。
oasdiff diff https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test1.yaml https://raw.githubusercontent.com/Tufin/oasdiff/main/data/openapi-test3.yaml -f text -p "/api"
您还可以排除端点。您可以使用该–match-path
命令过滤路径名,以过滤掉与特定表达式不匹配的路径。您还可以使用该–filter-extension
命令过滤掉特定的扩展名。
3. 将 oasdiff 集成到 Go 项目中
使用 oasdiff 的最大原因之一是集成到自动化工作流程中。如果您使用 Go 进行开发,则可以在代码中直接使用 oasdiff。只需使用以下命令:
diff.Get(&diff.Config{}, spec1, spec2)
下面是 Go 程序中 oasdiff 的一个示例。
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
s1, err := loader.LoadFromFile("../data/simple1.yaml")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load spec with %v", err)
return
}
s2, err := loader.LoadFromFile("../data/simple2.yaml")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load spec with %v", err)
return
}
diffReport, err := diff.Get(diff.NewConfig(), s1, s2)
if err != nil {
fmt.Fprintf(os.Stderr, "diff failed with %v", err)
return
}
bytes, err := yaml.Marshal(diffReport)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to marshal result with %v", err)
return
}
fmt.Printf("%s\n", bytes)
返回以下输出:
aths:
modified:
/api/test:
operations:
added:
- POST
deleted:
- GET
endpoints:
added:
- method: POST
path: /api/test
deleted:
- method: GET
path: /api/test
``
您还可以使用 oasdiff 来检测代码内部的重大变化。
package main
import (
"fmt"
"os"
"strings"
"github.com/getkin/kin-openapi/openapi3"
"github.com/tufin/oasdiff/checker"
"github.com/tufin/oasdiff/diff"
"github.com/tufin/oasdiff/load"
)
func main() {
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
s1, err := load.LoadSpecInfo(loader, load.NewSource("../data/openapi-test1.yaml"))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load spec with %v", err)
return
}
s2, err := load.LoadSpecInfo(loader, load.NewSource("../data/openapi-test3.yaml"))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load spec with %v", err)
return
}
diffConfig := diff.NewConfig().WithCheckBreaking()
diffRes, operationsSources, err := diff.GetPathsDiff(diffConfig,
[]*load.SpecInfo{s1},
[]*load.SpecInfo{s2},
)
if err != nil {
fmt.Fprintf(os.Stderr, "diff failed with %v", err)
return
}
errs := checker.CheckBackwardCompatibility(checker.GetDefaultChecks(), diffRes, operationsSources)
// process configuration file for ignoring errors
errs, err = checker.ProcessIgnoredBackwardCompatibilityErrors(checker.ERR, errs, "../data/ignore-err-example.txt", checker.NewDefaultLocalizer())
if err != nil {
fmt.Fprintf(os.Stderr, "ignore errors failed with %v", err)
return
}
// process configuration file for ignoring warnings
errs, err = checker.ProcessIgnoredBackwardCompatibilityErrors(checker.WARN, errs, "../data/ignore-warn-example.txt", checker.NewDefaultLocalizer())
if err != nil {
fmt.Fprintf(os.Stderr, "ignore warnings failed with %v", err)
return
}
// pretty print breaking changes errors
if len(errs) > 0 {
localizer := checker.NewDefaultLocalizer()
count := errs.GetLevelCount()
fmt.Print(localizer("total-errors", len(errs), count[checker.ERR], "error", count[checker.WARN], "warning"))
for _, bcerr := range errs {
fmt.Printf("%s\n\n", strings.TrimRight(bcerr.SingleLineError(localizer, checker.ColorNever), " "))
}
}
}
返回:
4 breaking changes: 1 error, 3 warning
error at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score removed the success response with the status '201' [response-success-status-removed].
warning at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score deleted the 'cookie' request parameter 'test' [request-parameter-removed].
warning at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score deleted the 'header' request parameter 'user' [request-parameter-removed].
warning at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score deleted the 'query' request parameter 'filter' [request-parameter-removed].
关于 oasdiff 的最终想法
随着 API 为软件行业提供更多动力,API 版本控制将变得越来越普遍。无论如何,区分两个版本只是 oasdiff 的潜在用途之一。能够检测到重大更改(尤其是在发生之前)更是将 oasdiff 集成到您的工作流程中的理由。几行代码就可以防止您和您的客户出现服务中断和意外停机。