Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@Domao

OpenAPI Generatorの出力モデルをMongoDBで扱いたかった(失敗)

OpenAPIとは

OpenAPI(旧 Swagger)というREST API用のインターフェイス定義方法をご存知でしょうか? OpenAPIに沿った定義ファイルを、Stoplight等を用いて書くと、openapi-generator-cli等のジェネレータを使うことで、Go/Python-Flaskなどのサーバーのテンプレート、Go/Python/TypeScript等のクライアントライブラリ、モックサーバー、ドキュメント等をある程度自動で生成させることができます。

ジェネレータを使うことで、一度定義ファイルを用意すれば、サーバー側、クライアント側で1から全部書かずに 自動生成したモデルを利用して割と迅速に実装を進めることができる、らしいです。

今回やろうとしたこと

相当時間をかけてOpenAPIのドキュメントを書いたので、可能な限り自動生成ベースでAPIサーバーを実装したい。
前バージョンはFlaskと生のSQLで割と闇なコードを書いて実装していたので、それをよりまとも(?)にリプレースするためGoとMongoDBで書き直す。そこで、リクエストされたモデルをバリデーションした上でMongoDBに直接保管したい。

使ったジェネレータ

openapi-generator-cliの go-server(net/http) ビルドした。ginやecho(oapi-gen)でもビルドできるようだが、goをはじめて使う自分的には、書く部分がなるべく少ないやつを使いたかったためnet/httpにした。Goに限って言えばGo-Swaggerというのが結構使えるようなのだが現在の定義ファイルの書き方OpenAPI(v3)になる前のSwagger(v2)にしか対応していないようなので、利用することができなかった。

使ったライブラリ

MongoDBオフィシャルのmongo-driverを使った。以前はmgoがかなりおすすめされていたようだが、既にメンテナンスが終わってしまっているので渋々これを使うことにした。

今回ジェネレートしたモデル

アカウントのデータモデルです。アカウント情報作成時にはこのモデルのフィールドをほぼ全部埋めたもの、アカウント情報更新時にはこのモデルの一部フィールドを埋めたものが渡されます。
image.png

MongoDBに登録するモデル

ジェネレートしたモデルで定義されているフィールドと別にMongoDBにだけ保存する 自動ID/Totp用のシークレットを入れたい。なのでこのモデルを埋込み型(継承的なもの)を使って MongoDB保存用のモデルを作成した。
image.png

MongoDBへの挿入

ユーザーという変数を作り、MongoDBに保存したいフィールドを適当に埋める。Insertするときは、Struct型をJSON文字列型に変換し、JSON文字列をBson型に変換することで、MongoDBに挿入できるようになる。(このとき_idフィールドがstringになってしまうので強引ではあるがbytesに戻してある) 元のジェネレートしたモデルに bson:フィールド名という指定がなくてもこれで挿入はうまく行った。
image.png
image.png

MongoDBからの取得

こっちで詰んでしまった。まず、取ってきたものを ジェネレートしたモデルを改変したものにデコードしようとしてみた。するとジェネレートしたモデルに追加した部分しかデータが入らなかった。おそらくジェネレートされた部分にbson:フィールド名の指定がなく、MongoDBから受け取ったフィールドをどこに割り当てていいかわからないってことなのだろう。(だからといってジェネレートされたモデルにbson:部を付けてもなぜか変換できなかったのだが)
image.png
次に、ジェネレートしたモデルそのものにデコードしようとしてみた。 すると(おそらく追加したフィールド分の余ってるデータが原因で?)cannot decode string into a boolean と言われてしまった。booleanを使う部分はジェネレートしたフィールド、追加したフィールドのどこにもないので完全に想定外の動作をさせてしまっているようだ。
image.png
ちなみにフィールドを追加せず、ジェネレートされたモデルそのままのドキュメントを挿入した場合はなぜかデコードできる。(bson:部がないのになぜかできてしまう。)
image.png

ちょっとあがいてみる

Decodeを使わず、一旦bson.Rawにデータを渡し、raw→Bsonモデル→BsonBytes→JsonBytes→Jsonモデルに変換しようとした。ジェネレートしたstructにbsonへの変換フィールドがないってことが原因なら、一旦JSONにしてしまえばJSONのマッピング方法はわかるんじゃないかという魂胆だ。
image.png
その結果のデータは空。というかたぶんそもそもbsonをjsonに変換するのができてない。
image.png
変換途中の出力では、Key/Valueのデータは確かに存在しているのだが、これをStructにマッピングする方法がどうしてもわからないようだ。フィールド名の1文字目が大文字になっているだけで全部同名のフィールドを付けているのだが...
image.png
とにかくこの状態から何かどうこうするのは相当めんどくさいようだった(reflectを使って1つずつ手動でマッピングすればなんとかできるのだろうが、とにかく大変そうである)

今回の結論

OpenAPI-generatorで生成したモデルを使って、そのままMongoDBに挿入/取得することは一応できる。だが、埋込み型(継承)を使ってフィールドを増やすと、挿入ができても取得したときに改変したモデルへのマッピングができなくて詰んでしまう。というか、たぶんMongoDBに直接挿入するのは想定していないらしいしやめたほうが良さそうである。素直にMongoDBに挿入するためだけのモデルを別途用意しましょう。

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Domao
主にPythonで遊んでる人です。最近Goもはじめました。スクレイピングとかたまにします。おうちラズパイ鯖がだいすきです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?