Posts for: #Openapi

api ドキュメント保守の考察

今晩から東京出張なので運動はおやすみ。縄跳びだけもっていく。

api ドキュメントを継続的に保守することへの考察

お手伝い先のプロダクトを1年半以上、継続している。web api の機能もどんどん増えてきて50は超えていると思う。api ドキュメントをビルドするために redocly cli というツールを使っている。次のスクリプトは .gitlab-ci.yml の設定だけど、このぐらいの手間で openapi.yml から1つの html ドキュメントを生成してくれる。この index.html を api サーバーに同梱している。

  before_script:
    - node --version
    - npm --version
    - npm install @redocly/cli@latest
    - npx redocly --version
  script:
    - |+
      npx redocly build-docs schema/openapi.yml \
        --output index.html \
        --theme.openapi.schemaExpansionLevel=10 \
        --theme.openapi.expandResponses=all \
        --theme.openapi.requiredPropsFirst=true \
        --theme.openapi.jsonSampleExpandLevel=10 \
        --theme.openapi.hideLoading=true \
        --theme.openapi.pathInMiddlePanel=true      
    - mkdir -p public
    - mv index.html public/

web api の機能数が少ないときはこれで十分だった。しかし、50を超えてくると yml で api ドキュメントを保守するのが辛くなってくる。xml/yml/json を問わず、サイズの大きいこれらのファイルを保守するのはつらい。人間の能は複数種別の情報を同時処理できるが、一定量を超えた情報量をうまく処理できない。もちろん機能別にファイル分割して管理しているのだけど、それでも openapi.yml のエンドポイントの定義はどんどん増えていく。それがだんだん辛くなってきたのが現状になる。

以前 静的サイトジェネレーター勉強会の手伝い をしたときに slate という markdown で記述できる api ドキュメントツールがあることを教えてもらった。これはいいなとそのとき思ったのだけど、コミット履歴などをみているともう保守モードで活発に開発されていない。oss あるある話しで継続的に開発されていないツールはすぐに廃れるのでいまから採用するには躊躇してしまう。

postman という api プラットフォームがある。昔から api クライアントとして使う開発者も多かったのでツール自体は知っていた。この機会に私も実際に使ってみて評価した。既存の openapi.yml + redocly cli を使っているドキュメント管理と postman との比較をした。

  • postman はドキュメントのバージョン管理ができる
  • postman アプリを api ドキュメントのエディターとして使える
    • テキストの説明欄に markdown を記述できる
    • エクスポートすると json データになる
      • ドキュメントのデータとして yml と大きな違いはないが、エディターで隠蔽されるからドキュメントの保守はやりやすくなる
        • 但し、エディターは postman アプリに依存する
  • postman はリクエスト CLI とレスポンスの対応関係をセットで管理できる
  • ドキュメントの構造や見た目はあまり変わらない

postman そのものは悪くないけど、うちらのいまの開発のワークフローにすぐに適用できる状況でもない。postman の機能を有効に活用できるように開発のやり方を変えないといけない。そうしないと、ドキュメント管理だけに使うには外部サービス依存が大きくて学習コストも高くなってしまう。

別のアプローチとして openapi.yml の編集をテキストファイルで行うのではなく、人間にとって操作しやすいエディター上で行えるようにしたものが stoplight studio になる。ググると評判もよいし、私も実際に使ってみて機能はまさに探していたツールではある。しかし、このツールの先行きも怪しい。これまでデスクトップ版のエディターは無償で提供されていて、いまもアカウント登録すれば無償で使える。しかし、昨年あたりに SmartBear 社 (swagger を作ってた会社ね) に買収されている。おそらく swagger editor の移行先を探していてちょうど都合がよいのだと思う。SmartBear 社は過去の openapi 移行時のゴダゴダがあって印象がよくないし、ビジネス寄りの会社になるので将来的に stoplight studio を無償で使い続けられるかどうかにも懸念がある。

今回は調査したどのツールにも懸念があって採用を見送ることにした。api ドキュメントを書くツールを探すのは難しい。

寝台特急

久しぶりの寝台特急。11時25分頃に乗車券を使って JR 三ノ宮駅の改札を通過しようとするとエラーになった。30日から有効とは書いてあるものの、以前は通過できていた気がしたので駅員さんに聞いたら30分前から通れるはずとの。その後11時38分には改札を通れた。これまで改札でエラーになったことがなかったのは11時30分をまわってからだったんだと初めて気付いた。運が悪いことに0時11分発の予定が32分遅延した。調子の悪いときはこんなもので段取りもうまくいかない。最終的に東京駅には15分ほど遅れになった。

今日は普通のB寝台シングルを予約した。下側の狭い部屋でゆっくり寝ることにした。乗車後10分ほどして車掌さんが切符確認にきた。これもわかっているから扉を開けて切符も用意して待ってた。寝台特急は薄い布団しかついていないから冬はちょっと寒い。逆に夏はエアコンが効いているし薄い布団で問題ない。寝台特急は暖かい時期に乗るのがよさそうだということもわかってきた。

go の処理系も驚く sdk のコード生成

0時に寝て何度か起きて6時に起きた。そのまま7時過ぎまでだらだらしてた。しんどい。

msgraph-sdk-go のサイズ問題

先週 msgraph-sdk-go を使った開発 を終えてデプロイする段階になってライブラリのサイズが大きくて、コンパイル速度が遅くなったり、バイナリサイズが大きくなったりする弊害があることに気付いた。コンパイル速度は2-3倍遅くなり (3分が7-10分ぐらい)、バイナリサイズも2-3倍大きくなる (30 MiB が 100 MiB とか) 。たまたまこのリポジトリは他のツール類からも依存パッケージとして使われるものなので想定よりも影響が大きいことに気付いた。

朝からチームのメンバーとミーティングして、本来は qa に入ったこの時期にこんな変更をすべきではないが、これは放置するデメリットが大きいのでリポジトリ分割 (モジュール分割) しようと提案して了承を得た。私がやれば作業は1日もあれば完了するだろうと見積もって、見積もり通り、夕方には分割したモジュールをテスト環境にデプロイして当面の解決を得た。アプリケーションのモジュール構造をちゃんとレイヤー化して作ってあるから、今回みたいに急遽、モジュール分割が必要になってもほぼ変更する必要はなかった (たった1箇所だけ) 。

この本質的な問題は次の issue のコメントで説明されている。

ざっと機械翻訳してみる。

コンテキスト

この SDK は kiota を使用してメタデータから自動的に生成されます。オリジナルのメタデータは、Microsoft Graph の配下にあるすべてのサービスチーム(v1用とベータ用)によって入力された CSDL です。この CSDL は最終的に OpenAPI のフォーマットに変換されますが、これは非常に大きなものです(1k 以上のエンドポイント、1.5k のモデル …)。API のサイズが大きいため、完全な API surface の SDK を手作りすることは実現不可能でしょう。

私たちは、SDK を複数のサブモジュール(ファイル用、メール用など)に “スライス” して、人々が関心のあるものだけを簡単に入手できるようにすることをしばらく考えてきました。実際、私たちは PowerShell でこれを実現しました。しかし、“グラフ” の性質(すべてのモデルは互いにある程度関連している)と構築されるアプリケーションの多様性により、スライスは誰にとっても “正しい” ものにはならない(大きすぎたり、小さすぎたり、モデルの重複につながったり…)。

そのような理由から、私たちは「完全なSDK」を提供することにしました。すべての人にとって理想的とは言えないかもしれませんが、Go開発者の中には「アプリケーションを作るためのSDKが欲しいだけ」という人もいると感じています(以下で説明する2つ目のオプションとは対照的です)。

Go の欠点

Go の探求を通して、いくつかの欠点に気づいた。現時点では、私たちのプロジェクトやパッケージが適切にセットアップされていないせいなのか、Go や大規模プロジェクトの制限のせいなのかはわからない:

go build は、変更されておらず、依存関係も変更されていないサブパッケージをリビルドすることが多い。go build が直前に実行されていても、go test がリビルドすることがよくあります。なぜある種のキャッシュに頼らないのでしょうか?同じ問題が go lint にもある。
私には、たくさんのサブパッケージがある大きなプロジェクトをビルドするコストは、依存関係が更新されたり、キャッシュが削除されたり、コードが変更されたりしない限り、「セッションごとに一度」だけ支払われるべきだと感じます。

私たちのプロジェクト構成/構造において、そのような状況を改善するための最適化について、自由に概説してください。また、Goコミュニティ(Goコンパイラを開発している人たちなど)と関わって、そのようなフィードバックを提供する方法があれば、喜んでそうします。私たちのプロジェクトは、その規模の大きさから、世の中にあるほとんどのGoパッケージと比べると、ちょっと変わり者だとわかっています。

適切なサイズの SDK

最後に、すべてのエンドポイントを備えた完全な SDK を持つことは、様々な理由からすべての人に適しているわけではないことを認識しています。私たちは新しい “適切なサイズのセルフサービスSDKエクスペリエンス” を可能にするために取り組んでいます。そこでは、APIユーザーは誰でも、この SDK と同じように見え、同じように感じる SDK を生成することができますが、完全な API サーフェスの代わりに、彼らのアプリケーションのために彼らが気にするエンドポイント/モデルのみが含まれています。 私たちは今、そのような取り組みに本当に早くから取り組んでいますが、それでもフィードバックをいただけるとうれしいです。大まかな手順はこんな感じだ:

  1. 新しいgoプロジェクトを作成するか、既存のプロジェクトを特定する。
  2. kiotaの依存関係を追加するか、msgraph-sdk-go-coreを追加します(これはKiotaの依存関係をプルし、いくつか追加します)。
  3. グラフエクスプローラで必要なリソースを選択(左パネル、2番目のタブ、…、“コレクションに追加”)。
  4. コレクションをプレビューをクリックし、postmanコレクションとしてエクスポートします。
  5. hidi を postmanコレクションと先ほど共有したOpenAPIの完全な説明文と一緒に使って、“フィルタリングされた” OpenAPI フォーマットを生成する。
  6. kiotaを使って、プロジェクトにMicrosoft Graph用のGoクライアントを生成する。
  7. APIの呼び出しを開始する。

この時点で、私たちはこれらのステップをすべて文書化し、効率化するために取り組んでいます(おそらくステップ4~5を圧縮しています)。このアプローチの素晴らしいところは、ステップ5から7までが、Microsoft Graphだけでなく、呼び出したいOpenAPIで記述されたAPIで動作することだ。
繰り返しますが、この最後の提案はまだ初期段階です。自由に試して、様々な場所でフィードバックを提供してください。

この長い投稿で、私たちがどこに向かっているのかが明らかになり、Goコミュニティからこれらの側面すべてについてさらにフィードバックが得られると本当に助かる!

簡単に言えば、ms graph api の体系が巨大過ぎて、その定義は openapi.yaml にあるが、この定義からすべてコード生成すると巨大なモデル定義をもつ sdk が出来上がってしまったという話しである。後半に書いてあるワークアラウンドとして kiota で必要なモデルだけを選択して専用 sdk を生成すればサイズを小さくできるとある。しかし、それはそれで graph explorer で選択しないといけなかったりして面倒そうではある。次のドキュメントでもその手順について書いてある。

うちの用途ではモジュール分割により、局所化したのでひとまずこの問題は大きな影響をもたないようになった。また余裕があるときにモデルを選択して専用 sdk を自動的に生成する仕組みを構築できるならそれに挑戦してもよいかもしれない。

openapi 勉強会

1時に寝て7時に起きた。昨日はたまたま遅くなって0時頃に帰ってきたが、最近は22時前後に帰ってくることが多い。疲労困憊。

nginx でつくるリバースプロキシ

ちょっとググってみつかる記事を参考にして設定したらすぐにできた。私は tls 周りの設定に詳しくないのでこういうまとめ記事はとても助かる。インターネットって便利。

openapi 勉強会

昨日の続き。openapi についてチーム勉強会を開催した。wiki に次の目次で説明を書いてその内容を勉強会で話した。ちょうど1時間におさまって内容もわかりやすいものができたのではないかと自画自賛。毎週のチーム勉強会のネタとしてちょうどよい粒度だった。

  • code generator が fork した背景
  • スキーマ駆動開発
    • メリット
    • デメリット
  • OpenAPI スキーマからドキュメント生成
  • OpenAPI スキーマからコード生成
  • リファレンス

余談だけど、過去に働いていた会社の発表資料が slideshare から docswell というサービスに移管されてた。slideshare は広告が鬱陶しいサービスになってしまってひどいユーザー体験だからこれは適切な判断だと思う。

openapi 再び

1時に寝て何度か起きて7時に起きた。1-2時頃に吐き気がして苦しむ割に後半はなにもなかったかのように眠れることがある。

openapi-generator の調査

毎週の勉強会に向けて最新の openapi-generator を使って出力した go client のコードを読んだりしていた。openapi-generator を簡単に試すためのチュートリアルのようなものとして、過去にリポジトリに整理しておいた。このリポジトリを使うとコード生成とドキュメント生成の両方を試してスキーマがあることのメリットを体験できるようになっている。

chatgpt で遊ぶ

【おあそぶ会】GPT3と遊ぶ に参加した。gpt3 についてちょうさんが調べたメモにも目を通して参考になった。

ちょうさんの gpt3 の説明を聞きながら chatgpt で葬送のフリーレンについてチャットしてた。今日の成果はこれかな。

openapi-ext-tools をまた使う日がきた

0時に寝て4時に起きて7時に起きた。わりとよく眠れた。

ストレッチ

トレーナーさんと月曜日の日本対クロアチア戦の感想を話したりしていた。今日の開脚幅は開始前153cmで、ストレッチ後156cmだった。先週は疲弊と疲労で散々な数値になっていたものが復調してきつつある。今週も毎日8-22時はオフィスで缶詰め状態だった。たくさん座っている (同じ体勢でいる) 時間が増えると筋肉にはよくない。まだまだ右腰と右太もも周りの張りは強く復調にはもう少し時間がかかるようにみえる。一方で忙しさのピークを越したと思うので今週以降は少しペースダウンしながら体作りをしていく。いまお手伝いしている開発は12月にすべての集中力を費やしてもよいと考えている。残りは期間はメンバーに委譲するような体制になるとベストかもしれない。そのための体力づくりは重要。

openapi-ext-tools 再び

github pages ならぬ gitlab pages がある。ふと web api のドキュメントを作るために openapi のスキーマを定義したら gitlab の ci/cd と連携できていいんじゃないかと思い付いた。スキーマがあればフロントエンドのクライアント生成や e2e テストコードの自動生成などに使えるかもしれないし。過去に作った openapi-ext-tools を oss にしておいたからいまも使える。oss 万歳。先のことはわからない。redoc を使ってちゃっちゃと実装した。

pages:
  only:
    changes:
      - schema/*
  stage: deploy
  image: alpine:latest
  before_script:
    - apk --no-cache add python3 nodejs npm
    - python --version
    - python -m ensurepip
    - pip3 --version
    - node --version
    - npm --version
    - npm install --global redoc-cli
    - redoc-cli --version
    - pip3 install openapi-ext-tools
    - pip3 freeze openapi-ext-tools | grep openapi
  script:
    - openapi-spec-cli --spec-path schema/openapi.yml
    - |+
      redoc-cli bundle bundled_openapi.yaml \
        --output index.html \
        --options.expandResponses=all \
        --options.requiredPropsFirst=true \
        --options.jsonSampleExpandLevel=10 \
        --options.hideLoading=true \
        --options.pathInMiddlePanel=true      
    - mkdir -p public
    - mv index.html public/
  artifacts:
    paths:
      - public

久しぶりに触ったら openapi-ext-tools が依存ライブラリの変更で動かなくなっていたので直した。

openapi generator の設定

3時に寝て6時半に起きた。昨日は夕方に昼寝したので夜は眠れなかった。

openapi generator の x-implements 機能

外部ベンダーの api client の wrapper を実装していて、api client が扱うリクエストやレスポンスを型 (インターフェース) で抽象化できるとよさそうと思って openapi generator の設定を調べていた。maven-plugin の設定と openapi-generator の設定の2つがあるので両方のドキュメントを確認しないといけない。

そんなに都合よくインターフェースを指定できるような仕組みがなければ、最悪は mustache テンプレートをカスタマイズするしかないかなぁとか考えていた。テンプレートを操作すると、今後の保守コストが上がってしまうのでそのメリット・デメリットを比較して考えないといけない。諦めかけていたときに so でこの issue をみつけた。

ちょうどこの5月末にリリースされたばかりの 6.0.0 に x-implements と指定すれば、任意のインターフェースを implements できる機能が追加された。これはスキーマに対する設定なのでテンプレートをカスタマイズするよりずっと保守コストは小さくて済む。

例えば、openapi schema の json で設定すると、コード生成したときにそんな風にインターフェースが付く。

       "SomethingApiResponse": {
+        "x-implements": "com.example.app.MyResponse",
         "title": "SomethingApiResponse",
         "type": "object",
         "properties": {
-public class SomethingApiResponse {
+public class SomethingApiResponse implements com.example.app.MyResponse {

あまりにも意図していた機能をみつけて嬉しくてツィートしてしまった。