SendGrid のチュートリアル

このチュートリアルでは、Cloud Functions を使って、SendGrid プラットフォームからメールを送信したり、ウェブフック経由で SendGrid から分析用データを受信したり、分析のためにデータを Google BigQuery で読み込ませたりする方法について説明していきます。

目標

  • SendGrid アカウントを作成する。
  • 2 つの HTTP Cloud Functions を書き込み、デプロイする。
  • 1 つの バックグラウンド Cloud Function に書き込み、デプロイする。
  • SendGrid 経由でデプロイした関数からメールを送信する。
  • ウェブフックを経由して SendGrid から分析用データを受信する。
  • SendGrid にあるデータを分析のために BigQuery に読み込ませる。

料金

このチュートリアルでは、以下を含む、Cloud Platform の有料コンポーネントを使用します。

  • Google Cloud Functions
  • Google BigQuery
  • Google Cloud Storage

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを出すことができます。

Cloud Platform を初めて使用する方は、無料トライアルをご利用いただけます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. GCP プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  3. Google Cloud Platform プロジェクトに対して課金が有効になっていることを確認します。 詳しくは、課金を有効にする方法をご覧ください。

  4. Cloud Functions、Cloud Storage、Google BigQuery API を有効にします。

    APIを有効にする

  5. Cloud SDK をインストールして初期化します。
  6. gcloud コンポーネントを更新し、インストールします。
    gcloud components update &&
    gcloud components install ベータ版
  7. Node.js 開発用に環境を準備します。

    セットアップ ガイドに移動

データの流れを可視化

SendGrid のチュートリアル アプリケーションでのデータの流れでは、次の手順が行われます。

  1. HTTP 経由で Cloud Function をトリガーすると SendGrid からメールが送信されます。
  2. SendGrid は HTTP を介して分析用データを別の Cloud Function に送信します。
  3. 分析用データは JSON(改行区切り)として Cloud Storage に保存されます。
  4. 3 番目の Cloud Function は JSON 新規ファイルによりトリガーされ、データが分析できる BigQuery に JSON ファイルが読み込まれるよう、キューに入れます。

次はこの手順を可視化した図です。

アプリケーションの準備

  1. SendGrid アカウントを作成します。SendGrid ウェブサイトから手動で行うことも、アカウントを作成して課金データを統合する Google Cloud Launcher を使って行うことも可能です。

    Cloud Launcher を使って SendGrid アカウントを作成するをご覧ください。

  2. SendGrid API キーを作成します。

    1. https://app.sendgrid.com から SendGrid アカウントにログインします。
    2. [設定] > [API キー] の順に移動します。
    3. 「General API Key」を新規作成します。
    4. API キーを作成するときは、必ず(少なくとも)「メール送信」権限を選択してください。
    5. API キーが表示されたら、これをコピーします(1 回しか表示されませんので、このチュートリアルの最後に Cloud Function を呼び出せるようどこかに貼り付けておきます)。
  3. SendGrid イベント通知を作成します。

    1. https://app.sendgrid.com から SendGrid アカウントにログインします。
    2. [設定] > [メールの設定] の順に移動します。
    3. [イベント通知] タブを開きます。
    4. [編集] をクリックし、次の情報を HTTP POST URL 項目に入力します。

      https://[YOUR_USERNAME]:[YOUR_PASSWORD]@[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/sendgridWebhook
      

      ここで、

      • [YOUR_USERNAME][YOUR_PASSWORD] は、任意のユーザー名とパスワードです。これらの 2 つの変数を次の手順で config.json ファイルに入力します。
      • [YOUR_PROJECT_ID] は Cloud プロジェクト ID で、関数のデプロイが完了するとターミナルに表示されます。
      • [YOUR_REGION] は関数がデプロイされる領域で、関数のデプロイが完了するとターミナルに表示されます。
    5. 最後に、イベント通知機能を [オン] にします。

  4. BigQuery bq コマンドライン ツールを使用して BigQuery データセットを作成します。

    bq mk [YOUR_DATASET_NAME]
    

    ここで、[YOUR_DATASET_NAME] は新規作成された BigQuery データセット名です。BigQuery コンソールからデータセットを作成することもできます。

  5. JSON ファイルを保存するために Cloud Storage バケットを作成します。ここで、[YOUR_EVENT_BUCKET_NAME] はグローバルに固有なバケット名です。

    gsutil mb gs://[YOUR_EVENT_BUCKET_NAME]
    

  6. アプリケーション コードとなるディレクトリをローカル システム上で作成します。

    Linux / Mac OS X

    ディレクトリを作成します。

    mkdir ~/gcf_sendgrid

    そのディレクトリに移動します。

    cd ~/gcf_sendgrid

    Windows(CMD)

    ディレクトリを作成します。

    mkdir %HOMEDRIVE%%HOMEPATH%\gcf_sendgrid

    そのディレクトリに移動します。

    cd %HOMEDRIVE%%HOMEPATH%\gcf_sendgrid

  7. Cloud Functions サンプル プロジェクトから GitHub に index.jspackage.json をダウンロードし、これを gcf_sendgrid ディレクトリに保存します。

  8. gcf_sendgrid ディレクトリに config.json ファイルを作成し、次の内容を含めます。

    {
      "EVENT_BUCKET": "[YOUR_EVENT_BUCKET_NAME]",
      "DATASET": "[YOUR_DATASET_NAME]",
      "TABLE": "events",
      "USERNAME": "[YOUR_USERNAME]",
      "PASSWORD": "[YOUR_PASSWORD]"
    }
    
    • [YOUR_EVENT_BUCKET_NAME] を JSON ファイルを保存するために使用したバケット名と置換します。
    • [YOUR_DATASET_NAME] を BigQuery データセット名と置換します。
    • [YOUR_USERNAME] を、SendGrid から受信するデータを検証するために使用される任意のユーザー名と置換します。
    • [YOUR_PASSWORD] を、SendGrid から受信するデータを検証するために使用される任意のパスワードと置換します。

コードを理解する

依存関係をインポートする

アプリケーションは Google Cloud Platform サービスでやりとりするため、いくつかの依存関係をインポートする必要があります。

Node.js

const Buffer = require('safe-buffer').Buffer;
const sendgrid = require('sendgrid');
const config = require('./config.json');
const uuid = require('uuid');

// Get a reference to the Cloud Storage component
const storage = require('@google-cloud/storage')();
// Get a reference to the BigQuery component
const bigquery = require('@google-cloud/bigquery')();

メール送信

次の関数はメール送信のための SendGrid クライアントを作成します。

Node.js

/**
 * Returns a configured SendGrid client.
 *
 * @param {string} key Your SendGrid API key.
 * @returns {object} SendGrid client.
 */
function getClient (key) {
  if (!key) {
    const error = new Error('SendGrid API key not provided. Make sure you have a "sg_key" property in your request querystring');
    error.code = 401;
    throw error;
  }

  // Using SendGrid's Node.js Library https://github.com/sendgrid/sendgrid-nodejs
  return sendgrid(key);
}

次の関数はメールを送信するために SendGrid クライアントを使用します。

Node.js

/**
 * Send an email using SendGrid.
 *
 * Trigger this function by making a POST request with a payload to:
 * https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/sendEmail?sg_key=[YOUR_API_KEY]
 *
 * @example
 * curl -X POST "https://us-central1.your-project-id.cloudfunctions.net/sendEmail?sg_key=your_api_key" --data '{"to":"bob@email.com","from":"alice@email.com","subject":"Hello from Sendgrid!","body":"Hello World!"}' --header "Content-Type: application/json"
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.query The parsed querystring.
 * @param {string} req.query.sg_key Your SendGrid API key.
 * @param {object} req.body The request payload.
 * @param {string} req.body.to Email address of the recipient.
 * @param {string} req.body.from Email address of the sender.
 * @param {string} req.body.subject Email subject line.
 * @param {string} req.body.body Body of the email subject line.
 * @param {object} res Cloud Function response context.
 */
exports.sendgridEmail = (req, res) => {
  return Promise.resolve()
    .then(() => {
      if (req.method !== 'POST') {
        const error = new Error('Only POST requests are accepted');
        error.code = 405;
        throw error;
      }

      // Get a SendGrid client
      const client = getClient(req.query.sg_key);

      // Build the SendGrid request to send email
      const request = client.emptyRequest({
        method: 'POST',
        path: '/v3/mail/send',
        body: getPayload(req.body)
      });

      // Make the request to SendGrid's API
      console.log(`Sending email to: ${req.body.to}`);
      return client.API(request);
    })
    .then((response) => {
      if (response.statusCode < 200 || response.statusCode >= 400) {
        const error = Error(response.body);
        error.code = response.statusCode;
        throw error;
      }

      console.log(`Email sent to: ${req.body.to}`);

      // Forward the response back to the requester
      res.status(response.statusCode);
      if (response.headers['content-type']) {
        res.set('content-type', response.headers['content-type']);
      }
      if (response.headers['content-length']) {
        res.set('content-length', response.headers['content-length']);
      }
      if (response.body) {
        res.send(response.body);
      } else {
        res.end();
      }
    })
    .catch((err) => {
      console.error(err);
      const code = err.code || (err.response ? err.response.statusCode : 500) || 500;
      res.status(code).send(err);
      return Promise.reject(err);
    });
};

ユーザーが関数のエンドポイントに HTTP POST リクエストを送信した時点で sendgridEmail 関数はモジュールによりエクスポートされ、実行されます。

分析用データを受信

次の関数はユーザーが選択したユーザー名とパスワードを確認し、受信した SendGrid リクエストを認証します。

Node.js

/**
 * Verify that the webhook request came from sendgrid.
 *
 * @param {string} authorization The authorization header of the request, e.g. "Basic ZmdvOhJhcg=="
 */
function verifyWebhook (authorization) {
  const basicAuth = Buffer.from(authorization.replace('Basic ', ''), 'base64').toString();
  const parts = basicAuth.split(':');
  if (parts[0] !== config.USERNAME || parts[1] !== config.PASSWORD) {
    const error = new Error('Invalid credentials');
    error.code = 401;
    throw error;
  }
}

次の関数は SendGrid から分析用データを受信し、JSON(改行区切り)として Cloud Storage に保存します。

Node.js

/**
 * Receive a webhook from SendGrid.
 *
 * See https://sendgrid.com/docs/API_Reference/Webhooks/event.html
 *
 * @param {object} req Cloud Function request context.
 * @param {object} res Cloud Function response context.
 */
exports.sendgridWebhook = (req, res) => {
  return Promise.resolve()
    .then(() => {
      if (req.method !== 'POST') {
        const error = new Error('Only POST requests are accepted');
        error.code = 405;
        throw error;
      }

      verifyWebhook(req.get('authorization') || '');

      const events = req.body || [];

      // Make sure property names in the data meet BigQuery standards
      fixNames(events);

      // Generate newline-delimited JSON
      // See https://cloud.google.com/bigquery/data-formats#json_format
      const json = events.map((event) => JSON.stringify(event)).join('\n');

      // Upload a new file to Cloud Storage if we have events to save
      if (json.length) {
        const bucketName = config.EVENT_BUCKET;
        const unixTimestamp = new Date().getTime() * 1000;
        const filename = `${unixTimestamp}-${uuid.v4()}.json`;
        const file = storage.bucket(bucketName).file(filename);

        console.log(`Saving events to ${filename} in bucket ${bucketName}`);

        return file.save(json).then(() => {
          console.log(`JSON written to ${filename}`);
        });
      }
    })
    .then(() => res.status(200).end())
    .catch((err) => {
      console.error(err);
      res.status(err.code || 500).send(err);
      return Promise.reject(err);
    });
};

SendGrid が関数のエンドポイントに HTTP POST リクエストを作成した時点で sendgridWebhook 関数はモジュールによりエクスポートされ、実行されます。

データの BigQuery へのインポート

最後に、次の関数は、JSON(改行区切り)データを BigQuery にインポートします。

Node.js

/**
 * Cloud Function triggered by Cloud Storage when a file is uploaded.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} event.data A Cloud Storage file object.
 * @param {string} event.data.bucket Name of the Cloud Storage bucket.
 * @param {string} event.data.name Name of the file.
 * @param {string} [event.data.timeDeleted] Time the file was deleted if this is a deletion event.
 * @see https://cloud.google.com/storage/docs/json_api/v1/objects#resource
 */
exports.sendgridLoad = (event) => {
  const file = event.data;

  if (file.resourceState === 'not_exists') {
    // This was a deletion event, we don't want to process this
    return;
  }

  return Promise.resolve()
    .then(() => {
      if (!file.bucket) {
        throw new Error('Bucket not provided. Make sure you have a "bucket" property in your request');
      } else if (!file.name) {
        throw new Error('Filename not provided. Make sure you have a "name" property in your request');
      }

      return getTable();
    })
    .then(([table]) => {
      const fileObj = storage.bucket(file.bucket).file(file.name);
      console.log(`Starting job for ${file.name}`);
      const metadata = {
        autodetect: true,
        sourceFormat: 'NEWLINE_DELIMITED_JSON'
      };
      return table.import(fileObj, metadata);
    })
    .then(([job]) => job.promise())
    .then(() => console.log(`Job complete for ${file.name}`))
    .catch((err) => {
      console.log(`Job failed for ${file.name}`);
      return Promise.reject(err);
    });
};

新しい JSON(改行区切り)ファイルが Cloud Storage に保存されると、sendgridLoad 関数はモジュールによりエクスポートされ、実行されます。

関数のデプロイ

  1. HTTP トリガーで sendgridEmail 関数をデプロイするには、gcf_sendgrid ディレクトリで次のコマンドを実行します。

    gcloud beta functions deploy sendgridEmail --trigger-http
    

  2. HTTP トリガーで sendgridWebhook 関数をデプロイするには、gcf_sendgrid ディレクトリで次のコマンドを実行します。

    gcloud beta functions deploy sendgridWebhook --trigger-http
    

    ここで、[YOUR_STAGING_BUCKET_NAME] はステージングされる Cloud Storage バケットの名前です。

  3. ストレージのトリガーで sendgridLoad 関数をデプロイするには、gcf_sendgrid ディレクトリで次のコマンドを実行します。

    gcloud beta functions deploy sendgridLoad --trigger-bucket [YOUR_EVENT_BUCKET_NAME]
    

    ここで、

    • [YOUR_STAGING_BUCKET_NAME] はステージングされる Cloud Storage バケットの名前です。
    • [YOUR_EVENT_BUCKET_NAME] は JSON ファイルを保存するための Cloud Storage バケット名です。

メール送信

  1. メールを送信します。

    curl -X POST "https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/sendgridEmail?sg_key=YOUR_SENDGRID_KEY" --data '{"to":"YOUR_RECIPIENT_ADDR","from":"YOUR_SENDER_ADDR","subject":"Hello from Sendgrid!","body":"Hello World!"}' --header "Content-Type: application/json"

    ここで:

    • [YOUR_REGION] は関数がデプロイされる領域です。関数のデプロイが完了すると、ターミナルに表示されます。
    • [YOUR_PROJECT_ID] は Cloud プロジェクト ID です。関数のデプロイが完了すると、ターミナルに表示されます。
    • [YOUR_SENDGRID_KEY] は、ユーザーの SendGrid API キーです。
    • [YOUR_SENDER_ADDR] は、ユーザーの SendGrid アカウントのメールアドレスです。
    • [YOUR_RECIPIENT_ADDR] は受信者のメールアドレスです。
  2. 実行した内容が完了していることをログで確認します。

    gcloud beta functions logs read --limit 100
    

  3. config.json ファイルの EVENT_BUCKET 値で指定された Cloud Storage バケットに保存した JSON ファイルを表示することができます。

  4. 次の URL から BigQuery にインポートした分析用データを表示することができます。

    https://bigquery.cloud.google.com/table/[YOUR_PROJECT_ID]:[YOUR_DATASET_NAME].events
    

    ここで:

    • [YOUR_PROJECT_ID] はユーザーの Google Cloud プロジェクト ID です。
    • [YOUR_DATASET_NAME] はユーザーが config.json ファイルで設定した BigQuery データセット名です。

クリーンアップ

このチュートリアルで使用するリソースについて、Google Cloud Platform アカウントに課金されないようにする手順は次のとおりです。

プロジェクトの削除

課金を停止する最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除する手順は次のとおりです。

  1. GCP Console で [プロジェクト] ページに移動します。

    プロジェクト ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

Cloud Functions の削除

Cloud Functions を削除しても、Cloud Storage または BigQuery に保存されたリソースは削除されません。

Cloud Function を削除するには、次のコマンドを実行します。

gcloud beta functions delete [NAME_OF_FUNCTION]

ここで、[NAME_OF_FUNCTION] は削除する関数の名前です。

Google Cloud Platform Console から Cloud Functions を削除することもできます。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Functions のドキュメント