OpenAPIをLinterとFormatterで管理するまで

    OpenAPIをLinterとFormatterで管理するまで

     

    本記事はAsiaQuest Advent Calendarの13日目です。

    目次

      はじめに

      こんにちは、DE部GE課の松浦です。
      前回はOpenAPIの移行についての話でしたが、今回は LinterとFormatterの話です。
      このあたりの管理は面倒なので、やっぱり機械に任せたいですよね。

      OpenAPIのLinter

      Linterとはコードを分析して、プロジェクト内で決めた記述方法やルールと違っている部分を検出するツールのことです。
      JavaScript とかだと、ESLint が有名ですね。
      OpenAPIではフォーマットがバージョンごとに決まっているため、それに従っていない場合にエラーとして判定してくれます。
      また、問題はありませんが非推奨みたいな書き方はwarningとして表示してくれたりします。
      このあたりはツールによって表示の仕方が違っていたりします。

      どのツールにするかですが、以下の基準で選定しました。

      1. CLIとして実行できる
      2. エラー出力がわかりやすい
      3. ドキュメントが充実している
      4. リポジトリの更新が活発
      5. カスタマイズができる
      6. 導入が簡単
        • npmをメインに利用しているので、npmベースが望ましい
      7. ある程度普及している

      上記の条件で自分が調べた限りでは、以下2つが良いのかなと思いました。
      自分のチームではSpectralの方を採用しましたが、どちらも良いツールだと思います。
      使い方も似ているので、ドキュメントや出力、ルールセットを確認してみて、よりチームにあったものを選ぶのが良いと思います。
      RedoclyとSpectralでは、デフォルトだと出力結果が違っていることがあるので、その点には注意してください。

      本記事では上記2ツールについての比較しますが、他にも同様の機能を有していると思われるopticdev/optic、Go製のdaveshanley/vacuum、シンプルなNode製CLIツールのsuperfaceai/openapi-linter、LinterツールをまとめてCI上で実行できるsuper-linter/super-linter(super-linterで動く実態はSpectral)等があるので、興味があればチェックしてみてください。

      Spectral

      Stoplight社が開発しているツールになります。
      GUI上でOpenAPIが書けるStoplight Studioも開発してたりします。

      Spectralはnpm経由でインストールでき、実行も以下のコマンドで終わるので、非常に簡単です。

      spectral lint target_file.yaml

      複数ファイルに対して実行したい場合や、YAMLやJSONに適応したい場合でも、glob syntaxが使えるので、以下のように書けばOKです。

      spectral lint /your/openapi/file/**/*.{json,yml,yaml}

      チェック項目については、組み込みの設定を読み込む仕組みが提供されています。
      .spectral.yamlファイル内にextends: "spectral:oas"と定義するだけで、OpenAPIのバージョンに応じた設定を読み込んでくれます。

      ルール詳細はこちらのOpenAPI Rulesに掲載されています。
      未使用Component等、推奨事項ではない項目についてはデフォルトだとwarningとして出力されますが、Change Rule Severityに掲載されている上書き方法で、自身のプロジェクトにあったルールへ変更できます。

      実行時の出力はこんな感じです。一行で出力される感じが見やすくて良いですね。
      行数もちゃんと出てます。

      /your/yaml/path/petstore.yaml
      2:6 warning info-contact Info object must have "contact" object. info
      2:6 warning info-description Info "description" must be present and non-empty string. info
      11:9 warning operation-description Operation "description" must be present and non-empty string. paths./pets.get
      15:11 warning operation-tag-defined Operation tags must be defined in global tags. paths./pets.get.tags[0]
      41:10 warning operation-description Operation "description" must be present and non-empty string. paths./pets.post
      45:11 warning operation-tag-defined Operation tags must be defined in global tags. paths./pets.post.tags[0]
      56:9 warning operation-description Operation "description" must be present and non-empty string. paths./pets/{petId}.get
      56:9 error path-params Operation must define parameter "{petId}" as expected by path "/pets/{petId}". paths./pets/{petId}.get
      60:11 warning operation-tag-defined Operation tags must be defined in global tags. paths./pets/{petId}.get.tags[0]
      62:11 error oas3-schema "0" property must match exactly one schema in oneOf. paths./pets/{petId}.get.parameters[0]
      110:17 error oas3-schema "type" property must be equal to one of the allowed values: "array", "boolean", "integer", "number", "object", "string". Did you mean "array"?. components.schemas.Error.properties.message.type

      ✖ 11 problems (3 errors, 8 warnings, 0 infos, 0 hints)

      OAIが出してるOpenAPIのexampleを改変して読ませました。

      Redocly CLI

      Redoc社が開発しているツールになります。
      OpenAPI仕様書をイケてるHTMLドキュメントに変換するRedocがあったりします。
      Redocに関しては以前ブログでも紹介したことがあるので興味があれば、そちらもチェックしてもらえると嬉しいです。

      Redocly CLIもnpm経由でインストールでき、実行もSpectralと同様にとても簡単です。glob syntaxも使えます。

      redocly lint target_file.yaml

      チェック項目についても、Spectral同様に組み込みルールがRedocly CLI側で用意されています。
      組み込みルールを利用する場合はSpectralと違って、とくにファイルは必要ありません。

      Ruleはredocly.yamlを定義することでオーバーライドできます(Configファイルの設定案内)。
      ルール詳細については、こちらのRulesに掲載されているので、Spectral同様にカスタマイズ可能です。

      実行結果は、Redoclyの方が全体のどこにあるかはわかりやすいですが、行数が多いです。
      デフォルトのconfigを利用しているアナウンスが最初にでるので、親切です。
      Redoclyだと、10項目のうち6項目がエラーで4項目がwarningとして出力されているので、デフォルトの内容に違いがあります。

      No configurations were provided -- using built in recommended configuration by default.

      validating petstore.yaml...
      [1] petstore.yaml:110:17 at #/components/schemas/Error/properties/message/type

      `type` can be one of the following only: "object", "array", "string", "number", "integer", "boolean", "null".

      Did you mean: array ?

      108 | format: int32
      109 | message:
      110 | type: arrayg
      111 |

      Error was generated by the spec rule.


      [2] petstore.yaml:62:11 at #/paths/~1pets~1{petId}/get/parameters/0

      The field `in` must be present on this level.

      60 | - pets
      61 | parameters:
      62 | - name: petId
      63 | required: true
      … | < 2 more lines >
      66 | type: string
      67 | responses:
      68 | '200':

      Error was generated by the spec rule.


      [3] petstore.yaml:61:7 at #/paths/~1pets~1{petId}/get/parameters

      The operation does not define the path parameter `{petId}` expected by path `/pets/{petId}`.

      59 | tags:
      60 | - pets
      61 | parameters:
      62 | - name: petId
      63 | required: true

      Error was generated by the path-parameters-defined rule.

      ----------
      長いので省略
      ----------

      [10] petstore.yaml:67:7 at #/paths/~1pets~1{petId}/get/responses

      Operation must have at least one `4XX` response.

      65 | schema:
      66 | type: string
      67 | responses:
      68 | '200':
      69 | description: Expected response to a valid request

      Warning was generated by the operation-4xx-response rule.


      petstore.yaml: validated in 19ms

      ❌ Validation failed with 6 errors and 4 warnings.
      run `redocly lint --generate-ignore-file` to add all problems to the ignore file.

      Formatter

      Formatter とはコードを整形してくれるツールのことです。
      YAMLであれば、文字列のクォートや、配列の書き方を統一してくれます。

      YAMLのFormatterについては、Linterほど選択肢がありませんでした。

      以上の3つを候補としてあげましたが、Prettierを採用しました。
      少なくとも自分は知らなかったのですが、2018-07-29 から Prettier で YAML のフォーマットができるようになっていたようです
      Prettierを選択した理由としては、以下の3つが主な要因です。

      • npm管理できること
      • すでにTypeScriptで利用実績があったこと
      • 自分のチームではクォーテーションの問題が主に取り上げられていたので、クォーテーションの統一ができればよかったこと

      どのようにして自動実行させたか

      huskyを利用しました。
      huskyはgit commit前に任意の処理を簡単に挟み込めるようにするツールです。
      LinterやFormatterで採用したツールと同様にnpm経由でインストールできます。

      huskyを利用して、package.jsonにLintとFormatの処理をまとめたコマンドをキックさせるようにしました。
      デメリットとしては、コミット処理が若干重くなります。

      別の方法としては、GitHub Actions等のCIツール上でLint、Formatをキックして自動コミットや、フォーマット違反によるエラー検知を実施する方法もあります。
      今回は対象のファイルが少なかったことや、コミット前に実施したかったことがあったので、huskyを利用しました。

      まとめ

      OpenAPIのバージョン移行に引き続き、今回ではLinterとFormatterの導入しました。
      導入の結果、統一されていなかったフォーマット問題の解決や、ルール違反の事前検知ができるようになりました。

      こういった作業を人力で行うのはつらいので、OpenAPIを書いている方は導入をオススメします!
      機械に任せられるところは、機械に任せて楽に仕事しましょう。