Open4

OpenAPI Generatorのあれこれ

kamimikamimi

このスクラップに書いてることを記事にした

https://zenn.dev/kamimi01/articles/7c6f79e42c3750

Swift OpenAPI Generator

  • Swift Package Pluginで提供されている
  • Appleが提供する純正の生成ツール

使用手順

  1. 以下をAdd Dependenciesする

package pluginを提供する

https://github.com/apple/swift-openapi-generator

生成されたコードから使用される、共通の型と抽象化を提供する

https://github.com/apple/swift-openapi-runtime

生成されたコードはどの特定のHTTPクライアントライブラリにも紐づいていないので、紐付けたいライブラリを選択する。
iOSアプリの場合は、URLSessionを使うが、他の場合はドキュメントに記載されている。

https://github.com/apple/swift-openapi-urlsession

  1. OpenAPI Generator Pluginを使うためのターゲットを設定できる

ターゲットのBuild Phases のRun Build Tool Plug-insでOpenAPI Generatorを追加する

  1. 2つのファイルを作成する

OpenAPI Generatorは2つのファイルを期待している

  • OpenAPI ドキュメント
    • API仕様が定義される
  • plug-in 設定ファイル
    • どのコードをプラグインが生成するかを決める

生成するために忘れちゃいけない項目

  • operationId(MUSTって書いてた)
Unique string used to identify the operation. The id MUST be unique among all operations described in the API. The operationId value is case-sensitive. Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is RECOMMENDED to follow common programming naming conventions.
# Appleのチュートリアル
Provide an operationId to give code generators a useful hint of what to call the generated method.

https://spec.openapis.org/oas/v3.1.0#fixed-fields-7

謎のエラー、、

  1. プラグインを初めて使用するときは、trust&enable allする

原則

  1. OpenAPI ドキュメントを忠実に表現する

OpenAPI ドキュメントは信頼できる情報源とみなされます。ジェネレーターは、可能な限りドキュメントを反映するコードを生成することを目的としています。これには、仕様構造と、OpenAPI ドキュメントの作成者が使用する識別子が含まれます。

その結果、生成されたコードは必ずしも慣用的な Swift スタイルであるとは限らず、独自のカスタム スタイル ガイドラインに準拠しているとは限りません。たとえば、API 操作識別子は lowerCamelCaseではない場合があります。

生成されたコードを特定のスタイルに準拠させる必要がある場合は、OpenAPI ドキュメントを前処理して識別子を更新し、別のコードを生成することをお勧めします。

大きなドキュメントの場合は、これをプログラムで実行することもできます。Swift で実行する場合は、 Swift OpenAPI Generator で使用されるのと同じライブラリであるOpenAPIKitを使用できます。

  1. OpenAPI ドキュメントに合わせて進化するコードを生成する

このジェネレーターは、OpenAPI ドキュメントの進化に応じて人間工学的に進化するコードを生成することを目的としています。

その結果、特に単純な操作の場合、生成されたコードが不必要に冗長になる可能性があります。

この具体的な例は、文書化されたシナリオが 1 つしかない場合の列挙型の使用です。これにより、新しいシナリオが OpenAPI ドキュメントに追加されるときに、生成された Swift コードに新しい列挙型ケースを追加できるようになり、生成されたコードのユーザー エクスペリエンスが向上します。

別の例は、入力型または出力型内での空の構造体の生成です。たとえば、API オペレーションに文書化されたヘッダー フィールドがない場合でも、入力型にはヘッダー フィールドのネストされた構造体が含まれます。

  1. ジェネレーター実装の複雑さを軽減する

一部のジェネレーターは、コード生成プロセスに影響を与える多くのオプションを提供します。プロジェクトの合理化と保守性を維持するために、Swift OpenAPI Generator にはオプションがほとんどありません。

この具体的な例の 1 つは、ユーザーが生成されたコードのアクセス修飾子を構成できないこと、また、生成されたコードは、生成先のターゲット内での名前空間の衝突をまったく考慮していないことです。

代わりに、ユーザーは独自のターゲットにコードを生成し、Swift のモジュール システムを使用して、生成されたコードをそれを使用するコードから分離することをお勧めします。

詳細については、「生成されたコードの API の安定性」を参照してください。

https://swiftpackageindex.com/apple/swift-openapi-generator/0.1.2/documentation/swift-openapi-generator/api-stability-of-generated-code

疑問

  • VSCodeのようにPreviewやコード補完をサポートしてくれない?

    • Xcodeではプレビューやコード補完はサポートしていないので、VSCodeでSwagger ViewerやOpenAPI Lintを使って書く方が便利そう。(間違えた場合にコードが生成できなかったり、おかしなコードが生成されて使えないといったことが発生する)
  • 特殊ケースのコード生成には対応しているか?

    • OpenAPIの仕様として書くことはできるが、生成はされないケースというのがあった。
  • APIの一部として展開するのはどうか?

上記のような複雑なルールがあるため、生成されたコードを他の人が信頼できるように公開しないことをお勧めします。

生成されたコードをパッケージのAPIの一部として公開する場合、特にパッケージがセマンティック・バージョニングを使用している場合は、APIを監査して変更を確認することをお勧めします。

生成されたコードを実装の詳細として使用するSwiftライブラリパッケージを維持することは、生成されたシンボルがあなたの公開APIでエクスポートされない限り、サポートされています(そして推奨されています)

  • 独自のカスタム定義をするのは想定されていないので、swift-openapi-runtimeが(おそらく)依存しているOpenAPIKitを使ってカスタムできる

  • APIのエンドポイントひとつであっても、ビルドに結構な時間がかかる。

    • なんとかしてビルド時間を短くする方法を検討した方が良さそう

https://swagger.io/docs/specification/describing-request-body/multipart-requests/

  • OpenAPIで定義を忘れると、Any型で生成されることもある。書き方にとにかく注意が必要。これはSwift OpenAPI Generatorに限った話ではないが

  • specの記載が間違っていた時のエラーがわかりにくい

    • spectralを入れる。VSCodeのOpenAPI Lintなどを使う。GUIでspecを定義できるサービスを使うなど、あらかじめ間違いを防ぐ仕組みが必要そう。
      • Build Phasesにspectralを入れたチェックを入れようとしたが、Run ScriptをRun Build Tool Plug-insの前にすることができず、実行できなかった

エラーにはならないけど理由がわからず使いたいAPIが見つからないこともある。

  • ドキュメント生成はされるのか?

  • yamlを複数に分けることは対応しているか?

    • してない。Pluginを実行するときにopenapi.yamlまたはopenapi.jsonに統合する必要がある

https://github.com/apple/swift-openapi-generator/issues/25

  • Content-Typeの検証はできるのか?できない。しないでリクエストの処理を始める。JSONDecoderのところでエラーになるので、エラーが不可解な感じがする

https://github.com/apple/swift-openapi-generator/issues/16

  • 生成されたコードはどこにある?
    • DerivedDataの中。

  • specを変えて、DerivedDataの中のコードも変わっているのに、Xcodeが参照しているコードは古いままの場合がある

本家OpenAPI Generatorとの比較

  • 生成されたコードを取り込む手間があった。生成するタイミングも自分で実装する必要があった。

    • 今回はAppleがSwift Package Pluginという形で提供したので、導入コストが低くなっている気がする

本家の順番

  1. 生成する
  2. 取り込む
  3. 実行する
  • 生成したコードを別途管理する必要もあった

Swift OpenAPI Generator

  1. 初期設定さえ終われば、ビルドするたびに最新のコードが生成される
  • 生成が楽
  • source controlに配置する必要もない

https://openapi-generator.tech/docs/generators/swift5/

感想

  • Swiftでモックサーバーの実装をできるのは良さそう
    • バックエンドの実装を待たずにモックサーバーの実装をすることができる
    • 以前TypeScriptで実装されたモックサーバーのメンテナンスをするといったことがあったが、TypScriptに慣れておらず大変だったりした。
  • 違うファイル名で生成はできない
  • 違う拡張子にすることもできない(e.g. yml
  • 間違ったフォーマットの場合はどうなるのか?
kamimikamimi

Swift OpenAPI Generatorのメモ

生成されたコードがスネークケースになる

let response = try await client.completions(Operations.completions.Input(
            body: .json(Components.Schemas.Completions(
                model: "",
                prompt: "",
                temperature: 0.8,
                max_tokens: 200,
                top_p: 1,
                frequency_penalty: 0.0,
                presence_penalty: 1.0
            ))
        ))