[GAS] TypescriptでGoogle Apps Scriptの開発を加速する

Google Apps Script(GAS)は手軽で **Spreadsheets** や **Gmail** などいろんなGoogleサービスをアクセスできるので、人気が集まっています。

Typescriptは**VS Code**などモーダンなEditorで手厚く支援されていて、TSの力でGASの開発を加速できると思います。

# TL;DR
ソースコードは下記のRepoにアップしました。 すぐハンズオンできます。
[typescript-gas-scaffold](https://github.com/jerrywdlee/typescript-gas-scaffold)

```shell
$ git clone https://github.com/jerrywdlee/typescript-gas-scaffold.git <YOUR-NEW-REPO-NAME>
$ cd <YOUR-NEW-REPO-NAME>
```

PS: 上記Scaffoldは@nsawaさんの[記事](https://qiita.com/nsawa/items/96c5300c811856024789)を基づいて作られています。

# 使い方(Spreadsheetsで使うパターン)
## Spreadsheetsを作り、スクリプトを作成
![typescript-gas-scaffold_-_Google_スプレッドシート.png](https://qiita-image-store.s3.amazonaws.com/0/87438/bc8db2f1-0363-e4c8-db23-a76844066b91.png)
![無題のプロジェクト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/2350ddd0-e15b-4335-b89f-d410862e4ae3.png)

名前を入れて保存します。
![無題のプロジェクト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/cb66c19e-e4b7-bee7-5202-e24b9fa5d3c4.png)

開いたポップアップの中の `スクリプトID`をメモします。
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/6deacfcd-683c-7fda-d605-16af6f3f43f1.png)

ついでに `コード.gs`を `bundle.gs`にリネーム
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/61ec2dab-598e-c696-1b0c-eff5842788a5.png)

## レポジトリを整備

```shell
$ git clone https://github.com/jerrywdlee/typescript-gas-scaffold.git <YOUR-NEW-REPO-NAME>
$ cd <YOUR-NEW-REPO-NAME>
$ npm install
```

## Googleアカウントにログイン

```shell
$ npm run clasp login
```
ブラウザーは下記のページが開かれたら、ログインして「許可」を押してください。
![ログイン_-_Google_アカウント.png](https://qiita-image-store.s3.amazonaws.com/0/87438/119bd5b0-3ef2-d15c-5562-d697181be9ca.png)
![ログイン_-_Google_アカウント.png](https://qiita-image-store.s3.amazonaws.com/0/87438/f1e9e61a-187f-938a-a301-87f79b89bf27.png)

ログインできたら、こんなページが開かれます。
![localhost_58597__code_4_swDhBDp2K75HHAL-feyRBMtvcxa6xV3o3BxgVUKbBQSqC2_1XtawVqkrbr2fLGl6WfcArv8xUN-EKBNQcBS2Krc_scope_https___www_googleapis_com_auth_script_deployments_https___www_googleapis_com_auth_script_projects_https___www_googleapis_.png](https://qiita-image-store.s3.amazonaws.com/0/87438/d7e5fa75-7c33-89b7-b83e-5764d84869ec.png)



## 開発レポジトリとGASスクリプトと繋ぐ

```shell
$ npm run clasp clone <先ほどメモした「スクリプトID」>

> typescript-gas-scaffold@0.0.4 clasp $HOME/typescript-gas-scaffold
> cd build/ && clasp "clone" "1tcL2...........fjjevH"

Cloned 2 files.
└─ appsscript.json
└─ bundle.js
```

`build/`フォルタ配下に`.clasp.json`と`appsscript.json`が生成されたかを確認します。
<img width="193" alt="_gitignore_—_typescript-gas-scaffold.png" src="https://qiita-image-store.s3.amazonaws.com/0/87438/05ef64d1-5399-9817-3f6d-6932b92b29e9.png">

## ローカルのコードをアップ
ここからは開発タイムです。 `npm run watch:push` を実行すれば、 `src/`と`public/`　配下の変更を監視し、GASにコンパイルされ、コード圧縮を行い、Googleにアップします。

```shell
$ npm run watch:push

> typescript-gas-scaffold@0.0.4 watch:push $HOME/typescript-gas-scaffold
> watch 'npm run lint && npm run build && npm run push' src/ public/

> Watching src/
> Watching public/

> typescript-gas-scaffold@0.0.4 lint $HOME/typescript-gas-scaffold
> tslint -c tslint.json 'src/**/*.ts'


> typescript-gas-scaffold@0.0.4 build $HOME/typescript-gas-scaffold
> webpack


[at-loader] Using typescript@2.9.2 from typescript and "tsconfig.json" from $HOME/typescript-gas-scaffold/tsconfig.json.


[at-loader] Checking started in a separate process...

[at-loader] Ok, 0.158 sec.
Hash: 6ed0593799d5e5ec97c9
Version: webpack 3.12.0
Time: 1749ms
      Asset       Size  Chunks             Chunk Names
  bundle.js    8.46 kB       0  [emitted]  main
sample.html  641 bytes          [emitted]
   [0] ./src/app.ts 3.31 kB {0} [built]
   [1] (webpack)/buildin/global.js 509 bytes {0} [built]
   [2] ./src/lib/index.ts 246 bytes {0} [built]
   [3] ./src/lib/spreadsheet.ts 6.56 kB {0} [built]
   [4] ./src/lib/transport.ts 1.52 kB {0} [built]
   [5] ./src/lib/slack.ts 820 bytes {0} [built]
   [6] ./src/Test.ts 292 bytes {0} [built]

> typescript-gas-scaffold@0.0.4 push $HOME/typescript-gas-scaffold
> npm run clasp push


> typescript-gas-scaffold@0.0.4 clasp $HOME/typescript-gas-scaffold
> cd build/ && clasp "push"

└─ appsscript.json
└─ bundle.js
└─ sample.html
Pushed 3 files.
```

アップロードできたら、`npm run open`でアップロードしたのを確認できます。
![Gasテスト_と_Apps_Script_-_設定.png](https://qiita-image-store.s3.amazonaws.com/0/87438/33db6fa1-05d3-c49a-f515-cfe19b88f475.png)

### 初回プッシュ注意
_**Googleで初めてGASの開発を行った場合、下記エラーが発生する場合があります。**_

```
Push failed. Errors:
User has not enabled the Apps Script API. 
Enable it by visiting https://script.google.com/home/usersettings then retry. 
If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
```
もし上記エラーが発生した場合、https://script.google.com/home/usersettings を開き、「Google Apps Script API」を「ON」にしてください。
![Apps_Script_-_設定.png](https://qiita-image-store.s3.amazonaws.com/0/87438/cf2e4acb-4562-1033-9b0d-fc4fca4f4ab6.png)

## 試して実行

`src/app.ts`を確認すると、`global.test()`という関数があります。

```ts
global.test = () => {
  const test = new Test()
  test.echo('new world.')
}
```

この関数はスクリプトエディターの「関数を選択」のところで選び、実行することができます。(初回の実行は権限を求める場合があります、許可してください)
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/fc7ed0a0-964a-3ab6-cfd5-b6933f6a03b1.png)
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/64aededa-6cde-e80a-bf7a-3a9cb458d7e5.png)
![ログイン_-_Google_アカウント.png](https://qiita-image-store.s3.amazonaws.com/0/87438/13aa155b-d4b3-c604-a25a-81a57ab8a9cd.png)
![ログイン_-_Google_アカウント.png](https://qiita-image-store.s3.amazonaws.com/0/87438/e912a7f1-4c21-6875-e4fd-01425e82a168.png)
![7292ed63-e3b8-6e39-8589-d86d4e4283b7.png](https://qiita-image-store.s3.amazonaws.com/0/87438/2fd8853f-e88f-edae-e4fb-d5835a4a81f1.png)
![ログイン_-_Google_アカウント.png](https://qiita-image-store.s3.amazonaws.com/0/87438/2aa848c1-296a-4c18-326e-3a8742b85b75.png)

実行ができたら、「表示」=>「ログ」で結果を確認できます。
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/af715d00-a3ae-3347-beb8-ca99a98d65e7.png)
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/9c313f4e-b9bb-5883-1ae3-e9e047de17ff.png)

`src/app.ts`を少し修正して、保存してみます。

```diff
global.test = () => {
  const test = new Test()
-  test.echo('new world.')
+  test.echo('brave new world.')
}
```

もう一度スクリプトエディターで実行してみると、修正が反映されたことがわかりました。
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/e5382e1e-f6e1-d87b-4857-6fa5b6208310.png)

## APIとして公開
GASでは `doGet()` と `doPost()`２つ組み込み関数があります。`src/app.ts`で `global.doGet()` や `global.doPost()` を作成すれば、`GET`または`POST`APIを作成できます。

本Scaffoldでは `global.doGet()` のサンプルを提供しています。

```ts
global.doGet = (e) => {
  Logger.log(JSON.stringify(e))
  // Need run on dev console and apply your app can connect to Spreadsheets
  ...
}
```

その関数をAPIとして公開するには、下記の設定が必要です。
まず、`プロジェクト バージョン`を作成します。
スクリプトエディターの「公開」=>「ウェブ アプリケーションとして導入」でも作成できますが、下記コマンドでもすぐできます。

```shell
$ npm run deploy

> typescript-gas-scaffold@0.0.4 deploy $HOME/typescript-gas-scaffold
> npm run clasp deploy


> typescript-gas-scaffold@0.0.4 clasp $HOME/typescript-gas-scaffold
> cd build/ && clasp "deploy"

Created version 1.
- AKfy....X6gqiMjf6jg @1.
```

先ほどログの中に、 `Created version ` を確認し、Deployしたバージョンをメモして、スクリプトエディターの「公開」=>「ウェブ アプリケーションとして導入」を開いて、該当バージョンを選びます。
そして他の設定も下記画像のようにして、「導入」を押します。

![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/fd5addd7-748b-bdd8-d90a-ad00660decd0.png)
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/5b33c7c9-d911-273e-0dd2-40bc2e415b8f.png)


ここに公開されたAPIのURLが発行されます。
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/8570cef8-d400-b8a5-bfca-a3589b516a7a.png)

- **「現在のウェブ アプリケーションの URL」**は、このバージョンのみ有効し、GASを変更して、アップロードしても、反映されません。(変更を反映するには、また`deploy`して、新しいバージョンのURLを再発行する必要があります。)
- **「最新のコード」**リンクは、いつでも最新変更が確認できるURLです。しかしアクセスできるのはアカウントをもつ本人のみ

ここで**「現在のウェブ アプリケーションの URL」**の方をコピーして、アクセスみましょう。
こんなページが表示するはずです。
![https___script_google_com_macros_s_AKfycbzUlfM3V....sUf_exec.png](https://qiita-image-store.s3.amazonaws.com/0/87438/0921b398-11a7-bb72-45c1-d5a00885982e.png)
ちなみにこの時、スクリプトエディターの「ログ」を確認すれば、このようなログが確認できます。これはGASの`doGet(params)`関数の引数の中身です。
![Gasテスト.png](https://qiita-image-store.s3.amazonaws.com/0/87438/31a7a10a-6550-bdcd-fc5b-aad431be8e2c.png)

そして、先ほどのURLに`?res=json`を繋いで、CURLでアクセスみましょう。

```shell
$ curl -L https://script.google.com/macros/s/AKfycbz....f/exec?res=json
{"foo":"bar"}
```

以上、簡単な使い方説明でした。
より高度な使い方は[ソースコード](https://github.com/jerrywdlee/typescript-gas-scaffold)を確認しながら、使ってみてください。