我们的SDK发电机
客户sdks有点像伏特加酒,他们要么手工制作且非常昂贵,要么便宜,只让您感到遗憾和宿醉。为了获得具有良好开发人员体验的SDK,历史上需要投入大量资源来手工制作。唯一的选择是使用裸露的OSS产品。
我们提供了一条中间路;免费使用的SDK发电机,为出色的开发人员体验奠定了基础,无需投资。 我们已经在支持GO,Python,TypeScript和Java(Alpha)的支持下启动。我们计划尽快添加Ruby以及其他语言!
构成伟大开发人员的经验当然是主观的,但我们专注于:
- 完全键入,使SDK觉得它们是由人写的:易于阅读和调试。
- 电池包括,从重新恢复到API的所有内容都作为SDK一代的一部分处理(在我们的封闭beta中可用)。
- 易于使用,我们的SDK发电机是容错的,并且旨在在可能的情况下始终输出可用的SDK。如果我们无法输出可用的SDK,我们将提供人类可读的错误消息传递,以了解为什么您的OpenAPI规格验证失败(而不是仅仅默默失败或使用晦涩的错误消息或给您损坏的SDK)
- 完整的OpenAPI覆盖范围,我们计划对OpenAPI规格进行广泛的覆盖范围,同时深入关注定义API的最常见方法,并确保您可以将SDK用作输出。 li>
为了说明我们的SDK发电机和开源示例之间的差异,我们通过两者进行了规范的PETSHOP示例,并比较了输出。但是TLDR是:
- Speakeasy SDK发电机安装了没有其他依赖性的啤酒
- 没有npm。没有Java。一切都包装在一个独立的二进制中
- 我们暂时支持更少的语言,但我们所做的语言更加惯用,因此用法使用自然语言。
- 我们为SDK提供了一个简单的API,易于模拟和测试。
- 我们生产的SDK具有完全键入的输出,包括枚举等。
发电机已经在数千个API上进行了战斗,我们正在分享the results in our github repo。如果您想自己尝试一下,请安装download the CLI或Brew Install并在几分钟内开始:
brew brew安装speakeasy-api/homebrew-tap/speakeasy
speakeasy生成sdk -s openapi.yaml -o ./sdk -l go
我们使用OpenAPI生成器的经验
在我们了解SDK生成器与他人进行比较的详细信息之前,我们想浏览OpenAPI生成器的体验,该发电机首先导致该产品创建。使用OpenAPI工具的人可能会很熟悉。
安装
我们的努力是GO的挣扎。这里的关键词是尝试,因为没有成功的保证。我们尚未设置多个用于使用NPM的问题:
- 通过NPM在全球安装的权限问题
- 出错了错误:/bin/sh:1:java:找不到第一次运行时找不到。
要解决问题,我们必须同时安装NPM和Java,然后才能使安装工作。在页面上进一步的Homebrew Install说明,我们有更好的运气。他们首次尝试安装,下载了所有所需的依赖项。
模式验证
验证我们的OpenAPI规格工作正常,是使用OpenAPI-Generator的一个很好的开始。
openapi-generator validate -i openapi.yaml
Validating spec (openapi.yaml)
No validation issues detected.
SDK世代
即使我们的模式被该工具视为有效,我们也会遇到问题,当我们开始尝试从openapi yaml产生GO SDK时。我们收到的错误是最无益的,我们无法确定为什么我们的规格可能导致问题。
openapi-generator generate -i openapi.yaml -g go -o ../speakeasy-client-sdk-go-openapi-gen
...
Exception: Property and is missing from getVars
at org.openapitools.codegen.DefaultGenerator.processOperation(DefaultGenerator.java:1187)
at org.openapitools.codegen.DefaultGenerator.processPaths(DefaultGenerator.java:1078)
at org.openapitools.codegen.DefaultGenerator.generateApis(DefaultGenerator.java:580)
at org.openapitools.codegen.DefaultGenerator.generate(DefaultGenerator.java:915)
at org.openapitools.codegen.cmd.Generate.execute(Generate.java:465)
at org.openapitools.codegen.cmd.OpenApiGeneratorCommand.run(OpenApiGeneratorCommand.java:32)
at org.openapitools.codegen.OpenAPIGenerator.main(OpenAPIGenerator.java:66)
Caused by: java.lang.RuntimeException: Property and is missing from getVars
at org.openapitools.codegen.DefaultCodegen.addRequiredVarsMap(DefaultCodegen.java:7319)
at org.openapitools.codegen.DefaultCodegen.addVarsRequiredVarsAdditionalProps(DefaultCodegen.java:7354)
at org.openapitools.codegen.DefaultCodegen.fromParameter(DefaultCodegen.java:4972)
at org.openapitools.codegen.DefaultCodegen.fromOperation(DefaultCodegen.java:4394)
at org.openapitools.codegen.DefaultGenerator.processOperation(DefaultGenerator.java:1155)
... 6 more
与PetShop示例直接比较
让我们进入一个示例,该示例确实与OpenAPI-Generator(来自OpenAPI规范的规范PETSTORE API)一起使用。首先,让我们看一下用于生成SDK的命令:
OpenAPI :
openapi-generator generate -i petstore.yaml -g go -o ./openapi
Speakeasy :
speakeasy generate sdk -s petstore.yaml -o ./speakeasy -l go
OpenAPI和Speakeasy Generator都为PETSTORE API输出可用的SDK。我们正在使用此示例,但是我们的生成器支持GO,Python和Typescript。请注意,OpenAPI发电机支持我们计划在道路上添加到Speakeasy发电机的大量其他语言。
虽然OpenAPI发电机确实支持许多语言,因为我们一直在寻找GO语言的SDK实际上是类似Java的语言,并且对GO语言的惯用性很小:
OpenAPI :
ctx := context.Background()
client := openapi.NewAPIClient(openapi.NewConfiguration())
ctx = context.WithValue(ctx, openapi.ContextAccessToken, "special-key")
r := client.PetApi.FindPetsByStatus(ctx)
r = r.Status("pending")
pets, res, err := r.Execute()
if err != nil {
log.Fatal(err)
}
if res.StatusCode != 200 {
log.Fatalf("unexpected status code: %d", res.StatusCode)
}
data, _ := json.Marshal(pets)
fmt.Println(string(data))
Speakeasy :
ctx := context.Background()
sdk := speakeasy.New()
status := operations.FindPetsByStatusStatusEnumPending
queryParams := operations.FindPetsByStatusQueryParams{Status: &status}
security := operations.FindPetsByStatusSecurity{
PetstoreAuth: shared.SchemePetstoreAuth{
Authorization: "Bearer special-key",
},
}
res, err := sdk.FindPetsByStatus(ctx, operations.FindPetsByStatusRequest{
QueryParams: queryParams,
Security: security,
})
if err != nil {
log.Fatal(err)
}
if res.StatusCode != 200 {
log.Fatalf("unexpected status code: %d", res.StatusCode)
}
data, _ := json.Marshal(res.Pets)
fmt.Println(string(data))
由于设置请求并执行请求所需的多个方法调用,OpenAPI-Generator的SDK也很难模拟。使用SpeakeAsy SDK,单个方法调用就足够了。
>也值得研究在存在一些差异的每个SDK的格式和样式:
- OpenAPI-Generator输出评论以帮助使用(很快就会为Speakeasy发电机提供)。
- OpenAPI-Generator生成了许多需要的额外的Getter/setter,实例化和序列化方法,这些方法需要降低SDKS代码的可读性。
- OpenAPI SDK代码缺少格式,而我们的SDK代码是惯用格式的。
- 我们的发电机为所有事物生成了完整类型(如有可能)。
我们认为的最后一点很重要。例如,OpenAPI将枚举视为字符串,而我们生成的枚举减少了使用错误。我们对枚举的支持是以支持PETSTORE API的单个边缘情况为代价的,它允许将状态枚举提供为逗号分隔字符串以过滤多重状态,可以通过在OpenAPI规格中定义类型为“枚举数组而不是仅仅是字符串。
OpenAPI :
// Pet struct for Pet
type Pet struct {
Id *int64 `json:"id,omitempty"`
Name string `json:"name"`
Category *Category `json:"category,omitempty"`
PhotoUrls []string `json:"photoUrls"`
Tags []Tag `json:"tags,omitempty"`
// pet status in the store
Status *string `json:"status,omitempty"`
}
// NewPet instantiates a new Pet object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewPet(name string, photoUrls []string) *Pet {
this := Pet{}
this.Name = name
this.PhotoUrls = photoUrls
return &this
}
// NewPetWithDefaults instantiates a new Pet object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewPetWithDefaults() *Pet {
this := Pet{}
return &this
}
// GetId returns the Id field value if set, zero value otherwise.
func (o *Pet) GetId() int64 {
if o == nil || o.Id == nil {
var ret int64
return ret
}
return *o.Id
}
// GetIdOk returns a tuple with the Id field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Pet) GetIdOk() (*int64, bool) {
if o == nil || o.Id == nil {
return nil, false
}
return o.Id, true
}
// ... And continues with getters/setters for every field
Speakeasy :
type PetStatusEnum string
const (
PetStatusEnumAvailable PetStatusEnum = "available"
PetStatusEnumPending PetStatusEnum = "pending"
PetStatusEnumSold PetStatusEnum = "sold"
)
type Pet struct {
Category *Category `json:"category,omitempty"`
ID *int64 `json:"id,omitempty"`
Name string `json:"name"`
PhotoUrls []string `json:"photoUrls"`
Status *PetStatusEnum `json:"status,omitempty"`
Tags []Tag `json:"tags"`
}
概括
希望人们发现比较有趣/有用。我们期待听到有关如何改善SDK发电机以及接下来应该构建的内容的反馈。如果您有任何疑问,please join our slack community,您可以直接给我们发消息。