Heroku + SendGrid → GAS + Gmail に移行したよ

 

2022/11/28にHerokuのフリープランがなくなるので、
Heroku + SendGridで作っていたものを、GAS + Gmailに移行しました。

実際に移行したのは、過去記事にもまとめたSlackコマンドです。

slackコマンドを作ってみた!

Heroku + SendGrid → GAS + Gmail対応のdiff:
https://github.com/kin29/slack2mail/compare/5ead7fc8f0bccd368c99dd5215e507bd78e38887…2e8bb4af713276237cb3c9a03c33d73be8bbc8bc

GASでGmailを送信する

const toAddress = 'to@example.com';
const title = 'メール件名';
const content = 'メール内容';
const options = {
  'from': 'from@example.com',
};
GmailApp.sendEmail(toAddress, title, content, options);

claspを使って、実装済みのGASをcloneする

$ clasp clone [scriptId]

さいごに

もう1つHerokuのフリープランを使って動かしているアプリがあるので、コレも早く対応したいところではあります…

GASのテスト(Jest)を書く!!!

 

GASを書いていて、テストを書きたくなる時って結構ありませんか?
ただ、なかなか手をつけることができずにテストを書かずにおいてしまい罪悪感が積もりに積もったので、ついにテストを書きました 🎉🍾✨
そのことについてまとめます❕
(TypeScriptはまだまだ勉強中でエセなのでご了承ください🙇)

もくじ

 

準備🎒

npm install -D clasp @types/google-apps-script

$ npm install -D clasp @types/google-apps-script

tsconfig.jsonを追加

$ tsc -init
//tsconfig.json
{
  "compilerOptions": {
    "lib": ["esnext"],
    "experimentalDecorators": true
  }
}

npx clasp create --type standalone --rootDir src

$ npx clasp create --type standalone --rootDir src

// src/appsscript.json と .clasp.json が生成される

// src/appsscript.json
{
  "timeZone": "America/New_York",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}

// .clasp.json
{"scriptId":"xxx","rootDir":"src"}

テストコードはclasp pushする必要がないためsrc/以下にappsscript.jsonを生成しました。

//構成
.
├── README.md
├── node_modules/
│
├── src    ←clasp pushの対象ルートディレクトリ 
│   ├── _utils.ts
│   ├── appsscript.json
│   └── code.ts
│
├── tests ←テストコードのディレクトリ(clasp pushしない)
│   ├── _utils.test.ts
│   ├── date-mock.ts
│   └── gmail-message-mock.ts
│
├── jest.config.js
├── package.json
├── tsconfig.json
└── .clasp.json

npm install -D jest "@types/jest" ts-jest typescript

$ npm install -D jest "@types/jest" ts-jest typescript

jest.config.jsを追加

$ jest --init

参考: https://typescript-jp.gitbook.io/deep-dive/intro-1/jest#suteppu2jestwosuru

//jest.config.js
module.exports = {
  "roots": [
    "<rootDir>"
  ],
  "testMatch": [
    "**/__tests__/**/*.+(ts|tsx|js)",
    "**/?(*.)+(spec|test).+(ts|tsx|js)"
  ],
  "transform": {
    "^.+\\.(ts|tsx)$": "ts-jest"
  },
}

tsconfig.jsonにesModuleInterop=trueを追加

以下のwarningがでたため、言われるがままに”esModuleInterop”: trueを追加しました。

ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`)
//tsconfig.json
{
  "compilerOptions": {
    "lib": ["esnext"],
    "experimentalDecorators": true,
+   "esModuleInterop": true,  //追加
  }
}

src/appsscript.jsonのtimeZoneを”Asia/Tokyo”に修正

//src/appsscript.json
{
- "timeZone": "America/New_York",
+ "timeZone": "Asia/Tokyo",
  ...
}

npm testでテスト実行できるようにする

//package.json
{
  ....
+ "scripts": {
+   "test": "jest"
+ }
}

テストを書く📊

GmailMessageのモックを作る

テストできるように、インターフェースGmailMessageを実装してモック(GmailMessageMock)を作成しました。

tests/gmail-message-mock.ts

import GmailMessage = GoogleAppsScript.Gmail.GmailMessage;

export class GmailMessageMock implements GmailMessage {
    constructor(
        private body: string,
    ) {
    }

    getBody(): string {
        return this.body;
    }
    ...
}

tests/_utils.test.ts

import { Utils } from "../src/_utils";
import { GmailMessageMock } from "./gmail-message-mock";

describe('Utils.createBodyMessage', () => {
    test('[body]xxxを生成できること', () => {
        const inputGmailMessage = new GmailMessageMock('テストメッセージです');

        const util = new Utils();
        const actual = util.createBodyMessage(inputGmailMessage);
        expect(actual).toBe('[body]テストメッセージです');
    })
})

src/_utils.ts

export class Utils {
    public createBodyMessage(gmailMessage: GoogleAppsScript.Gmail.GmailMessage): string {
        return "[body]" + gmailMessage.getBody();
    }
}

ついに、テストできるようになりましたー🎉🎉🎉

$ npm test

> test
> jest

 PASS  tests/_utils.test.ts
  Utils.createBodyMessage
    ✓ [body]xxxxを生成できること (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.657 s
Ran all test suites.

メモ📝

– GASではimportができないので、読み込み順序をいい感じにするために先に読み込まれる必要があるファイルについては_hoge.tsのようにアンダーバーから始まるファイル名にしました🚨
– clasp pushされるとcode.ts→code.gsに変換されます。

参考にした記事🙇

https://developers.google.com/apps-script/guides/clasp
– https://kotamat.com/post/gas-testing/

最後に

詳細が気になる方は、https://github.com/kin29/line-notify-from-gmailにコミット履歴があるので参考にしてみてください。

(最初yarn使ってたのですが、npmだけでいけそうなので最後にyarn.lockを消したりしてます)

 

claspでGAS(GoogleAppsScript)ファイルをGit管理する。

突然ですが、
私、ガチャガチャが好きなんです。
25才の今でも、ガンガンやっちゃってます。
最近の一押しはワニワニパニックとこれです。

 

はい、本題に入ります。

最近、社内ツールをGASで作ることが多かったり、
私自身slackのbotはGASで作ることが多いため、
Git管理をしたかったので方法を探しておりました。
そこで、この記事を参考にGit管理ができたので、書きます!
https://qiita.com/rf_p/items/7492375ddd684ba734f8

 
下記リポジトリはgasで作ったLINEbotです。
kin29/linebot_calendar
このソースも GAS + clasp + Gitをつかって管理しています。

 

使うもの

– GASファイル(プロジェクト)
– clasp
– Gitリポジトリ(GithubでもBitbucketでもなんでもok!)

 

claspとは?

Googleドライブ上にあるGoogleAppScriptなどのファイルを
コマンド操作でファイルの変更や保存などがブラウザでなく、
ローカル側で行うことができるものです。
GASって普通はGoogleドライブからブラウザ上でしかソースを書けないと思っていました。
claspを使うとエディターを使って、
コード整形とかも簡単にできるので超便利です。
参考:https://qiita.com/HeRo/items/4e65dcc82783b2766c03

 

0.claspコマンドの導入

npm i @google/clasp -g

 

1.clasp login

https://script.google.com/home/usersettings
にアクセスし、Google Apps Script APIをオンに。
これで該当アカウントのGASプロジェクトをclaspから操作が可能になります。
そして、ターミナルでclasp loginと打ちます。
すると、ブラウザが開いて許可しますかてきな質問が出てくるのでokすると成功したよメッセージがでてくればログイン完了です

$ clasp login
No credentials given, continuing with default...
🔑  Authorize clasp by visiting this url:
https://accounts.google.com/o/oauth2/v2/auth?access_type=XXXXXXXXXXXX...

Authorization successful.
Default credentials saved to: ~/.clasprc.json

 

2.既にあるGASファイルをローカルでpullする

$ mkdir [ファルダ名]  //このフォルダ以下をGit管理します。
$ cd [フォルダ名]
$ clasp clone [スクリプトID]
Cloned 2 files.
└─ コード.js
└─ appsscript.json

スクリプトIDは、 Git管理したいGASファイルを開いて、
ファイル>プロジェクトのプロパティ>情報の「スクリプト ID」に書いてます。

$ vi .clasp.json
{"scriptId":"[スクリプトID]"}

↑をしないと、複数GASプロジェクトが存在していると、
clasp pullした時にどのプロジェクトをpullしてくるかわからないので、
予期しないプロジェクトをpullしてきたりするのでした方がいいです!

 

3.Gitにファーストコミットする

リモートリポジトリは、GithubでもBitbucketでもなんでもokです。

$ git init
$ vi .gitignore //.clasp.jsonはGit管理外にする。
$ git status
$ git add -A
$ git commit -m 'first commit'
$ git remote add origin [リポジトリURL]
$ git push -u origin master

 

4-1.GoogleドライブよりGASファイルを更新後、ローカルにpullする

Googleドライブでコード変更した時は、ローカルにpullして同期します。

$ clasp pull

.clasp.jsonのスクリプトIDに基づくGASプロジェクトをpullしてきます。

 

4-2.ローカルよりGASファイルを更新後、ブラウザ側にpushする

ローカルでコード変更した時は、Googleドライブにpushして同期します。

$ clasp push

.clasp.jsonのスクリプトIDに基づくGASプロジェクトにpushします。

 

Gitで変更分をコミットする。

$ git add -A
$ git commit -m 'バグ修正'
$ git push origin [ブランチ名]

こんな感じです。
ちょっと面倒ですが、Git管理できるのは良きです。