まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ibm

IBM Bluemix からも提供されている IBM の DBaaS サービスである dashDB に Node.js からアクセスする方法を紹介します。実際には dashDB だけでなく、DB2 のサービスやオンプレミスデータベースへも同様に応用できますが、今回は Bluemix 上の DB2/dashDB 関連サービスを例に紹介します:
2017063002


dashDB は行指向/列指向型のテーブルをどちらも作成することができるリレーショナル・データベースのサービスですが、そのデータベースシステムとしての実体は IBM DB2 です。というわけで、このライブラリを使ってアクセスします:
https://www.npmjs.com/package/ibm_db

2017063001


まず以下のコマンドを実行して ibm_db をインストールします(このコマンドだけで DB2 ODBC Driver ごとインストールされます):
$ npm install ibm_db


そして以下のようなコードを用意して dashDB にアクセスします:

(settings.js)
exports.db_host = 'dashdb-entry-yp-XXXXXXXX.services.dal.bluemix.net';
exports.db_port = 50000;
exports.db_name = 'BLUDB';
exports.db_username = 'dashNNNN';
exports.db_password = 'PASSWORD';

(sample.js)
var ibm_db = require( 'ibm_db' );
var settings = require( './settings' );

var db_str = "DATABASE=" + settings.db_name
  + ";HOSTNAME=" + settings.db_host
  + ";UID=" + settings.db_username
  + ";PWD=" + settings.db_password
  + ";PORT=" + settings.db_port
  + ";PROTOCOL=TCPIP";
var sql = "select OBJECTID, NAME from SAMPLES.GEO_CUSTOMER limit 10";

ibm_db.open( db_str, function( err, conn ){
  if( err ) return console.log( err );

  conn.query( sql, function( err, data ){
    if( err ) console.log( err );
    else console.log( data );

    conn.close( function(){
      console.log( 'done.' );
    });
  });
});

settings.js の中身はユーザー名やパスワードといった dashDB に接続するためのサービス資格情報です。IBM Bluemix の画面から取得できる値を使って、実際の値で書き換えて使ってください:

2017063003


アプリケーションの実体は sample.js です。今回の例ではシンプルに接続して、サンプルデータとして GEO_CUSTOMER テーブルから OBJECTID と NAME の値を 10 件だけ取得する、という SQL (青字部分)を実行しました。また settings.js で定義した情報を取り出して接続文字列(赤字部分)を生成しています。

node コマンドで sample.js を実行して、以下のような結果が表示されれば成功です:
$ node sample.js
[ { OBJECTID: 1322, NAME: 'Kami Labarbera' },
  { OBJECTID: 1323, NAME: 'Johnathon Tunney' },
     :
  { OBJECTID: 1587, NAME: 'Althea Alcazar' } ]
done.







 

IBM Bluemix(Cloud Foundry) のプラットフォームが現在持っている制約の1つが「IPアドレスによるアクセス制限」に関するものです。残念ながら現時点ではベースとなっている Cloud Foundry にこの機能がなく、IBM Bluemix でも実装されていません。

というわけで、現状この機能を実現するにはプラットフォーム側ではなくアプリケーション側で用意する必要があります。Node.js アプリケーションでこれを実現する方法の1つとして、Express-IpFilter があります:
https://www.npmjs.com/package/express-ipfilter

2017060601



名前の通りの機能です。Node.js の Express フレームワークの中で IP アドレス制限(許可/拒絶)を簡単に実現することができます。

Express-IpFIlter をインストールするには npm で以下を実行します:
$ npm install express-ipfilter
(実際には express のインストールも必要です)

例えば、以下のような Node.js + Express のシンプルなアプリケーションを例に IP アドレス制御をかける例を紹介します。まずアプリケーション(app.js)は以下のような内容のものを使います:
//. app.js

var express = require( 'express' );
var app = express();

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

アプリケーションの中でスタティックディレクトリを /public/ に指定しています。そこで /public/index.html というファイルを用意し、中身を以下のようなものにします:
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>はろーわーるど</h1>
</body>
</html>

これを普通に Node.js で実行します:
$ node app

3000 番ポートを listen するように指定しているので、このポートを指定して同一マシンからウェブブラウザでアクセスすると、スタティックディレクトリに用意した index.html を見ることができます:
2017060602


ではこのアプリにアクセス制御をかけてみます。まずは '127.0.0.1' からのアクセスを拒絶するようなフィルターをかけてみます。app.js の内容を以下のように変更します(赤字部分を追加):
//. app.js

var express = require( 'express' );
var ipfilter = require( 'express-ipfilter' ).IpFilter;
var app = express();

var ips = [ '127.0.0.1' ];
app.use( ipfilter( ips ) );

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

この例では ips という配列変数を用意し、その中に対象とする IP アドレスを文字列配列の形式で代入します。そして ipfilter を有効にしています。

この状態で app.js を起動し、先程と同じように同一マシンからウェブブラウザでアクセスすると以下のようになります:
2017060603


IP アドレス制御が有効になり、アクセスは拒否されました。

では今後は逆に '127.0.0.1' からのアクセスのみ許可するようなフィルタをかけてみます。app.js の内容を以下のように変更します(青字部分を追加):

//. app.js

var express = require( 'express' );
var ipfilter = require( 'express-ipfilter' ).IpFilter;
var app = express();

var ips = [ '127.0.0.1' ];
app.use( ipfilter( ips, { mode: 'allow' } ) );

app.use( express.static( __dirname + '/public/' ) );

app.listen( 3000 );

この状態で再度アプリケーションを起動し、同様にウェブブラウザでアクセスすると、今度は元のように表示されます(つまり IP Filter はデフォルトだと拒絶、'allow' モードを指定すると許可のフィルタをそれぞれ有効にします):
2017060604


これでアプリケーションレベルでの IP アドレス制限が実現できます。

話題(?)のブロックチェーンを小型コンピュータであるラズベリーパイ(以下、「ラズパイ」)から操作する、ということに挑戦してみました。


まずは操作の対象となるブロックチェーン環境を用意します。今回は IBM のクラウドプラットフォームである IBM Bluemix から提供されているマネージドハイパーレジャー環境である IBM Blockchain サービスを使うことにします(2017/Jun/04 現在では HyperLedger Fabric v0.6 と v1.0 がベータ提供されています)。IBM Bluemix アカウントが必要になりますが、30日間無料試用も用意されているので、必要に応じてアカウントを取得してください。

IBM Bluemix にログイン後、米国南部データセンターになっていることを確認し、「サービスの作成」をクリックします:
2017060404


そして「アプリケーション・サービス」カテゴリ内にある "Blockchain" を探してクリック:
2017060405


利用する Blockchain の種類をプランから選択します。今回は無料の "Starter Developer plan(beta)" を選択します。ちなみにこのプランの場合、Hyperledger Fabric は v0.6 を使うことになり、4つのブロックチェーンピアが作成されます。最後に「作成」ボタンをクリック:
2017060406


しばらく待つと IBM Blockchain サービスが起動します。ここでダッシュボードに移動することもできますが、この後で利用する情報を先に取得しておきます。左ペインの「サービス資格情報」を選択:
2017060407


サービス資格情報から「資格情報の表示」をクリックして、表示される JSON テキストを全てコピーしておきます(この後、ラズパイ内で使います):
2017060408


改めて「管理」タブを選択し、「ダッシュボードを起動」ボタンをクリックして、ダッシュボード画面に切り替えておきましょう:
2017060409


IBM Blockchain のダッシュボード画面が表示されます。4つのピアが動いていますが、この時点ではまだチェーンコードは作られていないことが確認できます。これでブロックチェーン側の準備は完了です:
2017060401


では続けてラズパイ側の準備に取り掛かります。以下はラズパイ上での作業です(僕の検証では3Bを使いました)。ラズパイにはネットワークのセットアップが済んだ Raspbian Jessie を用意し、そこに Hyperledger Fabric Node SDK ごとサンプルコードを導入してデモアプリケーションを動かします。というわけでまずは Node.js と npm を用意します。まずはこのドキュメント内にあるように、Node.js v6.2.x ~ v6.10.x のバージョンの環境を用意します(以下は v6.10.3 の例です。v7.x は未対応):
$ sudo apt-get install -y nodejs npm
$ sudo npm cache clean
$ sudo npm install n -g
$ sudo n v6.10.3
$ node -v
v6.10.3  <- v6.10.3 の導入が完了

続けて npm を最新版(以下の例では v5.0.2)に更新します:
$ sudo npm update -g npm
$ sudo npm outdated -g
$ npm -v
v5.0.2  <- v5.0.2 の導入が完了

git でデモアプリをダウンロードし、必要なライブラリをインストールします:
$ git clone https://github.com/IBM-Blockchain/SDK-Demo.git
$ cd SDK-Demo
$ npm install

ダウンロードしたデモアプリの中(直下のディレクトリ)にある ServiceCredentials.json ファイルの中身を、上記でコピーしたサービス資格情報の JSON テキストの中身で全て書き換えます:
{
  "peers": [
    {
      "discovery_host": ******

      :
      :
"cert_path": "/certs/peer/cert.pem" }

これでラズパイ側の準備も完了しました。後は以下のコマンドを実行して、デモアプリを実行します:
$ node helloblockchain.js

以下のようにコマンドが次々を流れていきます。実際にはこの中でチェーンコードの初期化や、初期データ入力、データ変更などが行われています。最後に "Successfully ****" というメッセージがいくつか表示されていればコマンドは成功です:

2017060403


この最後のメッセージについて補足します。この helloblockchain.js ではチェーンコード初期化などの作業を行った後に「"a" という入れ物に 100 、"b" という入れ物に 200 のデータを保存」します(ここまでが初期化)。そして「"a" から "b" へ 10 移動」するようなトランザクションが実行されます(なのでここまでの処理が完了すると "a" に 90 、"b" に 210 入っている状態になるはずです)。最後に「"a" の値を取り出す」処理が実行されます。その辺りの様子が以下のメッセージから分かります("a" が 90 になっています):
  :
  :
Chaincode ID : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Successfully deployed chaincode: request={"fcn":"init","args":["a","100","b","200"],"chaincodePath":"chaincode","certificatePath":"/certs/peer/cert.pem"}, response={"uuid":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}

Successfully submitted chaincode invoke transaction: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"invoke","args":["a","b","10"]}, response={"uuid":"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"}

Successfully completed chaincode invoke transaction: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"invoke","args":["a","b","10"]}, response={"result":"Tx zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz complete"}
Custom event received, payload: "Event Counter is 1"


Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=90


※↑数値の移動は Submit して Complete しているのが別行で表示されているので、全部で4行表示されています。


なお初期化部分は最初の1回しか実行されないため、この helloblockchain.js を何度か実行すると、"a" の値が 10 ずつ減っていく様子がわかります:
$ node helloblockchain.js

  :
  :
Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=80

$ node helloblockchain.js

  :
  :
Successfully queried  chaincode function: request={"chaincodeID":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","fcn":"query","args":["a"]}, value=70


また、これらの処理を実行すると、ダッシュボード上からもチェーンコードが生成されて、実行されている様子が確認できます(初期状態だと画面更新が3分ごとなので、画面に反映されるまで少し時間がかかるかもしれません):
2017060402


まだ環境の一部がベータ版だったりする状態ではありますが、今やラズパイでもブロックチェーンが動くようになったんですねー。

Watson API の1つで、複数の選択肢の中から自分の与えた条件に合うアイテムをトレードオフ判断で絞り込んでくれる、という便利な Tradeoff Analytics API があります(ありました)が、残念ながら5月11日をもって新規インスタンスの生成ができなくなってしまいました(作成済みインスタンスは2018年4月まで使えるようです。詳しくはこちら)。

Bluemix に Watson API が追加された当初から存在していた API の1つであるだけでなく、個人的にもデモなどで使っていて、非常にわかりやすくて便利な API だっただけに残念でした。

このアナウンスがされたタイミングから準備はしていたのですが、実はこの Tradeoff Analytics と同様の(近い互換性を持つ)API を自分でオープンソース化前提で作っています。まだ途中といえば途中ですが、制約付きで一応動く状態にはなっています。新インスタンスが生成できなくなったこのタイミングで一旦公開してみようと思いました:
https://github.com/dotnsf/yatradeoff


なお、この API を実際にアプリケーションサーバー上で稼働させているものもこちらに用意しました。ただ API だけ使いたい人はこちらをどうぞ:
https://yatradeoff.au-syd.mybluemix.net/dilemmas


この URL にブラウザで GET アクセスしても何も起こりません(エラーメッセージが表示されるだけ)が、POST アクセスすると API として動きます。その際にはユーザー名: username &パスワード: password を指定してください(オリジナル同様 Basic 認証です)。


元の Tradeoff Analytics API 同様、/dilemma という POST API エンドポイント1つだけが定義されています。ポストする JSON 型インプットデータのフォーマットには互換性があります。

API の実行結果としての JSON データにも互換性をもたせました。ただ現状はオリジナルのサブセットになっています。現状、以下の3点の制約事項があります:
(1) preferable_solutions データは常にブランクになります
(2) 解析結果は solutions に含まれますが、'FRONT' ステータスを持つデータだけが含まれます
(3) map information を出力するようパラメータで指定しても map information は返されません



ただ1点、オリジナルにはなかった優先順位を考慮する機能も加えています。エンドポイント URL のパラメータに prioritised=1 を追加して実行(例: /dilemma?prioritised=1)すると、インプットデータの JSON データは優先順位順に解釈されます(先にある方が高い優先順位であると解釈されて実行されます。例えば「項目AとBを両方ともトレードオフの材料にする」のではなく、「項目AとBを両方ともトレードオフの材料にするが、Aの方をBよりも優先する」というトレードオフを可能にしています)。


そして、このパラメータを付けて実行した時の API 実行結果も優先順位順に返されます。先にある方がよりオススメな結果、ということになります。まあ preferable_solutions の代わりという位置付けです。


※実はこの機能を付けたくて自分で API を改良した、という経緯があります。


結果の JSON テキストの solutions 内がトレードオフの結果です。オリジナル API では全ての選択肢に対して 'FRONT' か 'EXCLUDE' かの結果を含めていましたが、現状この API では 'FRONT' のもの(トレードオフ判断の結果、選択肢の候補として残ったもの)だけが返される点に注意してください。


こちらの続きです。以下の紹介はこちらの中で書かれた作業を前提としているので、実際に試してみたい方はまずこちらを参照ください:
Watson Workspace と Zoom を連携する


Watson Workspace に追加した Zoom のチャットボット "Zoom.ai" を使って、第三者との会議の調整を依頼した時の様子を紹介します:
2017050301



この Zoom.ai 機能を有効にしたスペースの中で(誰にともなく)"schedule a meeting with (メールアドレス) tomorrow."((メールアドレス)さんと明日の会議を予定してくれ)と打ち込みます。すると、Zoom.ai がこのメッセージに反応し、"you asked me to schedule a meeting with (メールアドレス) tomorrow sometime between 9:00am and 5:00pm. Is this correct?"((相手の名前)さんと明日の 09:00AM から 05:00PM までのどこかで会議を調整すればよろしいですね?)と聞いてきました。前回の作業でアドレス帳を共有していたので、メールアドレスから名前を引いてきたわけか、なかなかわかってるね:
2017050211

というわけで "Sure"(そうだよ)と返事をすると、"I have emailed (相手の名前), and asked them to choose a time slot based on your availability and will let you know what happens."((相手の名前)さんにメールして、あなたのスケジュールの空きに合わせた日時での選択肢といっしょに打ち合わせの候補日時を選ぶよう伝えておきました。どうなるか、しばらくお待ち下さい)だそうです。あ、もう問答無用でメールしちゃうのね。。 (^^;:
2017050212


その頃、その相手の人には(僕があなたと打ち合わせしたがっている、という内容の)このようなメールが届いていました。で、メールに書かれたリンクをクリックすると・・・:
2017050201


僕の顔写真と打ち合わせ調整の画面が表示されます。で、場所は相手に選ばせて、時間帯は僕の予定がない、いくつかの候補が表示されていて、この中から選択できるようになっています。日時のタイムゾーンは必要に応じて変更できます(デフォルトは Asia/Tokyo でした):
2017050202


で、この人は場所に「俺の家」、時刻は 07:30AM-08:30AM の早朝会議を選択したことにします。Google カレンダーに同期、にもチェックしました。で "Confirm" ボタン:
2017050203


すると、この人の画面には選択した内容が確認の意味で表示されます:
2017050204


同時に、この人の Google カレンダーにはこの会議がセットされました:
2017050205


その頃、元々の依頼をした僕の所にはスケジュール予約のメールが届いていました。画面はノーツですが、カレンダー(iCal)対応のメーラー環境であれば、このようにカレンダーメールとして届きます。これを "Accept" すれば、この予定が自分のカレンダーにも反映され、相手には「了承」メールが届きます:
2017050206


同時にこんな確認メールも届いていました。これで双方にとって都合のいい時間を選んだ上での会議日程が調整できたことになります:
2017050207


とまあ、こんな感じ。会議を依頼した僕は Zoom.ai にその旨を伝えただけで、Zoom.ai は(共有済みの)僕のスケジューラーを確認した上で空き時間帯からいくつかの候補と併せて相手にメールで送り、相手が時間帯を選んだら双方に会議招集の確認メールを送る、という機能を代行してくれたことになります。そしていざ会議が始まる段階になったら、決めた場所に行ってもいいし、Zoom が提供する機能でウェブ会議を行うこともできる、ということになりますね。確かに会議の日時調整は面倒な作業なので、そこを最小限代行してくれてることになります。

この Zoom.ai はまだ英語でしか動かないし、Watson Workspace も(まだプレビュー版ですが)日本語が使える機能があったりなかったりするので、ここで紹介した機能が日本語でできるようになるまではまだしばらくかかるかもしれません。ただ、それでも近未来的な機能が実際に使えている場面を目の当たりにするのは刺激的です。


このページのトップヘ