GASを外部から実行する方法（Apps Script API）

# はじめに
GASをAppsScriptAPIを使って外部から実行する方法を検索しても、あまり詳細に書いてある記事がヒットしなかったため、まとめました。できるだけ分かりやすいように書いてみたので、冗長と思う方は以下のリンクなどを参考にしてください。

https://recruit.gmo.jp/engineer/jisedai/blog/run-gasscript-via-api/

https://japan.appeon.com/technical/techblog/manipulate-gas-api/

# 目次
Qiitaの都合上、目次と見出しではGASをgas、GCPをgcp、Node.jsをnode jsとしています。

[1.はじめに](#はじめに)
[2.gasとgcpの設定](#gasとgcpの設定)
[3.外部から実行](#外部から実行)
[4.node jsサンプル](#node-jsサンプル)

# gasとgcpの設定
### GCPからプロジェクト作成とGASとの連携
GCP（Google Cloud Platform）からプロジェクトを新規作成します。
[GoogleCloudPlatform](https://console.cloud.google.com/)
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/58402877-c57c-11ec-87a6-3354e1630273.png)




### AppsScriptAPIの有効化
AppsScriptAPIを検索して有効化してください。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/a560946b-98c5-e14d-8d3d-89a7dd0b8361.png)


### OAuth同意画面・OAuthクライアントIDを作成
OAuth同意画面からアプリケーションの設定をしてください。**その際、テストユーザーの欄に自分のメールアドレスを追加しておくこと。**
![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/4edd5347-22c7-369c-9bb4-02b71bfdfa6d.png)


### OAuthクライアントIDを作成
![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/7dba4227-8ec7-a4ad-eae7-aa9b887095cf.png)
作成する際に承認済みリダイレクトURLの欄に`https://developers.google.com/oauthplayground`
を貼り付けてください。後で使います。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/270c3ea4-749b-9adf-51b4-0c85d12166f1.png)
作成出来たら**クライアントID**と**クライアントシークレット**が確認できるようになります。もしクライアントシークレットが表示されない場合は右上の**シークレットをリセット**をクリックすれば再発行されます。


### GASの新規プロジェクト作成
[GoogleAppsScript](https://script.google.com/home) 
新しいプロジェクトを作成します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/da8f0a76-b0e2-4f69-08b3-e83684caad4a.png)

作成出来たら、myFunctionに適当に処理を書いてください。今回はreturnさせるだけです。
```js
function myFunction(){
    return "Hello World!";
}
```
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/0431e158-613b-1560-5145-29860b001c8a.png)

### GASとGCPの連携
GASの設定 ⚙ の`GoogleCloudPlatform(GCP)プロジェクト`欄にGCPの`プロジェクト番号`を張り付けます。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/d309c888-2066-020c-3320-512cc938377d.png)
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/219a5aac-fe2a-d084-2291-5e083fa256e6.png)

### デプロイ
GASの画面から、**デプロイ→新しいデプロイ→種類の選択→実行可能API→デプロイ**を選択します。
デプロイ出来たら`実行可能APIのURL`が表示されるので確認します。（後から見れます）
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/a0c5fee0-d21d-006a-4a2f-0d7338b626ef.png)
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/1835e8fe-1a7f-4292-1fb7-67354c84e4cd.png)

# 外部から実行
### OAuth 2.0 Playgroundを使用して外部から実行
これまでの操作で取得した、
`クライアントID`
`クライアントシークレット`
`実行可能APIのURL`
を使って外部から実行してみます。今回は簡易的に 
[OAuth 2.0 Playground](https://developers.google.com/oauthplayground/) 
を使用します。
OAuth 2.0 Playground の設定から `Use your own OAuth credentials` にチェックを付け、取得したクライアントIDとクライアントシークレットを貼り付けます。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/2e48830e-4f5c-37aa-13ca-31e1eed3b633.png)

次に左側のStep1欄に取得するアクセストークンのスコープを選択します。今回はGASを実行するため、以下の中から選んでください。
[認証スコープ](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run?hl=ja)
私は`https://www.googleapis.com/auth/script.external_request` を選択します。よくわからない人は私と同じURLを記入してください。
選択したら`AuthorizeAPIs`をクリックし認証します。もし、認証できない場合はGCPのOAuth 2.0 クライアント IDより認証済みリダイレクトURLに`https://developers.google.com/oauthplayground` が追加されているか確認してください。
認証出来たらStep2へと移り認証コードが取得できます。`Exchange authorization code for tokens`をクリックすることで、`Refresh token`と`Access token`が取得できます。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/4aa33553-70d8-9344-c250-2b71ed6aac35.png)

### GASを実行
Step3へと移り`RequestURL`に`実行可能APIのURL`を貼り付け、`Enter request body`に`{'function':'myFunction'}`のように実行する関数名を記入し`Send the request`をクリックすれば実行できます。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878870/fec89b62-dba1-0555-69b1-78c470fc654c.png)
お疲れさまでした。

# node jsサンプル
最後にNode.jsを使用したサンプルを載せておきます。httpリクエストは標準のhttpsモジュールを使用しています。`getAccessToken`関数ではリフレッシュトークンを使用してアクセストークンを取得しています。リフレッシュトークンを新たに取得する処理は考えてみてください。OAuth 2.0 Playgroundでリフレッシュトークンを取得してもいいと思います。

```node.js
const SCRIPT_ID = 'スクリプトID(実行可能APIのURLのID部分)'
const CLIENT_ID = 'クライアントID'
const CLIENT_SECRET = 'クライアントシークレット'
const REFRESH_TOKEN = 'リフレッシュトークン(OAuth 2.0 Playgroundで取得したrefresh_token)'
/**
 * main関数
 */
async function main() {
  try {
    // アクセストークンを取得する(Promise)
    const accessToken = await getAccessToken().then(function (res) {
      return res
    })
    
    // GASを実行
    const url = `https://script.googleapis.com/v1/scripts/${SCRIPT_ID}:run`;
    const data = JSON.stringify({
      function: 'myFunction'
    });
    const options = {
      method: "POST",
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    };
    const reque = https.request(url, options, res=>{
      res.on('data', (chunk) => {
        //GASからのreturnデータ
        console.log('RESPONSE: ' + chunk)
      });
    });
    reque.write(data)
    reque.end()
  } catch (e) {
    throw e
  }
}
/**
 * アクセストークンを取得する関数
 * @return string
 */
async function getAccessToken(){
  return new Promise(function (resolve) {
    const data = JSON.stringify({
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      refresh_token: REFRESH_TOKEN,
      grant_type: 'refresh_token',
    });
    const options = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    };
    const reque = https.request(TOKEN_URL, options, (res)=>{
      // console.log(res)
      res.on('data', (chunk) => {
        let access_token = JSON.parse(chunk.toString())
        resolve(access_token.access_token)
      })
    })
    reque.write(data)
    reque.end()
  })
}
main()
```

基本的な流れでは
**スコープとクライアントIDから認証コードを取得**

**→　認証コードとクライアントID、シークレットIDからアクセストークンとリフレッシュトークンを取得（grant_typeはauthorization_code）**

**→　アクセストークンからAPIを実行**
といった流れになります。アクセストークンやリフレッシュトークンについての詳細は調べてみてください。OAuthとかで調べたら出てきます。

おわり