$30 off During Our Annual Pro Sale. View Details »

Rustで始めるコードファーストなOpenAPI定義の生成 🦀

TaKO8Ki
July 26, 2023

Rustで始めるコードファーストなOpenAPI定義の生成 🦀

Rust、何もわからない... #9
https://estie.connpass.com/event/289216/

TaKO8Ki

July 26, 2023
Tweet

More Decks by TaKO8Ki

Other Decks in Technology

Transcript

  1. Rustで始めるコードファーストな
    OpenAPI定義の生成 🦀
    Takayuki Maeda / TaKO8Ki

    View Slide

  2. ● Rust committer (member of
    compiler contributors team
    and diagnostic working group)
    ● Software Engineer
    @Moneyforward
    Takayuki Maeda /
    TaKO8Ki
    @TaKO8Ki
    @TaKOBKi

    View Slide

  3. 何を作っているか?

    View Slide

  4. 何を作っているか?
    ref: https://corp.moneyforward.com/news/release/service/20230216-mf-press/

    View Slide

  5. 何を作っているか?
    国際規格「Peppol(ペポル)」とは、受発注や請求にかかる電子文書をネットワーク上で
    やり取りするための「文書仕様」「ネットワーク」「運用ルール」の規格で、国際的な非営
    利組織であるOPEN PEPPOLが管理しているグローバルな標準規格


    View Slide

  6. What is Peppol?
    ref: Introduction to Peppol AS4 12

    View Slide

  7. What is Peppol?
    ref: Introduction to Peppol AS4 12
    電子文書はXMLでやり取りされ

    View Slide

  8. What is Peppol?

    View Slide

  9. What is Peppol?

    View Slide

  10. What is Peppol?

    View Slide

  11. 何を作っているか?
    自分がいるチームではこのSMPとAccess Pointのラッパー・請求書のvalidatorである
    Peppol APIをRustで開発している。


    View Slide

  12. コードファーストな
    OpenAPI定義の生成を
    したい

    View Slide

  13. コードファーストなOpenAPI定義の生成をしたい
    Peppol APIはマネーフォワードの各プロダクトで利用されるマイクロサービスなので、
    RESTful APIのインターフェイスを定義したい。


    View Slide

  14. コードファーストなOpenAPI定義の生成をしたい
    Peppol APIはマネーフォワードの各プロダクトで利用されるマイクロサービスなので、
    RESTful APIのインターフェイスを定義したい。加えてできるだけ実装と対応した定義に
    したい。


    View Slide

  15. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    コードからOpenAPI documentationを生成し
    たい

    View Slide

  16. コードファーストなOpenAPI定義の生成をしたい
    そこで便利なのが utoipa というcrate

    utoipa - Simple, Fast, Code first and Compile time generated OpenAPI
    documentation for Rust


    View Slide

  17. 例えばこんな感じでattributeを使ってpathを定義できる

    View Slide

  18. 例えばこんな感じでattributeを使ってpathを定義できる

    View Slide

  19. こんな感じでderiveを使ってschemaを定義できる

    View Slide

  20. こんな感じでderiveを使ってschemaを定義できる

    View Slide

  21. Doc用のstructをOpenApi deriveをつけて定義して、

    View Slide

  22. Documentation用のstructをOpenApi deriveをつけて定義して、

    View Slide

  23. こんな感じで定義を出力できます

    View Slide

  24. もしくは、こんな感じでroutingを設定すると/swagger-uiでSwagger UIが見
    られる

    View Slide

  25. コードファーストなOpenAPI定義の生成をしたい
    総じてすごく使いやすい。特にstructにattributeとしてdescriptionやexampleを書け
    るのがメンテナンスしやすくて良い。


    View Slide

  26. コードファーストなOpenAPI定義の生成をしたい
    ただ気をつけないといけないこともある。


    View Slide

  27. コードファーストなOpenAPI定義の生成をしたい
    例えば、utoipaでは、$refでスキーマなどにアクセス可能かどうかは自分達で担保する必要が
    ある。


    > Note! Utoipa does not guarantee that free form ref is accessbile via OpenAPI doc
    or Swagger UI, users are eligible to make these guarantees.




    ref: https://docs.rs/utoipa/latest/utoipa/attr.path.html#request-body-attributes

    View Slide

  28. どういうことかというと、例えばこれは、

    View Slide

  29. こうなって、

    View Slide

  30. Tagをschemasに追加し忘れると存在しないschemaを参照してしまう

    View Slide

  31. また例えば定義されていない型をbodyとして指定すると、

    View Slide

  32. 普通にコンパイルが通り、refになってしまう

    View Slide

  33. コードファーストなOpenAPI定義の生成をしたい
    ToSchema derive、path attributeでは、ユーザーが定義したものについてはrefで参
    照するような実装になっている。つまり、すごく大きなStructなどを扱う時に全ての
    Struct・Enumをschemaとして追加しないといけない?


    View Slide

  34. 壊れにくいOpenAPI
    定義を作るために

    View Slide

  35. 壊れにくいOpenAPI定義を作るために
    まず、新しいpathやschemaを追加した時に、存在しないschemaを参照してOpenAPI
    定義が壊れにくいようにしたい。


    View Slide

  36. そんな時に使えるのが #[schema(inlline)]。これをもとに定義を作ると、

    View Slide

  37. こんな感じでtagがinline化される

    View Slide

  38. 壊れにくいOpenAPI定義を作るために
    これによって存在しないスキーマを参照することを防げる。


    ただ個人的にはデフォルトがinlineでrefにしたい時だけ#[schema(ref)]にするみたい
    な実装の方がユーザ的には使いやすいんじゃないかと思う。結構大きな変更なのでプ
    ルリク投げるか難しいところ。


    View Slide

  39. 壊れにくいOpenAPI定義を作るために
    次に、定義してない型を指定した時にコンパイルエラーになって欲しい。


    View Slide

  40. これも同じようにpathでinlineを使うことで一応防げる。
    下記はコンパイルエラーになる。

    View Slide

  41. 壊れにくいOpenAPI定義を作るために
    本当はinline無しでも定義してない型はコンパイルエラーになって欲しい。


    View Slide

  42. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    じゃあなぜこういう実装になってるのか?

    View Slide

  43. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    ここで少し実装を覗いてみる

    View Slide

  44. 壊れにくいOpenAPI定義を作るために
    まず、 cargo-expand でpathがどういうコードを生成するのか覗いてみる。


    View Slide

  45. View Slide

  46. View Slide

  47. View Slide

  48. utoipa-gen/src/component.rs:746:759

    View Slide

  49. utoipa-gen/src/component.rs:746:759
    受け取ったpathをstringに
    そのnameを渡しているのでコンパイルエラーにな
    らない。

    View Slide

  50. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    じゃあinlineの時はどうなってるか?

    View Slide

  51. utoipa-gen/src/component.rs:726:740
    type_pathがstringに変換されずにそのまま
    使われている

    View Slide

  52. utoipa-gen/src/component.rs:726:740
    type_pathがstringに変換されずにそのまま
    使われている
    じゃあこれをnot inlineでも実現すればいいのでは?

    View Slide

  53. 壊れにくいOpenAPI定義を作るために
    色々みるとpathのbodyなどには定義された型以外も渡ってくることが判明した。


    View Slide

  54. asを使って代替pathを設定でき、これがbodyとして使える。

    View Slide

  55. asを使って代替pathを設定でき、これがbodyとして使える
    このケースでコンパイルエラーになってはいけない

    View Slide

  56. 壊れにくいOpenAPI定義を作るために
    最終的にbodyに定義されていない型が来たときにコンパイルエラーにする実装を途中
    まで書いていたところでこれに気づいて断念した。


    View Slide

  57. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    元の話に戻ります

    View Slide

  58. 壊れにくいOpenAPI定義を作るために
    壊れにくいOpenAPI定義を作るためにというか、openapi.yamlと実装に差分が出ない
    ように別クレートを作ってunit testをその中に含めている。


    View Slide

  59. コードから生成したものと実際の openapi.ymlに差分
    があるかチェックし、あれば更新を促している。

    View Slide

  60. まとめ

    View Slide

  61. © Money Forward, Inc.
    まとめ
    ● Rustでは、utoipaを使ってコードファーストなOpenAPI定義の生成ができ
    る。

    ● utoipaは気をつけるべきポイントはあるが、総じて使いやすい。

    ● 疑問点があったら実装を覗いてみて改善点があればパッチを投げてみる
    のが良さそう。


    自分は最終的にtypo修正だけして終わりました。

    https://github.com/juhaku/utoipa/pull/669


    View Slide

  62. Invoice XML is complicated and
    has “161” elements and some
    attributes.
    We are hiring!

    View Slide

  63. View Slide