通过示例学习:第11部分 - 从您的GO RES REST API生成GO SDK(API客户端库)
#初学者 #api #go #sdk

在以前的文章中,我们创建了一个HTTP REST API server和一个CLI

创建一个直接称为我们API的CLI很酷,但是一个好习惯是在用户和API之间具有抽象(SDK,客户端库)。从头开始创建SDK并不像您想象的那么容易。如果我告诉您可以使用发电机从我们的REST API中创建我们的SDK,而更准确地说是从我们的开放API规格中创建我们的SDK?

mario gopher

让我们的go!

SDK

让我们通过谈论API和SDK来开始本文。

拥有API很酷,但是如果您没有SDK(客户端库),它们是用户和API之间的抽象层,您的用户必须心脏了解API的体系结构,并且每次API都会发展,他们也需要在一边更改应用程序。

SDK

您还可以为您的应用程序,下文,产品,服务提供多个SDK,一种通过您的社区使用的编程语言。

OpenAPI发电机

如果您在Internet上进行搜索,您会发现有几种工具允许在纸上从Swagger文件中生成SDK/A客户端库。每个工具都有其优点和缺点。对于我们的SDK,我们将使用研究期间最多的工具,并记录下来,具有目标语言列表,并具有现成的CLI:OpenAPI Generator。

OpenAPI Generator

简而言之,OpenAPI Generator允许生成API客户端库(SDK生成),服务器存根,文档和配置自动给出了OpenAPI Spec(V2,V3)。

使用50+ client generators,对于各种编程语言,可以生成代码与任何曝光OpenAPI文档的服务器进行交互。

OpenAPI发电机是开源的,并具有GitHub repository

在本文中,我们将使用允许我们生成GO SDK的GO Generator,并使用参数定义。

停止Blahblah,让我们安装OpenAPI Generator。
来自来源的几个installation options of the CLI exists,docker,npm ...

让我们从Brew中安装CLI:

$ brew install openapi-generator

让我们检查CLI在本地正确安装:

$ openapi-generator version
6.6.0

我们想要什么?

很酷,我们已经安装了发电机,但是我们想做什么?

我们有一个漂亮的api Gophers API

Gophers API

这个简单的API处理可爱的Gophers,并将您带到您:

  • 列出现有的地鼠
  • 显示有关Gopher的信息
  • 创建一个新的Gopher
  • 删除Gopher
  • 更新Gopher的路径和URL

您如何从这个不错的API开始并在GO中创建SDK? ð

初始化

首先,我们可以在GitHub中创建我们的存储库(为了共享和开源)。

为此,我登录GitHub website,单击“存储库”链接,单击“新”绿色按钮,然后创建了一个名为“gophers-sdk-go”的新存储库。

现在,在您的本地计算机中,git克隆这个新的存储库您想要:

$ git clone https://github.com/scraly/gophers-sdk-go.git
$ cd gophers-sdk-go

现在,我们必须初始化GO模块(依赖关系管理):

$ go mod init github.com/scraly/gophers-sdk-go
go: creating new go.mod: module github.com/scraly/gophers-sdk-go

这将创建一个这样的go.mod文件:

module github.com/scraly/gophers-sdk-go

go 1.19

OpenAPI定义

我们找到了一个很酷的工具来从招摇文件/OpenAPI定义生成SDK,所以让我们看看specs definition of our Gophers API

consumes:
- application/json
info:
  description: HTTP server that handle cute Gophers.
  title: gophers-api
  version: 0.1.0
produces:
- application/json
host: localhost:8080
schemes:
- http
swagger: "2.0"
tags:
  - name: gophers
    description: Handle Gophers

paths:
  /healthz:
    get:
      description: Check Health
      tags:
        - gophers
      operationId: checkHealth
      produces:
      - text/plain
      responses:
        '200':
          description: OK message.
          headers:
            Access-Control-Allow-Origin:
              type: string
          schema:
            type: string
            enum:
            - OK
  /gophers:
    get:
      description: List Gophers
      tags:
        - gophers
      produces:
       - application/json
      responses:
        200:
          description: Return the Gophers list.
          headers:
            Access-Control-Allow-Origin:
              type: string
          schema:
            type: array
            items:
              $ref: '#/definitions/Gopher'


  /gopher:
    post:
      summary: Add a new Gopher
      tags:
        - gophers
      consumes:
        - application/json
      parameters:
        - in: body
          name: gopher
          description: The Gopher to create.
          schema:
            type: object
            required:
              - name
              - displayname
              - url
            properties:
              name:
                type: string
              displayname:
                type: string
              url:
                type: string
      responses:
        201:
          description: Created
          schema:
            type: object
            $ref: '#/definitions/Gopher'
        409:
          description: Gopher already exists
    get:
      description: Get a gopher by a given name
      tags:
        - gophers
      produces:
       - application/json
      parameters:
        - name: name
          in: query
          type: string
          required: true
          description: Gopher name
      responses:
        200:
          description: A gopher
          headers:
            Access-Control-Allow-Origin:
              type: string
          schema:
            type: object
            $ref: '#/definitions/Gopher'
        404:
          description: A gopher with the specified Name was not found.
          headers:
            Access-Control-Allow-Origin:
              type: string
    delete:
      description: Delete a gopher by a given name
      tags:
        - gophers
      parameters:
        - name: name
          in: query
          type: string
          required: true
          description: Gopher name
      responses:
        200:
          description: OK
        404:
          description: A gopher with the specified Name was not found.
    put:
      description: Update a gopher
      tags:
        - gophers
      parameters:
        - in: body
          name: gopher
          description: The Gopher to update.
          schema:
            type: object
            required:
              - name
              - displayname
              - url
            properties:
              name:
                type: string
              displayname:
                type: string
              url:
                type: string
      responses:
        200:
          description: Updated
          schema:
            type: object
            $ref: '#/definitions/Gopher'
        404:
          description: A gopher with the specified Name was not found.

definitions:
  Gopher:
    type: object
    properties:
      name:
        type: string
        example: my-gopher
      displayname:
        type: string
        example: My Gopher
      url:
        type: string
        example: https://raw.githubusercontent.com/scraly/gophers/main/arrow-gopher.png

有用的提示

为了使我们的SDK使用正确的信息生成,我们必须小心地定义Swagger File的某些元素。
因此,我们将遵循我们的“ Yoda Gopher”的建议,看看要遵循什么技巧。

您将拥有的主机/服务器

yoda gopher

默认情况下,发电机认为在没有定义端口的Localhost中可以访问API,因此请定义带有URL的主机或服务器,该URL允许访问您的API。这些字段由发电机读取。
因此,当用户/一个开发人员使用您的SDK时,它将直接访问API。

在我身边,您可以在Swagger文件中看到,我使用的是host字段等于localhost:8080
因此,我的API必须在端口8080上本地运行,以便通过SDK进行调用。

您将定义的标签

yoda gopher

如果我们不在Swagger文件中定义标签,则生成的SDK将具有API名称:DefaultApi。就我个人而言,我认为与DefaultApi拥有SDK并不好。因此,我们可以指定标签,并且由于标签,发电机将在您的方便下命名您的API。

例如,通过定义和使用gophers标签,生成器将生成API名称GophersApi,它比DefaultApi ^^。

好得多。

操作ID您不会忘记

yoda gopher

一些发电机,包括OpenAPI Generator,使用operationId字段来命名方法。每个operationId必须在Swagger File中的所有操作中都是唯一的。

检查规格的有效性

在进行进一步之前,我们可以检查我们的Swagger文件是否对发电机有效。

$ openapi-generator validate -i https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml
Validating spec (https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml)
No validation issues detected.

完美。

配置应用程序

我们将使用将为我们创建并修改一堆文件的生成器。为了告诉工具不要修改或删除某些文件,OpenAPI Generator支持.openapi-generator-ignore文件。就像.gitignore.dockerignorefileð。

让我们创建一个.openapi-generator-ignore文件,以便告诉OpenAPI Generator不要生成或编辑go.modgo.sumLICENSE文件,因为我们已经使用go mod init命令和github中的许可证文件创建它们:

# OpenAPI Generator Ignore

go.mod
go.sum
LICENSE

创建我们的应用程序

让我们使用OpenAPI Generator CLI生成我们的SDK:

$ openapi-generator generate \
  -i https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml \
  -g go \
  --additional-properties packageName=gopherssdkgo,packageVersion=0.0.4,useTags=true \
  --git-user-id scraly \
  --git-repo-id gophers-sdk-go

一个好习惯是定义我们想要的所有参数以根据我们的需要生成SDK。

让我们解释一下我们的定义:

  • -i--input-spec)参数允许您定义Swagger/OpenAPI定义文件的源
  • -g--generator-name)参数允许您定义所需的SDK。在这里,我们想要一个SDK
  • --additional-properties参数允许您自定义生成的go sdk,并根据您的需求:包装的名称,包装的版本,如果您想在“ Abounding Swagger/Openapi”标签中使用我们之前谈论的wim -lim ...
  • 默认情况下,SDK将生成这样的导入行:openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID"--git-user-id--git-repo-id将允许您使用git存储库进行自定义。

/!\不要使用“ - ”,“ _”或packageName字段中的非字母字符,当您使用sdkð:
时,您将有一个奇怪的错误消息

$ go run sample.go
# github.com/scraly/gophers-sdk-go
../../../../go/pkg/mod/github.com/scraly/gophers-sdk-go@v0.0.0-20230716090011-35a148834c43/api_gophers.go:11:16: syntax error: unexpected -, expected semicolon or newline

有关更多信息,您可以查看official documentation about package name in Go

请转到OpenAPI Generator documentation了解Generate命令的所有可能参数。

请注意,如果您想了解更多有关要配置和使用的可能参数的信息,请查看Go generator documentation

在此执行命令时,您将拥有的输出:

[main] INFO  o.o.codegen.DefaultGenerator - Generating with dryRun=false
[main] INFO  o.o.codegen.DefaultGenerator - OpenAPI Generator: go (client)
[main] INFO  o.o.codegen.DefaultGenerator - Generator 'go' is considered stable.
[main] INFO  o.o.c.languages.AbstractGoCodegen - Environment variable GO_POST_PROCESS_FILE not defined so Go code may not be properly formatted. To define it, try `export GO_POST_PROCESS_FILE="/usr/local/bin/gofmt -w"` (Linux/Mac)
[main] INFO  o.o.c.languages.AbstractGoCodegen - NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).
[main] INFO  o.o.codegen.InlineModelResolver - Inline schema created as _gopher_put_request. To have complete control of the model name, set the `title` field or use the inlineSchemaNameMapping option (--inline-schema-name-mappings in CLI).
[main] INFO  o.o.codegen.TemplateManager - writing file ./model_gopher.go
[main] INFO  o.o.codegen.TemplateManager - writing file ./docs/Gopher.md
[main] INFO  o.o.codegen.TemplateManager - writing file ./model__gopher_put_request.go
[main] INFO  o.o.codegen.TemplateManager - writing file ./docs/GopherPutRequest.md
[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: get /gophers. Renamed to auto-generated operationId: gophersGet
[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: get /gopher. Renamed to auto-generated operationId: gopherGet
[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: put /gopher. Renamed to auto-generated operationId: gopherPut
[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: post /gopher. Renamed to auto-generated operationId: gopherPost
[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: delete /gopher. Renamed to auto-generated operationId: gopherDelete
[main] INFO  o.o.codegen.TemplateManager - writing file ./api_gophers.go
[main] INFO  o.o.codegen.TemplateManager - Skipped ./test/api_gophers_test.go (Test files never overwrite an existing file of the same name.)
[main] INFO  o.o.codegen.TemplateManager - writing file ./docs/GophersApi.md
[main] INFO  o.o.codegen.TemplateManager - writing file ./api/openapi.yaml
[main] INFO  o.o.codegen.TemplateManager - writing file ./README.md
[main] INFO  o.o.codegen.TemplateManager - writing file ./git_push.sh
[main] INFO  o.o.codegen.TemplateManager - writing file ./.gitignore
[main] INFO  o.o.codegen.TemplateManager - writing file ./configuration.go
[main] INFO  o.o.codegen.TemplateManager - writing file ./client.go
[main] INFO  o.o.codegen.TemplateManager - writing file ./response.go
[main] INFO  o.o.codegen.TemplateManager - Ignored ./go.mod (Ignored by rule in ignore file.)
[main] INFO  o.o.codegen.TemplateManager - Ignored ./go.sum (Ignored by rule in ignore file.)
[main] INFO  o.o.codegen.TemplateManager - writing file /Users/aurelievache/git/github.com/scraly/gophers-sdk-go/./.travis.yml
[main] INFO  o.o.codegen.TemplateManager - writing file ./utils.go
[main] INFO  o.o.codegen.TemplateManager - Skipped ./.openapi-generator-ignore (Skipped by supportingFiles options supplied by user.)
[main] INFO  o.o.codegen.TemplateManager - writing file ./.openapi-generator/VERSION
[main] INFO  o.o.codegen.TemplateManager - writing file ./.openapi-generator/FILES
################################################################################
# Thanks for using OpenAPI Generator.                                          #
# Please consider donation to help us maintain this project 🙏                 #
# https://opencollective.com/openapi_generator/donate                          #
################################################################################

很酷,已经生成了SDK!

命令为您生成了有用的文件:

.
├── LICENSE
├── README.md
├── api
│   └── openapi.yaml
├── api_gophers.go
├── client.go
├── configuration.go
├── docs
│   ├── Gopher.md
│   ├── GopherPutRequest.md
│   └── GophersApi.md
├── git_push.sh
├── go.mod
├── go.sum
├── model__gopher_put_request.go
├── model_gopher.go
├── response.go
├── sample
│   └── sample.go
├── test
│   └── api_gophers_test.go
└── utils.go

您可以看到,发电机还生成了文档文件,例如README.mddocs文件夹,它们将帮助您知道如何使用新的新SDK。

现在,我们可以在GITHUB中推动我们生成的SDK的第一个版本。

$ git add .
$ git commit -m "feat: first version of the generated go sdk" *
$ git push

好练习

Yoda gopher

再次,让我们听我们的YodaGopherð。

一个好的做法是创建一个标签(和版本)您的SDK的每个新版本。它将帮助用户获得您已发行/发布SDK的最新版本(或想要的版本)。

让我们测试它

啊,我喜欢这一刻,我们将能够测试我们创建的内容(最终生成^^)。

在本地运行API

首先,正如我们所看到的,Swagger/OpenAPI规格定义了我们的Gophers API在localhost:8080中运行,因此我们需要在本地运行。

克隆地鼠API存储库:

$ git clone https://github.com/scraly/gophers-api.git
$ cd gophers-api

当我们在Taskfile中定义任务以自动化我们的常见任务,例如以前的文章,我们只需要执行task run命令即可在localhost中启动API:8080:

$ task run
task: [run] GOFLAGS=-mod=mod go run internal/main.go
2023/07/16 11:53:35 Serving gophers API at http://[::]:8080

让我们测试我们的API。是的,对不起,但是我喜欢测试项目的每个步骤^^。

$ curl localhost:8080/gophers
[{"displayname":"5th Element","name":"5th-element","url":"https://raw.githubusercontent.com/scraly/gophers/main/5th-element.png"}]

使用和测试我们的SDK

现在,我们有一个跑步和发布/发布的GO SDK为我们的Gophers,让我们创建一个小样本,可以测试2个API调用:

  • /healthz
  • /gophers

让我们创建一个带有以下内容的sample.go文件:

package main

import (
    "context"
    "fmt"
    "os"

    gopherssdk "github.com/scraly/gophers-sdk-go"
)

func main() {
    config := gopherssdk.NewConfiguration()
    client := gopherssdk.NewAPIClient(config)

    // Check Health
    // When we call GophersApi.CheckHealth method, it return a string
    // equals to OK if the Gophers API is running and healthy

    health, healthRes, healthErr := client.GophersApi.CheckHealth(context.Background()).Execute()
    if healthErr != nil {
        fmt.Fprintf(os.Stderr, "Error when calling `GophersApi.CheckHealth``: %v\n", healthErr)
        fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", healthRes)
    }
    // response from `CheckHealth`: string
    fmt.Fprintf(os.Stdout, "Response from `GophersApi.CheckHealth`: %v\n", health)

    // Get Gophers
    gophers, gophersRes, GophersErr := client.GophersApi.GophersGet(context.Background()).Execute()
    if GophersErr != nil {
        fmt.Fprintf(os.Stderr, "Error when calling `GophersApi.GophersGet``: %v\n", GophersErr)
        fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", gophersRes)
    }

    // response from `GophersGet`: []Gopher
    if gophersRes.StatusCode == 200 {

        // Get and display all existing Gophers
        fmt.Println("Response from `GophersApi.GophersGet`:")

        fmt.Println("Number of Gophers:", len(gophers))

        for _, myGopher := range gophers {
            fmt.Println("DisplayName: ", *myGopher.Displayname)
            fmt.Println("Name:", *myGopher.Name)
            fmt.Println("URL:", *myGopher.Url)
        }

    }
}

在此文件中:

  • 导入GO SDK ^^
  • 使用新的配置启动客户
  • 调用GophersApi.CheckHealth调用/healthz路线并显示结果
  • 的方法
  • 调用GophersApi.GophersGet调用/gophers路线并显示返回的地鼠的列表

让我们测试它:

$ go run sample.go
Response from `GophersApi.CheckHealth`: OK
Response from `GophersApi.GophersGet`:
Number of Gophers: 1
DisplayName:  5th Element
Name: 5th-element
URL: https://raw.githubusercontent.com/scraly/gophers/main/5th-element.png

酷!我们正在使用GO SDK来调用我们的API在Localhost:8080(不知道API的体系结构)! ð

下一步是什么

在本文中,我们看到了如何从Swagger File/OpenAPI Specs生成GO SDK。
但是,当我们的招摇文件更改时会发生什么?
一个想法可以是在我们的Swagger File/OpenAPI规格更改的所有更改中自动再生我们的SDK。

由于我们的API和SDK托管在GitHub中,因此可以使用GitHub Actionð。

固定此自动化。

例如,我们可以考虑使用Hook workflow_dispatch,它允许更改一个存储库来触发其他存储库中的操作。

结论

正如您在本文和以前的文章中所看到的那样,可以在GO中创建应用程序:CLI,REST API ...以及使用有用的工具,可以帮助我们创建SDK。

我们应用程序的所有代码均可在以下方式提供:https://github.com/scraly/gophers-sdk-go

也可以使用该文档:https://pkg.go.dev/github.com/scraly/gophers-sdk-go

在以下文章中,我们将在GO中创建其他应用程序/类型的应用程序。

希望您会喜欢的。