5分くらいでわかるSwagger

最近身の回りでSwaggerを聞くことが多くなってきたと感じます。

上記記事でも言及されていますが、AWS API Gateway Importerでも、APIAPI GatewayにインポートするためのAPIの入力仕様としてSwaggerが使われています。
私もちょっと前から、AWS API Gatewayを使ってます。API GatewayAPIを作成するにあたり、GUIからポチポチAPIを作っていくのが辛いというのは多くの人の共通認識だと思うので、AWS謹製のインポータを使うのが今のところは最善の選択だと実感しています。

というわけで、Swaggerについての簡単なまとめ資料みたいなものを作りました。

Swaggerで調べると、Swagger codegenやSwagger UIをメインにした記事は多いのですが、Swagger Specificationの書き方そのものについて紹介している記事って少ないなぁと感じたので、その辺を意識して作りました。とはいえ、Swagger Specificationの構文はシンプルであり、公式ドキュメントも丁寧なので、5分くらいでざっくり分かる、というレベルにしています。

スライドでは触れていないですが、Swaggerの場合、'x-'で始まる独自の拡張項目を使うこともできて、AWS API Gateway Importerの場合、 x-amazon-apigateway-auth, x-amazon-apigateway-integration がそれに該当します。
これらについては、資料の趣旨が変わるので、別の機会にまとめようかと思います。

追記

Swaggerの周辺ツールとして、SpringのアノテーションからSwagger Specification(JSON)を生成するSpringFoxというものがあることを知りました。

Google Maps API v3で任意のDOMをマーカーにしてアニメーションで移動させたい

なぜかGoogle Maps APIを使うことがここ最近は増えました。

さて、Google Maps API v3を使って地図上にマーカーを表示させる場合、標準APIだけだと少し不足感を感じることがあります。
まず、標準APIで使えるのはデフォルトのピン(画像データ)や、指定した画像、SVG Path記法などに限られます。
また、アニメーションによってマーカーを移動させることはできません。標準で用意されているマーカーアニメーションもあるにはある ( Marker Animations)のですが、指定した位置にマーカーが降ってきてバウンドする、といったものなので、アニメーションによるマーカーの移動はできません。地図自体は panTo メソッドによって移動することができるのですが、マーカーだけをアニメーションでスムーズに移動することはできません。

Google Maps APIをサポートするライブラリ

というわけで、標準APIでできないことを可能にするために提供されているライブラリについて調査。

  • Google Maps API Libraries
    • Googleが出しているライブラリ郡です。GitHubのGoogle Maps OrganizationにはJavaPython用のクライアントなんかもあります。
    • 「任意のDOMをマーカーにする」というのは、まさにrich markerで実現可能です。
    • また、MarkerWithLabelというものもあります。マーカーにラベルを表示させるものですが、labelContentプロパティには任意のDOMを指定することができます。
  • SlidingMarker
    • Google謹製ではないですが、マーカーをアニメーションさせるためのライブラリ。marker-animateをベースに、移動後にマーカーがバウンドしないように改良されたものです。アニメーション処理自体はjQuey easing(animate)で行われているみたいですね。

結論

というわけで、SlidingMarkerとMarkerWithLabelを組み合わせることにしました。なぜこの組み合わせかというと、一番の理由は、そのまんまのデモが用意されているからですw

https://github.com/terikon/marker-animate-unobtrusive/blob/master/demo/markerwithlabelmove-sliding.html

webpackでビルドできるようにして使う

ここからは蛇足。

SlidingMarkerとMarkerWithLabelを使った別の理由として、SlidingMarkerとMarkerWithLabelのどちらもnpmとして提供されているのもポイントでした。最近はwebpackを使っているので、モジュールはBowerでなくnpmで提供されているものだけに集約したいのです。まぁ、js-rich-markerはbowerで提供されており、bowerで提供されているライブラリの依存解決もwebpackならできるため(Usage with Bower)、SlidingMarker + RichMarkerの組み合わせがベストっぽいですが…。

それはさておき、webpackでビルドすることを前提に、フロント用のJSもCommonJSスタイルで開発する際の手順ですが、2つポイントがありました。

  • 【ポイント1】デモで使われている markerwithlabel は、npmで提供されているものでなく、独自にカスタマイズされたもの(markerwithlabel.terikon.js)を使わなければならないです。
    • カスタマイズ版を使わないと、マーカーはきちんとアニメーションしますがラベルはアニメーションしないので、本体のマーカーが移動後に瞬間移動する、という動きになってしまいます。
    • すなわち、結局npmで提供されているmarkerwithlabelは使えませんでした…。
  • 【ポイント2】SlidingMarkerとMarkerWithLabelも共にgoogle.maps.Markerを拡張したものであるため、google.maps.MarkerをSligindMarkerでグローバルに置き換える必要があります。

MarkerWithLabelとしてmarkerwithlabel.terikon.jsを使うようにする

webpackの設定は webpack.config.js で記述します。ポイント的なところだけを抜粋すると以下の様なコンフィグになります。

var path = require('path');

var customMarkerWithLabelPath = path.join(__dirname,
  'node_modules',
  'marker-animate-unobtrusive',
  'vendor',
  'markerwithlabel.terikon.js');

module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: path.resolve(__dirname, 'public', 'js'),
    filename: '[name].js'
  },
  module: {
    loaders: [
      { test: customMarkerWithLabelPath, loader: 'exports?MarkerWithLabel' }
    ]
  },
  resolve: {
    root: __dirname,
    alias: {
      MarkerWithLabel: customMarkerWithLabelPath
    },
    extensions: ['', '.js', '.jsx', 'es6'],
    moduleDirectories: [
      'src',
      'node_modules'
    ]
  }
};

こちらを参考にしました。

2no.hatenablog.com

google.maps.MarkerをSligindMarkerで置き換える

SligingMarker.initializeGloballyを呼び出しても、webpackが色々と良きに計らってくれているため、MarkerWithLabel側が参照するgoogle.maps.Markerは、完全にSlidingMarkerに置き換わりません。
仕方なく、メインエントリ用のjsで以下のようにしました。

require('expose?$!expose?google.maps.Marker!marker-animate-unobtrusive'),

この辺りは、かなりバッドプラクティス感が漂っている気がします…。

そんなわけで、一応やりたいことはできました。SlidingMarker + RichMarkerの組み合わせができるかどうかに続きます(多分)

ちなみに、今回確認に使ったリポジトリはこちら(READMEすら書いていないですが、npm install 後に webpack でビルド後に npm start するとと動きます):

github.com

Spring Session利用時、Redis上のセッション情報をデバッグするツールを作った

Spring Bootによるアプリケーションでは、Spring Sessionという仕組みでRedisにセッション情報を保持させることができ、複数サーバ間でセッション情報を共有する仕組みを簡単に実現できます。

さて、デバッグのために、Redis上のセッション情報の中身を確認したいという状況があったのですが、セッション情報はシリアライズされているため、Redis CLIで取得した結果は読みやすいものとは言えず、もう少し読みやすい形で表示できるツールが欲しいと思いました。

そんなわけで、Spring Sessionに関連するデータをRedisから取得して、テーブル表示するツール(Spring Boot製のWebアプリケーション)を作ってみました。

ちなみに、デシリアライズに失敗した場合は、値を(無理やり)文字列化した値を表示します。

developとトピックブランチだけで運用してたらmasterを作ってなかったので作った

GitHubを使った開発フローで、本流であるdevelopと、プルリクを送るためのトピックブランチを使ってサイクルを回していました。

しばらくコミットを重ね、ようやくリリースに至り、masterブランチにdevelopをマージしようとしたところ、masterブランチを作ってなかったことに気づきました。

そんな時にやった手順。

$ git clone リポジトリのURL
$ git rm {すべてのファイル・フォルダ}
$ git checkout --orphan master
$ git commit --allow-empty "first commit"
$ git push origin master
  • 作業場所と別の場所にリポジトリをcloneします。
  • checkout時に --orphan オプションをつけると、空ブランチを作ることができます。
    ただ、既存(コミット済み)のファイルはunstageなファイルとして残るので、ブランチ作成前に削除しています(別の場所にcloneしたのはこのため)
  • 空コミットは --allow-empty オプションをつけて作成します。

改めてブログ記事にするとなんか面倒なことをしてる気もしますが、ともかくこれで、 "first commit" という1つの空コミットだけがある master ブランチをリモートに作成できます。

--orphan オプション便利ですね。GitHub Pagesを作る時なんかにも役立ちそうです。

Boot Campで起動したWindows 7上でUSBメモリが認識しない

(注)プログラムと関係ないですが、ハマった話を書いておきます。

タイトルの通りで、Boot Campで起動したWindows 7上でUSBメモリが認識しない現象に遭遇しました。

この手の症状は検索すると色んな対処法が出てくるのですが、どれを試しても解決に至らず困っていたのですが、ようやく解決しました。

環境

現象

  • USBメモリを挿しても、コンピュータのハードディスク一覧に表示されない
  • [コンピュータの管理] → [ディスクの管理] で見ても、ボリュームが表示されていない
  • 手持ちのUSBメモリ何本かで試したが、症状は同じ(=USBメモリの問題ではない)
  • USB HDD自体は振動しているので、電気信号は通っている模様
  • USBメモリを挿すポートを変えても、症状は同じ
  • デバイスマネージャのユニバーサルシリアルバスコントローラを確認しても、異常なし
  • Windows 7をインストールしてからずっと同じ症状

試したけど効果がなかったこと

原因と対処

結局ドライバの問題でした…。

  1. デバイスマネージャの表示がデフォルトで、「デバイス(種類別)」になっているので、「デバイス(接続別)」に表示を切り替え
  2. 「ACPI x64-based PC」→「PCIバス」→「インテル(R) USB 3.0 eXtensibleホスト・コントローラー」→「インテル(R) USB 3.0 ルートハブ」と辿る
  3. すると「不明なデバイス」が存在(!)
  4. 上記デバイスのプロパティを開き、ドライバーを更新(最新版を自動検索)

ドライバーの更新が終わると、ドライバーインストール後にUSBメモリが認識されるようになりました。よかったよかった。
デバイスマネージャの表示を切り替えないと「不明なデバイス」が現れないのが罠ですね。

GitHub FlowとPull Request駆動開発をYWTでふりかえり

最近関わったプロジェクトでは、GitHub FlowとPull Requestベースの開発を行いました。
個人的には、まともにこのスタイルで開発するのは初めてだったということもあり、YWT(やったこと、わかったこと、次にやること)形式でふりかえった結果を紹介していきます*1

モダンな開発現場では当たり前に行われていることなので、「今更?」と思うかもしれませんが、「本やWebを真似しても、小難しいコマンドがたくさん出てくるばかりで、本当に開発効率が上がってるのか分からない゚・(ノД`)・゚・。」みたいな人もいるのではと思います(私がそうでした)。

ちなみに、プロジェクト開始前の自分のGit/GitHubスキルは以下のとおりでした。

  • GitHubは使っていたが、一人でmasterブランチに対して「add->commit->push」を行うだけ
  • 書籍「GitHub実践入門」を通読した

というわけで、「実践」してみて分かった内容として、重箱の隅っぽいところを紹介していきたいと思います。
なお本エントリはGitやGitHubGitHub Flowそのものについての解説記事ではないのであしからず。。

以下、プルリクエストはPRと略します。

Y: やったこと

Y1) ごく普通のGitHub Flow

  • masterブランチとPR用のトピックブランチのみの運用
  • masterに対してpushは厳禁
  • トピックブランチはレビュー後にmasterにマージ

また、hubSourceTreeなどは使わず、Git CLIGitHubのUIのみ使用。

Y2) レビュー依頼はチャットで

PRを誰がレビューするかは、GitHubのAssigneeは使わずに、HipChatでメンションする形式でした。
規模が小さいから可能な運用かもしれないです。

Y3) PullRequestはフォーマットを決めて

テンプレートについては、『Pull Request のフォーマットを決めるとレビューの効率が3倍よくなる(Crocos Engineering Blog)』を参考にしました。

テンプレート自体は社内のWikiにあって、コピペして、PRを送信する、という流れです(記事と同じですね)。

開発当初は、上記の記事で紹介されているフォーマットをほぼそのまま使っていました。
ただ、テンプレートを埋める時間が結構な手間になっていたので、最終的には以下の通り、かなりシンプルなテンプレートになりました。

## 概要
## 詳細(主に技術的変更点)
## 使い方・確認方法(設定変更やコマンドが必要ならばそれも書く)
## 保留した項目・TODOリスト
## その他(レビューで見てもらいたい点、不安な点、参考URLなど)

Y4) レビューする時は git stash で今の作業を退避させる

プロジェクトの特性上、レビューはGitHub上でdiffを眺めるだけでなく、手元で動かして確認することが多かったです。

$ git stash
$ git checkout master
$ git fetch
$ git checkout feature-to-review
  • git stash
    • 作業中ブランチの変更を一時的に退避
  • git checkout master
    • masterブランチに移動(1つ前のブランチに戻るのと同じことなので git checkout - でも可)。
  • git fetch
  • git checkout feature-to-review
    • レビュー対象のブランチ(feature-to-review)に移動

これで手元にレビュー対象のコードが置かれたことになるので、実際に動かして確認します。実際に動かすために、PRのテンプレートにある「使い方・確認方法」の項目が役立ちます。
もちろん、動くだけでなくコードそのもののレビューも一緒に行います。

レビューが終わったら、感謝と賞賛の気持ちを込めて LGTM :+1: し、以下の通り、元の作業に戻ります。

$ git checkout my-feature
$ git stash apply
  • git checkout my-feature
    • 作業中ブランチ(my-feature)に戻る。
  • git stash apply
    • 退避した内容を戻す。

Y5) マージはGitHub画面から

トピックブランチのマージは画面から行います。マージするのはレビュアーでなく依頼者(コード書いた人)。

GitHubの画面からマージできない=コンフリクトが起きている、なので、マージする人の責任でコンフリクトを解消します。

Y6) PullRequestのラベルを活用

なるべくPRがクローズされるまでのサイクルは短くすべきですが、それでも溜まる時は溜まってしまいます。

一覧で見た時に状況がぱっと見で分かるように、ラベルを活用しました。
作ったラベルは以下です。

  • WIP
  • レビュー待ち
  • 緊急・重要
  • 緊急・重要じゃない
  • 緊急じゃない・重要
  • 緊急じゃない・重要じゃない

下4つは「7つの習慣」にある時間管理マトリクスからですね。

Y7) コマンドでGitを使う場合、git-prompt, git-completionが便利

コマンドでGitを操作していると、以下のあたりが作業効率を下げます。

  • git status を何度も使う。
  • 作業ブランチ名を確認するために、 git branch を何度も使う。
  • トピックブランチの名前はなるべく特徴を表す名前にするため、ブランチに移動するのに長いブランチ名を毎回入力する。

git-prompt, git-completionを使うと、この辺の問題を解決できます。 調べるといっぱい出てくるので、手順は割愛しますが、要するに、プロンプトに各種情報・作業ブランチ名を表示させたり、ブランチ名で補完を効かせる仕組みです。

W: わかったこと

W1) レビューする側の心理

なるべく早くレビューしてあげたいというのが人の心理だと思いますが、レビュー依頼を受けてすぐレビューすると、コンテキストスイッチが発生します。
そこで、毎日この時間(昼休み明け)にレビューする、みたいに自分ルールを決めてレビューするのがよいかなと思います。 「☓☓はいついつにレビューします」「△△は急ぎでレビューしてください」などを朝会・夕会で共有するのも重要ですね。

W2) Gitコマンドは意味を確認しながら動かすと理解が深まる

ネットに載っているコマンドをそのまま打っているだけの時はなかなかGitの操作に馴染めなかったですが、一つ一つ調べながら確認していくと理解が深まっていったと思います。

W3) レビュー中のトピックブランチから次のトピックブランチを作ることの是非

レビュー中のトピックブランチ b1 がある時に、b1 の変更を元にしたトピックブランチ b2 を開発していきたい、というケースでどうするのがベストなのか、悩ましい問題でした。

b1 のレビューで指摘が入ると、b2 側でもそれに伴って rebase/merge したり、修正自体が無駄になることがありますよね。

そもそも、 b1のレビューが完了し、 b1 -> master へのマージが行われて b1 がリモートリポジトリから削除されると、b1 から派生した b2 はどうなるんだ?みたいな問題があります。

先に b2 -> master ないしは b2 -> b1 へのPRを作ってレビューしてもらう、というのも変ですしね。

割とよくあるケースな気もするのですが、他のプロジェクトってどうしてるんでしょう?

T: 次にやること(やりたいこと)

T1) PRの状況をHubotで通知

PRの状況を見に行くのに、毎回GitHubの画面にアクセスしなくても済むように、Hubot等で通知してくれるようにしたいと思います。

T2) PRの作成を楽にする

やったことで述べたとおり、PRのフォーマットは社内のWikiからコピペしていたのですが、これが結構面倒でした。

リンククリックなど、ワンステップでテンプレート付きのPRが作成できるようにしたいです。

GitHub Issueの場合、これが可能です。  https://github.com/{account}/{project}/issues/new?title=aaa&body=bbb みたいなURLで、クエリパラメータにタイトルや本文を渡せるので、これをプロジェクトの README.md に書いておけば、そのリンクをクリックしてテンプレート付きのIssueが作成画面に遷移できるようになります。

同じことをPRでもしたいのですが、PRの場合、URLにマージ元ブランチ名が必要であり、そのブランチ名が分からないので、決まったURLを使えないのですよね。

調べた感じだと、ChromeのExtensionや、hubコマンドで使える仕組みがあるみたいですが。。

おわりに

というわけで、実践してみて色々とわかったことがありました。

  • レビュー中のトピックブランチから次のトピックブランチを作りたい場合の運用
  • ワンステップでテンプレートからPRを作成する方法

上記2点をはじめ、今後も改善しながら続けていきたいです。

*1:念のためですが、プロジェクトのふりかえりも開発チーム内で行いました。

IonicとCordovaのバージョンについて整理してみる

ここ最近はIonicを使ったハイブリッドアプリを開発してました(個人でなく、会社でのプロジェクトです)。

さて、こちらの記事の通り、Cordova Android脆弱性が見つかり、修正版がリリースされました。

IonicではCordovaのAPIを利用してネイティブの機能を使うので、今のプロジェクトでも「脆弱性の影響ないかな?」「そもそも、今のCordovaのバージョンなんだっけ?」と思ったのですが、調べていく過程で色々と混乱してしまっていたので、整理がてらまとめておきます。

Cordova iOS, Cordova Androidのバージョンを調べる

最初に結論なんですが、Ionic(Cordova)プロジェクト内のCordovaのバージョンを調べたい場合、プロジェクトの直下で

# cordova platform list

とすればよいです(Ionicの場合はionic platform listでもOKですね)。

サンプル出力は以下。

Installed platforms: android 4.0.2, ios 3.8.0
Available platforms: amazon-fireos, blackberry10, browser, firefoxos

Cordova Androidが4.0.2、Cordova iOSが3.8.0であることが分かります。

紛らわしいなーと感じた点は、あるハイブリッドアプリについて、以下のバージョンがどれも違うところです(カッコ内は手元の環境におけるバージョンです)

そのため、本エントリにおける以下の説明ではこれらは使い分けていきます。

ionicとionic-cliのバージョンは別物

バージョンを調べたい時は、慣れで

# ionic --version

としてしまいますが、ここで返ってくる結果はionicではなく、ionic-cliのバージョンです。
手元の環境だと1.4.5という結果が返ってきますが、Ionic自体はつい先日1.0がリリースされたばかりなので、Ionicが1.4.5というのはおかしいですしね。

ionicの諸々のバージョンを調べたい場合は、

# ionic info

とすると、以下のような結果が返ります。

Your system information:

Cordova CLI: 5.0.0
Ionic Version: 1.0.0
Ionic CLI Version: 1.4.5
Ionic App Lib Version: 0.0.22
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Mac OS X Mavericks
Node Version: v0.12.4
Xcode version: Xcode 6.2 Build version 6C131e

通常、Ionicはグローバルにインストール( npm install -g ionic )すると思いますが、これでインストールされるのは、正確にはionicではなくionic-cliのようです。

ionic-cliはその名の通りコマンドラインツールであり、ionic-cliにおけるモジュールの依存関係的には ionic-cli -> ionic-app-lib -> cordova-lib となっていて、ionicには依存しません。
ionic-app-libはプロジェクトのジェネレータ、cordova-libはCordovaモジュールです。
というわけで、ionicとionic-cliは直接の依存関係はないし、cordova-libとCordova iOS/Cordova Androidにも直接の依存関係はない、ようです。

じゃあ、Ionicのバージョンはいつ、どこで決まってるのか?

…という疑問が湧いてきたので調べました。

どうやら、ionic startを実行する時(=プロジェクトを作成する時)に決まり、{PROJECT_ROOT}/www/lib/ionic/version.json で定義されるようです*1

流れとして、Ionicプロジェクトを作る場合、ionic start コマンドで雛形を生成することが多いと思いますが、プロジェクトのテンプレートに依らず、基底の雛形である ionic-app-baseというのがいて、その中でIonicのバージョンが指定されています。

つまり、ionic-cliをバージョンアップしなくても、ionic start を実行した時点で最新のIonicに基づくプロジェクト雛形が生成される、ようです(試してないので確証はないですが…)

なお、既存のプロジェクトのIonicをバージョンアップしたい場合は、プロジェクトで

# ionic lib update

とします。この辺りを知っておけば、プロジェクト作成後もionicのバージョンを自分達で管理することができます。

Cordova iOS, Cordova Androidのインストール

各プラットフォーム(Cordova iOS, Cordova Android)は、

# cordova platform add ios
# cordova platform add android

を実行することで、 platforms の下に ios, android ディレクトリが作られ、インストールされます。

プロジェクトのプラットフォーム情報は、package.json および platforms/platforms.json に書き込まれます。

package.json では以下のようにインストールされているプラットフォームが列挙されています(抜粋)。

"cordovaPlatforms": [
  "ios",
  "android"
 ]

platforms/platforms.json には以下のように、プラットフォームとバージョンが記載されています。

{
    "ios" : "3.8.0",
    "android" : "4.0.0"
}

自分達の環境では、IonicプロジェクトをGitでバージョン管理しているのですが、platforms 以下はダウンロードされている依存ライブラリということで .gitignore してます。
インストールするためには

# ionic state restore

とすると、package.json の中身を見てプラットフォームやプラグインをよしなにインストールしてくれます。

バージョンを指定してCordova iOS, Cordova Androidをインストール

2015.5.30時点で、 ionic platform add android を実行しても、Cordova Androidのバージョンとして(リリースされたばかりの)4.0.2ではなく4.0.0がインストールされていまいました…。

そこで、4.0.2をインストールするべく、明示的にバージョンを指定してインストールします。
なお、すでにプラットフォームがインストールされている場合は、一度削除してから追加した方が無難です。

# ionic platform rm android
# ionic platform add android@4.0.2

これでめでたくCordova Android 4.0.2が入りました。package.json

"cordovaPlatforms": [
  "ios",
  {
    "platform": "android",
    "version": "4.0.2",
    "locator": "android@4.0.2"
  }
]

となります(抜粋)。

まとめ

用途 コマンド
ionic, ionic-cli, etcのバージョン確認*2 ionic info
Cordovaのバージョン確認 ionic platform list
ionicのバージョンアップ ionic lib update
Cordovaのバージョンアップ ionic platform rm {ios|android} (インストール済の場合のみ)
ionic platform add {ios|android}@{version}

*1:version.json自体は単なるJSONファイルであり、プロジェクトにおけるIonic本体は、{PROJECT_ROOT}/www/lib/ionic 以下に配置されています。

*2:Ionicプロジェクト以外のディレクトリで実行すると、ionicのバージョンは表示されません。グローバルにインストールされているionic-cli等のバージョンだけが確認できます。