22
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WebサイトのJSからGASの実行可能APIを最短最高で叩く

Last updated at Posted at 2020-05-07

#はじめに
お金はないですがGoogleCloudPlatformのアカウントは持っているのでスプレッドシートをDBにしてWebサービスを作ってます。GASをけいゆうしてデータの読み込みから書き出しまでやろうと思ったんですが、JSから叩くのに3日かかったのでシンプルにまとめます。情報が少ない上、設定が多いのでかなりかかりました。QuickStartが全然クイックじゃないしサンプルがほんとに意地悪でした。あとは重要なことが文が多すぎて埋もれてしまってます…Googleのサポート問題は日本語化されていない時点でお察しですが、なんとか英訳してやってみました。

また、この記事は以下の記事を全面的に元にしています。
【GAS】Execution APIを使ってJavaScriptからGASにアクセスする

手順1 クライアント側

Webサイトを作ってください。URLが確定してないとだめです。サーバーレスでFirebaseHostingとかいいですよ。

あらかじめ説明しときますが制作したウェブサイトのURLの

https://example.xxx.xxx/yyyy/zzzz

https://をスキーム、exampleをサーバー名、xxx.xxxはドメイン、example.xxx.xxxの部分はオーソリティ、yyyy/zzzzはパス名といいます。(諸説あるかもしれません)

手順2 GAS側

適当に新しいプロジェクトを作って関数を作ります。

コード.gs
function helloWebAPI(str) {
  return "Hello!" + str;
}

文字列を受け取ってHello!とくっつけるだけです。
次にGoogleCloudPlatformとの連携を行います。GASは昔は自動で連携してたんですがその機能がOFFになっているそうで自分で作らなければいけません。

手順3 GoogleCloudPlatform側

これは課金が必要です。よくわからないですが無料トライアルに銀行口座が必要らしいです。自動で請求しないみたいですが。

  1. 新しいプロジェクトを作る。(名前はGASと同じがわかりやすい)
  2. APIとサービスからOAuth 同意画面に移動する。同意画面を作成していきます。
  3. アプリケーション名(適当)と承認済みドメインに使ってるドメインを入力し作成。(複数可)
  4. APIとサービスから認証情報に移動する。
  5. 認証情報を作成をクリック。OAuthクライアントIDを作成していきます。
  6. アプリケーションの種類はウェブアプリケーション。名前は適当。承認済みのJavaScript生成元はオーソリティまでを、承認済みのリダイレクトURIはパスまで全部のURLを入れときます。(複数可)そして作成。
  7. 出てきたクライアントIDの方だけを控えときます。こうゆうトークンがこれから何個か出てくるので注意です。
  8. 認証情報を作成をクリック。APIキーを作成します。すぐにできます。
  9. 出てきたAPIキーを控えます。
  10. APIキーは制限を行わないといけないのでペンマークで編集を行い、アプリケーションの制限API の制限を行ってください。アプリケーションはウェブアプリケーションのみ、APIのほうはこれから使うAppsScriptAPIのみでいいでしょう。

10のAPIの制限に関しては個人でGoogleCloudPlatformに別のAPIを適応したりするときには他のAPIにもチェックを付けてください。自分はWindowsApplicationも作る予定があったのでアプリケーションの制限は行いませんでした。

次は外部からこれらのトークンを使って叩けるようにしましょう。
APIとサービスからライブラリに移動して検索窓にGoogle Apps APIと入力して出てきたやつを有効にしてください。

ホームのダッシュボードからプロジェクト情報からプロジェクト番号を控えましょう。

次はこのプロジェクトとGASのプロジェクトをつなげましょう。


これらの設定は以下の記事を参考にしました。現在のバージョンと違うので画面が違かったり、APIの名前が違かったりします。
【GAS】Execution APIを使ってJavaScriptからGASにアクセスする
GASのExecution APIを使ってGASを外部からぶっ叩く

ほぼ同じことをしている上、スクショつきでとてもわかりやすいです。
【GoogleAppsScript】実行可能API(Execution API)の初期設定でハマったので流れをメモする


手順4 GAS側

メニューバーからリソースCloud Platform プロジェクトを押下。プロジェクト番号を入力して横のボタンを押して、上の方にこのスクリプトが現在関連付けられているプロジェクト:が表示されたらオッケーです。

次はこのGASのソースコードを実行可能APIにします。
メニューバーの公開から実行可能APIとして導入を押します。バージョンを新規作成にして説明を入力。導入を押せばオッケーです。ここで出てくるAPI IDは使いません。

さて、次は必要な情報を取得します。
メニューバーのファイルからプロジェクトのプロパティを押してプロパティを見ます。
必要なのは、

  • スクリプト ID
  • スコープ

です。スクリプトIDは情報タブに、スコープはスコープタブにあります。スクリプトIDはGASプロジェクトの判別トークンですが、スコープはこのGASプロジェクトを実行するときに許可しないといけない権限です。これらを許可していないと実行はできません。何もない人は大丈夫ですが、現在あるいは今後、スプレッドシートを見たりいじったりDriveを見たりいじったり、ほかのGoogleサービスに対して処理を行う際はこのスコープも必要になります。もちろんスコープは複数ある場合もあります。

ではいよいよクライアント側の実装に移ります。現在必要なのは以下のトークンたちです。

  • クライアントID
  • APIキー
  • スクリプトID
  • スコープ

こうゆうのってトークンいっぱいあるからいやになりますよね...

手順5 クライアント側

ファイル分ける派です。

HTML

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>WebサイトのJSからGASの実行可能APIを最短最高で叩く</title>
  </head>
  <body>
    <button id="authorize_button" style="display: none;">Authorize</button>
    <button id="signout_button" style="display: none;">Sign Out</button>
    <script async defer src="https://apis.google.com/js/api.js"
      onload="this.onload=function(){};handleClientLoad()"
      onreadystatechange="if (this.readyState === 'complete') this.onload()">
    </script>
  <!-- index.jsのロード -->
  </body>
</html>

APIを叩くにはユーザーにログインさせないといけないのでログインボタンを配置します。実用化のときには要らないですね。CDN(?)でapiをロードし、ロードが終わったときにhandleClientLoad()を実行します。

JS

index.js
var CLIENT_ID = /* クライアントID */;
var API_KEY = /* APIキー */;
var SCOPES = [/* スコープリスト */];
var scriptId = /* スクリプトID */;
var DISCOVERY_DOCS = ["https://script.googleapis.com/$discovery/rest?version=v1"]; // そのまま

var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');

// 勝手に実行される
function handleClientLoad() {
  gapi.load('client:auth2', initClient);
}

function initClient() {
  // GoogleAPIのinitialize
  gapi.client.init({
    apiKey: API_KEY,
    clientId: CLIENT_ID,
    discoveryDocs: DISCOVERY_DOCS,
    scope: SCOPES.join(' ')
  }).then(function () {
    // ログインしているかをupdateSigninStatusに渡す
    updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
    // ボタンが押された時の処理を代入。
    authorizeButton.onclick = handleAuthClick;
    signoutButton.onclick = handleSignoutClick;
  }, function(error) {
    appendPre(JSON.stringify(error, null, 2));
  });
}

function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    // ログイン済み
    authorizeButton.style.display = 'none';
    signoutButton.style.display = 'block';
    // APIを叩く
    callAppsScript("helloWebAPI", ["World!"], function(resp) {
      // コールバック関数
      if (resp.error) {
        // エラーあり
        if (resp.error.status) {
          console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
        } else {
          var error = resp.error.details[0];
          console.log('Script error! Message: ' + error.errorMessage);
        }
      } else {
        // エラーなし
        appendPre(resp.response.result);
      }
    });
  } else {
    // ログインしていない
    authorizeButton.style.display = 'block';
    signoutButton.style.display = 'none';
  }
}

// ログインボタンが押された時
function handleAuthClick(event) {
  // ログインの処理
  gapi.auth2.getAuthInstance().signIn();
}

// ログアウトボタンが押された時
function handleSignoutClick(event) {
  // ログアウトの処理。
  gapi.auth2.getAuthInstance().signOut();
}

// 画面に出力する
function appendPre(message) {
  var pre = document.getElementById('content');
  var textContent = document.createTextNode(message + '\n');
  pre.appendChild(textContent);
}

// APIを叩く関数
function callAppsScript(functionName, parameters, callbackFunc) {
  var request = { 'function': functionName, 'parameters': parameters };
  var op = gapi.client.request({
    'root': 'https://script.googleapis.com',
    'path': 'v1/scripts/' + scriptId + ':run',
    'method': 'POST',
    'body': request
  });
  // APIを叩いてコールバック関数を呼ぶ
  op.execute(callbackFunc);
}

随分と長いですがやってることはむずくないです。
handleClientLoad()は準備が完了したときに実行されます。(HTMLに記載)initClient()はOAuthと通信を行い、必要なすべてのスコープが許可されているか、ログインしているかを確認します。そこけっかはすぐ下の.then()コールバックに返ってきます。結果によってログイン/アウトボタンを設定し、ログインしていればAPIを叩きます。APIを叩く際、callAppsScript()という関数を作ってそれを実行しています。

第一引数に実行GASの関数名、第二には引数を配列で渡し、第三にはそのコールバック関数を設定してください。

実際に叩いているのはcallAppsScript()内のop.execute()です。リクエストJsonを制作し叩きます。resp.response.resultがreturnされた結果です。

さいごに

Google許すまじ!!

まぁ、頭が悪い自分が悪いんですが。実はAPIがもう一種類あって今回使ったのがhttps://apis.google.com/js/api.jsなのに対しhttps://apis.google.com/js/client.jsてやつがあるんですよね。この二つの情報が錯綜していてgapi.client.request()を使ってリクエストを送る方法を取りました。この方法だとどんなリクエストでも送れるみたいです。client.jsにはscript.scripts.runというやつがあってこれは今回やったGASの関数を実行する専用のようです。ということはapi.jsの中にclient.jsがあるのかな?わがんね。

Twitter→https://twitter.com/CyberHacnoshuke

22
25
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?