Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話

OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話

Avatar for Daichi Koike

Daichi Koike

June 22, 2019
Tweet

More Decks by Daichi Koike

Other Decks in Technology

Transcript

  1. 本日のおはなし • OpenAPI 3.0 の yaml ファイルを分割管理しようとし た • $ref

    で死にまくったので $ref を解決するツールをプロ ジェクトで自作した • 人間と機械でそれぞれ触る yaml を分けるという方法 をとった • OpenAPI のツールチェインはよく調べたほうがいい �2
  2. OpenAPI • Google、マイクロソフト、IBM などが立ち上げた Open API Initiative が提唱する REST API

    の I/F 記述フォー マット(OpenAPI Specification) • Swagger がベース • 3.0 と 2.0 で大分仕様が違っており、今回は 3.0 の話 • https://github.com/OAI/OpenAPI-Specification �3
  3. OpenAPI https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml �4 paths: /pets: get: summary: List all pets

    operationId: listPets tags: - pets parameters: - name: limit in: query description: How many items to return at one time (max 100) required: false schema: type: integer format: int32 responses: '200': description: A paged array of pets headers: x-next: description: A link to the next page of responses schema: type: string content: application/json: schema: $ref: "#/components/schemas/Pets"
  4. yaml で病むる • 膨大な数の API になるため yaml は分割して管理した い •

    評価のシステムの管理画面だけで 40 API ある(BFF 含めると 80 API)。管理系だけで1万行になった • yaml 分けたいけど entity とか再定義するのはミスも 多発するだろうしいい感じに使いまわしたい • 2.0 は yaml マージツールがある https:// www.npmjs.com/package/multi-file-swagger �11
  5. もともとはこう �15 paths: /users/{id}: get: - লུ - responses: "200":

    content: application/json: schema: $ref: "#/components/schemas/User" components: schemas: User: title: MyUser type: object properties: id: type: integer name: type: string
  6. components 以下を切り出している �16 paths: /users/{id}: get: - লུ - responses:

    "200": content: application/json: schema: $ref: "./entities.yaml#/User"
  7. merger で $ref 解決 �18 paths: '/users/{id}': get: - লུ

    - responses: '200': content: application/json: schema: title: MyUser type: object properties: id: type: integer name: type: string
  8. ちょっとまった • $ref で別ファイルの定義を参照するのは OpenAPI 3.0 の仕様にそもそもあるのでは?? • あります!! •

    ただしツールによってはサポートしていなかったり、多段 $ref はスルーされたりします • 他にも細かい制約があります �19 仕様: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#relative-references-in-urls
  9. kamakura/openapi.yaml �22 paths: /users/{id}: get: - লུ - responses: "200":

    content: application/json: schema: $ref: "./entities.yaml#/User"
  10. kamakura-bff/openapi.yaml �23 paths: /users/{id}: get: - লུ - responses: $ref:

    "../kamakura/openapi.yaml#/paths/ ~1users~1{id}/get/responses" response のステータスコード、entity の定義を $ref で 引き込みたい ~1 は / のエスケープ文字
  11. 多段 $ref 問題 • kamakura-bff/openapi.yaml -> kamakura/ openapi.yaml -> kamakura/entities.yaml

    この $ref を Swagger UI は解決できない • ツールによっては最初の $ref で解決できなくて死ぬも のもある �26
  12. $ref を解決できない 😇 �28 $ openapi-generator validate -i kamakura/openapi.yaml Validating

    spec (kamakura/openapi.yaml) Errors: -attribute paths.'/users/ {id}'(get).responses.responses.$ref is not of type `object` [error] Spec has 1 errors.
  13. 開発フロー • merger で生成された $ref 解決済みの yaml に対し て validation

    をかけている • 人間が触る yaml と機械が 触る yaml を分ける • テストで request/response の validate に使いたい ので pre-commit hook で $ref 解決済み yaml を生 成して commit している • invalid な yaml が生成されても validation をすり抜 けることもあるので Swagger UI で表示してみて問題 ないか確認するのが良い �29
  14. (再掲)kamakura-bff/openapi.yaml �31 paths: /users/{id}: get: - লུ - responses: $ref:

    "../kamakura/openapi.yaml#/paths/ ~1users~1{id}/get/responses"
  15. (再掲)kamakura-bff/openapi.yaml �32 paths: /users/{id}: get: - লུ - responses: $ref:

    "../kamakura/openapi.yaml#/paths/ ~1users~1{id}/get/responses" これ paths 以下を $ref で引き込めばいいのでは??
  16. ハマりポイント • JSON Schema と完全に同じではない • JSON Schema では valid

    だが OpenAPI では invalid になることがある!! • 各ツールの仕様の網羅具合がまちまちなのもあり根気 強く向き合う覚悟がいる �37
  17. 一部の定義だけ override したい • バックエンドサービスとのやり取りで request ID やトレー シング用の trace

    ID をヘッダで引き回したい • code generate した際に BFF とバックエンドサービス の struct の名前が重複するので title(これが go の struct の名前になる) を変更したい • サービス側の定義を $ref で引き込みつつ、該当箇所 だけ定義を上書きするというやり方でこれらを実現した い • allOf を使うことで引き込んだ定義に手を加えることが できる �39
  18. parameters を override したい �40 kamakura/openapi.yaml paths: /users/{id}: get: parameters:

    - name: id in: path required: true schema: type: integer format: int64 - name: request_id in: query required: true schema: type: string BFF 側で request_id を ヘッダに入れてバックエンド サービスに渡す
  19. parameters を override したい �41 kamakura-bff/openapi.yaml paths: /users/{id}: get: allOf:

    - $ref: "../kamakura/openapi.yaml#/paths/ ~1users~1{id}/get" parameters: - name: id in: path required: true schema: type: integer format: int64 BFF 側は allOf で引き込んだ parameters に対して 後から override している
  20. request_id が消えないぞ?? �42 merger ࣮ߦޙͷ yaml paths: /users/{id}: get: parameters:

    - name: id in: path required: true schema: type: integer format: int64 - name: request_id in: query required: true schema: type: string
  21. わざとらしく name: hoge にする �44 kamakura-bff/openapi.yaml paths: /users/{id}: get: allOf:

    - $ref: "../kamakura/openapi.yaml#/paths/ ~1users~1{id}/get" parameters: - name: hoge in: path required: true schema: type: integer format: int64
  22. ほげええええ �45 merger ࣮ߦޙͷ yaml paths: /users/{id}: get: parameters: -

    name: hoge in: path required: true schema: type: integer format: int64 - name: request_id in: query required: true schema: type: string
  23. 最終的にこうなる �46 paths: /users/{id}: get: summary: $ref: "../kamakura/openapi.yaml#/paths/~1users~1{id}/get/responses" description: $ref:

    "../kamakura/openapi.yaml#/paths/~1users~1{id}/get/description" responses: $ref: "../kamakura/openapi.yaml#/paths/~1users~1{id}/get/responses" parameters: - name: id in: path required: true schema: type: integer format: int64 上書きたい要素を引き込まずに再定義する
  24. openapi-generator generate • model、api client あたりの利用に留めておくのがよい という所感 • api client

    は interface は生成されないので generate されたコードに対して自前で interface を生 やすか生 struct をそのまま触ることになる • テストのときに mock するといったことがやりにくいので 悩ましい �49
  25. model_my_user.go �50 /* * kamakura BFF API * * No

    description provided (generated by Openapi Generator https:// github.com/openapitools/openapi-generator) * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) */ package openapi type MyUser struct { Id int32 `json:"id,omitempty"` Name string `json:"name,omitempty"` }
  26. generate まわり • 2.0 では https://github.com/go-swagger/go- swagger というのがあるので 2.0 ではこちらを検討し

    てもよいかも • コードや GoDoc から yaml を生成するという方法もあり • 人間のドキュメンテーション用など厳密な定義が必要な い場合は今回取り上げた内容でも許容できるところもあ ると思うので、チームとしてどこまで OpenAPI に求める か次第 �51
  27. 本日のおはなし • OpenAPI 3.0 の yaml ファイルを分割管理しようとし た • $ref

    で死にまくったので $ref を解決するツールをプロ ジェクトで自作した • 人間と機械でそれぞれ触る yaml を分けるという方法 をとった • OpenAPI のツールチェインはよく調べたほうがいい �53