loopbackでアプリケーションを作成してユーザ登録APIを作るまで

StrongLoopが開発したloopbackは、Node.jsで作られたフレームワーク(所謂mBaaSのオープンソース実装)で以下の様な特徴があります。

  • RESTなAPIをモデルと対応させて素早く作れる (モデルを作るとCRUDするAPIが出来上がる)
  • モデルはコネクタを介して様々なDBで永続化できる
  • API自体は application/jsonAPIとして操作でき、各種クライアント向けのSDKを備えている

といった特徴があります。

利用実績としては、GoDaddyでpublic reseller APIに利用されている様です。

StrongLoop | GoDaddy Selects StrongLoop to Power Reseller API Platform

SPAなアプリケーションを2011年からREMPSTOBO
そしてRendrを利用してCastoを作ってきましたが、もしかしたらloopbackを使ってAPI開発をよりスムーズに
解決できるのではないか。という期待を抱きつつ試しに触ってみました。

ひとまずゴールとしては、

loopbackでアプリケーションを作成し、APIを介してユーザを作るところまで

とします。

準備

とにもかくにも strongloop コマンドをインストール。*1

$ npm install -g strongloop

これで slc コマンドが使える様になってscaffoldできます。

アプリケーション作成

実際にアプリケーションを作成してみます。(謎のキャラクターが表示されます)

☁  slc loopback remp-api
_-----_
|       |    .--------------------------.
|--(o)--|    |  Let's create a LoopBack |
`---------´   |       application!       |
( _´U`_ )    '--------------------------'
/___A___\
|  ~  |
__'.___.'__
´   `  |° ´ Y `
? What's the name of your application? remp-api
? Enter name of the directory to contain the project: remp-api
create remp-api/
info change the working directory to remp-api
Generating .yo-rc.json
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.
create .editorconfig
create .jshintignore
create .jshintrc
create README.md
(snip)
Next steps:
Change directory to your app
$ cd remp-api
Create a model in your app
$ slc loopback:model
Optional: Enable StrongOps monitoring
$ slc strongops
Run the app
$ slc run .

ユーザーモデルを作成

続いてユーザーを管理するためのモデルを作成してみます。

loopbackではいくつかのbase classが用意されていてその中にUserというクラスが存在するので今回はこれをこのまま利用します。

基本的なユーザ管理をする場合であればbase classのままでよいのですが、何か新たにプロパティ足したい場合は追加をすることもできます。今回はスキップ。

☁  slc loopback:model
? Enter the model name: user
? Select the data-source to attach user to: db (memory)
? Select model's base class: User
? Expose user via the REST API? Yes
? Custom plural form (used to build REST URL):
Let's add some user properties now.
Enter an empty property name when done.
? Property name:
☁  

上の操作で以下の様な要素を持ったユーザ管理のモデルが作成できます。

要素 -
realm (string, optional)
username (string, optional)
credentials (object, optional)
challenges (object, optional)
email (string)
emailVerified (boolean, optional)
verificationToken (string, optional)
status (string, optional)
created (string, optional)
lastUpdated (string, optional)
id (number, optional)

APIを呼び出してみる

では、モデルを作成 = API作成 ができたので実際にHTTPクライアントからAPIを呼び出してみます。

loopbackアプリケーションを起動します。

☁  node .
Browse your REST API at http://0.0.0.0:3000/explorer
Web server listening at: http://0.0.0.0:3000/

おもむろにloopbackアプリケーションを呼び出します。

先ほど作成したUserモデルに対応するAPIのエントリポイントは /api/users に対応しますので、そこへメールアドレスとパスワードをPOSTします。

☁  http -v POST http://0.0.0.0:3000/api/users password=hogehoge email=hideack@remp.jp
POST /api/users HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Length: 52
Content-Type: application/json; charset=utf-8
Host: 0.0.0.0:3000
User-Agent: HTTPie/0.8.0
{
"email": "hideack@remp.jp",
"password": "hogehoge"
}
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Connection: keep-alive
Content-Length: 34
Content-Type: application/json; charset=utf-8
Date: Sat, 02 May 2015 05:27:48 GMT
ETag: W/"22-3085cb67"
Vary: Origin, Accept-Encoding
X-Powered-By: Express
{
"email": "hideack@remp.jp",
"id": 2
}

ステータスコード200が帰って来てユーザを作成することができました。

ログインしてみる

では、先ほど作成したユーザでログインしてみます。ユーザ作成の際に利用したメールアドレスとパスワードを要素としてPOSTします。

ログイン用のエントリポイントは /api/users/login なので、ここにユーザ登録の際に設定したメールアドレスとパスワードを渡します。

☁  http -v POST http://0.0.0.0:3000/api/users/login email=hideack@remp.jp password=hogehoge
POST /api/users/login HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Length: 52
Content-Type: application/json; charset=utf-8
Host: 0.0.0.0:3000
User-Agent: HTTPie/0.8.0
{
"email": "hideack@remp.jp",
"password": "hogehoge"
}
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Connection: keep-alive
Content-Length: 135
Content-Type: application/json; charset=utf-8
Date: Sat, 02 May 2015 05:30:16 GMT
ETag: W/"87-4fc8ab24"
Vary: Origin, Accept-Encoding
X-Powered-By: Express
{
"created": "2015-05-02T05:30:16.742Z",
"id": "r3YFqmceF1INk5FlstmZgO7p2y42TyLYPlzSchPIDIDBlAbuOHQurS5j5gOSH82h",
"ttl": 1209600,
"userId": 2
}

ステータスコード200が帰って来て、API操作用のトークンが戻ってきました。なお、パスワードが誤ると、

HTTP/1.1 401 Unauthorized
{
"error": {
"code": "LOGIN_FAILED",
"message": "login failed",
"name": "Error",
"stack": "Error: login fail...",
"status": 401,
"statusCode": 401
}
}

となって期待通りです。

ログイン時のトークンを利用してユーザ情報を取る

続けて先ほどログイン時に取得したトークンを利用してユーザの情報を取得してみます。

現状だとUserモデルしか存在しないのでできることが少ないですがログインしているユーザ自身の情報を取ってみます。

/api/users/2?access_token=(ログイン時に得たtoken) に対してGETします。

☁  ~  http http://0.0.0.0:3000/api/users/2\?access_token\=r3YFqmceF1INk5FlstmZgO7p2y42TyLYPlzSchPIDIDBlAbuOHQurS5j5gOSH82h
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Connection: keep-alive
Content-Length: 34
Content-Type: application/json; charset=utf-8
Date: Sat, 02 May 2015 05:45:01 GMT
ETag: W/"22-3085cb67"
Vary: Origin, Accept-Encoding
X-Powered-By: Express
{
"email": "hideack@remp.jp",
"id": 2
}

scafoldする際に特にプロパティを追加していないのでidとemailのみ返ります。

API Explorer

ここまでコマンドラインのHTTPクライアントでAPIを操作してみましたが、実はscafoldした時点でStrong Loop API ExplorerというWebアプリが同時に起動しています。

http://0.0.0.0:3000/explorer

へブラウザでアクセスすると以下の様な画面を閲覧できて実際にAPI操作も行うことができます。

まとめ

loopbackを利用して、アプリケーションの作成とユーザを管理するモデルの作成(それと同時にユーザ操作をするAPIの作成)を行ってみました。

気づかれたかもしれませんが、上までの操作だとモデルを保管する先のストレージを何も設定していない状態なのでアプリケーションの再起動で全て消えます...。

データベースの接続はまた次の機会に。

*1:内部的にYeomanを使っているのでYeomanを既に使っていたら npm install -g generator-loopback

RunscopeでAPIの応答を監視する

Runscopeとは

Runscopeは2013年にローンチしされたAPI監視サービスです。

www.runscope.com

メリットとしては、"本番環境で提供しているAPIのステータス監視及び応答内容監視ができる" があります。

単純な死活監視ではなく、APIが返答するJSONXML, HTTPステータスなどの応答内容の監視まで行うことで仮に提供中のAPIから更に外部のAPIと連携しているなどしていた場合、外部のAPIの異常を検知できずにサービスが稼働を続けるということを防ぐことができます。

30日間, 25万リクエストまでであれば無償で試すことができます。

設定

ログイン直後の上部メニューの "Radar Tests" → "Create Test" を指定し、テストグループを作成した後、作成されたテストで "Edit Test" をクリックします。

そうすると下記の様なUIが表示されるため、設定自体はものすごくシンプルでリクエスト対象のURLを指定します。

API呼び出し時に認証がかかる場合は、BASIC認証及び、OAuthのキーも設定することができます。

"Assertions"のタブで期待する応答を指定します。チェックできる項目は以下の内容です。

  • JSON Body
  • XML Body
  • Response Header
  • Text Body
  • Status Code
  • Response Time (ms)
  • Response Size (bytes)

それぞれの内容について期待値を指定しそれを満たしているか否かというチェックが行えます。

例えば、あるAPIを呼び出された時に....

といった所謂APIで監視したくなる要素についてチェックができる様になっています。

この様に各テストを追加していくと各URLのエントリポイントの状態がダッシュボード上で一覧することができます。

テストは毎分〜1日1回の間で実行間隔を設定できる様になっており、また実行結果はSlackに通知させることも可能です。


Runscopeを利用すると死活監視とAPIの応答内容監視も行うことができてサービス内(あるいは会社内等)で複数API外部から利用されたた際にどの様な状態を返しているか という点を主体において監視する際に役立ちそうな印象でした。

また、API内部で更に第三者が運営する別のAPIを呼び出す等の連携が行われている場合、その部分の監視も併せて行えていることになるため、副次的なメリットも大きいかと思いました。

一時帰省

親戚に不幸があったりして一時帰省。

こういう機会が年齢を重ねるとどうしても増えてくる。

ひと通りの用事を済ませて東京に戻る帰路に新しくできていた駅には、壁にぽっかりと満月の様な穴が意図的に作られていて、そこから見える空がとても青く見えた。

evacを使ってスケジュール(iCal)をSlack経由で通知する

evacを公開した時のエントリで、iCalの予定を読み込んでMac上のgrowlに通知する等を紹介したのですが、ペパボ社内でSlackが使われる様になったので通知をSlackに移しました。

やり方としては自分用のチャンネルをSlack上に開設してmention付きで通知させることでMacの前で作業しているときはSlackのnoticeで通知され、ミーティング等でMacの前にいない場合もiPhoneにプッシュ通知されるので便利。(だと思います。)

ペパボでは誰でも利用できる形でtakosanが用意されているので、evacのikachanプラグインを使うことでslack通知させることができます。

といったフローになります。

$ npm install -g evac

evacはインストールができますので、以下の様なレシピを用意します。

ここでは実行時点から1時間以内の予定を通知対象にします。

in:
ical:
url: http://〜/****.ics
within: 1
out:
ikachan:
url: http://******
join: false
channel: "#evactest"
message: "@hideack __word__"

あるいは、incoming webhook が確保できればそちらを使うこともできます。

in:
ical:
url: http://〜/****.ics
within: 1
out:
slack:
url: https://hooks.slack.com/services/****/****/****
format: "@hideack __word__"

こういったYAMLファイルを用意した上でcron等で定期的に以下の様な形でevacに設定を渡すと...

$ evac /home/hideack/evac/plan-notice.yaml

以下の様にslack上で予定が通知されます。

これできっと「あ!」っということが減るはずです。

満開

いい色だった。

perceptual hash(phash)を利用して画像比較をしてみる

突然ですが画像がたくさんあってそれを人の目で分類するのって大変ですよね。

自動でこういったものを分類できないか興味があったので調べてみました。


perceptual hashとは

perceptual hash というのは、ハッシュ関数の実装なのですがSHA1等のハッシュ関数とは違い、以下の様な特徴があります。

  • 得られるハッシュ値は64bit
  • 対象は静止画, 画像, 音声等のマルチメディアデータ
  • コンテンツ内容が類似しているケースでハッシュを得た場合、例えば静止画画像の拡大、縮小といった加工の場合ハッシュ値が全く同じになる
  • また、色調の修正やノイズが加わった場合も得られるハッシュ値間のハミング距離が近くなる
    • 64bitのハッシュ値なので最も遠いハミング距離は64 (=全くコンテンツが異なっている)
    • 逆にハミング距離が0であればperceptual hashで得られた結果上は同一コンテンツ

こういった特徴があるため具体的にWebアプリケーションでの用途を考えると

  • コンテンツの重複判定
  • 類似画像検索

などに使えそうというのは上の特徴でわかるのではないでしょうか。

具体的なアルゴリズムはperceptual hashのオープンソース実装のphashのドキュメントにも記載があるのですが、

  • 対象画像を縮小する
  • 画像の輝度情報に対してDCT(離散コサイン変換)をかける
  • 低周波成分を得て、この部分をハッシュとして扱う

という考え方の様です。DCTまでは理解できたのですが、低周波成分をどの様にハッシュとして扱っているのか理解が届かない...。*1

node.jsで利用する方法

先に書いた http://www.phash.org/ の実装があり、そのラッパーがいくつか公開されています。今回は同名の phash を利用してみます。

で、実際に利用する前には環境にphashとimagemagickがライブラリを入れる必要があるのでMacであれば先に入れておきます。

$ brew install phash imagemagick

で続けてnpmコマンドでインストール。

$ npm install phash

実際に試してみる

では、いくつか画像を用意してハッシュ値の取得と画像の比較を行ってみます。

画像処理ではおなじみのLena画像を利用して実験。その画像をそれぞれ以下の様な加工をしてみて、元画像で得られたハッシュ値とのハミング距離を求めてみます。

  • 画像の縮小 (No.1)
  • 画像のセピア加工 (No.2)
  • 画像のモザイク化 (No.3)
  • 画像上に文字を重ねあわせる(No.4, No.5)
  • 画像を反転させる (No.6)

先ほどインストールしたnpmライブラリを利用してハッシュ値を求めた上で元画像とのハミング距離での比較を行います。

以下が得られた結果です。

No 画像 ハッシュ値 No.0とのハミング距離
0 (元画像) c91cb262775a9800 -
1 (縮小) c91cb262775a9800 0
2 (セピア化) c914b262775a9800 2
3 (モザイク) c98cb022375ad800 8
4 (文字追加) c9acb866635a9800 8
5 (文字記号追加) cb8cba66535a9800 8
6 (反転) dc69f7330287e000 34

perceptual hashの利用例等を見るとハミング距離20前後を閾値としているケースが多かったので、反転させたケース以外は全て同一画像として判断できているといってよさそうです。

反転させるという処理は根本的に画像の作りを変える操作(画素の並びを変える) になるのでハッシュ値は全く異なってしまいますね。仮に類似コンテンツを抽出する目的でphashを利用する場合はこの辺りも配慮する必要がありそうです。

まとめ

  • perceptual hash についてそれとなく把握しました
  • phashという実装がありそれをラップしたnpmライブラリを使ってみました
  • 実際に画像をいくつか用意してハッシュを得てハミング距離を比較し、縮小やセピア加工、文字を上に重ねる等の加工であれば同一コンテンツとして検出できた

*1:詳しい人教えてください...

追記

- 2020/6/6 WordPress移設に伴ってソースコードが読みにくくなっていたのでgistに貼り直しました。

比較三原則

みうらじゅんが各種メディアで唱えているのを読み聞きして賛同している三原則。

  • 自分を親と比べない
  • 自分を友達・知人と比べない
  • 自分を過去の自分と比べない

過去と他人 は変えられないという言葉もある通りの三原則。

とはいえ、どうしても生きていると上の比較はしがちだったりするのが人の常*1なので、そういったときは「癖」がついてしまったと思ってその癖を直していこうと思うとよいのかな。などと、思う今日このごろ。

唯一変えられるとしたら自分が将来向かっていく方向や姿勢ぐらいじゃないだろうか。

最近見事に上の原則を破っていること多かったので書いてみたが、多分、また原則を破ることはあると思うけど、その時に振り返れればそれでいい気もする。

*1:昔はよかった。などと年配の方が言ってしまうのも過去との比較だろうか

evac weekly – スクリーンショット取得プラグイン追加

evac - Node.js based simple aggregator - でこの一週間改良した箇所まとめです。

screenshotプラグイン追加

outputプラグインとしてPhantomJSでスクリーンショットを撮影できるプラグインを追加しました。入力として与えられたURLのスクリーンショットを撮影できる様になりました。

例えばこの様なレシピを用意して、

in:
textfile:
name: /home/hideack/recipe/url.txt
filter:
through:
out:
screenshot:
path: /home/hideack/recipe/screenshots

入力としてURLのリスト、

www.remp.jp/hello
www.yahoo.co.jp
hideack.github.io/evac/

を与えれば、以下の様にスクリーンショットをまとめて撮影することができます。

スクリーンショットはレシピで指定したパスにPNGファイルとして保存されます。

evac Ver.1.3 以降で利用可能です。

evac

Uptime RobotでWebサービスの死活監視する

REMPStobo , Casto のそれぞれのHTTP(S)レベルでの疎通監視に以前から使っていたUptime Robotというサービスのことを書いていなかったので思い出した様に書いてみます。*1

http://uptimerobot.com/

Uptime Robotは50箇所のモニタリングで且つ5分間隔であれば無料で使える監視のWebサービスでHTTP(S)の疎通監視したいサイトを登録すると上の様なダッシュボードで監視状況を見ることができて、且つアラートの通知先として、

  • 基本通知
  • アプリケーション連携
    • Pushbullet, Pushover, HipChat, Slack

が使えます。ちなみにTwitterは通知先のアカウントが @ をフォローすれば、DMで通知が届きます。

またダッシュボードで各監視しているサイトの情報は以下の様な感じでレスポンスタイムの遷移と直近起きたUp / Down 状況とともに確認できます。

無償内でも個人で利用する上では十分な機能を抑えていますので、個人サービス等運営されている方は利用してみてはどうでしょうか。

*1:無論、今日アラートがここから飛んだから書いているということは言うまでもない訳で...。