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. プロジェクトに対して課金が有効になっていることを確認します。

    課金を有効にする方法について

  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 のドキュメント