Node.js based smart shopping cart app

Information

In this page, there is English description followed by Japanese one.
本ページは英語の説明の後に日本語の説明が続きます。

Getting Started

Installing Node.js and npm

You need to have Node.js and npm installed.

To check if you have them installed, open a terminal or command prompt and run the following commands:

node --version
npm --version

If you see an number output similar to x.x.x for each command, then they are both installed and you can continue to the next section.

To install on Windows or mac, you can download the installer from https://nodejs.org/en/download.

To install on Linux, you can use a package manager. Instructions for installing Node.js on your distribution can be found in the following link:
https://nodejs.org/en/download/package-manager

Prerequisite

This example assumes that you've already run the following program that can communicate the decoded result of barcode data with mbed Device Connector:

Import programGR-PEACH_mbed-os-client-ZXingSample

Fork of https://developer.mbed.org/teams/Renesas/code/GR-PEACH_mbed-os-client-ZXingSample/

Configuring the Applition

In order to run this app, you need to set up the options shown below via .env file stored in the folder GR-PEACH_mbed-os-QRCode-ShoppingCart-node which should be automatically created when cloning this program.

  • ACCESS_KEY
    Set this variable to your Access Key that you can create on mbed Device Connector. if you don't have an Access Key yet, please create it in accordance with the following procedure.
    1. Access https://connector.mbed.com/#accesskeys.
    2. Click CREATE NEW ACCESS KEY.
    /media/uploads/HinoNaka/screenshot7.png
    3. When prompted, enter a name for the access key (e.g. "demo") and click ADD.
    /media/uploads/HinoNaka/screenshot8.png

    After an access key is successfully created, please copy the access key you just created and use it in .env file as follows:

.env

ACCESS_KEY=...
  • PORT
    Set this variable as follows to override the default port for this example if needed. The default port is 8080.

.env

PORT=...

Also, user need to prepare for the comma-delimited list which includes "Item Name" and "Price" which is added to shopping cart, and save it by the name klist.txt as follows:
/media/uploads/HinoNaka/mbed_shoppingcart_fig8.png
The list should be stored in GR-Peach_mbed-os_QRCode_ShoppingCart_node\public\list.

Application Execution

This application consists of 2 tabs. One is Shopping Cart and the other is QR Code Login. In this section, it is described that how those work.

As mentioned above, GR-PEACH_mbed-os-client-ZXingSample should be executed beforehand. For details on how to set up and execute GR-PEACH_mbed-os-client-ZXingSample, please refer to https://developer.mbed.org/teams/Renesas/code/GR-PEACH_mbed-os-client-ZXingSample/. If the program works properly, you can see the following message in your terminal: /media/uploads/HinoNaka/mbed_shoppingcart_fig4.png

After it is confirmed that the program works properly, please follow the procedure below in order to execute this Web Application.

  • How to invoke this Web Application
    You can now run the application by invoking the following: /media/uploads/HinoNaka/shoppingcart_fig1.png
    Then, you should see the following message if the configuration is successfully done, :
    /media/uploads/HinoNaka/shoppingcart_fig2.png
    Please note that 8080 might be changed. It should depends on your PORT setting mentioned above. If you access the above URL using Web browser, you can see the following Smart Shopping Cart tab by default: /media/uploads/HinoNaka/mbed_shoppingcart_fig3.png

  • How Shopping Cart tab works
    When you decode QR code stored in GR-Peach_mbed-os_QRCode_ShoppingCart_node\qrcode by GR-PEACH successfully, the item should be added to shopping cart as follows:
    /media/uploads/HinoNaka/mbed_shoppingcart_fig5.png

  • How QR Code Login tab works
    When you choose QR Code Login tab, you can see the following screen indicating it's Locked status.
    /media/uploads/HinoNaka/mbed_shoppingcart_fig6.png
    Then, if you decode the Unlock qrcode described in GR-Peach_mbed-os_QRCode_ShoppingCart_node\qrcode\qr.pdf, screen should transit to the following one indicating it's Unlocked status.
    /media/uploads/HinoNaka/mbed_shoppingcart_fig7.png
    After that if you you decode the Lock qrcode described in the above qr.pdf, screen should re-transit to Locked status. In this way, the screen transits to Locked and Unlocked status in accordance with the decoded result of QR code.

How to Deploy this app on HEROKU

HEROKU ( https://id.heroku.com ) is PaaS (Platform as a Service) on which Node.js application can deploy free-of charge. By utilizing it, you can access mbed Shopping Cart and QR Code Login from mobile terminal.

Here is the procedure to deploy this application on HEROKU:

  1. Create your HEROKU account at https://signup.heroku.com/login if you haven't created it yet.
  2. Install Heroku Toolbelt
    32-bit: https://cli-assets.heroku.com/heroku-cli/channels/stable/heroku-cli-x86.exe
    64-bit: https://cli-assets.heroku.com/heroku-cli/channels/stable/heroku-cli-x64.exe )
  3. Launch command prompt (e.g. Windows PowerShell) and move to the project root directory. Then, invoke the command heroku login. At the first time, Email and Password is thought to be required. At that time, please type Email address and password registered at the step 1.
  4. Invoke the command git init
  5. Invoke the command git add . to prepare all the files staged for the next commit
  6. Invoke the command git commit -m "[commit message]" to commit the files staged at the step 5
  7. Invoke the command heroku create. If the command is successfully terminated, the remote repository heroku should be created and associated with the local repository
  8. Invoke the command git push heroku master If the command is successfully terminated, this application should be deployed to HEROKU and its URL should be shown as follows:

remote: -----> Launching...
remote:        Released v3
remote:        https://hogeoge.herokuapp.com/ deployed to Heroku

After all the steps are completed without error, please access the URL http:hogehoge.herokuapp.com/ (i.e URL where https in the URL shown in the step 7 is replaced with http) with Web browser. You should be able to show mbed Shopping Cart on Web browser.

-------------------------------------------------------------------------------------------------------------------------------------------------

Node.jsとnpmのインストール

本アプリケーションを実行するにあたっては、Node.jsとnpmのインストールが必要です。
お使いの開発環境(PC)にNode.js、npmがインストールされているかどうかは、下記コマンドで確認する事ができます。

node --version
npm --version

上記コマンド実行後、x.x.x というバージョン・リビジョン番号が表示されれば、Node.js、npmは既にインストールされていますので、次節以降の手順にしたがってアプリケーションのセットアップを行ってください。未インストールの場合は以降の手順にしたがってNode.js、npmをインストールしてください。

開発環境のホストOSがWindowsまたはmacの場合はhttps://nodejs.org/en/download から入手可能なインストーラを使用してインストールします。

開発環境のホストOSがLinuxの場合は、お使いのディストリビーションに応じたパッケージ管理システムを用いたインストールが可能です。詳細はhttps://nodejs.org/en/download/package-manager を参照ください。

前提条件

本アプリケーションを実行するにあたっては、GR-PEACH上のバーコードデータのデコード結果にmbed Device Connectorからアクセスする下記プログラムを事前にセットアップしておく必要があります。

Import programGR-PEACH_mbed-os-client-ZXingSample

Fork of https://developer.mbed.org/teams/Renesas/code/GR-PEACH_mbed-os-client-ZXingSample/

アプリケーションの設定

本アプリケーションの実行するためには、.envファイルを介して以下に示すオプションを設定する必要があります。なお、.envファイルはGR-PEACH_mbed-os-QRCode-ShoppingCart-nodeフォルダに格納されています。

  • ACCESS_KEY 本変数にはmbed Device Connector上で生成可能なAccess Keyを設定します。Access Keyが未生成の場合は下記に従って作成してください。
    1. https://connector.mbed.com/#accesskeys にアクセスします。
    2. CREATE NEW ACCESS KEYをクリックします。
    /media/uploads/HinoNaka/screenshot7.png
    3. access keyの名称を入力するように促されたら、任意の名称(例えばdemo等)を入力し、ADDをクリックします。
    /media/uploads/HinoNaka/screenshot8.png

    access keyが正常に生成できたら、コピーして下記のように.envファイルでACCESS_KEYに設定します。

.env

ACCESS_KEY=...
  • PORT 本変数にはWebアプリへのアクセスに使用するポート番号を下記のように設定します。デフォルトのポート番号は 8080 です。8080から変更の必要がなければ、設定は省略可能です。

.env

PORT=...

また、ショッピングカートに追加する品目と値段をカンマ区切りで下記のように記載したリストを準備し、klist.txtという名称でGR-Peach_mbed-os_QRCode_ShoppingCart_node\public\listフォルダに格納してください。

/media/uploads/HinoNaka/mbed_shoppingcart_fig8.png

アプリケーションの実行

本アプリケーションでは、Shopping CartQR Code Loginという2つのタブが実装されています。以下、それぞれの動作方法について記載します。
なお上述の通り、本アプリケーションの実行に際しては、事前にGR-PEACH_mbed-os-client-ZXingSampleを実行しておく必要があります。GR-PEACH_mbed-os-client-ZXingSampleのセットアップ方法、および実行方法に関しては、https://developer.mbed.org/teams/Renesas/code/GR-PEACH_mbed-os-client-ZXingSample/ を参照ください。プログラムが正常に動作している場合、下記のメッセージがターミナルに表示されます。
/media/uploads/HinoNaka/mbed_shoppingcart_fig4.png

プログラムの正常動作が確認できたら、以降の手順に従ってWebアプリケーションを実行します。

  • Webアプリケーションの起動
    下記コマンドでWebアプリケーションを起動可能です。
    /media/uploads/HinoNaka/shoppingcart_fig1.png
    上述のアプリケーション設定が正常に行われていれば、下記メッセージが表示されます。
    /media/uploads/HinoNaka/shoppingcart_fig2.png
    なお、8080PORT設定に応じた値となります。
    続いて、上述のURLにWebブラウザでアクセスすると、下記のSmart Shopping Cartの画面が表示されます。
    /media/uploads/HinoNaka/mbed_shoppingcart_fig3.png

  • Shopping Cartタブ
    GR-Peach_mbed-os_QRCode_ShoppingCart_node\qrcode\qrcode.pdf に記載されている Birthday CakeParty CupsBeer KegsPound of beefBuflle-proof vest のQRコードをデコードすると、下記のようにデコードしたアイテムが都度ショッピングカートに追加されます。
    /media/uploads/HinoNaka/mbed_shoppingcart_fig5.png

  • QR Code Loginタブ
    このタブを選択すると、下記のLock状態を示す画面が表示されます。 /media/uploads/HinoNaka/mbed_shoppingcart_fig6.png
    ここで、GR-Peach_mbed-os_QRCode_ShoppingCart_node\qrcode\qrcode.pdf に記載のUnlock QRコードをデコードすると、Unlock状態を示す下記の画面に遷移します。 /media/uploads/HinoNaka/mbed_shoppingcart_fig7.png

    この後、Lock QRコードをデコードすると画面はLock状態に再度遷移します。

    このようにQR Code Loginタブでは、LockUnlockのQRコードをデコードする都度、対応する画面に遷移するアプリケーションです。

HEROKUへのデプロイ

HEROKU ( https://id.heroku.com) はNode.jsアプリが無償でデプロイ可能なPaaS (Platform as a Service)と呼ばれるサービスです。HEROKUを活用する事で、mbed Shopping CartQR Code Loginにモバイル端末等からアクセスする事が可能となります。

以下、HEROKUへ本アプリをデプロイする手順を説明します。

  1. HEROKUのアカウントを所有していない場合は https://signup.heroku.com/login でアカウントを作成してください
  2. Heroku Toolbeltをインストールします
    32-bit版: https://cli-assets.heroku.com/heroku-cli/channels/stable/heroku-cli-x86.exe
    64-bit版: https://cli-assets.heroku.com/heroku-cli/channels/stable/heroku-cli-x64.exe )
  3. ターミナルソフトウェア(例えばWindows PowerShell)を立上げます。そして本アプリのルートディレクトリへ移動し、heroku loginコマンドを実行します。Emailアドレスとパスワードの入力を求められた場合は、アカウント生成時に入力したアドレスとパスワードを入力してください。
  4. git initコマンドを実行します
  5. git add .コマンドを実行し、全ファイルをコミット対象に設定します
  6. git commit -m "[commit message]" で、全ファイルをコミットします
  7. heroku createを実行します。本コマンドが正常終了すると、リモートリポジトリ herokuが設定されます
  8. git push heroku master本コマンドが正常終了すると、HEROKUへのデプロイが完了し、下記のように本WebアプリのURLが表示されます

remote: -----> Launching...
remote:        Released v3
remote:        https://hogeoge.herokuapp.com/ deployed to Heroku

上記の全ての手順が完了したら、http:hogehoge.herokuapp.com/ ( 8. で表示されたURL中のhttpshttpに置換えたもの)へブラウザ等でアクセスします。途中でエラーが発生していなければ、mbed Shopping CartがWebブラウザ上に表示されます。

Committer:
Osamu Nakamura
Date:
Fri Jan 12 19:05:14 2018 +0900
Revision:
0:a40b9a259b52
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Osamu Nakamura 0:a40b9a259b52 1 /*
Osamu Nakamura 0:a40b9a259b52 2 * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
Osamu Nakamura 0:a40b9a259b52 3 * SPDX-License-Identifier: Apache-2.0
Osamu Nakamura 0:a40b9a259b52 4 *
Osamu Nakamura 0:a40b9a259b52 5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
Osamu Nakamura 0:a40b9a259b52 6 * not use this file except in compliance with the License.
Osamu Nakamura 0:a40b9a259b52 7 * You may obtain a copy of the License at
Osamu Nakamura 0:a40b9a259b52 8 *
Osamu Nakamura 0:a40b9a259b52 9 * http://www.apache.org/licenses/LICENSE-2.0
Osamu Nakamura 0:a40b9a259b52 10 *
Osamu Nakamura 0:a40b9a259b52 11 * Unless required by applicable law or agreed to in writing, software
Osamu Nakamura 0:a40b9a259b52 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
Osamu Nakamura 0:a40b9a259b52 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Osamu Nakamura 0:a40b9a259b52 14 * See the License for the specific language governing permissions and
Osamu Nakamura 0:a40b9a259b52 15 * limitations under the License.
Osamu Nakamura 0:a40b9a259b52 16 */
Osamu Nakamura 0:a40b9a259b52 17
Osamu Nakamura 0:a40b9a259b52 18 var urljoin = require('url-join');
Osamu Nakamura 0:a40b9a259b52 19 var util = require('util');
Osamu Nakamura 0:a40b9a259b52 20 var utility = require('./utility');
Osamu Nakamura 0:a40b9a259b52 21 var extend = require('extend');
Osamu Nakamura 0:a40b9a259b52 22
Osamu Nakamura 0:a40b9a259b52 23 var mbedConnectorApi = require('./common');
Osamu Nakamura 0:a40b9a259b52 24
Osamu Nakamura 0:a40b9a259b52 25
Osamu Nakamura 0:a40b9a259b52 26 /**
Osamu Nakamura 0:a40b9a259b52 27 * GETs the current callback data
Osamu Nakamura 0:a40b9a259b52 28 * @param {function} [callback] - A function that is passed the arguments
Osamu Nakamura 0:a40b9a259b52 29 * `(error, callbackData)` where `callbackData` is an object containing a `url`
Osamu Nakamura 0:a40b9a259b52 30 * string and a `headers` object
Osamu Nakamura 0:a40b9a259b52 31 * @param {Object} [options] - Optional options object
Osamu Nakamura 0:a40b9a259b52 32 */
Osamu Nakamura 0:a40b9a259b52 33
Osamu Nakamura 0:a40b9a259b52 34 mbedConnectorApi.prototype.getCallback = function(callback, options) {
Osamu Nakamura 0:a40b9a259b52 35 options = extend(true, {}, this.options, options || {});
Osamu Nakamura 0:a40b9a259b52 36
Osamu Nakamura 0:a40b9a259b52 37 var requestData = {
Osamu Nakamura 0:a40b9a259b52 38 method: 'GET',
Osamu Nakamura 0:a40b9a259b52 39 url: urljoin(options.host,
Osamu Nakamura 0:a40b9a259b52 40 options.restApiVersion,
Osamu Nakamura 0:a40b9a259b52 41 'notification',
Osamu Nakamura 0:a40b9a259b52 42 'callback'),
Osamu Nakamura 0:a40b9a259b52 43 headers: {
Osamu Nakamura 0:a40b9a259b52 44 accept: 'application/json'
Osamu Nakamura 0:a40b9a259b52 45 }
Osamu Nakamura 0:a40b9a259b52 46 }
Osamu Nakamura 0:a40b9a259b52 47
Osamu Nakamura 0:a40b9a259b52 48 this.makeRequest(requestData, function(error, data) {
Osamu Nakamura 0:a40b9a259b52 49 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 50 if (error) {
Osamu Nakamura 0:a40b9a259b52 51 callback(error);
Osamu Nakamura 0:a40b9a259b52 52 } else {
Osamu Nakamura 0:a40b9a259b52 53 callback(null, JSON.parse(data.payload));
Osamu Nakamura 0:a40b9a259b52 54 }
Osamu Nakamura 0:a40b9a259b52 55 }
Osamu Nakamura 0:a40b9a259b52 56 }, options);
Osamu Nakamura 0:a40b9a259b52 57 };
Osamu Nakamura 0:a40b9a259b52 58
Osamu Nakamura 0:a40b9a259b52 59
Osamu Nakamura 0:a40b9a259b52 60 /**
Osamu Nakamura 0:a40b9a259b52 61 * PUTs callback data
Osamu Nakamura 0:a40b9a259b52 62 * @param {Object} data - The callback data
Osamu Nakamura 0:a40b9a259b52 63 * @param {string} data.url - The callback URL
Osamu Nakamura 0:a40b9a259b52 64 * @param {Object} [data.headers] - The headers that should be set when
Osamu Nakamura 0:a40b9a259b52 65 * mbed Device Connector PUTs to the given callback URL
Osamu Nakamura 0:a40b9a259b52 66 * @param {function} [callback] - A function that is passed a potential `error`
Osamu Nakamura 0:a40b9a259b52 67 * object
Osamu Nakamura 0:a40b9a259b52 68 * @param {Object} [options] - Optional options object
Osamu Nakamura 0:a40b9a259b52 69 */
Osamu Nakamura 0:a40b9a259b52 70
Osamu Nakamura 0:a40b9a259b52 71 mbedConnectorApi.prototype.putCallback = function(data, callback, options) {
Osamu Nakamura 0:a40b9a259b52 72 options = extend(true, {}, this.options, options || {});
Osamu Nakamura 0:a40b9a259b52 73
Osamu Nakamura 0:a40b9a259b52 74 var requestData = {
Osamu Nakamura 0:a40b9a259b52 75 method: 'PUT',
Osamu Nakamura 0:a40b9a259b52 76 url: urljoin(options.host,
Osamu Nakamura 0:a40b9a259b52 77 options.restApiVersion,
Osamu Nakamura 0:a40b9a259b52 78 'notification',
Osamu Nakamura 0:a40b9a259b52 79 'callback'),
Osamu Nakamura 0:a40b9a259b52 80 headers: {
Osamu Nakamura 0:a40b9a259b52 81 accept: 'application/json'
Osamu Nakamura 0:a40b9a259b52 82 },
Osamu Nakamura 0:a40b9a259b52 83 json: data
Osamu Nakamura 0:a40b9a259b52 84 }
Osamu Nakamura 0:a40b9a259b52 85
Osamu Nakamura 0:a40b9a259b52 86 this.makeRequest(requestData, function(error) {
Osamu Nakamura 0:a40b9a259b52 87 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 88 if (error) {
Osamu Nakamura 0:a40b9a259b52 89 callback(error);
Osamu Nakamura 0:a40b9a259b52 90 } else {
Osamu Nakamura 0:a40b9a259b52 91 callback(null);
Osamu Nakamura 0:a40b9a259b52 92 }
Osamu Nakamura 0:a40b9a259b52 93 }
Osamu Nakamura 0:a40b9a259b52 94 }, options);
Osamu Nakamura 0:a40b9a259b52 95 };
Osamu Nakamura 0:a40b9a259b52 96
Osamu Nakamura 0:a40b9a259b52 97
Osamu Nakamura 0:a40b9a259b52 98 /**
Osamu Nakamura 0:a40b9a259b52 99 * DELETEs the callback data (effectively stopping mbed Device Connector from
Osamu Nakamura 0:a40b9a259b52 100 * PUTing notifications)
Osamu Nakamura 0:a40b9a259b52 101 * @param {function} [callback] - A function that is passed a potential `error`
Osamu Nakamura 0:a40b9a259b52 102 * object
Osamu Nakamura 0:a40b9a259b52 103 * @param {Object} [options] - Optional options object
Osamu Nakamura 0:a40b9a259b52 104 */
Osamu Nakamura 0:a40b9a259b52 105
Osamu Nakamura 0:a40b9a259b52 106 mbedConnectorApi.prototype.deleteCallback = function(callback, options) {
Osamu Nakamura 0:a40b9a259b52 107 options = extend(true, {}, this.options, options || {});
Osamu Nakamura 0:a40b9a259b52 108
Osamu Nakamura 0:a40b9a259b52 109 var requestData = {
Osamu Nakamura 0:a40b9a259b52 110 method: 'DELETE',
Osamu Nakamura 0:a40b9a259b52 111 url: urljoin(options.host,
Osamu Nakamura 0:a40b9a259b52 112 options.restApiVersion,
Osamu Nakamura 0:a40b9a259b52 113 'notification',
Osamu Nakamura 0:a40b9a259b52 114 'callback'),
Osamu Nakamura 0:a40b9a259b52 115 headers: {
Osamu Nakamura 0:a40b9a259b52 116 accept: 'application/json'
Osamu Nakamura 0:a40b9a259b52 117 }
Osamu Nakamura 0:a40b9a259b52 118 }
Osamu Nakamura 0:a40b9a259b52 119
Osamu Nakamura 0:a40b9a259b52 120 this.makeRequest(requestData, function(error) {
Osamu Nakamura 0:a40b9a259b52 121 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 122 if (error) {
Osamu Nakamura 0:a40b9a259b52 123 callback(error);
Osamu Nakamura 0:a40b9a259b52 124 } else {
Osamu Nakamura 0:a40b9a259b52 125 callback(null);
Osamu Nakamura 0:a40b9a259b52 126 }
Osamu Nakamura 0:a40b9a259b52 127 }
Osamu Nakamura 0:a40b9a259b52 128 }, options);
Osamu Nakamura 0:a40b9a259b52 129 };
Osamu Nakamura 0:a40b9a259b52 130
Osamu Nakamura 0:a40b9a259b52 131
Osamu Nakamura 0:a40b9a259b52 132
Osamu Nakamura 0:a40b9a259b52 133 /**
Osamu Nakamura 0:a40b9a259b52 134 * Begins long polling constantly for notifications
Osamu Nakamura 0:a40b9a259b52 135 * @param {function} [callback] - A function that is passed a potential `error`
Osamu Nakamura 0:a40b9a259b52 136 * object
Osamu Nakamura 0:a40b9a259b52 137 * @param {Object} [options] - Optional options object
Osamu Nakamura 0:a40b9a259b52 138 */
Osamu Nakamura 0:a40b9a259b52 139
Osamu Nakamura 0:a40b9a259b52 140 mbedConnectorApi.prototype.startLongPolling = function(callback, options) {
Osamu Nakamura 0:a40b9a259b52 141 var firstPoll = true;
Osamu Nakamura 0:a40b9a259b52 142 var waitingForFirstResponse = false;
Osamu Nakamura 0:a40b9a259b52 143
Osamu Nakamura 0:a40b9a259b52 144 if (!options) {
Osamu Nakamura 0:a40b9a259b52 145 options = {};
Osamu Nakamura 0:a40b9a259b52 146 }
Osamu Nakamura 0:a40b9a259b52 147
Osamu Nakamura 0:a40b9a259b52 148 if (options && !options.requestCallback) {
Osamu Nakamura 0:a40b9a259b52 149 options.requestCallback = this.longPollRequestCallback;
Osamu Nakamura 0:a40b9a259b52 150 }
Osamu Nakamura 0:a40b9a259b52 151
Osamu Nakamura 0:a40b9a259b52 152 options = extend(true, {}, this.options, options || {});
Osamu Nakamura 0:a40b9a259b52 153
Osamu Nakamura 0:a40b9a259b52 154 var _this = this;
Osamu Nakamura 0:a40b9a259b52 155
Osamu Nakamura 0:a40b9a259b52 156 var requestData = {
Osamu Nakamura 0:a40b9a259b52 157 method: 'GET',
Osamu Nakamura 0:a40b9a259b52 158 url: urljoin(options.host, options.restApiVersion, 'notification', 'pull'),
Osamu Nakamura 0:a40b9a259b52 159 headers: {
Osamu Nakamura 0:a40b9a259b52 160 accept: 'application/json',
Osamu Nakamura 0:a40b9a259b52 161 Connection: 'keep-alive'
Osamu Nakamura 0:a40b9a259b52 162 },
Osamu Nakamura 0:a40b9a259b52 163 qs: {
Osamu Nakamura 0:a40b9a259b52 164 'noWait': false
Osamu Nakamura 0:a40b9a259b52 165 }
Osamu Nakamura 0:a40b9a259b52 166 };
Osamu Nakamura 0:a40b9a259b52 167
Osamu Nakamura 0:a40b9a259b52 168 function poll(error, data) {
Osamu Nakamura 0:a40b9a259b52 169 if (error) {
Osamu Nakamura 0:a40b9a259b52 170 if (waitingForFirstResponse) {
Osamu Nakamura 0:a40b9a259b52 171 waitingForFirstResponse = false;
Osamu Nakamura 0:a40b9a259b52 172
Osamu Nakamura 0:a40b9a259b52 173 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 174 callback(error);
Osamu Nakamura 0:a40b9a259b52 175 }
Osamu Nakamura 0:a40b9a259b52 176 } else {
Osamu Nakamura 0:a40b9a259b52 177 console.log('ERROR: Long poll failed [Status ' + error.status + ']');
Osamu Nakamura 0:a40b9a259b52 178 }
Osamu Nakamura 0:a40b9a259b52 179 } else {
Osamu Nakamura 0:a40b9a259b52 180 if (data) {
Osamu Nakamura 0:a40b9a259b52 181 _this.handleNotifications(data);
Osamu Nakamura 0:a40b9a259b52 182 }
Osamu Nakamura 0:a40b9a259b52 183
Osamu Nakamura 0:a40b9a259b52 184 if (firstPoll) {
Osamu Nakamura 0:a40b9a259b52 185 firstPoll = false;
Osamu Nakamura 0:a40b9a259b52 186 waitingForFirstResponse = true;
Osamu Nakamura 0:a40b9a259b52 187 requestData.qs.noWait = true;
Osamu Nakamura 0:a40b9a259b52 188 } else {
Osamu Nakamura 0:a40b9a259b52 189 requestData.qs.noWait = false;
Osamu Nakamura 0:a40b9a259b52 190 }
Osamu Nakamura 0:a40b9a259b52 191
Osamu Nakamura 0:a40b9a259b52 192 _this.longPollRequestObj = _this.makeRequest(requestData, poll, options);
Osamu Nakamura 0:a40b9a259b52 193
Osamu Nakamura 0:a40b9a259b52 194 if (waitingForFirstResponse) {
Osamu Nakamura 0:a40b9a259b52 195 waitingForFirstResponse = false;
Osamu Nakamura 0:a40b9a259b52 196 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 197 callback();
Osamu Nakamura 0:a40b9a259b52 198 }
Osamu Nakamura 0:a40b9a259b52 199 }
Osamu Nakamura 0:a40b9a259b52 200 }
Osamu Nakamura 0:a40b9a259b52 201 }
Osamu Nakamura 0:a40b9a259b52 202
Osamu Nakamura 0:a40b9a259b52 203 if (this.longPollRequestObj) {
Osamu Nakamura 0:a40b9a259b52 204 this.stopLongPolling();
Osamu Nakamura 0:a40b9a259b52 205 }
Osamu Nakamura 0:a40b9a259b52 206
Osamu Nakamura 0:a40b9a259b52 207 poll();
Osamu Nakamura 0:a40b9a259b52 208 };
Osamu Nakamura 0:a40b9a259b52 209
Osamu Nakamura 0:a40b9a259b52 210
Osamu Nakamura 0:a40b9a259b52 211 /**
Osamu Nakamura 0:a40b9a259b52 212 * Stops long polling for notifications
Osamu Nakamura 0:a40b9a259b52 213 */
Osamu Nakamura 0:a40b9a259b52 214
Osamu Nakamura 0:a40b9a259b52 215 mbedConnectorApi.prototype.stopLongPolling = function() {
Osamu Nakamura 0:a40b9a259b52 216 if (this.longPollRequestObj) {
Osamu Nakamura 0:a40b9a259b52 217 this.longPollRequestObj.abort();
Osamu Nakamura 0:a40b9a259b52 218 this.longPollRequestObj = null;
Osamu Nakamura 0:a40b9a259b52 219 }
Osamu Nakamura 0:a40b9a259b52 220 };
Osamu Nakamura 0:a40b9a259b52 221
Osamu Nakamura 0:a40b9a259b52 222 mbedConnectorApi.prototype.longPollRequestCallback = function(error,
Osamu Nakamura 0:a40b9a259b52 223 response,
Osamu Nakamura 0:a40b9a259b52 224 body,
Osamu Nakamura 0:a40b9a259b52 225 callback) {
Osamu Nakamura 0:a40b9a259b52 226 if (typeof callback === 'function') {
Osamu Nakamura 0:a40b9a259b52 227 if (error || (response && response.statusCode >= 400)) {
Osamu Nakamura 0:a40b9a259b52 228 utility.handleError(error, response, body, callback);
Osamu Nakamura 0:a40b9a259b52 229 } else {
Osamu Nakamura 0:a40b9a259b52 230 if (response.statusCode === 200) {
Osamu Nakamura 0:a40b9a259b52 231 // New notifications
Osamu Nakamura 0:a40b9a259b52 232 var data = body;
Osamu Nakamura 0:a40b9a259b52 233 if (util.isString(data)) {
Osamu Nakamura 0:a40b9a259b52 234 try {
Osamu Nakamura 0:a40b9a259b52 235 data = JSON.parse(body);
Osamu Nakamura 0:a40b9a259b52 236 } catch (e) {
Osamu Nakamura 0:a40b9a259b52 237 // Not a JSON string, failing silently
Osamu Nakamura 0:a40b9a259b52 238 }
Osamu Nakamura 0:a40b9a259b52 239 }
Osamu Nakamura 0:a40b9a259b52 240
Osamu Nakamura 0:a40b9a259b52 241 callback(null, data);
Osamu Nakamura 0:a40b9a259b52 242 } else {
Osamu Nakamura 0:a40b9a259b52 243 callback();
Osamu Nakamura 0:a40b9a259b52 244 }
Osamu Nakamura 0:a40b9a259b52 245 }
Osamu Nakamura 0:a40b9a259b52 246 }
Osamu Nakamura 0:a40b9a259b52 247 };
Osamu Nakamura 0:a40b9a259b52 248
Osamu Nakamura 0:a40b9a259b52 249 mbedConnectorApi.prototype.asyncResponseHandler = function(asyncResponse) {
Osamu Nakamura 0:a40b9a259b52 250 if (this.asyncCallbacks[asyncResponse.id]) {
Osamu Nakamura 0:a40b9a259b52 251 if (asyncResponse.status >= 400) {
Osamu Nakamura 0:a40b9a259b52 252 utility.handleAsyncError(asyncResponse,
Osamu Nakamura 0:a40b9a259b52 253 this.asyncCallbacks[asyncResponse.id]);
Osamu Nakamura 0:a40b9a259b52 254 } else {
Osamu Nakamura 0:a40b9a259b52 255 if (asyncResponse.payload) {
Osamu Nakamura 0:a40b9a259b52 256 var payload = new Buffer(asyncResponse.payload, 'base64');
Osamu Nakamura 0:a40b9a259b52 257 asyncResponse.payload = payload.toString();
Osamu Nakamura 0:a40b9a259b52 258 }
Osamu Nakamura 0:a40b9a259b52 259
Osamu Nakamura 0:a40b9a259b52 260 this.asyncCallbacks[asyncResponse.id](null, asyncResponse);
Osamu Nakamura 0:a40b9a259b52 261 }
Osamu Nakamura 0:a40b9a259b52 262
Osamu Nakamura 0:a40b9a259b52 263 delete this.asyncCallbacks[asyncResponse.id];
Osamu Nakamura 0:a40b9a259b52 264 }
Osamu Nakamura 0:a40b9a259b52 265 };
Osamu Nakamura 0:a40b9a259b52 266
Osamu Nakamura 0:a40b9a259b52 267 mbedConnectorApi.prototype.handleNotifications = function(payload) {
Osamu Nakamura 0:a40b9a259b52 268 var _this = this;
Osamu Nakamura 0:a40b9a259b52 269
Osamu Nakamura 0:a40b9a259b52 270 if (payload['notifications']) {
Osamu Nakamura 0:a40b9a259b52 271
Osamu Nakamura 0:a40b9a259b52 272 payload['notifications'].forEach(function(notification) {
Osamu Nakamura 0:a40b9a259b52 273 var data = new Buffer(notification.payload, 'base64');
Osamu Nakamura 0:a40b9a259b52 274 notification.payload = data.toString();
Osamu Nakamura 0:a40b9a259b52 275
Osamu Nakamura 0:a40b9a259b52 276 /**
Osamu Nakamura 0:a40b9a259b52 277 * Resource notification event.
Osamu Nakamura 0:a40b9a259b52 278 *
Osamu Nakamura 0:a40b9a259b52 279 * @event mbedConnectorApi#notification
Osamu Nakamura 0:a40b9a259b52 280 * @property {Object} notification - A notifcation object
Osamu Nakamura 0:a40b9a259b52 281 */
Osamu Nakamura 0:a40b9a259b52 282 _this.emit('notification', notification);
Osamu Nakamura 0:a40b9a259b52 283 });
Osamu Nakamura 0:a40b9a259b52 284
Osamu Nakamura 0:a40b9a259b52 285
Osamu Nakamura 0:a40b9a259b52 286 }
Osamu Nakamura 0:a40b9a259b52 287
Osamu Nakamura 0:a40b9a259b52 288 if (payload['registrations']) {
Osamu Nakamura 0:a40b9a259b52 289 payload['registrations'].forEach(function(registration) {
Osamu Nakamura 0:a40b9a259b52 290 /**
Osamu Nakamura 0:a40b9a259b52 291 * Endpoint registration event.
Osamu Nakamura 0:a40b9a259b52 292 *
Osamu Nakamura 0:a40b9a259b52 293 * @event mbedConnectorApi#registration
Osamu Nakamura 0:a40b9a259b52 294 * @property {Object} registration - A registration object
Osamu Nakamura 0:a40b9a259b52 295 */
Osamu Nakamura 0:a40b9a259b52 296 _this.emit('registration', registration);
Osamu Nakamura 0:a40b9a259b52 297 });
Osamu Nakamura 0:a40b9a259b52 298 }
Osamu Nakamura 0:a40b9a259b52 299
Osamu Nakamura 0:a40b9a259b52 300 if (payload['reg-updates']) {
Osamu Nakamura 0:a40b9a259b52 301 payload['reg-updates'].forEach(function(regUpdate) {
Osamu Nakamura 0:a40b9a259b52 302 /**
Osamu Nakamura 0:a40b9a259b52 303 * Endpoint registration update event.
Osamu Nakamura 0:a40b9a259b52 304 *
Osamu Nakamura 0:a40b9a259b52 305 * @event mbedConnectorApi#reg-update
Osamu Nakamura 0:a40b9a259b52 306 * @property {Object} regUpdate - A registration update object
Osamu Nakamura 0:a40b9a259b52 307 */
Osamu Nakamura 0:a40b9a259b52 308 _this.emit('reg-update', regUpdate);
Osamu Nakamura 0:a40b9a259b52 309 });
Osamu Nakamura 0:a40b9a259b52 310 }
Osamu Nakamura 0:a40b9a259b52 311
Osamu Nakamura 0:a40b9a259b52 312 if (payload['de-registrations']) {
Osamu Nakamura 0:a40b9a259b52 313 payload['de-registrations'].forEach(function(endpoint) {
Osamu Nakamura 0:a40b9a259b52 314 /**
Osamu Nakamura 0:a40b9a259b52 315 * Endpoint de-registration event.
Osamu Nakamura 0:a40b9a259b52 316 *
Osamu Nakamura 0:a40b9a259b52 317 * @event mbedConnectorApi#de-registration
Osamu Nakamura 0:a40b9a259b52 318 * @property {string} endpoint - The name of the endpoint
Osamu Nakamura 0:a40b9a259b52 319 */
Osamu Nakamura 0:a40b9a259b52 320 _this.emit('de-registration', endpoint);
Osamu Nakamura 0:a40b9a259b52 321 });
Osamu Nakamura 0:a40b9a259b52 322 }
Osamu Nakamura 0:a40b9a259b52 323
Osamu Nakamura 0:a40b9a259b52 324 if (payload['registrations-expired']) {
Osamu Nakamura 0:a40b9a259b52 325 payload['registrations-expired'].forEach(function(registrationExpired) {
Osamu Nakamura 0:a40b9a259b52 326 /**
Osamu Nakamura 0:a40b9a259b52 327 * Endpoint registration expiration event.
Osamu Nakamura 0:a40b9a259b52 328 *
Osamu Nakamura 0:a40b9a259b52 329 * @event mbedConnectorApi#registration-expired
Osamu Nakamura 0:a40b9a259b52 330 * @property {string} endpoint - The name of the endpoint
Osamu Nakamura 0:a40b9a259b52 331 */
Osamu Nakamura 0:a40b9a259b52 332 _this.emit('registration-expired', registrationExpired);
Osamu Nakamura 0:a40b9a259b52 333 });
Osamu Nakamura 0:a40b9a259b52 334 }
Osamu Nakamura 0:a40b9a259b52 335
Osamu Nakamura 0:a40b9a259b52 336 if (payload["async-responses"]) {
Osamu Nakamura 0:a40b9a259b52 337 payload['async-responses'].forEach(function(asyncResponse) {
Osamu Nakamura 0:a40b9a259b52 338 _this.asyncResponseHandler(asyncResponse);
Osamu Nakamura 0:a40b9a259b52 339 });
Osamu Nakamura 0:a40b9a259b52 340 }
Osamu Nakamura 0:a40b9a259b52 341 };