Gmail APIを使ってJavaScriptからメールを送る (async/await版)

Gmail APIを使ってJavaScriptからメールを送るサンプルコードを紹介します。

[Gmail APIを使ってJavascriptでメールを送ってみる](http://tech.respect-pal.jp/gmailapi-send-mail/)というわかりやすい記事があるのですが、こちらをベースに以下の点を改良してみました。

* [Google Sign-In JavaScript client](https://developers.google.com/identity/sign-in/web/reference) (gapi.auth2) という、より使いやすい認証ライブラリが出ていたので、そちらを使用。
* コールバックの代わりにES6のPromiseとasync/awaitを使用。

Google APIs Client Libraryの多くのメソッドはPromiseに対応しているため、そのままawaitを使って呼び出すことができます。

ちなみに[async/awaitのブラウザ対応状況はこんな感じ](https://caniuse.com/#feat=async-functions)です。Babelなどを使っているか、またはIEを切り捨てていいなら実用レベルではないでしょうか。

# Step 1: Gmail API用のOAuthクライアントIDの取得

こちらは特に[元記事](http://tech.respect-pal.jp/gmailapi-send-mail/)から変更がないので、[元記事](http://tech.respect-pal.jp/gmailapi-send-mail/)の「事前準備」にそのまま従ってください。

# Step 2: コードを書く

```html
<!DOCTYPE html>
<html><head>
  <meta charset="utf-8"> 
</head><body>
  <button onclick="signIn()">Sign in</button>
  <button onclick="sendEmail()">Send email</button>
  <button onclick="signOut()">Sign out</button>

  <script>
    // Step 1で取得したOAuthクライアントIDをここに書く。
    const CLIENT_ID = 'xxxxxx.apps.googleusercontent.com';
    
    async function onLoad() {
      try {
        // Google APIs Client Libraryの初期化。
        await gapi.load('client:auth2');
        await gapi.client.init({
            clientId: CLIENT_ID,
            scope: 'https://www.googleapis.com/auth/gmail.send'
        });
        await gapi.client.load('gmail', 'v1');
        console.log('Initialized');
      } catch (e) {
        console.error(e);
      }
    }
    
    async function signIn() {
      try {
        await gapi.auth2.getAuthInstance().signIn();
        console.log('Signed in');
      } catch (e) {
        console.error(e);
      }
    }

    async function signOut() {
      try {
        await gapi.auth2.getAuthInstance().signOut();
        console.log('Signed out');
      } catch (e) {
        console.error(e);
      }
    }

    async function sendEmail() {
      try {
        // 送りたいメールアドレスに書き換えてください。
        const to = 'example@example.com';
        const subject = 'テスト';
        const body = 'これはテストです。';

        // サインイン済みかチェック。
        if (!gapi.auth2.getAuthInstance().isSignedIn.get()) {
          console.error('Sign in first');
          return;
        }

        // メールデータの作成。
        const mimeData = [
          `To: ${to}`,
          'Subject: =?utf-8?B?' + btoa(unescape(encodeURIComponent(subject))) + '?=',
          'MIME-Version: 1.0',
          'Content-Type: text/plain; charset=UTF-8',
          'Content-Transfer-Encoding: 7bit',
          '',
          body,
        ].join('\n').trim();
        const raw = btoa(unescape(encodeURIComponent(mimeData))).replace(/\+/g, '-').replace(/\//g, '_');

        // メールの送信。
        await gapi.client.gmail.users.messages.send({
          'userId': 'me',
          'resource': {raw: raw},
        });
        console.log('Sent email');

      } catch (e) {
        console.error(e);
      }
    }
  </script>

  <!-- Google APIs Client Libraryの読み込み。読み込み後にonloadに指定した関数が呼ばれる。 -->
  <script src="https://apis.google.com/js/client.js?onload=onLoad"></script>
</body></html>
```

ページを開いて[Sign in], [Send email]の順にボタンを押すとメールが届くはずです。

# Tipsなど

* 注意点として、`gapi.auth2.getAuthInstance().signIn()`はポップアップウィンドウを表示するため、ユーザのアクション (ボタンクリックなど) のハンドラ内で、**他のawait呼び出しをはさまずに**呼び出す必要があります。そうしないとブラウザのポップアップブロックに引っかかります。例えば、`onLoad()`の中で`signIn()`することはできません。

* コンポーネント化などの理由でHTMLドキュメントに直接

    ```html
    <script src="https://apis.google.com/js/client.js?onload=onLoad"></script>
    ```

    と書きたくない場合は、[google-client-api](https://www.npmjs.com/package/google-client-api)というnpmパッケージが便利かもしれません。こちらもPromiseに対応しているので、

    ```js
    import googleClientApi from 'google-client-api';

    const gapi = await googleClientApi();
    ```

    などと書くことができます。

* メールデータを作成するところがえらく泥臭いのですが、こういうもののようです。[js-base64](https://www.npmjs.com/package/js-base64)とか使えば多少すっきりするかもしれませんが。

* これぐらい単純なコードだとasync/awaitのご利益は微妙なところですが、もっと複雑な処理の一部として書く場合などは便利かもしれません。

# 参考資料

* [Gmail APIを使ってJavascriptでメールを送ってみる](http://tech.respect-pal.jp/gmailapi-send-mail/)
* [Users.messages: send | Gmail API](https://developers.google.com/gmail/api/v1/reference/users/messages/send)
* [Google Sign-In JavaScript client reference](https://developers.google.com/identity/sign-in/web/reference)
* [Google Sign-In JavaScript clientのサンプルコード](https://developers.google.com/api-client-library/javascript/samples/samples)