ばあちゃんにひ孫の写真をAPIで印刷＆郵送する(AWS Lambda x Amazon S3 x Lob.com)

ひ孫の写真をAmazon S3にアップロードすると、AWS Lambdaがそのイベントをとらえて、Lob.comのAPIをたたき、自動的に写真が印刷されて、ばあちゃんの家に郵便で届く、という人間的に暖かみのあるシステムを作ってみたいと思います。

うちでは、ばあちゃん(98歳)が健在なのですが、ひ孫(私の子供)の写真を一日中見て喜んでくれているようなので、API経由でとったばかりの写真をタイムリーに印刷＆郵送するともっと喜んでくれるかもしれません。
![lambda.png](https://qiita-image-store.s3.amazonaws.com/0/6069/428b633a-ffd7-feb5-7cc4-10c46510872a.png "lambda.png")

# Lob.comとは
[Lob.com](https://lob.com/)は、Printing API (印刷API)を提供しているサンフランシスコのサービスで、APIを通じて実際に写真や印刷物の印刷と郵送を依頼することができます。APIをたたいてから2、3営業日で印刷され、米国のみならず日本にも郵便物を配送できます。値段は[こちら](https://lob.com/pricing)で、写真を日本に送る場合は1枚で$1.84です。

# 全体の流れ

全体の流れとしては、まずはLob.comの準備をしておき、その後、Lambdaのコードを書き、S3とLambdaを設定しましょう。S3に写真がアップロードされたイベントがLambdaに対してPushされるので、それを捕まえてLob.comのAPIをたたくことになります。

* Lob.comのセットアップ
* AWS Lambdaで実行するコードの準備
* Amazon S3のセットアップ
* AWS Lambdaのセットアップ
* 仕上げ


# Lob.comのセットアップ
[Lob.com](https://lob.com/)からアカウントを作ります。Lob.comのAPIを使って郵便物を送る手順は以下の通りです。
* 宛先と送り元を指定する「アドレス」を作る
* 写真や印刷物などの「オブジェクト」を作る
* 印刷して郵送を指示する「ジョブ」を作る

こちらにあるように、Lob.comでは、REST APIのみならずNode, Ruby, Python, PHPのSDKが存在しています。
![2014-12-07_20-12-40.png](https://qiita-image-store.s3.amazonaws.com/0/6069/e67b0895-ab16-d364-fe85-b69b3fc6ae44.png "2014-12-07_20-12-40.png")


Lob.comにはDashboardも用意されており、WebコンソールからAPIをたたくことも可能です。例えば、宛先アドレスの設定はこちらからできます。
![2014-12-07_20-12-41.png](https://qiita-image-store.s3.amazonaws.com/0/6069/669a02b6-ada1-f849-5874-fd8110ec8d79.png "2014-12-07_20-12-41.png")

宛先と送り主のアドレスを作っておきます。
![2014-12-07_20-12-42.png](https://qiita-image-store.s3.amazonaws.com/0/6069/78e6de80-a794-321e-fc4a-df3686fb69a8.png "2014-12-07_20-12-42.png")

写真などをアップロードすることで、実際の印刷対象となるオブジェクトを作成することもできます。
![2014-12-07_20-12-41_01.png](https://qiita-image-store.s3.amazonaws.com/0/6069/9cb5d048-9614-27ea-0ad9-2f1509522a57.png "2014-12-07_20-12-41_01.png")

![2014-12-07_20-23-40.png](https://qiita-image-store.s3.amazonaws.com/0/6069/8cf06bce-52f0-2543-f316-eb2e26a4f653.png "2014-12-07_20-23-40.png")

上記のアドレス作成、オブジェクトアップロードをしておくことで、印刷を行うジョブもこちらから作れます。そうすると、Lob.comのほうで印刷されて宛先に郵送される、という仕組みになっています。

![2014-12-07_20-24-20.png](https://qiita-image-store.s3.amazonaws.com/0/6069/5159c983-ce2d-5eb7-b8d6-ae0c563ff76a.png "2014-12-07_20-24-20.png")


Webコンソールにしろ、APIにしろ、テストモードが用意されており、思う存分APIをたたいて実験することができます。
![2014-12-07_20-12-39_01.png](https://qiita-image-store.s3.amazonaws.com/0/6069/d90028f3-d365-cf4e-a366-b3e3d722e83d.png "2014-12-07_20-12-39_01.png")


Webコンソールではなく、下記のようにcurlでtest用のクレデンシャルを用いてAPIをたたくとジョブが作成されます。認証用のクレデンシャルにもテスト用があるので安心です。

```
curl https://api.lob.com/v1/jobs 
	-u test_6ef7ef33b91586a9e52e094e6d7xxxxx: //クレデンシャル
	-d "name=KidsPhoto" 
	-d "to=adr_d43ec8d3d5ddxxxx" 
	-d "from=adr_9770400886ccxxxx" 
	-d "object1=obj_6e82444d14e5xxxx”
```

実際にAPIをたたくとこのように課金がはじまる様子がみられます。
![2014-12-10_00-43-33.png](https://qiita-image-store.s3.amazonaws.com/0/6069/41542e7c-3c8a-6d2c-0d09-4222bf601493.png "2014-12-10_00-43-33.png")

# Lambdaで実行するJavaScriptのコードを準備する

Lambdaで実行するコードを準備します。JavaScriptをたたくのでlobのnode_modulesをインストールしておいて、ローカルの開発環境で色々と試してみると良いでしょう。

```
npm install lob
```

すでにwebコンソールでLobのアドレスとオブジェクトは作っておいたので、ここではジョブだけJavaScriptで実行してみます。

```javascript:s3lob.js
var lobFactory = require('lob');
var Lob = new lobFactory('test_6ef7ef33b91586a9e52e094e6d71f21XXXX');
console.log(Lob);

Lob.jobs.create({
        name: 'Lob Kids Photo Job',
        to: 'adr_d43ec8d3d5ddXXXX',
        from: 'adr_9770400886ccXXXX',
        object1: 'obj_6e82444d14e5XXXX'
        }, function (err, res) {
        console.log(err, res);
});
```

このコードをローカルで実行してみましょう。

```
node s3lob.js
```

するとLobのAPIが実行されて下記のようなJSONが返ってきます。

```js

{ id: 'job_df6b42227877xxxx',
  name: 'Lob Kids Photo Job',
  to: 
   { id: 'adr_d43ec8d3d5ddxxxx',
     name: 'Kiyoko XXXX',
     phone: '+81862XXXXXX',
     email: 'xxxx@gmail.com',
     address_line1: 'XXXX, XXXX, Nakaku',
     address_line2: null,
     address_city: 'Okayama',
     address_state: null,
     address_zip: '703XXXX',
     address_country: 'Japan',
     date_created: '2014-12-07T09:06:09.527Z',
     date_modified: '2014-12-07T09:06:09.527Z',
     object: 'address' },
  from: 
   { id: 'adr_9770400886ccXXX',
     name: 'Ken Tamagawa',
     phone: '+8190XXXXXXXX',
     email: 'XXXX@gmail.com',
     address_line1: 'XXXX, XXXX, Setagaya',
     address_line2: null,
     address_city: 'Tokyo',
     address_state: '157',
     address_zip: 'XXXX',
     address_country: 'Japan',
     date_created: '2014-12-07T09:03:36.778Z',
     date_modified: '2014-12-07T09:03:36.778Z',
     object: 'address' },
  objects: 
   [ { id: 'obj_6e82444d14e5XXXX',
       name: 'Three kids',
       quantity: 1,
       pages: 1,
       full_bleed: 0,
       setting: [Object],
       double_sided: 0,
       url: 'http://assets.lob.com/obj_6e82444d14e58b12.pdf',
       template: 0,
       thumbnails: [Object],
       date_created: '2014-12-07T11:24:21.072Z',
       date_modified: '2014-12-07T11:24:21.072Z',
       object: 'object' } ],
  packaging: 
   { id: 1,
     name: 'Smart Packaging',
     description: 'Automatically determined optimal packaging for safe and secure delivery',
     object: 'packaging' },
  service: null,
  price: '1.84',
  tracking: 
   { id: 'trk_a3a580dfacfe1cea',
     tracking_number: '8977141234567890',
     carrier: 'USPS',
     link: null,
     events: [ [Object] ],
     object: 'tracking' },
  status: 'processed',
  date_created: '2014-12-09T22:31:55.747Z',
  date_modified: '2014-12-09T22:31:55.781Z',
  expected_delivery_date: '2015-01-02',
  object: 'job' }
```

Cool!
では、Lambdaにアップロードするコードは、以下のように作ってみました。

```javascript:lambda_lob.js
var lobFactory = require('lob');
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});

// Lambdaのイベントハンドラ                                                                                      
exports.handler = function(event, context) {
    console.log('Received event:');
    console.log(JSON.stringify(event, null, '  '));

    // イベントからS3のオブジェクトを取得                                               
    var bucket = event.Records[0].s3.bucket.name;
    var key = event.Records[0].s3.object.key;

    // S3のsigned URLを取得                                                                                   
    s3.getSignedUrl('getObject', {Bucket:bucket, Key:key},
        function(err, url) {
            if (err) {
                console.log('error getting object URL');
                context.done('error','error getting URL'+err);
            }
            else {
                console.log('The URL is', url);
                // Lobのセットアップ                                                                                                                                            
                var Lob = new lobFactory('test_6ef7ef33b91586a9e52e094e6d71f21xxxx');
                console.log(Lob);
                // Signed URLを用いてLobのオブジェクトを作成                                                                          
                Lob.objects.create({
                        name: 'kids',
                            file: url,
                            setting_id: 500
                            }, function (err, res) {
                        console.log(err, res);
                        console.log("lobobject:" + res.id);
                        // Lobのオブジェクトを使い印刷ジョブを作成                                                                        
                        Lob.jobs.create({
                                name: 'Lob Kids Photo Job',
                                    to: 'adr_d43ec8d3d5ddxxxx',
                                    from: 'adr_9770400886ccxxxx',
                                    object1: res.id
                                    }, function (err, res) {
                                console.log(err, res);
                                context.done(null,'success');//ここで終了
                            });
                       });
            }});
};
```
エラーハンドリングも適当ですし、コールバック地獄になりかけですが、あくまでこういう形ということでご参考までに。

このコードをLambdaにアップロードするために、node_modulesを含めてzipファイルを作成します。node_modulesの中にlobのモジュールを含めてください。


```
zip -r code.zip s3UrlLob.js  node_modules
```
注)node_modulesをパッケージするのははまりがちなので注意。Lob.comのモジュール以外にその他ネイティブモジュールも幾つか必要でした

# Amazon S3を設定する

写真をアップロードする先のバケットを作成しておきます。S3の設定はこれだけです。
![2014-12-10_00-31-06.png](https://qiita-image-store.s3.amazonaws.com/0/6069/dc235be2-8150-eed8-6ff4-6ecfe61f4a25.png "2014-12-10_00-31-06.png")


# AWS Lambdaを設定する
Lambdaの設定をしていきます。S3に写真が投稿された際に実行されるLambdaファンクションを作成します。ここで先ほど作成したzipファイルをアップロードしていきます。
![2014-12-10_00-31-05.png](https://qiita-image-store.s3.amazonaws.com/0/6069/30a12009-f6c2-c6f0-6cf7-a9af9397a300.png "2014-12-10_00-31-05.png")

ほぼデフォルトの設定で構いませんが、APIをたたくのに少し時間がかかるのでタイムアウト時間を20秒くらいに伸ばしておきます。
![2014-12-10_01-08-16.png](https://qiita-image-store.s3.amazonaws.com/0/6069/bd7743fc-1893-1c03-5369-8dc47714dc53.png "2014-12-10_01-08-16.png")


Lambdaファンクションが作成されると、イベントソースを設定します。
![2014-12-10_00-31-07.png](https://qiita-image-store.s3.amazonaws.com/0/6069/85f9f533-1676-be65-2b89-2a6c15d486ee.png "2014-12-10_00-31-07.png")

写真を投稿するバケットを選択し、S3からLambdaを実行するために用いるIAMの設定はそのままで構いません。
![2014-12-10_00-31-07_01.png](https://qiita-image-store.s3.amazonaws.com/0/6069/f5dc7b89-1e8e-854f-aeea-33943fe14305.png "2014-12-10_00-31-07_01.png")

# 仕上げ

さて、では実際にバケットに写真を入れてみましょう！と、その前にじつはLambdaではテスト用に、イベントを仮に発生させてテストするための仕組みがあります。こちらのEdit/Testを押すと、
![2014-12-10_01-10-19.png](https://qiita-image-store.s3.amazonaws.com/0/6069/cc8db3da-dc66-d450-b04d-fb637f105bc8.png "2014-12-10_01-10-19.png")

こちらの画面の左側で発生させたいイベントを選択したり書いたり、関数のコードを直接エディットしたりアップロードすることができます。この状態で右下のInvokeを押すと実際に実行をして、その結果を見ることができます。うまく動かない場合はこちらからzipファイルをアップロードしなおして何度かやることになるかもしれません。
![2014-12-10_01-11-05.png](https://qiita-image-store.s3.amazonaws.com/0/6069/45aeb746-538c-2d9a-dda9-688e174505b5.png "2014-12-10_01-11-05.png")

このテストモードで動作確認をしてから、実際にS3に写真をいれてみましょう。
![2014-12-10_01-17-47.png](https://qiita-image-store.s3.amazonaws.com/0/6069/133b23a9-e10d-b286-c2b9-1e25b2472aa6.png "2014-12-10_01-17-47.png")

例えばこんな写真をいれてみます。
(ちなみに、写真のサイズが日本より少し大きい4x6サイズからの設定なので、4x6に合う形で切り抜き、サイズ変更などして準備しておく必要があります)
![2014-12-10_07-17-32.png](https://qiita-image-store.s3.amazonaws.com/0/6069/6cff1330-1350-88b3-e198-5412df075d2d.png "2014-12-10_07-17-32.png")

そうすると、十数秒すると、このようにジョブがLob.comに作られているのが分かります。これでサンフランシスコ？で印刷されて日本に送られてくるというのは胸熱ですね！
![2014-12-10_01-16-27.png](https://qiita-image-store.s3.amazonaws.com/0/6069/727b0fd7-872f-2dda-06f0-9e1341cfb8cc.png "2014-12-10_01-16-27.png")

写真をアップロードするには、S3のAPIを使うのはもちろん、CloudBerry(Windows)、Cyberduck(Mac OS X)などのS3の無料エクスプローラー的なツールを使うとより簡単に写真をS3にアップロード、Lamdba、Lob.comと連携できるでしょう。

Lambda x S3 x Lob.comで日本全国のおばあちゃんの笑顔をふやせるかもしれません :-)

#さいごに所感
今回Lob.comを使ってみましたが、このようなAPIベースでサービスを従量課金で提供するAWSライクなサービスは初期投資もいらずコーディングで自動化することができるのでエンジニアにとっては非常に有り難いサービスです。これからもどんどん発展していくでしょう。このようなAPIベースの経済圏はAPIエコノミーと呼ばれていますが、APIエコノミー万歳！