JSONスキーマにサーバ側APIについての記述を書けたり、ソースコードが生成できたりするので、いろいろ試してみました。
今回行ったことは次の通りです。
- IIJmioクーポンスイッチAPIのドキュメントを参考に、JSONスキーマを勝手に書いた
- JSONスキーマ用のツールprmdでAPIドキュメントを作成したり、スキーマの検証を行った
- JSONスキーマ用からGo言語のクライアントコードを生成するツールschematicでコードを生成して、それを使った
prmdを導入する
とりあえず、JSONスキーマ用のツールprmdを導入しておきます。gemで一発です。
1 | $ sudo gem install prmd |
JSONスキーマを記述する
IIJmioクーポンスイッチAPIを参考に、JSONスキーマを書いてみました。
prmd
に雛形を作成する機能があるので、それを利用して書いていきます。
1 2 | $ mkdir schemata $ prmd init base > schemata /base .json |
3つのAPIしかないとは言え、JSONスキーマの仕様について調べながらなので書くのに時間がかかりました。
- Understanding JSON Schema: わかりやすいJSONスキーマの解説
- JSON Schema and Hyper-Schema: JSONスキーマの仕様のドラフトなど
なお、prmd
でのスキーマを書く作法的には、メンテナンスしやすいように、APIを機能ごとファイルを分けるように書くとよいみたいです。
基本的には、definitions
あたりにデータやパラメータの定義を書いておき、properties
にAPIの戻り値オブジェクトの定義を書き、links
にそれが返されるAPIの定義を書く、という感じみたいです。
"$ref": "/foo/bar#/hoge/fuga"
みたいなのはJSON Pointerというもので、
この場合は、idが/foo/bar
なオブジェクトの["hoge"]["fuga"]
なオブジェクトを参照するということを意味しています。
ちなみに、既存のサービスとして、Google APIやHeroku Platform APIではAPIにJSONスキーマが用意されているので、それを見て参考にしたりしました。
これらは、例えば次のようにしてJSONスキーマを取得できます (Googleのはブラウザで直接見ることもできます)。
1 2 | curl https: //api .heroku.com /schema -H "Accept: application/vnd.heroku+json; version=3" curl -O https: //www .googleapis.com /discovery/v1/apis |
prmdでAPIドキュメントを作成する
JSONスキーマを書き終えたら、まずprmd
で分割したファイルを結合して、ひとつのスキーマにします。
1 2 | $ mkdir gen $ prmd combine --meta meta /meta .json schemata > gen /schema .json |
なお、meta.jsonは次のようなファイルです。
1 2 3 4 5 6 7 8 9 10 | $ cat meta /meta .json { "description" : "IIJmio Coupon API" , "id" : "iijmio" , "links" : [{ "rel" : "self" }], "title" : "IIJmio" } |
続いて、作成されたスキーマが正しいか検証してみます。
1 | $ prmd verify gen /schema .json |
エラー報告がなければドキュメントを生成してみましょう。
1 | $ prmd doc gen /schema .json > gen /schema .md |
これで生成されたドキュメントはこちら。
Attributesのあたりがやたら見づらいですが、それっぽい感じのドキュメントになっています。
schematicでGoのクライアントコードを生成する
最後に、Go言語のJSONスキーマ用ツールschematicでクライアントコードの雛形をつくって、それを利用してみましょう。
まず、schematicをてきとうに導入します。
1 2 3 | $ git clone https: //github .com /interagent/schematic .git $ cd schematic $ go build cmd /schematic/schematic .go |
schematic
の使いかたは、schematic foo.json > foo.go
でよいのですが、日本語コードが入っているとエラーになるようなので、次のようにてきとうに取り除きました。
1 2 | $ mkdir -p src /iijmio $ schematic =( sed -E 's/"description":.+//' gen /schema .json) > src /iijmio/iijmio .go |
そして、生成されたコードがそのままでは利用できないので、を次のように修正しました。
time
パッケージがなぜかimportされているが、不要なのでコメントアウトしたVersion
が空文字なので入れておいたreq.Header
にX-IIJmio-Developer
とX-IIJmio-Authorization
を入れておいた- コードでは
Get
がトップレベルがリストであるデータ取得を想定しているが、クーポンAPIではそうではないので、リストを取らないようにした
具体的な修正内容はこのコミットを見てください。
あとは、これを利用するメインコードを書いて終わりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package main import ( "iijmio" "fmt" ) func main() { mio := iijmio.NewService( nil ) cs, err := mio.CouponList( nil ) fmt.Println(cs, err) ps, err := mio.PacketList( nil ) fmt.Println(ps, err) } |
これを実行すると次のような感じで出力されます (実際にはインデントや改行は入りません)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | &{[ {[{0xc208249140 bundle 1420} {0xc208249160 bundle 2000} {0xc208249180 topup 0} {0xc2082491a0 topup 0} {0xc2082491c0 topup 0} {0xc2082491e0 topup 0}] hddzzzzzzzz [{[{<nil> sim 1}] true hdoxxxxxxxx GDxxxxxxxxxxxx yyyyyyyyyyy false false false }] Minimum Start} {[{0xc208249330 bundle 120} {0xc208249350 bundle 2000} {0xc208249370 topup 0} {0xc2082493a0 topup 0} {0xc2082493c0 topup 0} {0xc2082493e0 topup 0}] hddxxxxxxxy [{[{<nil> sim 6}] true hdoxxxxxxxy DNxxxxxxxxxxxxy yyyyyyyyyyz false true true }] Minimum Start}] OK} <nil> &{[ {hddxxxxxxxx [{hdoxxxxxxxx [{20141006 33 0} {20141007 64 0} {20141008 60 15} : ]}] Minimum Start} {hddxxxxxxxy [{hdoxxxxxxxxy [{20141006 0 0} {20141007 0 0} {20141008 0 0} ]}] Minimum Start} ] OK} <nil> |
ちなみに、クーポン利用状態変更については、できるかどうか確認していません。というか、メソッドCouponChange
を呼ぶためのコードが私には書けませんでした。
1 2 3 4 5 6 7 8 9 10 11 | func (s *Service) CouponChange(o struct { CouponInfo [] struct { HdoInfo [] struct { CouponUse bool `json:"couponUse"` HdoServiceCode string `json:"hdoServiceCode"` } `json:"hdoInfo"` } `json:"couponInfo"` }) (*Coupon, error ) { var coupon Coupon return &coupon, s.Put(&coupon, fmt.Sprintf( "/coupon/" ), o) } |
おわりに
JSONスキーマをつくって、そこからGo言語コードを自動生成してみました。
JSONオブジェクトを定義するだけではなく、REST APIをより詳しく定義したい場合にはSwaggerというものもあるみたいです。swaggerの仕様にはREST API向けの属性がいろいろ定義されているので便利そうです。
また、今回はJSONスキーマを手書きで書きましたが、ソースコードから自動生成するというアプローチもあるみたいです。例えば、Go言語のbeego Webフレームワークを利用していたら、コードのコメントからSwaggerのスペックファイルを生成したりできるようです。
関連リンク
- Safx: MacでGoのデバッグ環境を構築する
- Safx: Go言語のBDDテストツールGoConveyを使ってみる
- IIJmioクーポンスイッチAPI
- interagent/prmd · GitHub
- interagent/schematic · GitHub
- Schemataのドキュメント
- Understanding JSON Schema — Understanding JSON Schema 1.0 documentation
- JSON Schema and Hyper-Schema
- draft-ietf-appsawg-json-pointer–07 - JavaScript Object Notation (JSON) Pointer
- Swagger: A simple, open standard for describing REST APIs with JSON | Reverb for Developers
- Getting Started - Google APIs Discovery Service — Google Developers
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。