Swagger スポットライト: SquareはどのようにSDKを作成しているか

Squareでは、OpenAPI標準、Swagger Codegen、GitHubを活用し、スケーラブルな方法でクライアントSDKを構築・提供しています。
  2018年7月24日
今回のSwagger Spotlightでは、SquareのデベロッパーアドボケートであるTristan Sokol氏が、彼のチームがSDK生成にSwagger Codegenをどのように使用しているかについて語ります。Swagger Spotlightに独自のトピックを提出するには、こちらをクリックしてください。 

Squareのデベロッパープラットフォームチームは、他のほとんどのチームとは少し異なります。

私たちは、開発者向け製品のために個別のAPIを構築するのではなく、自社製品が使用するAPIを公開することに焦点を当て、開発者にとってシームレスな体験を生み出しています。

外部向けAPIには多くのアップストリームチームが関係しており、常に新しい機能を公開し、改善したいと考えています。

これは、SDKをどのように構築すべきかを決定する上で重要な要素でした。私たちは、製品チームが新機能をリリースする前にSDKの更新が完了するのを待たなければならないようなボトルネックになりたくありませんでした。

それを避ける主な方法はSDK生成です。

SDK生成 

各SDKを手作業で作成する代わりに(これは時間がかかり、エラーが発生しやすく、SDKへの新機能のリリースを遅らせることになります)、SDK生成に大きく依存するプロセスを使用しています。SDK生成にはさまざまな種類があるため、同様の方法をSDKに採用することを検討している場合は、さまざまな可能性を調べて、自分に合ったものを見つけるようにしてください。

私たちの好む方法は、OpenAPI仕様を使用してAPIエンドポイントを定義し、Swagger Codegenを使用してSDKのコードをプログラムで生成します。 

API仕様

私たちは、OpenAPI(旧Swagger Specification)標準を使用してAPIを定義しています。私たちにとって、これはURL、作成するHTTPリクエストの種類、および各APIエンドポイントに提供または取得を期待する情報の種類を定義するJSONファイルです。私たちの仕様は、一般情報/メタデータ、パス、モデルの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.dokyumento.jp/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ツールと同様に)によってサポートされているオープンソースプロジェクトであり、Open API仕様を、わずかな設定を追加して、さまざまな言語の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.dokyumento.jp/licenses/LICENSE-2.0 Apache License v2
 * @link     https://squareup.com/developers
 */
class  implements ArrayAccess
{
...

設定

これらは実際にははるかに単純で、本質的には、SDKの側面、一般的には関連するパッケージマネージャーにどのように適合するかを記述する小さなjsonファイルです。



{
  "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コミュニティのメンバーが自身の知見やプロジェクトを他のコミュニティと共有する機会です。次に取り上げられる方法について詳しく知るには、こちらをご覧ください!