【演習】IBM CloudでNode.jsアプリを構築する - Part2: 非同期コールバック

#はじめに
これはIBM Redbooksの[Developing Node.js Applications on IBM Cloud](http://www.redbooks.ibm.com/abstracts/sg248406.html)をなぞってみたものです。
この記事では第2章の「Understanding asynchronous callback」に取り組んでいきます。

この章ではEclipse Orion Web IDEとIBM Cloudを使って、Node.jsベースのサーバアプリを構築します。

コールバック関数とはパラメータとして他の関数に引き渡される関数です。コールバック関数は呼び出された関数が実行された後にのみ実行されます。

この記事ではWatson Language Translatorを使ってコールバック関数の働きを見ていきます。

#必要物
- IBM Cloudアカウント
- インターネットに接続できる環境
- Google Chrome または Firefox
- OS: Linux, Mac OS または Windows

# IBM Cloud上にNode.js環境を立ち上げる
まずはIBM Cloudにログインします。
すると、ダッシュボードが表示されるので、右上の「Create resource」をクリックします。
(あるいは右上の「カタログ」をクリックします。)

カタログに移動したら、左のメニューから「Cloud Foundryアプリ」を選ぶか検索バーに「Node.js」を入力し、「SDK for Node.js」サービスを選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b0a5d2f2-ea5a-1292-f30a-3a1d9bb924c4.png)

任意のアプリ名を入力し、右下の「作成」をクリックします。
このアプリ名はIBM Cloud上の他のアプリと被らないものでなくてはいけません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/d1850b58-928b-2434-bf32-33f7f7678df2.png)

サービスの利用準備ができたら、アプリ名の横の表示が緑に変わりますので、それまで数分待ちます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/d5d0fdb5-0407-ee4f-72a8-2666b45afafc.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/9caaef39-0218-2efb-b58c-28053c63ea20.png)

# Continuous Deliveryを有効化する
「概要」タブに移動します。
下部にある「継続的デリバリー」を「有効化」ボタンを押して有効化します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b85a8acf-29ad-8a5d-4e6d-a1b50eb34c46.png)

Continuous Deliveryツールチェーンの作成画面が現れますので、「地域」と「組織」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/6fe31fb6-13b3-1008-1018-0835baedaf8b.png)

下にスクロールして、「ツール統合」欄を設定します。
「リポジトリタイプ」はデフォルトでは「クローン」が選択されていますが、この演習では「新規作成」に変更します。そのほかの設定はそのままにして「作成」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/bfd6ff35-e794-ca94-144b-690621d6e7f0.png)

作成完了後、以下のような画面が表示されます。
フェーズは「THINK」「CODE」「DELIVER」の3つに分かれています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/1aa1e397-e7c8-9d65-6072-58e359ae7bc1.png)

# Language Translatorサービスを作成する
右上の「カタログ」を新規タブで開きます。
(現在開いているページから開いてしまってもメニューのコンソールからツールチェーンに戻ってくることができます。)
カタログの左側のメニューから「Watson」を選ぶ、あるいは検索バーに入力して「Language Translator」を選択します。
項目を全てデフォルトのまま保持して右下の「作成」をクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/f5db41fc-ac6e-8331-d122-0b3fc20c3a64.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/716a0885-3baa-9e09-f557-d645628861d9.png)

# Node.jsアプリとTranslatorサービスを連携する
作成されたTranslatorサービスの左側の「接続」タブをクリックします。
右側の「接続の作成」をクリックします。
リストから先ほど作成したNode.jsアプリを選択します。
（Watson側からNode.jsにうまく接続できなければ、Node.js側からWatsonに接続してみましょう）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/702c7a54-0432-1ef4-24c9-a75e8a5df2a2.png)

接続する前にあぷりの再ステージをすることを確認されるので「再ステージ」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/fef323dc-accd-c8d3-859c-b9da09418ebf.png)

# Language Translatorの資格情報を獲得する
Language Translatorの「サービス資格情報」タブを選択して、右側の「新規資格情報」をクリックして、出てきたウィンドウの「追加」をクリックします。
「資格情報の表示」をクリックして出てきた情報はどこかにメモしておきましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/65cc1a90-cf35-2ed9-51fa-ddb83bd18409.png)

# Node.jsアプリからLanguage Translatorサービスにアクセスする。
ツールチェーンのEclipse Orion Web IDEをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/af23743a-d177-5ce2-eb9e-59c324cbaf52.png)

左側のメニューにあるアプリ名を右クリックして、「新規作成」→「ファイル」を選択します。
ファイル名を「manifest.yml」としてEnterを押します。
このファイルにはIBM Cloudにアプリをデプロイするための情報が表記されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/52ee2c47-e4ac-c000-fb57-23034c0037a8.png)

ファイルに以下のように記述します。
各項目は各々の環境に合わせて変更してください。
ホスト名は変更していなければアプリ名と同じものになっているはずです。

```manifest.yml
applications:
- path: .
memory: 256M
instances: 1
domain: mybluemix.net
name: vy102-munemasa-nodejs
host: vy102-munemasa-nodejs
disk_quota: 1024M
```


ドメイン(domain)はサービス作成時のリージョンによって変更する必要があります。

```
米国南部 US SOUTH -> mybluemix.net
英国　UNITED KINGDOM -> eu-gb.mybluemix.net
シドニー SYDNEY -> syd.mybluemix.net
ドイツ　GERMANY -> eu-de.mybluemix.net
```

「ファイル」->「保存」でファイルを保存します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/71448f36-b87a-e009-5e09-4744543b16d3.png)

先ほどと同様にして、今度は「package.json」というファイルを新規作成します。
dependenciesの項目で既存のパッケージが使われていることに注目してください。
ここで用いられているwatson-developer-cloudというパッケージはWatson Developer CloudサービスにアクセスするためのNodeライブラリです。つまりLanguage Translatorサービスにアクセスするためにここでは用いられます。

```package.json
{
    "name": "NodejsStarterApp",
    "version": "0.0.1",
    "description": "App for understanding Callback",
    "scripts": {
        "start": "node app.js"
    },
    "dependencies": {
        "watson-developer-cloud": "^1.0.0"
    }
}
```

さらにもう一つ「app.js」というファイルを作成し、以下のように記述して保存します。
1行目のhttpモジュールはhttp関数の呼び出しに用いられます。
2行目はwatsonパッケージにアクセスするためのものです。どのWatsonサービスにもアクセスできますが、ここではLanguage Translatorサービスにアクセスするのに用います。
3行目からのコードはhttpリクエストを得るためのサーバを作成するためのものです。handleRequestという関数はリクエストが出されるたびに呼び出されます。

function handleRequests内の3つの変数はそれぞれ「Language Translatorを使って訳す言葉」「原語の言語」「訳出後の言語」を示しています。Watsonモジュール内のLanguage Translator用のAPIを使うには本来は資格情報などの情報をJSON形式で与える必要がありますが、今回はこのNode.jsアプリにWatsonサービスがバウンドされているため、そういったものを書き出す必要はありません。

コールバック関数はLanguage Translatorサービスが処理を終えてから呼び出されるものです。
errオブジェクトは関数でエラーが起こった際に呼び出されます。
dataオブジェクトはLanguage Translatorサービスで返されるデータを含んでいます。
翻訳データはuserResponseオブジェクトで送られます。

```app.js
const http = require('http');
const watson = require('watson-developer-cloud');

var portNumber = process.env.VCAP_APP_PORT || 8080;
const server = http.createServer(handleRequests);
server.listen(portNumber, function() {
    console.log('Server is up!');
});

function handleRequests(userRequest, userResponse) {
	
	userResponse.writeHead(200, {
		'Content-Type': 'text/plain'
	});

	var helloText = 'Hello';
	var fromLanguage = 'en';
	var toLanguage = 'es';
	var language_translator = watson.language_translator({
		version: 'v2'
	});

	var callback = function(err, data) {
		if (err) {
			console.log(err);
			userResponse.end('Error: ' + err);
		} else {
			console.log(data);
			userResponse.end('Translation of ' + helloText + " is " + data.translations[0].translation);
		}

	};

	language_translator.translate({
		text: helloText,
		source: fromLanguage,
		target: toLanguage
	}, callback);

}
```

```function handleRequests(userRequest, userResponse) ``` の行でエラーが出た場合は、アイコンにカーソルをかざして、「no-unused-paramsを使用不可にする」を選択してください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/9f0899cc-bb0f-77af-ee2d-5829eaee5e43.png)

ツールバーの中に、「新規起動構成の作成」というドロップダウンがありますので、その中の「＋」をクリックします。出てきた設定画面の設定を確認して、正しければ確定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/6b79ad37-83ed-17e7-4d18-a0daf84a38bf.png)

三角形のデプロイボタンをクリックします。
再デプロイの確認画面が出たらOKをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/cb5914a3-0e28-f273-f4fc-749042be1573.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/8662518a-24d6-953c-aac3-7d961eccc2bf.png)

デプロイをしている間は起動構成の部分が「デプロイ中」になります。
デプロイが終わったら同部分は緑の丸がつき、「実行中」と表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/f2bf9a1c-e862-505c-0ab3-de1077c8e1ce.png)

デプロイが終わったら、デプロイされたアプリケーションを開きます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/05a0caa6-d7b6-ae27-6882-fdfbb555521f.png)

HelloがHolaに翻訳されたでしょうか？
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/8860df83-fd3f-81c7-e4c5-5db953c0081a.png)

# Node.jsモジュールからTranslatorサービスにアクセスする
ここではtranslatorという名前のモジュールを作成し、それを使ってLanguage Translatorサービスにアクセスします。

エディタの左のメニューのアプリ名を右クリックし、「新規作成」->「フォルダ」でtranslatorという名称のフォルダを作成します。
translatorフォルダ内に「package.json」という名称のファイルを作成します。
package.jsonファイル内に以下を記述します。

```package.json
{
    "name": "translator",
    "main": "./lib/translator",
    "dependencies": {
        "watson-developer-cloud": "^1.0.0"
    }
}
```

translatorフォルダの中に「lib」フォルダを作成します。
libフォルダの中に「translator.js」ファイルを作成します。
translator.jsファイルはこの時点では空のままにしておきます。

先ほど作成したapp.jsファイルに記述されたコードを全て消して以下のように書き直します。

1行目はhttpモジュールを読み込むためのもので、2行目はtranslatorモジュールを読み込むためのものです。


```app.js
const http = require('http');
const translatorModule = require('./translator');

var helloText = 'Hello';
var fromLanguage = 'en';
var toLanguage = 'es';

var portNumber = process.env.VCAP_APP_PORT || 8080;
const server = http.createServer(handleRequests);
server.listen(portNumber, function() {
	console.log('Server is up!');
});

function handleRequests(userRequest, userResponse) {
	userResponse.writeHead(200, {
		'Content-Type': 'text/plain'
	});

	var callback = function(error, translatorOutput) {
		if (error) {
			userResponse.end(error);
		} else {
			userResponse.end('Translation of ' + helloText + " is " + translatorOutput);
		}
		
	};

	translatorModule.getTranslation(helloText, fromLanguage, toLanguage, callback);
}
```

translator.jsファイルを開き以下のコードを記述します。
getTranslator関数はhelloText、fromLanguage、toLanguageフィールドとコールバック関数を受け取ります。

```translator.js
var watson = require('watson-developer-cloud');

exports.getTranslation = function getTranslation(helloText, fromLanguage, toLanguage, callback) {
	var language_translator = watson.language_translator({
			version: 'v2'
		});
	
		var translatorCallback = function(err, data) {
			if (err) {
				console.log(err);
				callback(err, null);
			} else {
				console.log(data);
				callback(null, data.translations[0].translation);
			}
		
		};
		language_translator.translate({
			text: helloText,
			source: fromLanguage,
			target: toLanguage
		}, translatorCallback);
};
```

デプロイしてアプリを開きます。
うまくできてたら以下のメッセージが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/39db9be9-7468-bd43-041e-18dca2c5e4ab.png)

# アプリを停止する
もし、IBM Cloudライトアカウントをご利用中の場合、メモリは他のアプリも合わせて256MBに制限されています。
そのため、このアプリを停止することで、このアプリで利用中のリソースを他に明け渡したいということがあるかもしれません。
そんな際は、四角い停止ボタンをクリックしブラウザを閉じます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/6d0d6e34-5701-a523-9d96-0ea682fa9a79.png)

#おわり
今回の内容は以上です。
原本である[Developing Node.js Applications on IBM Cloud](http://www.redbooks.ibm.com/abstracts/sg248406.html)には続章がありますので、引き続きまとめられたらいいなと思っています。