SORACOMのSDKを生成して使ってみた。 for AngularJS #soracom

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

好物はインフラとフロントエンドのかじわらゆたかです。
本記事はSORACOM Advent Calendar 2015の8日目です。
7日目の延長で、生成したSDKを使ってみたといった軽めのネタを考えていた、そんな時期もありました。

SDKの生成・CORSによるアクセス制御に引っかかる。

前回のNode.jsとほぼ同一になります。

リポジトリ取得・モジュール取得・自動生成

$ git clone hogehogehoge
$ npm install
$ node angularGenerate.js
soracom SDK for angular.js Generated

生成に成功すると以下のディレクトリに生成されたSDKが配置されます。

$ ls ./soracom-sdk/angularjs
soracomSdk.js

生成されたSDKをAngular側から呼んでみます。

LoginCtrl.js

function LoginCtrl($scope,SoracomSDK,$log) {
    'ngInject';

    // ViewModel
    const vm = this;
    vm.doLogin = function(){
        doLogin();
    };

    var doLogin = function(){
        var option = {};
        var formData = $scope.loginForm;
        var email = formData.mail.$modelValue;
        var password = formData.password.$modelValue;
        var soracomService = new SoracomSDK(option);
        var authRequest = {
            'email' : email,
            'password': password,
            'tokenTimeoutSeconds':0
        };
        var req = {};
        req.auth = authRequest;
        var promise = soracomService.auth(req);
        promise.then(successHandler);
        promise['catch'](failHandler);

    };
    var successHandler = function(){
        $log.debug('login success');
    };
    var failHandler = function(){
        $log.debug('login fail');
    };
}

Login.html

    <form class="form-signin" name="loginForm">
        <h2 class="form-signin-heading">Please sign in</h2>
        <label for="inputEmail" class="sr-only">Email address</label>
        <input type="email"  name = "mail" id="inputEmail" class="form-control" placeholder="Email address" required autofocus ng-model="login.mail">
        <label for="inputPassword" class="sr-only">Password</label>
        <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required ng-model="login.password">
        <div class="checkbox">
            <label>
                <input type="checkbox" value="remember-me"> Remember me
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="login.doLogin()">Sign in</button>
    </form>

上記を動かしたところ、以下のようになりました。

SORACOM_20151208_1fix

フロントエンド開発でよくあるCORSによるアクセス制御で引っ掛かっています。
SORACOMのAPIが異なるドメインから呼び出される前提で作られていないため、
このようなエラーになっているものと想定できます。

アドベントカレンダーのネタがCORSによるアクセス制御でWEBブラウザ上からは呼べませんでした、
では流石にあかんと思ったので、呼べる方法を検討してみたいと思います。

要はWebブラウザからAPI直接呼ぶからエラーになるわけで、
それならば手前にサーバーを立てて呼べば良いのですが、それも芸がありません。

というわけで、API GatewayでSORACOMのAPIをラップして呼び出してみればと思い、実施してみました。

Amazon API Gatewayを用いたCORSによるアクセス制御回避

基本編

まずは、以下のエントリーを参考にラップしていきます。
Amazon API Gateway を使って AWS 以外のサービスの API をラップする | Developers.IO

その際、ResouceはラップするAPIと同一にしてください。
/auth をラップするAPIのResouceはauthとします。
また、Method同一に、Endpoint URLも対応するようにします。
SORACOM_20151208_2

以前、API GatewayCORSに対応するためには、以下の様な手順を踏む必要がありました。
Amazon API Gateway をクロスオリジンで呼び出す (CORS) | Developers.IO

11/3のAPI Gatewayの更新でCORSを有効にする機能が追加されました。
Amazon Web Services ブログ: 週刊AWS - 2015年11月2日

今回はその機能を使ってみたいと思います。
作成したResouceを選択し、EnableCORSを押下します。
SORACOM_20151208_3 その後、表示されたEnable CORS and replace existing CORS Headers を押下します。
SORACOM_20151208_4
最後の確認ダイアログもそのままYes,replace existing valuesを押下します。
SORACOM_20151208_5

上記のようにすることで、CORS対応のAPIを作ることができます。

リソースベースのルーティング

SORACOMのAPIをAPI Gatewayでラップするにあたり、
/groups/{group_id}といった形でURL内に操作対象のリソースを埋め込まれている場合、
以下のようにすることでラップすることが可能です。

  1. API GatewayのResouceとして、/groupsとその配下に/{group-id}を作成します。
    SORACOM_20151208_6

  2. GETメソッドを作成し、EndpoitURLのリソースを指定する個所を{}で囲みます。
    今回の場合group-idがパラメータのため、
    Endpointはhttps://api.soracom.io/v1/groups/{group-id} となります。

  3. Integration Request内のURL Path ParametersでAdd pathを押下し、以下のように設定します。

Name Mapped from
group-id method.request.querystring.group-id

SORACOM_20151208_11

HeaderへのRequestTokenの埋め込み

SORACOMのAPIを呼び出す際に、ほとんどのAPIでAPIKeyとTokenをHeaderに設定する必要があります。
SORACOM API 利用ガイド

上記のリソースベースのルーティングでラップした https://api.soracom.io/v1/groups/{group-id}
に対しても設定が必要な為、設定したいと思います。

今回の作例では、フロントからの呼び出しを容易にするため、フロントで設定するheaderはapiKeyとtokenとし、
API Gatewayの中で、soracom用のheader(X-Soracom-API-Key・X-Soracom-Token)に変換することにします。

  • Method Request内にてHTTP Request Headers → Add Headerと進み、以下の項目を追加します。
Name Mapped from
X-Soracom-API-Key method.request.header.apiKey
X-Soracom-Token method.request.header.token

SORACOM_20151208_8

  • Integration Request内にてHTTP Headers内でAdd headerと進み、以下の項目を追加します。
Name Mapped from
X-Soracom-API-Key method.request.header.apiKey
X-Soracom-Token method.request.header.token

SORACOM_20151208_8

  • 最初に書いた基本編に基づき、作成したResouceをCORS対応にします。
    その際今回はRequest HeaderにapiKeyとtokenが増えているので、その旨を記載する必要があります。
    Enable CORSを押下後に、Access-Control-Allow-Headersの中にapiKeyとtokenを追記します。
    また、自動生成したSDKがoperatoridという項目のRequestもするため、その旨も追記します。
    SORACOM_20151208_9

設定が完了したら、Deploy APIを押下し、設定したAPIを使えるようにします。

実装例

生成したSDKで上記で作成したAPIを用いる際は、払いだされたAPIのURLをSDKに指定する必要があります。
ログインして、Headerに埋め込んでGroupの情報を参照するといった流れは以下になります。

LoginCtrl.js

function LoginCtrl($scope,SoracomSDK,$log) {
    'ngInject';

    // ViewModel
    const vm = this;

    var doLogin = function(){
        var option = {'domain':'API Gatewayで払いだされたURL'};
        var soracomService = new SoracomSDK(option);
        var formData = $scope.loginForm;
        var email = formData.mail.$modelValue;
        var password = formData.password.$modelValue;
        var authRequest = {
            'email' : email,
            'password': password,
            'tokenTimeoutSeconds':0
        };
        var req = {};
        req.auth = authRequest;
        var promise = soracomService.auth(req);
        promise.then(successHandler);
        promise['catch'](failHandler);

    };
    var successHandler = function(result){
        $log.debug('login success',result);
        getGroupInfo(result);
    };

    var failHandler = function(){
        $log.debug('login fail');
    };

    var getGroupInfo = function(soracomTokenObj){
        var option = {'domain':'API Gatewayで払いだされたURL'};
        var soracomService = new SoracomSDK(option);
        soracomService.setTokenObj(soracomTokenObj);
        var param = {};
        param['group_id'] = '参照対象のGroup ID';
        var promise = soracomService.get_group(param);
        promise.then(successGetGroupHandler);
        promise['catch'](failGetGroupHandler);

    };
    var successGetGroupHandler = function(result){
        vm.result = result;
        $log.debug('Get Group Info success',result);
    };
    var failGetGroupHandler = function(){
        $log.debug('Get Group Info fail');
    };

    vm.doLogin = doLogin;
}

SORACOM_20151208_10

まとめ

AngularからSORACOMのAPIが簡単に呼べました、みたいなネタにするはずが、
API Gatewayを使ってSORACOM APIをラップするといった内容になってしまいました。

今回はSORACOMのAPIを対象にしましたが、
Swaggerが公開されているAPIならば同様の方法を用いることができるかと思います。