続: S3+AWS SDKで動的Webサイトホスティング

 API AWS-SDK JavaScript Facebook OAuth2.0
2014.09.26

前回の続編です。気づけば3ヶ月経っていました・・フフ・・・

 

Part 3: D3.jsライブラリを使ってグラフにしてみる

JavaScriptのライブラリ、D3 を使ってグラフにしてみましょう!
D3をより簡単に使うための NVD3 というライブラリも使います。
 
HTMLを書き換えます。

<head>
  <title>AWS Javascript Browser SDK Example</title>
  <meta charset="UTF-8">
+  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.1.15-beta/nv.d3.min.js">
  <script src="https://sdk.amazonaws.com/js/aws-sdk-2.0.16.min.js"></script>
</head>
<body>

-  <div id="browser-count"></div>
  <fb:login-button scope="public_profile" onlogin="checkLoginState();"></fb:login-button>
+  <div id="chart"><svg style="width:500px;height:500px;" /></div>

+  <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js" charset="utf-8"></script>
+  <script src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.1.15-beta/nv.d3.min.js" charset="utf-8"></script>
+  <script src="//code.jquery.com/jquery.js"></script>

 
DynamoDBへ閲覧者のブラウザを登録した後、以下も行うJavaScriptに書き換えます。

  1. Dynamoに入っている全データの取得
  2. データをD3のグラフ描画用に整形
  3. NVD3を使ってグラフを描画
  4. 以上を 10秒ごとに行う

 

    db.updateItem(params, function (err, data) {
      if (err) {
        console.log(err, err.stack);
        return;
      }
-      document.getElementById('browser-count').innerHTML =
-          'Successful authentications by people with browsers like yours: ' +
-          data.Attributes.count.N;
      $('.fb_iframe_widget').hide();
+      updateBrowserCount();
    });

この直後に以下の関数を追加します。

  // display current detected browser count
  function updateBrowserCount() {
    db.scan({TableName: 'browsers'}, function (err, data) {
      var browsers = [];
      $.each(data.Items, function () {
        browsers.push({
            label: this.browser.S + '-' + this.version.S,
            value: this.count.N});
      });
      // draw the chart using D3
      drawChart(browsers);
    });
    // repeat...
    setTimeout(updateBrowserCount, 1000 * 10);
  }
  // draw the chart with NVD3
  function drawChart(browsers) {
    nv.addGraph(function() {
      var chart = nv.models.pieChart()
        .x(function(d) { return d.label; })
        .y(function(d) { return d.value; })
        .showLabels(true)
        .labelThreshold(.05)
        .labelType("percent");
      d3.select("#chart svg")
        .datum(browsers)
        .transition().duration(100)
        .call(chart);
      nv.utils.windowResize(chart.update);
    });
  }

変更したファイルを、前回と同じ手順でS3にアップロードします。
ブラウザを更新して、改めてログインを押してみると・・
 
 

グラフになりましたね!  
 
 

Part4. 閲覧者に応じたきめ細かいアクセス制御

ここまでの例は、facebookアカウントさえもっていれば
誰もがあなたのAmazon DynamoDBリソースを使える
状況でした。
そこで最後に、IAMを使ってどのように権限を絞ったり、
ユーザごとのデータを分離できるかをみていきます。
 
(この制御には、昨年 re:Invent 2013で発表された
 Fine-grained Access Controlという機能を利用します)
 
 
まずDynamoDBに「サイト閲覧者 “ごと” のブラウザが何か」
というデータを保存するためのテーブルを作ります。
 
https://console.aws.amazon.com/dynamodb/home をひらき、
“Create Table” をクリック。

 
テーブル名を “user-browsers” としましょう。
プライマリキー種別(Primary Key Type)は “Hash and Range” を選び
ハッシュキー(Hash Attribute Name)としてStringで “userId“、
レンジキー(Range Attribute Name)にはStringで “browser” を指定します。

 
例によって、
user-browsersテーブルが作成できたら ARNを控えておきましょう。
 
続いて、IAMで定義していたポリシーを変更します。
 
https://console.aws.amazon.com/iam/home をひらき、
“Roles”、前回作ったロールをクリック、”Permissions” の
“Manage Policy” をクリックします。

 
以下のARNをつい先ほどのDynamoDBのリソース名に置き換えつつ、
“Policy Document” をこんな感じに書き換えます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:Query",
        "dynamodb:UpdateItem",
        "dynamodb:Scan"
      ],
      "Resource": [
        "arn:aws:dynamodb:ap-northeast-1:123456789:table/user-browsers"
      ],
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys":["${graph.facebook.com:id}"],
          "dynamodb:Attributes": [
            "userId", "browser", "count"
          ]
        },
        "StringEqualsIfExists": {
          "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:Scan"
      ],
      "Resource": [
        "arn:aws:dynamodb:ap-northeast-1:123456789:table/user-browsers"
      ],
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:Attributes": [
            "browser"
          ]
        },
        "StringEqualsIfExists": {
          "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
        }
      }
    }
  ]
}

 
HTMLをちょっとだけ書き換えます。
自分自身のブラウザ利用率をグラフで表現しつつ、
他のユーザの利用ブラウザ一覧を表示するリストを用意します。

 <fb:login-button scope="public_profile" onlogin="checkLoginState();"></fb:login-button>
+  <div>Browsers used by you:</div>
  <div id="chart"><svg style="width:500px;height:500px" /></div>
+  <div>
+    <span>Browsers used by all users:</span>
+    <ul id="browser-list"></ul>
+  </div>

  <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js" charset="utf-8"></script>

 
JavaScriptもDynamoDBの定義にあわせ、変更します。
まずDynamoDBをUpdateする部分。

  function doSomethingWithAwsSdk(userID) {
    var detectedBrowser = identifyBrowser();
    var params = {
-      TableName: 'browsers',
-      Key: {'browser': {'S': detectedBrowser.n},
-          'version': {'S': detectedBrowser.v}
-      },
+      TableName: 'user-browsers',
+      Key: {
+        userId: { S: userID},
+        browser: { S: detectedBrowser.n + '-' + detectedBrowser.v}
+      },
      AttributeUpdates: {
        count: { Action: 'ADD', Value: { N: '1'}}
      },

 
更新されたDynamoDBの値をもってくる
updateBrowserCount関数の中身は、以下のように書き換えます。

function updateBrowserCount(userID) {
  var params = {
    TableName: 'user-browsers',
    Select: 'SPECIFIC_ATTRIBUTES',
    AttributesToGet: ['browser', 'count'],
    KeyConditions: {
      'userId': {
        ComparisonOperator: 'EQ',
        AttributeValueList: [{ S: userID}]
      }
    },
  };
  db.query(params, function (err, data) {
    if (err) {
      console.log(err, err.stack);
      return;
    }
    var browsers = [];
    $.each(data.Items, function() {
      browsers.push({
          label: this.browser.S,
          value: this.count.N});
    });
    // draw the chart using D3
    drawChart(browsers);
  });
  var params = {
    TableName: 'user-browsers',
    Select: 'SPECIFIC_ATTRIBUTES',
    AttributesToGet: ['browser']
  }
  db.scan(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
    } else {
      var list = $('#browser-list').html('');
      $.each(data.Items, function() {
        list.append('<li>'  this.browser.S  '</li>');
      });
      // de-dupe browser list
      var seen = {};
      list.find("li").each(function(index, elem) {
        var txt = $(this).text().toLowerCase();
        if (seen[txt]) {
          $(this).remove();
        } else {
          seen[txt] = true;
        }
      });
    }
  });
  // repeat...
  setTimeout(function(){updateBrowserCount(userID)}, 1000 * 10);
}

 
変更したindex.htmlをS3にアップロードし、
ブラウザからアクセスしてみましょう!!


 
うまくいきましたか?
 
 
 
最終的な挙動を実際に試せるページはこちらです!
(急遽このデモが動かなくなる日が来ても許してね・・)
HTML/JavaScriptはこちらのGistで見ていただけます。
 
自分で組んでうまくいかないようなときは
このあたりを参考にしてみてください^^
 
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-intro.html
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/frames.html#!AWS/DynamoDB.html