Swagger 焦点:Square 如何制作其 SDK

在 Square,我们利用 OpenAPI 标准、Swagger Codegen 和 GitHub 以可扩展的方式构建和交付我们的客户端 SDK。
  2018年7月24日
在这一期 Swagger Spotlight 中,Tristan Sokol,Square 的开发者布道师,讲述了他的团队如何使用 Swagger Codegen 进行 SDK 生成。要提交您自己的 Swagger Spotlight 主题,请点击此处。 

Square 的开发者平台团队与大多数团队有点不同。

我们不是为我们的开发者产品构建单独的 API,而是专注于暴露我们第一方产品使用的 API,以便为开发者创造无缝体验。

我们有许多上游团队是我们的外部 API 的利益相关者,他们不断希望暴露新功能和进行改进。

这是我们决定如何构建 SDK 时的一个重要因素;我们不希望我们的团队成为瓶颈,产品团队必须等待我们完成 SDK 更新才能发布新功能。

我们避免这种情况的主要方法是 SDK 生成。

SDK 生成 

我们没有手动编写每个 SDK(这不仅耗时、容易出错,还会减慢新功能发布到 SDK 的速度),而是使用一种严重依赖 SDK 生成的流程。市面上有许多 SDK 生成方式,因此如果您打算为您的 SDK 采用类似的方法,请务必查看各种可能性并找到适合您的方法。

我们偏好的方法是使用 OpenAPI 规范来定义我们的 API 端点,并使用 Swagger Codegen 以编程方式生成 SDK 的代码。 

API 规范

我们使用 OpenAPI(前身为 Swagger 规范)标准来定义我们的 API。对我们来说,这是一个 `JSON` 文件,定义了 URL、要发出哪种 HTTP 请求,以及为每个 API 端点提供或期望获取哪种信息。我们的规范由 3 个主要部分组成:一般信息/元数据、路径和模型。

一般信息/元数据

规范的这一部分包含 API 的整体描述性信息,例如在哪里可以找到许可信息,或者联系谁寻求帮助。

  "info": {
    "version": "2.0",
    "title": "Square Connect API",
    "description": "Client library for accessing the Square Connect APIs",
    "termsOfService": "https://connect.squareup.com/tos",
    "contact": {
      "name": "Square Developer Platform",
      "email": "[email protected]",
      "url": "https://squareup.com/developers"
    },
    "license": {
      "name": "Apache 2.0",
      "url": "https://apache.ac.cn/licenses/LICENSE-2.0.html"
    }
  },

路径

这些描述了 API 的各个端点(或 URL 路径)。它描述了要发出哪种 `HTTP` 请求,如何进行授权,以及应该在请求中添加哪种信息,以及您期望得到什么返回。

在下面的示例中,您可以看到它是一个 `POST` 请求,URL 中有几个必需参数,主体中有一个,您会返回一个 `CreateRefundResponse` 对象。


"/v2/locations/{location_id}/transactions/{transaction_id}/refund": {
  "post": {
    "tags": [
      "Transactions"
    ],
    "summary": "CreateRefund",
    "operationId": "CreateRefund",
    "description": "Initiates a refund for a previously charged tender.\n\nYou must issue a refund within 120 days of the associated payment. See\n(this article)[https://squareup.com/help/us/en/article/5060] for more information\non refund behavior.",
    "x-oauthpermissions": [
      "PAYMENTS_WRITE"
    ],
    "security": [
      {
        "oauth2": [
          "PAYMENTS_WRITE"
        ]
      }
    ],
    "parameters": [
      {
        "name": "location_id",
        "description": "The ID of the original transaction\u0027s associated location.",
        "type": "string",
        "in": "path",
        "required": true
      },
      {
        "name": "transaction_id",
        "description": "The ID of the original transaction that includes the tender to refund.",
        "type": "string",
        "in": "path",
        "required": true
      },
      {
        "name": "body",
        "in": "body",
        "required": true,
        "description": "An object containing the fields to POST for the request.\n\nSee the corresponding object definition for field details.",
        "schema": {
          "$ref": "#/definitions/CreateRefundRequest"
        }
      }
    ],
    "responses": {
      "200": {
        "description": "Success",
        "schema": {
          "$ref": "#/definitions/CreateRefundResponse"
        }
      }
    }
  }
},

模型

模型描述了 API 交互的不同对象。它们主要用于将 API 的 JSON 响应序列化为每种语言的原生对象。在此示例中,`CreateRefundResponse`,您可以看到它包含其他几个模型,以及描述甚至响应示例。


"CreateRefundResponse": {
  "type": "object",
  "properties": {
    "errors": {
      "type": "array",
      "items": {
        "$ref": "#/definitions/Error"
      },
      "description": "Any errors that occurred during the request."
    },
    "refund": {
      "$ref": "#/definitions/Refund",
      "description": "The created refund."
    }
  },
  "description": "Defines the fields that are included in the response body of\na request to the [CreateRefund](#endpoint-createrefund) endpoint.\n\nOne of `errors` or `refund` is present in a given response (never both).",
  "example": {
    "refund": {
      "id": "b27436d1-7f8e-5610-45c6-417ef71434b4-SW",
      "location_id": "18YC4JDH91E1H",
      "transaction_id": "TRANSACTION_ID",
      "tender_id": "TENDER_ID",
      "created_at": "2016-02-12T00:28:18Z",
      "reason": "some reason",
      "amount_money": {
        "amount": 100,
        "currency": "USD"
      },
      "status": "PENDING"
    }
  },
  "x-sq-sdk-sample-code": {
    "python": "/sdk_samples/CreateRefund/CreateRefundResponse.python",
    "csharp": "/sdk_samples/CreateRefund/CreateRefundResponse.csharp",
    "php": "/sdk_samples/CreateRefund/CreateRefundResponse.php",
    "ruby": "/sdk_samples/CreateRefund/CreateRefundResponse.ruby"
  }
},

您可以在我们 GitHub 上的 Connect-API-Specification 仓库中查看我们最新的规范版本。

该规范是我们生成过程的重要组成部分,因为它提供了关于我们 API 工作方式的真实来源。当其他团队想要扩展他们的 API、发布新的 API,或者只是增加模型描述的清晰度时,他们可以编辑这一个文件,并将他们的更改传播到所有客户端 SDK。

我们实际上从描述内部服务间通信的文件中生成了大部分规范,以实现更进一步的流程自动化和更简单的更改。

Swagger Codegen

现在我们已经准备好了 API 的规范,如何将其转换为面向客户端的 SDK 呢?答案是 Swagger Codegen。Swagger Codegen 是一个由 Smartbear 支持的开源项目(就像其他 Swagger 工具一样),它将您的 OpenAPI 规范应用于一系列针对不同语言 SDK 的模板,并加入了一些配置。

模板 

模板使用一种名为 mustache 的语言来定义其各个部分,并且在大多数情况下,它们看起来和读起来都像所需语言的文件。下面的示例是我们 PHP SDK 模板的一部分。您可以看到,像代码注释这样的有用内容也是自动生成的,因此最终的 SDK 可以拥有内置文档、代码片段等。 


<?php
{{#models}}
{{#model}}
/**
 * NOTE: This class is auto generated by the swagger code generator program.
 * https://github.com/swagger-api/swagger-codegen
 * Do not edit the class manually.
 */
namespace ;
use \ArrayAccess;
/**
 *  Class Doc Comment
 *
 * @category Class
 * @package  
 * @author   Square Inc.
 * @license  https://apache.ac.cn/licenses/LICENSE-2.0 Apache License v2
 * @link     https://squareup.com/developers
 */
class  implements ArrayAccess
{
...

配置

这些实际上要简单得多,本质上是一些小的 `json` 文件,描述了您 SDK 的各个方面,通常是关于它如何适应相关的包管理器。



{
  "projectName": "square-connect",
  "projectVersion": "2.8.0",
  "projectDescription": "JavaScript client library for the Square Connect v2 API",
  "projectLicenseName": "Apache-2.0",
  "moduleName": "SquareConnect",
  "usePromises": true,
  "licenseName": "Apache 2.0"
}

由于 Codegen 项目非常活跃,我们实际上会为我们支持的每个 SDK 签入一份模板文件的副本,并固定到特定的 Codegen 版本,以确保我们不会因为所有自动化而意外地向用户推送破坏性更改。

您可以在与我们规范文件相同的存储库中找到支持 {Java、PHP、C#、Python、Ruby、JavaScript} SDK 的所有模板和配置文件:Connect-API-Specification。 

其他想法

我们的流程已经发展了很多,Travis CI 等工具对流程产生了重大影响。您可以使用 CI & CD 工具使流程更加自动化,但请务必有良好的一套测试覆盖,以帮助防止意外更改潜入您发布的代码中。 

希望您喜欢我们对 SDK 生成过程的了解。您还可以观看我之前在 DevRelCon 上就此主题发表的录制演讲此处。如果您想了解更多关于我们的 SDK 或 Square 的其他技术方面,请务必关注此博客、我们的Twitter 账户,并注册我们的开发者通讯

这篇文章也发表在 Square 的技术博客上——查看它以了解更多信息 

Swagger Spotlight 是 Swagger 社区成员分享他们的见解和项目给其他社区成员的机会。了解更多如何成为下一位被专题报道的人!

© . All rights reserved.