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

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

IBM Cloud から提供されている NoSQL のマネージドデータベースである Cloudant (実体は Apache CouchDB)を便利に使っています。最大の特徴の一つが「無料でも一定条件下で使える」点ですが、今回は無料である以外の便利な使い方を紹介します。

この記事の中で紹介するのはこのような使いかたです:
(1) Cloudant の CORS を有効に設定する(特定ドメインのページからの REST API リクエストを受け付けて読み書きできるようにする)
(2) Github ページに用意したウェブページから (1) で有効に設定した Cloudant のデータを読み書きする


2021022401


最終的に実現したいのは、世の中の SaaS(といっていいのかな)の中でもかなり可用性高く、安定して動いている印象の Github ページを使ったウェブアプリケーションを作ることです。ただ静的ページそのものは安定して稼働していてもデータの読み書きが出来なければウェブアプリケーションとしては不十分で、そこを REST API で読み書きできる IBM Cloud の Cloudant データベースで賄えないか、と考えました。ただそのままでは CORS の制約もあって API が実行できないのですが、Cloudant 側の設定で CORS を正しく設定して Github ページからの読み書きを可能な状態にする、そのための Cloudant の設定手順を確認する、というのが目標です。

以下では Github のアカウントを持っている方を対象に、Github ページを使って作成するウェブページから Cloudant にアクセスするサンプルを紹介します。Github ページでなくても(静的ページのホスティング環境があれば)同様に可能なはずですが、その場合は適宜読み直してください(特に CORS 設定時のドメインなど)。


【CORS】
"Cross-Origin Resource Sharing" 、つまり「オリジン間リソース共有」の略語です。ここでの「オリジン」は「ドメイン」と読み替えたほうが理解しやすいかもしれまえん。

例えば A というウェブサーバーが https://aaa.com/ 上で、また B というデータベース・サーバーが https://bbb.net/ 上で動いていると仮定します(つまり全く別々に管理されている2つのサーバーです。この状態を Cross-Origin と呼びます)。A 上のウェブページで表示される画面の中で B から取得したデータを表示しようと思っています。

例えば A が Java などのプログラミング言語によって作られたアプリケーションサーバーであった場合、A は B からのデータ取得に Java を使うことになります。A の中で Java が実行され、B にアクセスしてデータを取り出し、その結果を使って A のページの画面を作成することができます。A の内部でプログラミング言語が使われている場合は、Cross-Origin であってもこのような方法でデータを取得することができます。

ところが A がアプリケーションサーバーではなく、ただの(既存の HTML ページを表示するだけの)ウェブサーバーであった場合、A から B にアクセスしてデータを取得する手段はかなり限られてしまいます。その限られた手段の1つが AJAX などに代表される JavaScript 処理です。A のサーバーにプログラミング言語が用意されていなくても、ウェブブラウザ自体が持つ JavaScript 実行環境を使って B サーバーにアクセスしてデータを取得する、という考え方です。ただし Cross-Origin の場合、A にアクセスしたウェブブラウザから B のサーバーに JavaScript で HTTP アクセスすることは原則できないことになっています(Cross-Origin でなければ、つまり A = B であれば可能です)。これが CORS の制約です。

CORS の制約はウェブブラウザが持っている制約なので、ウェブブラウザを使わない HTTP 通信(上述の Java 言語によるプログラミングによる通信など)には関係ありませんが、(ウェブブラウザの)利用者側でこの制約を解除する方法はありません。唯一の方法がリソース提供側(この場合だと B サーバー)による許可のみです。

今回のブログエントリは IBM Cloud のデータベースの1つである Cloudant の CORS をうまく設定することで、代表的な静的ウェブページの1つである Github ページから(Cloudant 内の)外部データを読み書きする方法について紹介する、というものです。


【Cloudant の用意】
まず IBM Cloud のアカウントを取得します。IBM Cloud アカウントを新規に取得した時点では「ライトアカウント」と呼ばれる無料の制約のあるアカウントとなります。今回紹介する作業を実行するだけであれば(ライトアカウントだとデータは 1GB までの格納となりますが、この容量などに問題なければ)ライトアカウントのまま実行いただいても構いません。

改めて取得したアカウントで IBM Cloud にログインし、「リソースの作成」ボタンクリック後に「Cloudant」を選択します:
2021022501

2021022502


インスタンス作成前にいくつか設定箇所があります。主なものは以下になりますが、"Authentification Method" だけはデフォルトの "IAM" ではなく "IAM and legacy credentials" を選択してください:
・Available Regions: インスタンスを作成するエリア、お好きな場所でいいが「東京」あたりが無難
・Authentification Method: 今回は必ず "IAM and legacy credentials" を選択
・Plan: 料金プラン。「Lite」であれば無料

最後に "Create" ボタンで作成します:
2021022503


Authentification Method について補足しておくと、デフォルトの "IAM" だといわゆる API キーを使った認証/認可を行います。ただ今回はアプリケーションサーバーからではなく(GitHub ページの)静的なページから Basic 認証を使ってデータベースの REST API を使うことを想定しています。この場合は "IAM" ではなく "IAM and legacy credentials" (API キーまたは Basic 認証を使う)を選択しておく必要があるためです。


少し待つと Cloudant インスタンスが起動済みになります。インスタンスを開いて「サービス資格情報」タブを選び、「新規資格情報」ボタンを押して資格情報を作成します。作成後に作成した資格情報(「サービス資格情報-1」のような名前になっていると思います)を選択・展開して、中身を確認します:
2021022504


資格情報の中身は以下のような内容の JSON 文字列になっているはずです(この中身は他人に見せないように注意してください):
{
  "apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "host": "xxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "iam_apikey_description": "Auto-generated for key xxxxxxxxxxxxxxxxxxx",
  "iam_apikey_name": "サービス資格情報-1",
  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/xxxxxxxxxxxxxxxxxxxx::serviceid:ServiceId-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "password": "パスワード",
  "port": 443,
  "url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "username": "ユーザー名"
}

この情報のうち、後で必要になるのが以下の(赤字の)4つです。メモするか、コピペできるようにしておいてください:
キー意味補足
hostnameホスト名*******-bluemix.cloudantnosqldb.appdomain.cloud といった感じの文字列になっています(******* 部分が可変)
portポート番号443 で固定のはずです
usernameユーザー名ログイン時にこの2つの値が必要です
passwordパスワード


認証情報を確認したら、実際に Cloudant データベースのダッシュボードを見てみましょう。画面左のメニューから「管理」タブを選択し、右上の「Launch Dashboard」ボタンをクリックします:
2021022505


(初期状態では空の)ダッシュボード画面が表示されます。最初にアクセスした時点ではまだ何のデータベースも作成されていないため、下図のような画面になります。後述の作業のためのデータベースをここで1つ作成しておくことにします。画面右上の「Create Database」ボタンをクリックします:
2021022506


データベースを作成するメニューが画面右に表示されます。ここでは Database name に「mydb(異なる名前を指定しても構いませんが、その場合は後述のコマンド等を作成した名前に読み替えて実行してください)」、Partitioning は「Non-partitioned」を選択し、「Create」ボタンをクリックします:
2021022507


作成した mydb データベースが出来上がりました。このデータベースもまだデータが1件もないので、このような画面になります。一旦元の画面に戻るため、画面左上の "<" マークをクリックします:
2021022508


一つ前のデータベース一覧画面に戻りました。先程は空でしたが、作成したデータベースが1つ追加された画面になっているはずです:
2021022509


Cloudant 側の準備はいったんここまでできていればOKとします。 次に CORS のための設定を行います。


【Cloudant に CORS を設定する】
次に Cloudant に CORS の設定をして外部からのアクセスを許可します。ダッシュボードのメニューから「アカウント」(下から3番め)を選び、「CORS」タブを選択します。以下のような画面(「CORS は有効になっていて、外部のどこからのリクエストも受け付けない」という内容)になるはずです:
2021022501


画面下部の「Restrict to specific domains」欄にアクセスを許可するドメインの URL を指定して「Add Domain」ボタンで追加します。ここでは "https://(github のアカウント名).github.io/" を指定することで自分の Github ページからのアクセスを許可することになるので、自分の github アカウントに合わせて設定してください:
2021022502


このような画面になれば自分の github ページ用の CORS 設定ができたことになり、自分の github ページからは JavaScript で Cloudant データを読み書きできることになります:
2021022503


【Cloudant に初期データを入力する】
今回の紹介では Github ページから Cloudant のデータを REST API 経由で表示する(CORS の設定をしていれば取得できるはず)、ということを試してみます。そのための「表示するデータ」をあらかじめ Cloudant に入力しておきます。

もちろん(ダッシュボードから)自分で好きにデータを入力いただいても構いませんが、比較的簡単に動作を確認できるサンプルを用意しており、そこで対象とするデータを curl 一発でまとめて入力できるように準備しているのでこちらの方法を紹介します。

まず以下の URL からサンプルデータをダウンロード(表示後に右クリック→名前を付けて保存で "prefs.json" という名前で保存)してください:
https://raw.githubusercontent.com/dotnsf/cloudant_cors/main/prefs.json

2021022701


このファイルの中身は以下のようなフォーマットになっており、各都道府県庁所在地と、その位置情報が格納されています:
{
  "docs": [
    { "code": 1, "prefecture": "北海道", "capital": "札幌市", "geometry": { "type": "Point", "coordinates": [ 141.34694, 43.06417 ] } },
    { "code": 2, "prefecture": "青森県", "capital": "青森市", "geometry": { "type": "Point", "coordinates": [ 140.74, 40.82444 ] } },
    { "code": 3, "prefecture": "岩手県", "capital": "盛岡市", "geometry": { "type": "Point", "coordinates": [ 141.1525, 39.70361 ] } },
    { "code": 4, "prefecture": "宮城県", "capital": "仙台市", "geometry": { "type": "Point", "coordinates": [ 140.87194, 38.26889 ] } },
    { "code": 5, "prefecture": "秋田県", "capital": "秋田市", "geometry": { "type": "Point", "coordinates": [ 140.1025, 39.71861 ] } },
    { "code": 6, "prefecture": "山形県", "capital": "山形市", "geometry": { "type": "Point", "coordinates": [ 140.36333, 38.24056 ] } },
    { "code": 7, "prefecture": "福島県", "capital": "福島市", "geometry": { "type": "Point", "coordinates": [ 140.46778, 37.75 ] } },
    { "code": 8, "prefecture": "茨城県", "capital": "水戸市", "geometry": { "type": "Point", "coordinates": [ 140.44667, 36.34139 ] } },
    { "code": 9, "prefecture": "栃木県", "capital": "宇都宮市", "geometry": { "type": "Point", "coordinates": [ 139.88361, 36.56583 ] } },
    { "code": 10, "prefecture": "群馬県", "capital": "前橋市", "geometry": { "type": "Point", "coordinates": [ 139.06083, 36.39111 ] } },
    { "code": 11, "prefecture": "埼玉県", "capital": "さいたま市", "geometry": { "type": "Point", "coordinates": [ 139.64889, 35.85694 ] } },
    { "code": 12, "prefecture": "千葉県", "capital": "千葉市", "geometry": { "type": "Point", "coordinates": [ 140.12333, 35.60472 ] } },
    { "code": 13, "prefecture": "東京都", "capital": "新宿区", "geometry": { "type": "Point", "coordinates": [ 139.69167, 35.68944 ] } },
    { "code": 14, "prefecture": "神奈川県", "capital": "横浜市", "geometry": { "type": "Point", "coordinates": [ 139.6425, 35.44778 ] } },
      :
      :
  ]
}

このデータを先程作成した Cloudant 内の mydb データベースに格納します。curl コマンドを使うと以下のコマンド(※)で格納できます:
$ curl -u "username:password" -XPOST "https://host/mydb/_bulk_docs" -H "Content-Type: application/json" -d @prefs.json

username, password, host の値は上述で確認した以下の値を指定して入力してください:
 username: 上述の資格情報で確認した username の値
 password: 上述の資格情報で確認した password の値
 host: 上述の資格情報で確認した host の値


このコマンドが成功すると Cloudant の mydb データベースに47都道府県のデータが挿入されます。ダッシュボードから mydb データベースを選択すると以下のように 47 件のデータが表示されるようになります:
2021022702


試しにどれか1つ選択してみると、個々の内容を確認することも可能です(下図は北海道の例、"Cancel" で一覧に戻ります):
2021022703


ここまでの作業で Cloudant に Github ページからアクセスするための CORS を設定し、表示用のデータも格納できました。ではこのデータを Github ページから REST API 経由で取得して表示してみましょう。


【Github ページを用意する】
では実際に Github ページからクロスオリジンな Cloudant データベースに REST API 経由でアクセスできることを確認してみます。

まず Github にログインし、その後で以下のページにアクセスし、画面右上の "Fork" をクリックしてください:
https://github.com/dotnsf/cloudant_cors

2021022706


Fork 処理が成功すると、
 https://github.com/(あなたの github アカウント名)/cloudant_cors
というリポジトリができるはずです。

このリポジトリの Github ページを有効にします。リポジトリページの右上にある "Settings" をクリックします:
2021022707


画面を下に "GitHub Pages" と書かれている箇所までスクロールします。"Source" に "main" ブランチを指定して保存すると、"main" ブランチの内容がそのまま Github ページとして公開され、この中にある CORS の動作確認用に作った index.html を参照するための URL(https://(あなたの github アカウント名).github.io/cloudant_cors/)とそのリンクが表示されます(リンク先が有効になるまで1分程度かかります):
2021022708


少し待ってからリンク先にアクセスします。以下のような画面が表示されるはずです(Github ページなので、****.github.io というドメインのページになっていることを再確認してください):
2021022704


改めて資格情報から取得した値を画面内の各該当箇所に入力します。上にある横幅の大きなフィールドには host の値、その下は左から作成した DB 名(mydb)、username、password の値を入力します。最後に "Refresh" ボタンを押すと入力された情報を使ってこのページから AJAX で Cloudant へ REST API を実行して文書一覧を取得し、表形式で出力します。成功すると以下のように47都道府県の情報が(クロスオリジンの制約を乗り越えて)表示されます:
2021022705


以上、正しく設定することでクロスオリジンの壁を超えて Github ページから Cloudant のデータを利用することができました。Github も Cloudant も(一定制限の中で)無料で利用できるので、安定したウェブページを Github で運用しつつ、表示データを Cloudant から提供する、といった形で利用することができそうです。


なお、Cloudant(CouchDB) の REST API についての詳しくは、こちらのリファレンスを参照してください:
https://docs.couchdb.org/en/stable/api/index.html


なお、今回使った「特定データベース内の全文書を取得する」 REST API はこちらです:
GET /{db}/_all_docs

また準備段階で都道府県データをバルクインサートしましたが、その REST API はこちらで紹介されています:
POST /{db}/_bulk_docs


ある意味、先日のこの記事の続きです:
「チームでアプリケーション開発を体験したい」、どうやる?


1つの案として(候補案4の)「オンラインエディタを使う」方法を紹介しました。ここで紹介したオープンソースのオンラインエディタ Eclipse Orion を Linux サーバーに、特に docker 環境下で簡単に導入する方法を紹介します。なお、ここでの「 docker 環境下」は正確には「x86_64 チップの docker 環境下」とさせてください(後述する docker イメージが linux/amd64 アーキテクチャ向けのため)。


まずは docker 環境を用意します。既に手元にあれば飛ばしていただいて構いませんが、こちらを参考いただくなどして環境にあった方法で docker エンジンが起動している状態にしておいてください:
Docker のインストール

今回紹介する方法ではこちらの Eclipse Orion 用 docker イメージを使わせていただきます:
https://hub.docker.com/r/cloudeity/orion


早速 docker pull して、・・・の前に、Eclipse Orion が参照する対象となるフォルダを自分の手元に用意しておきます。フォルダが空の状態から始めるのであれば空のフォルダを用意すればいいのですが、サンプルファイルが含まれた状態で始めるのであれば、対象ファイルがコピーされた状態のフォルダを用意しておく必要があります。

今回は以下のような index.html ファイル1つだけが用意されたフォルダを /tmp/web/ 以下に準備することにします(別のフォルダでも構いませんが、以下の内容を読み替えてください)。まず /tmp/web というフォルダを作ります(後でこのフォルダを Eclipse Orion の作業フォルダとします):
$ mkdir -p /tmp/web


そして以下の内容の index.html を作って、/tmp/web/ フォルダにコピーしておきます(/tmp/web/index.html ファイルを作ります):
<html>
Hello World.
</html>

改めて docker を使って Eclipse Orion をインストールします。まずは docker pull でイメージをダウンロードしておきます(初回のみ):
$ docker pull cloudeity/orion

次にコンテナ化して起動するのですが、その際に -v オプションで作業フォルダをボリューム指定します。今回のように /tmp/web を作業フォルダとする場合は以下のコマンドを実行します:
$ docker run -d --name orion -v /tmp/web:/opt/orion.client/modules/orionode/.workspace -p 8081:8081 cloudeity/orion

コマンドの実行に成功したらウェブブラウザで 8081 番ポートにアクセスします。成功していると /tmp/web フォルダがプロジェクトフォルダとなって、ウェブブラウザ画面から既存の index.html ファイルを編集したり、新規にフォルダやファイルを作成したり、編集したりができるようになります:

(同じマシンから http://localhost:8081 にアクセスした時の画面。他マシンからアクセスする場合は localhost 部分を IP アドレス指定にします)
2021022301

(index.html ファイルを選択するとエディタが開き、直接編集できます)
2021022302


Eclipse Orion 自体を終了するには docker コンテナを止めます:
$ docker stop orion




このたび、ご縁あって浜名湖競艇のとある単レースの冠スポンサーとなる機会をいただきました:



開催日時: 令和日(水)
開催場所: 浜名湖競艇 第
レース名: .nsf@dominoforever記念


リリースから今年で33年目を迎えたグループウェア「ノーツ・ドミノ」。管理会社も社目となり、なんとなくに縁がありそうですが、信じるか信じないかはあなた次第。。


boatrace



に縁のありそうな日に、同じくに縁のありそうなグループウェア「ノーツ・ドミノ」の記念レースを開催させていただくことになりました。

僕を個人的にご存知でない方のために補足しておくと、gmail やツイッター、ポケモン Go などの各種アカウントで "dotnsf" を使っています。これは ".nsf" のことで、もともとは「ノーツ・ドミノ」システムで使うデータベースファイルの拡張子(.nsf)を由来にしています。もともとはロータス株式会社で Lotus 1-2-3 やノーツを含めたソフトウェア製品の開発を担当しており、その頃から自分の製品には愛着があって今に至ります。そんなノーツ・ドミノは 1989 年(平成元年!)のバージョン1リリースから今年で 33 年目を迎えました(余談ですが、これだけ長く業務に使われているアプリケーションソフトって他に一太郎くらいしか思いつきません)。管理会社は Lotus 、IBM を経て、現在は社目となる HCL に移っています。こういった背景&経緯&愛着もあって、今回この滅多にない機会でのレースのネーミングライツに応募し、当選させていただきました。

※名前の "dominoforever" 部分はコミュニティのハッシュタグとしてよく使われるものですが、・・詳しくはググってみてください(笑)

dotnsf_logo_200x200


この個人スポンサーの冠レース、売上の利益はスポンサーには入りません。みなさんが賭けて外れてもそのぶん僕の懐が潤ったりはしませんので(苦笑)、興味ありましたら(今回、このような機会をいただいた浜名湖競艇様を応援する意味で)視聴したり、舟券を購入してみたりいただけると嬉しいです。

#ちなみに僕は当日は抜けられないお仕事があるので、結果を楽しみにしております。 d(ToT)



ところで今回、この冠レースに個人で申し込みを行って知ったのですが、このような公営競技に個人が冠レーススポンサーになることができるレースがいくつかあります(企業スポンサーほどは多くありません)。またスポンサーになった上での特典もまちまちです。今回の浜名湖競艇様はそんな個人スポンサーを募っている数少ない公営競技の1つです。どんな特典があるのか、といったことも含めて興味を持った方はこちらを参照ください(ここから協賛を申し込むこともできます):
個人向け応募概要

※2021 年2月申し込み時点では新型コロナウィルス感染拡大防止のため、無観客開催が行われており、花束プレゼンターとしての表彰式参加ができません。残念とも言えるし、それで抽選のライバルが減るのはラッキーとも言えるような・・・

この浜名湖競艇の場合は「レース名が20文字以内」、「PR文書が80文字以内」という制限が結構厳しく、色々考えた末にこのようなレース名&PR文書となりました(僕のはどちらもギリギリです)。


このページのトップヘ