Google Analytics Query Explorer ライクにauth認証動作を行う

## サンプルの改修箇所

前回のサンプルをどのように改修したかは、細かい所まで書きませんがおおまかには以下のようにしています。

**hello_analytics_api_v3.html**
　- 認証解除ボタンを作り、Get Sessionsボタンは削除
**hello_analytics_api_v3_auth.js**
　- プロファイル情報などのセレクトボックスを新規作成したので、表示/非表示の操作ができるようにした。ボタンが無くなったので、makeApiCall() を直接呼び出している。
**hello_analytics_api_v3.js**
　- プロファイル、プロパティ情報を取得し、それぞれをセレクトボックスに格納して情報を選択しやすくした。また、レポート採取の関数はPerlで実装済なのでここでは呼び出さないようにした。

## 実際のコード

```html:hello_analytics_api_v3.html
<!DOCTYPE>
<html>
  <head>
    <title>Hello Google Analytics API</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
  </head>
  <body>
    <!-- 認証処理系ボタン -->
    <button id="authorize-button" style="visibility: hidden">googleアカウント認証</button><br/>
    <button id="logout-button" style="visibility: hidden" onclick="location.href='https://accounts.google.com/logout'">googleアカウント認証解除</button>
    <!-- アカウント情報 -->
    <p id="g_account_name"></p>
    <!-- プロパティ情報 -->
    <label id="g_property_select_label"></label>
    <select id="g_property_select" name="g_property_select" style="visibility: hidden"></select>
    <br>
    <!-- プロファイル（ビュー）情報 -->
    <label id="g_profile_select_label"></label>
    <select id="g_profile_select" name="g_profile_select" style="visibility: hidden"></select>

    <!-- アナリティクスapi認証プログラム -->
    <script src="hello_analytics_api_v3_auth.js"></script>
    <script src="hello_analytics_api_v3.js"></script>

    <!-- htmlが読み込まれたときに、最初に実行される関数を定義 -->
    <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
  </body>
</html>
```

```js:hello_analytics_api_v3_auth.js
// アナリティクスAPIの認証を行う関数。

// scopes以外の値は、各自のアプリケーションによって変えてください。
var clientId = "your_clientid";
var apiKey = "your_apikey";
var scopes = "https://www.googleapis.com/auth/analytics.readonly";

// ライブラリが読み込まれたあとで、最初に呼ばれる関数
//　以下、呼び出す関数は関数名を書いた次のセクション（　ブレースで囲まれた部分　）に定義していく
function handleClientLoad() {
  // 1. APIキーをセット
  gapi.client.setApiKey(apiKey);

  // 2. ユーザ認証の確認
  window.setTimeout(checkAuth,1);
}

function checkAuth() {
  // 今のユーザーの認証状態を確認するため、グーグルアカウントサービスを呼び出す
  // handleAuthResult関数へ、以下の関数の結果を返却する
  gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}

function handleAuthResult(authResult) {
  if (authResult) {
    // ユーザーの認証が完了
    // アナリティクスアカウントの情報を読み込む
    loadAnalyticsClient();
  } else {
    // ユーザー認証が失敗
    handleUnAuthorized();
  }
}

// 認証されたユーザー
function handleAuthorized() {
  var authorizeButton = document.getElementById('authorize-button');
  var getPropertySelect = document.getElementById('g_property_select');
  var getProfileSelect = document.getElementById('g_profile_select');
  var logoutButton = document.getElementById('logout-button');

// 'セッション情報取得'ボタンを表示し、 その他を非表示
  authorizeButton.style.visibility = 'hidden';
  getPropertySelect.style.visibility = '';
  getProfileSelect.style.visibility = '';
  logoutButton.style.visibility = '';

  // 'セッション情報取得'ボタンがクリックされた場合、makeAapiCall functionを実行
  // makeApiCallButton.onclick = makeApiCall;
  makeApiCall();
}

// 認証されなかったユーザー
function handleUnAuthorized() {
  var authorizeButton = document.getElementById('authorize-button');
  var getPropertySelect = document.getElementById('g_property_select');
  var getProfileSelect = document.getElementById('g_profile_select');
  var logoutButton = document.getElementById('logout-button');

  // 'googleアカウント認証'ボタンを表示し、 その他を非表示
  authorizeButton.style.visibility = '';
  getPropertySelect.style.visibility = 'hidden';
  getProfileSelect.style.visibility = 'hidden';
  logoutButton.style.visibility = 'hidden';

  // 'googleアカウント認証'ボタンがクリックされた場合、handleAuthClick を実行
  authorizeButton.onclick = handleAuthClick;
}

function handleAuthClick(event) {
  gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
  return false;
}

function loadAnalyticsClient() {
  // アナリティクスアカウントの情報を呼び出し、 その中でhandleAuthorized関数を実行する。
  gapi.client.load('analytics', 'v3', handleAuthorized);
}

```

```js:hello_analytics_api_v3.js
//アナリティクスアカウント情報取得のためのjavascript

// apiの開始
function makeApiCall() {
  queryAccounts();
}

function queryAccounts() {
  console.log('Querying Accounts.');

  // 認証されたユーザーに紐付けられた全てのアナリティクスアカウントを取得
  gapi.client.analytics.management.accounts.list().execute(handleAccounts);
}

function handleAccounts(results) {
  if (!results.code) {
    if (results && results.items && results.items.length) {

      // 最初に登録されたアナリティクスアカウントidを取得
      var firstAccountId = results.items[0].id;
      var firstAccountName = results.username;
      var $jqObj;
      console.log('authorized account name is ' + firstAccountName);
      var $div = $("#g_account_name");
      $jqObj = $("<p>").text("現在 " + firstAccountName + " として認証されています");
      $div.append($jqObj);

      // セレクトボックス用のラベル生成
      var $propertyLabel = $("#g_property_select_label");
      $jqObj = $("<label>").text("Webプロパティ");
      $propertyLabel.append($jqObj);
      var $profileLabel = $("#g_profile_select_label");
      $jqObj = $("<label>").text("プロファイル（ビュー）");
      $profileLabel.append($jqObj);

      // アカウントのウェブプロパティをリクエストする
      queryWebproperties(firstAccountId);

    } else {
      console.log('No accounts found for this user.')
    }
  } else {
    console.log('There was an error querying accounts: ' + results.message);
  }
}

function queryWebproperties(accountId) {
  console.log('Querying Webproperties.');

  // アカウントの全てのウェブプロパティを取得する
  gapi.client.analytics.management.webproperties.list({'accountId': accountId}).execute(handleWebproperties);
}

// 取得したウェブプロパティを操作し、プロパティ選択用のセレクトボックスを作成
function handleWebproperties(results) {
  if (!results.code) {
    if (results && results.items && results.items.length) {

      var accountId = results.items[0].accountId; // ウェブプロパティ取得用に、アナリティクスアカウントidを取得
      var webpropertyId, webpropertyName, $jqObj;
      var $propertySelect = $("#g_property_select");
      $("#g_property_select > option").remove();
      var web_idx = 0;
      web_idx = parseInt(web_idx);
      while (web_idx < results.items.length) {
        webpropertyId = results.items[web_idx].id;
        webpropertyName = results.items[web_idx].name;
        console.log('webproperty Id/Name is ' + webpropertyId + '/' + webpropertyName);
        $jqObj = $("<option>").html(webpropertyName).val(webpropertyId);
        $propertySelect.append($jqObj);
        web_idx++;
      }
      
      // セレクトボックス選択したときのイベント追加
      $propertySelect.change(function () {
        webpropertyId = $("#g_property_select").val();
        $("#g_profile_select > option").remove();
        queryProfiles(accountId, webpropertyId);
      }).change();
      
    } else {
      console.log('No webproperties found for this user.');
    }
  } else {
    console.log('There was an error querying webproperties: ' + results.message);
  }
}

function queryProfiles(accountId, webpropertyId) {
  console.log('Querying Views (Profiles).');
  // アナリティクスアカウントに紐付けられているプロパティのプロファイル情報を全て取得
  gapi.client.analytics.management.profiles.list({
      'accountId': accountId,
      'webPropertyId': webpropertyId
  }).execute(handleProfiles);
}

// 取得したプロファイル情報を操作し、プロファイルid選択用のセレクトボックスを生成する
function handleProfiles(results) {
  if (!results.code) {
    if (results && results.items && results.items.length) {

      var profileId, profileName, $jqObj;
      var $profileSelect = $("#g_profile_select");
      var view_idx = 0;
      idx = parseInt(view_idx);
      while (view_idx < results.items.length) {
        profileId = results.items[view_idx].id;
        profileName = results.items[view_idx].name;
        console.log('profile id/name is ' + '/' + profileId + '/' + profileName);
        $jqObj = $("<option>").html(profileName).val(profileId);
        $profileSelect.append($jqObj);
        view_idx++;
      }

      // Step 3. 実際のレポートを取得する
      // queryCoreReportingApi(firstProfileId);

    } else {
      console.log('No views (profiles) found for this user.');
    }
  } else {
    console.log('There was an error querying views (profiles): ' + results.message);
  }
}

// function queryCoreReportingApi(profileId) {
//   console.log('Querying Core Reporting API.');

//   //　アナリティクスのデータを取得
//   gapi.client.analytics.data.ga.get({
//     'ids': 'ga:' + profileId,
//     'start-date': '2012-03-03',
//     'end-date': '2012-03-03',
//     'metrics': 'ga:sessions'
//   }).execute(handleCoreReportingResults);
// }

// function handleCoreReportingResults(results) {
//   if (results.error) {
//     console.log('There was an error querying core reporting API: ' + results.message);
//   } else {
//     printResults(results);
//   }
// }

// function printResults(results) {
//   if (results.rows && results.rows.length) {
//     console.log('View (Profile) Name: ', results.profileInfo.profileName);
//     console.log('Total Sessions: ', results.rows[0][0]);
//   } else {
//     console.log('No results found');
//   }
// }
```

以上です。

## デモ

1. アクセス
![WS000000.png](https://qiita-image-store.s3.amazonaws.com/0/34636/02e8f4fb-9aa3-b52f-43b9-8b60e3a704f2.png)

2. 認証ウインドウが出力されるので、情報を入力してログイン
![WS000001.png](https://qiita-image-store.s3.amazonaws.com/0/34636/2629922d-0ded-97bc-3bed-bc834bd270e1.png)

3. 認証情報が出力される
![WS000002.png](https://qiita-image-store.s3.amazonaws.com/0/34636/68c47d86-b949-e3b1-72b0-1e193a441f16.png)


4. 初期状態のプロファイルはこう
![WS000006.png](https://qiita-image-store.s3.amazonaws.com/0/34636/43bc2ca9-29bc-d06b-42c9-a4bc900f67c4.png)


5. プロパティの選択を変更すると。。
![WS000004.png](https://qiita-image-store.s3.amazonaws.com/0/34636/361b4969-9969-10c8-13d4-4d67697a520d.png)


6. プロファイルに結果が反映される
![WS000005.png](https://qiita-image-store.s3.amazonaws.com/0/34636/49485614-efa8-b5aa-b3fc-6abfd2ee327a.png)

7. 認証解除ボタンをクリックすると。。
![WS000007.png](https://qiita-image-store.s3.amazonaws.com/0/34636/b7842177-c488-1669-67df-c3bf2492fa4e.png)

8. googleの認証ウインドウに戻る。
![WS000008.png](https://qiita-image-store.s3.amazonaws.com/0/34636/e8128f6a-e594-5a43-a558-e10fea927424.png)

以上です。

## 注意点
アナリティクスAPIの仕様を理解するのと、 **JavaScriptのコールバック関数の概念を理解するところでかなりハマりました。。** アカウント認証するときと、プロパティ呼び出しの所で同じアカウントIDを呼び出している所もそうです。

共有してほしい情報がありましたら是非コメント欄まで！実装に関するツッコミもお待ちしております。


ともあれ、なんとか当初の目的を果たすことができました。