【GWSユーザー向け】ウェブアプリをGWS外からキックする画期的な話

GoogleWorkSpace（GWS）ユーザーのユーザーで、GASを、バッチ処理の中でキックして動かしたいと思ったことありませんか？

一般のGoogleユーザーなら、GASをウェブアプリとしてデプロイすれば、あとは、プログラム等でウェブアプリのURLをキックしてGASを動かすことが出来ます。

しかし、GWSユーザーだと、GASをデプロイして作ったウェブアプリに、アクセスできるユーザーが、組織のポリシーによって、ドメイン内ユーザーに限定されているということが多々あります。

詳しくは解説しませんが、ウェブアプリにアクセスできるユーザーが、ドメイン内ユーザーに限定されていると、ローカルやクラウド上のパソコンからウェブアプリの実行が出来ません。
例えば、**ジョブの中で、GASの処理を実行しようとしても出来ません。**

実行可能APIを作れば、この問題は解決しますが、実行可能APIを複数用意したり、扱い方も違うので、戸惑います。

このポストは、たった一つの、汎用的な実行可能APIをかませることで、ドメイン内ユーザーにアクセスが限定されているウェブアプリを、動かそうとするお話です。

---

### 問題点
ドメイン内にアクセスが限定されたウェブアプリを、任意のタイミングで、ローカルやクラウドのパソコンから、自動で実行したい。
しかし、ウェブアプリを実行するには、以下の方法しかなく、任意のタイミングで、かつ、自動では動かせない。
* Chromeブラウザからの手動実行
* GASのコードエディタからの手動実行
* トリガーによる定期実行

### 解決策
* ウェブアプリのURL、メソッド、パラメータを引数にする実行可能APIを作成する。
* 実行可能API経由でウェブアプリをキックする。

### 利点
* ローカルやクラウドのパソコンから、コマンド、コードを介して、好きなタイミングで、ウェブアプリを実行できる。
* 実行可能APIをひとつ作るだけで良い。
* 実行可能APIを紐づけるGCPプロジェクトをもひとつ作るだけで良い。
* （実行可能APIキックアプリを作る人以外は、）実行可能APIを動かす方法を考えなくても良い。
* ウェブアプリのリクエスト受信方法、すなわち、doPost()、doGet()関数が使える。

---

本来、ドメイン内にアクセスが限定されたGASを、好きなタイミング動かすには、実行可能APIにすることが必要になる。実行可能APIひとつにつき、１つのGCPプロジェクトを必要とするので、GCPプロジェクトの取得を渋る組織では、GCPプロジェクトをもひとつしか作らなくてよいなら、嬉しいのではないだろうか？

実行可能APIと、ウェブアプリの動かしかたは、かなり違う。ウェブアプリのリクエスト受信方法、すなわち、doPost()、doGet()関数が使えることは、学習コストを下げられるのではないか？

---


### 実現したいもののフロー図
実行可能APIをキックするものをjavaプログラムにしたのは、パソコンで、OS不問で、無料で、手軽に実行できるからだ。

![ざくっとしたウェブアプリキックツール-ページ1のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89689/1c5f970c-1912-ef55-4fd9-7547d790c7fb.png)

### コード
#### 実行可能APIのコード
```code.js
/**
 * アクセストークンを取得して、実行者を限定しているウェブアプリを実行する。
 *  @param {string} url - 実行したいウェブアプリのURL
 *  @param {string} method - メソッド（post　または　get）
 *  @param {string} data - 引き渡すパラメータ
 * 
 *  @return 実行結果
*/

function kickWebApp(url, method, data) {
  // 第二引数はまだ未対応
  method = 'post';

  //引数のテスト表示
  Logger.log(`WebAppliURL: ${url}`);
  Logger.log(`Method:      ${method}`);
  Logger.log(`SendingData: ${data}`);

  // 実行者のアクセストークンを取得
  DriveApp.getRootFolder().getName(); //これを行わないとウェブアプリ実行時に400エラー発生する。
  const token = ScriptApp.getOAuthToken();

  // 確認のためアクセストークンを表示
  Logger.log(`AccessToken: ${token}`);

  // リクエストヘッダーを作成
  const headers = {
    Authorization: `Bearer ${token}`
  };

  // オプションを作成
  const options = {
    method: method,
    headers: headers,
  };

  // データを渡す場合は、オプションにペイロードを追加
  if (data) {
    options.payload = data;
  }

  // ウェブアプリをキック
  const response = UrlFetchApp.fetch(url, options);
  Logger.log(response);

  // 返信用ペイロードを用意
  let payload = {};
  payload.data = JSON.parse(response);

  // ペイロードを返す
  return payload;
}
```

1. 上記コードを、GASコードエディタに貼り付ける。
1. GCPプロジェクトを取得して、コードとプロジェクトを紐づける。
1. 実行可能APIとしてデプロイする。
1. リフレッシュトークンを取得する。
    * スコープは、「https://www.googleapis.com/auth/script.external_request」と、「https://www.googleapis.com/auth/drive」を指定してください。
    * リフレッシュトークンは、次の記事を参考にしていただければ幸いです。

https://qiita.com/Cheap-Engineer/items/f849b9b1c2a4da9e25c1
 
:::note info
スコープ、「https://www.googleapis.com/auth/drive」が必要な理由。
実行可能APIの中で、DriveAppを利用しているため。
:::

#### ウェブアプリのサンプルコード
参考までに、サンプルコードを記載しておきます。
```code.js
function doGet(e) {
  Logger.log('メソッド: GET');
  Logger.log(`引き渡されたものをJSON文字列化したe:\n${JSON.stringify(e)}`);
  Logger.log(`引き渡されたものをJSON文字列化したe.parameter:\n${JSON.stringify(e.parameter)}`);
}

function doPost(e) {
  Logger.log('メソッド: POST');
  Logger.log(`引き渡されたものをJSON文字列化したe:\n${JSON.stringify(e)}`);
  Logger.log(`引き渡されたものをJSON文字列化したe.postData.contents:\n${JSON.stringify(e.postData.contents)}`);
  const contents = JSON.parse(e.postData.contents);//パースする。
  Logger.log(`JSON文字列化した、パース後contents: ${JSON.stringify(contents)}`);
  SpreadsheetApp.create(contents.name);
  let res = {};
  res.age = contents.age;
  res.name = contents.name;
  res.hobby = contents.hobby;
  return ContentService.createTextOutput(JSON.stringify(res));
}

```