0
Help us understand the problem. What are the problem?

posted at

updated at

auカブコム証券のkabuステーションREST APIのテスト用モックサーバーを作る

はじめに

前記事

  1. auカブコム証券のkabuステーションREST APIをcurlで叩く
  2. auカブコム証券のkabuステーションREST APIをjava(generated by the swagger code generator)で叩く
  3. auカブコム証券のkabuステーションREST APIの残高照会をcurlとjavaで叩く
  4. auカブコム証券のkabuステーションREST APIの残高照会から先物OPのdeltaを計算する

最近、思いついたビジネスロジックを、相場がお休みしている土日に作ろうと思ったのですが・・・

システムメンテナンスのため、4/2(土)9:00 ~ 4/3(日)20:00の間、ログインを含む全サービスを停止させていただきます。

ログインを含むというのがミソで、kabuステーションにログインできないと、本番APIが使えないのは当然のこと、検証APIも起動されません。
そこで、swaggerでクライアントスタブをジェネレートできたのなら、モックサーバーもジェネレートできるのでは、ということで自前REST APIを作ってみた。

事前準備

swagger editorを準備してください。
2番目の記事では、dockerから起動しています。

ツール

2番目の記事と同じ。

swagger editor

左側のyamlエディタに以下のURLのデータを貼り付ける。
kabuステーションAPI

[Generate Server]メニューから[spring]を選ぶ。
ダウンロードしたspring-server-generated.zipを適当に解凍する。

eclipse

前回は、mvn eclipse:eclipse して、eclipseにimportしたが、イマイチ問題が多いので、初めからmavenプロジェクトをImport (Maven/Existing Maven Projects)する。
pom.xmlではJava 7を指定して、Java 8の上でJava 7のまま動かすようです。

手動でプロジェクト設定をいじっていたら、

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

が出てから、無駄に時間を食います。未だに未解決。。。
https://github.com/springfox/springfox/issues/3791

コンパイルエラー

BoardApiController.javaがコンパイルエラーとなります。原因はJSON文字列が壊れています。

NG: \"OpeningPriceTime\" : \"8\\"2020-07-22T09:00:00+09:00\\"\",\n

OK: \"OpeningPriceTime\" : \"2020-07-22T09:00:00+09:00\",\n

理由を調べると、kabu_STATION_API.yaml自体にゴミ("8")が入っています。

NG: OpeningPriceTime: 8"2020-07-22T09:00:00+09:00"

OK: OpeningPriceTime: "2020-07-22T09:00:00+09:00"

サーバー起動

eclipseからio.swagger.Swagger2SpringBootを実行します。
ログを見ると、ポート18080を開いて、tomcat 9.0.37が起動します。

curlで認証API

1番目の記事と同じcurlコマンドを-vを付けて叩きます。

curl -v -X POST -H "Content-Type: application/json" "http://localhost:18080/kabusapi/token" -d "@login.txt"

ステータス501で、何も返ってきません。

* Connected to localhost (127.0.0.1) port 18080 (#0)
> POST /kabusapi/token HTTP/1.1
> Host: localhost:18080
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 26
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 501
< Content-Length: 0
< Date: Sat, 02 Apr 2022 15:26:34 GMT
< Connection: close
<
* Closing connection 0

Acceptヘッダ

io.swagger.api.TokenApiController#tokenPost()を確認すると、Acceptヘッダは"application/json"しか受け付けませんが、curlは"*/*"を渡しているので、trueになりません。

String accept = request.getHeader("Accept");
if (accept != null && accept.contains("application/json")) {
  // :
}
return new ResponseEntity<TokenSuccess>(HttpStatus.NOT_IMPLEMENTED);

仕方ないので、curlにAcceptヘッダを追加します。

curl -v -X POST -H "Content-Type: application/json" -H "Accept: application/json" "http://localhost:18080/kabusapi/token" -d "@login.txt"

レスポンスJSONが返ります。

{"ResultCode":0,"Token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

ステータス501

レスポンスJSONは返るが、HTTPステータスが501のままですが、これはHttpStatus.NOT_IMPLEMENTEDをHttpStatus.OKに修正する必要があります。

return new ResponseEntity<TokenSuccess>(objectMapper.readValue("{\n  \"Token\" : \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\n  \"ResultCode\" : 0\n}", TokenSuccess.class), HttpStatus.NOT_IMPLEMENTED);

動作確認

4番目の記事の最新のv3.MainGreeksを実行してみると、残高照会でも501を返してエラーとなるので、HttpStatus.OKに修正します。
モックデータが現物株しか返さないので、デルタ計算のためにオプション情報を取得しないので、0.0を表示して終了します。

class TokenSuccess {
    resultCode: 0
    token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
}
total: 0.0000 0.0000 0.0000 0.0000

その後

記憶では、kabuステーションの検証APIの残高照会は0件しか返さないので、ほとんどテストに使えません。
こうして自前にモックサーバーを立てることで、自分に都合のよいテストデータを返すことが可能となります。
過去の時間足などを蓄積して、隠しパラメータで日時を設定できるようにすれば、もうモックサーバーではなく、シミュレーターになりそうです。

追記:The parameter named "Symbol" does not match the case used in the path "/margin/marginpremium/{symbol}".

kabu_STATION_API.yamlをよく確認すれば、/margin/marginpremium/{symbol}:に対して、パラメータが

name: Symbol
description: 銘柄コード

と大文字になっています。
小文字に修正して、ジェネレートし直します。
→io.swagger.api.MarginApi#marginpremiumGet()の@PathVariable("Symbol")から@PathVariable("symbol")
→io.swagger.api.MarginApiController#marginpremiumGet()の@PathVariable("Symbol")から@PathVariable("symbol")

追記:JUnitテスト拡充(ランキングAPI除外)

2-4番目の記事のクライアント側のJUnitテストを実行できるようにした。
1つランキングAPIがFAILEDとなるが、モックサーバーがJSONでなく、""を返している。
RankingDefaultResponse, RankingByCategoryResponseのようなモデルクラスが多数あるので、パラメータにより返すJSONを切り替えればよいが、実際に動かした結果のサンプルデータがないとモックを作るのも難しいので、JUnit側は@Ignoreをつけて除外する。

追記:ランキングAPI修正

メンテが終了してログインできるようになったので、ランキングJSONを収集する。
モデルがInlineResponse200クラスを返すのがOneOfinlineResponse200インターフェイスのtype判定が上手くいかないようなので、RankingDefaultResponseクラスを直接返すようにする。
クライアント側の@Ignoreを取ったら、成功したので、モックサーバーレベルで一通り呼び出すことができた。

追記:APIパスワードが空文字列の場合は、認証エラーを返す

表題のソース修正をgithubへ反映。

追記:新市場区分に対応

kabu_STATION_API.yamlとspring-server-generated.zipを更新した。

githubソース

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?