【GAS】Execution APIを使ってJavaScriptからGASにアクセスする

#GAS側のスクリプトを用意する
　https://script.google.com
から独立したスクリプトファイルを新規作成します。ファイル名は何でもかまいません。 

　
　[公式サイト](https://developers.google.com/apps-script/guides/rest/quickstart/target-script)で紹介されているコードをそのままコピペし、保存します。

```js:コード.gs
function getFoldersUnderRoot() {
  var root = DriveApp.getRootFolder();
  var folders = root.getFolders();
  var folderSet = {};
  while (folders.hasNext()) {
    var folder = folders.next();
    folderSet[folder.getId()] = folder.getName();
  }
  return folderSet;
}
```

　グーグルドライブのルートディレクトリに作成されたフォルダ名をすべて返す関数です。

#スクリプトID取得
　画面上部のメニューバーから「ファイル」→「プロジェクトのプロパティ」から情報タブのなかにあるのでそれをどこかにコピペしておきます。<font color="Blue">あとでサーバー側のコードを書くときに使います。

　あるいはURL欄から調べても同じで、https\://script.google.com/d/スクリプトID/edit?usp=drive_web&folder=xxxxx&splash=yesのなかの、スクリプトIDがそれです。

#公開
　同じく画面上部メニューバーから「公開」→「実行可能APIとして導入」と進み、バージョンは「新規」として名前は適当に付け、スクリプトにアクセスできるユーザーは「自分のみ」とします。

　また、ダイアログボックスに「現在のAPI ID」という情報が表示されていますがこれは今回特に利用しません。

#Execution API有効化
　同じくメニューバーから「リソース」→「Developers Console プロジェクト」と進み、このスクリプトが現在関連付けられているプロジェクト:の下に青文字で表示されているリンク先をクリックします。

　API Manager画面になったら左サイドバーから「ダッシュボード」をクリックし、画面上部にある「APIを有効にする」をクリックするとライブラリの一覧が表示されます（最初から左バーからライブラリを選択しても同じか）。

![スクリーンショット 2016-11-13 23.56.44.png](https://qiita-image-store.s3.amazonaws.com/0/139134/da640619-9281-a4a1-ca2a-ff3601b1fd83.png)

　<font color="Red">ここがややこしいのですが、この中にはExcution APIが表示されていません。そこで検索ボックスに「Execution」などと入力してExcution APIを表示させる必要があります。そして「Google Apps Script Execution API」をクリックして次へ進みます（個人的にはここが一番ハマりました）。</font>

![スクリーンショット 2016-11-14 0.00.48.png](https://qiita-image-store.s3.amazonaws.com/0/139134/67b0024f-393d-0eb4-d143-09c5f3363078.png)


　そして「有効にする」をクリックして有効化します。

![スクリーンショット 2016-11-14 0.03.59.png](https://qiita-image-store.s3.amazonaws.com/0/139134/6098fda1-50b2-dc5f-8ae9-9b72ff89e48e.png)

#認証情報設定
　先程のAPI Manager画面の左サイドバーから「認証情報」をクリックします。

![スクリーンショット 2016-11-14 0.14.47.png](https://qiita-image-store.s3.amazonaws.com/0/139134/dd230160-c4c0-530d-4edd-598e72f49d02.png)

　ここからOAuthクライアントIDを選択します。

![スクリーンショット 2016-11-14 0.17.09.png](https://qiita-image-store.s3.amazonaws.com/0/139134/63c268fd-630a-0ae5-3b98-bf02f7e5d349.png)

　アプリケーションの種類は「ウェブアプリケーション」を選択します。名前は適当でかまいません。「承認済みのJavaScript生成元」は各自が使っているWebサーバーのhttp\://www.sample.com のように入力しておき（トップディレクトリと言ったらいいのか？）、下段の「承認済みのリダイレクトURI」はhttp\://www.sample.com/quickstart.html のように入力しておきます。quickstart.htmlはあとで作成します。

　で、作成ボタンをクリックします。

![スクリーンショット 2016-11-14 0.30.37.png](https://qiita-image-store.s3.amazonaws.com/0/139134/51eb194b-6058-431d-5cfd-4bdf696a0013.png)

　するとクライアントIDとクライアントシークレットが生成されており、<font color="Blue">今回はクライアントIDだけ使用するので、それをどこかにコピーしておきます。

#サーバー側でファイルを用意する

　[公式サイト](https://developers.google.com/apps-script/guides/rest/quickstart/js)のコードをそのままコピペしますが、 \<YOUR_CLIENT_ID>は認証情報設定時のクライアントIDを、ENTER_YOUR_SCRIPT_ID_HEREの部分は最初の方で示したスクリプトIDを入れます。これを認証設定のときの設定に沿ってhttp\:www.sample.com/quickstart.html に置きます。

```html:quickstart.html
<html>
  <head>
    <script type="text/javascript">
      // Your Client ID can be retrieved from your project in the Google
      // Developer Console, https://console.developers.google.com
      var CLIENT_ID = '<YOUR_CLIENT_ID>'; // ←クライアントID

      var SCOPES = ['https://www.googleapis.com/auth/drive'];

      /**
       * Check if current user has authorized this application.
       */
      function checkAuth() {
        gapi.auth.authorize(
          {
            'client_id': CLIENT_ID,
            'scope': SCOPES.join(' '),
            'immediate': true
          }, handleAuthResult);
      }

      /**
       * Handle response from authorization server.
       *
       * @param {Object} authResult Authorization result.
       */
      function handleAuthResult(authResult) {
        var authorizeDiv = document.getElementById('authorize-div');
        if (authResult && !authResult.error) {
          // Hide auth UI, then load client library.
          authorizeDiv.style.display = 'none';
          callScriptFunction();
        } else {
          // Show auth UI, allowing the user to initiate authorization by
          // clicking authorize button.
          authorizeDiv.style.display = 'inline';
        }
      }

      /**
       * Initiate auth flow in response to user clicking authorize button.
       *
       * @param {Event} event Button click event.
       */
      function handleAuthClick(event) {
        gapi.auth.authorize(
          {client_id: CLIENT_ID, scope: SCOPES, immediate: false},
          handleAuthResult);
        return false;
      }

      /**
       * Calls an Apps Script function to list the folders in the user's
       * root Drive folder.
       */
      function callScriptFunction() {
        var scriptId = "ENTER_YOUR_SCRIPT_ID_HERE"; // ←スクリプトID

        // Create an execution request object.
        var request = {
            'function': 'getFoldersUnderRoot'
            };

        // Make the API request.
        var op = gapi.client.request({
            'root': 'https://script.googleapis.com',
            'path': 'v1/scripts/' + scriptId + ':run',
            'method': 'POST',
            'body': request
        });

        op.execute(function(resp) {
          if (resp.error && resp.error.status) {
            // The API encountered a problem before the script
            // started executing.
            appendPre('Error calling API:');
            appendPre(JSON.stringify(resp, null, 2));
          } else if (resp.error) {
            // The API executed, but the script returned an error.

            // Extract the first (and only) set of error details.
            // The values of this object are the script's 'errorMessage' and
            // 'errorType', and an array of stack trace elements.
            var error = resp.error.details[0];
            appendPre('Script error message: ' + error.errorMessage);

            if (error.scriptStackTraceElements) {
              // There may not be a stacktrace if the script didn't start
              // executing.
              appendPre('Script error stacktrace:');
              for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
                var trace = error.scriptStackTraceElements[i];
                appendPre('\t' + trace.function + ':' + trace.lineNumber);
              }
            }
          } else {
            // The structure of the result will depend upon what the Apps
            // Script function returns. Here, the function returns an Apps
            // Script Object with String keys and values, and so the result
            // is treated as a JavaScript object (folderSet).
            var folderSet = resp.response.result;
            if (Object.keys(folderSet).length == 0) {
                appendPre('No folders returned!');
            } else {
              appendPre('Folders under your root folder:');
              Object.keys(folderSet).forEach(function(id){
                appendPre('\t' + folderSet[id] + ' (' + id  + ')');
              });
            }
          }
        });
      }

      /**
       * Append a pre element to the body containing the given message
       * as its text node.
       *
       * @param {string} message Text to be placed in pre element.
       */
      function appendPre(message) {
        var pre = document.getElementById('output');
        var textContent = document.createTextNode(message + '\n');
        pre.appendChild(textContent);
      }

    </script>
    <script src="https://apis.google.com/js/client.js?onload=checkAuth">
    </script>
  </head>
  <body>
    <div id="authorize-div" style="display: none">
      <span>Authorize access to Google Apps Script Execution API</span>
      <!--Button for the user to click to initiate auth sequence -->
      <button id="authorize-button" onclick="handleAuthClick(event)">
        Authorize
      </button>
    </div>
    <pre id="output"></pre>
  </body>
</html>
```


　初回アクセス時のみ、認証画面へのクリックボタンが現れて、認証成功すると処理が進み、GASが吐き出すデータをJavaScriptで受け取って表示、という流れになっています。

```js
var request = {
  'function': 'getFoldersUnderRoot'
};
```
　ここですね。getFolersUnderRootというのはさっきGAS側で書いた関数です。GASにリクエスト投げてこれを実行して結果をもらって表示ということです。

　http\://www.example.com/quickstart.html
にアクセスし、認証に成功してフォルダー名の一覧が表示されればゴールです。

　とにかく手順が分かりにくいのでそのうちやり方が大幅に変わってしまうんじゃないかと個人的には危惧しています。

#ちなみに
　実践的にはスプレッドシートから何かしら値を受け取ってという流れを作りたいでしょう。そのサンプルは後日紹介したいと思います。

#参考サイト
[GASのExecution APIを使ってGASを外部からぶっ叩く](http://qiita.com/soundTricker/items/1bcfc5c9e80d29a7ae4b)
[Create a Target Project for the Apps Script Execution API](https://developers.google.com/apps-script/guides/rest/quickstart/target-script)
[JavaScript Quickstart](https://developers.google.com/apps-script/guides/rest/quickstart/js)