Cloud Functions の関数で Google スプレッドシートへの CSV データのインポートを自動化

1. はじめに

この Codelab の目標は、Cloud Storage への CSV ファイルのアップロードに応答する Cloud Functions の関数を作成し、その内容を読み取り、Sheets API を使用して Google スプレッドシートを更新する方法を理解することです。

e9c78061022a6760.png

これは、通常は手動で「CSV としてインポート」する自動化とみなすことができます。示します。これにより、データが利用可能になり次第、スプレッドシートでデータ(別のチームが作成したもの)をすぐに分析できるようになります。

実装は次のようになります。

52df703605ae4bd3.png

2. 設定と要件

セルフペース型の環境設定

  1. Cloud Console にログインし、新しいプロジェクトを作成するか、既存のプロジェクトを再利用します(Gmail アカウントまたは G Suite アカウントをお持ちでない場合は、アカウントを作成する必要があります)。

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

プロジェクト ID を忘れないようにしてください。プロジェクト ID はすべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているので使用できません)。以降、このコードラボでは PROJECT_ID と呼びます。

  1. 次に、Google Cloud リソースを使用するために、Cloud Console で課金を有効にする必要があります。

このコードラボを実行しても、費用はほとんどかからないはずです。このチュートリアル以外で請求が発生しないように、リソースのシャットダウン方法を説明する「クリーンアップ」セクションの手順に従うようにしてください。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

3. Google スプレッドシートを作成して構成し、API を有効にする

まず、新しいスプレッドシート ドキュメントを作成します(このシートはどのユーザーにも属すことができます)。作成したら、ID を覚えておきます。作成する関数の環境変数として使用されます。

dd77d5fc1364ad3e.png

GCP Console の [API とサービス] に移動し、新しく作成したプロジェクトで Google Sheets API を有効にします。[API ライブラリ]をセクション :

c64e2e98b8b55f16.png

[IAM と管理者」[サービス アカウント] に移動してApp Engine のデフォルトのサービス アカウントのメールアドレスをメモします。形式は your-project-id@appspot.gserviceaccount.com です。もちろん、このアクション専用の独自のサービス アカウントを作成することもできます。

6e279d7e07d4febf.png

最後に、[Share] を使用して、このサービス アカウントにスプレッドシートの編集権限を付与します。ボタン :

c334062465ddf928.png

この設定では、Cloud Functions の関数を記述し、このサービス アカウントを使用するように構成します。これにより、先ほど作成したスプレッドシート ドキュメントに書き込むことができます。

4. Storage バケットを作成する

Cloud Functions の関数が新しい CSV ファイルをモニタリングするバケットを作成しましょう。

コンソールで、左側のメニューを使用して [Storage] に移動します。:

2ddcb54423979d25.png

次に、csv2sheet-POSTFIX という新しいバケットを作成します(POSTFIX を一意の名前に置き換えます)。他のすべての設定は、デフォルト値に設定されます。

dd637080ade62e81.png

5. Cloud Functions の関数を作成する

これで、特定の Cloud Storage バケットへのファイルのアップロードでトリガーされる csv2sheet という Cloud Functions の関数を作成できます。Node.js 8 でコードを記述する場合、Cloud コンソールのインライン エディタで直接非同期関数を使用します。

6ee1a5ce63174ae8.png

トリガーを「Cloud Storage」に設定してくださいバケット名を前のステップで作成したものに変更します。

また、csv2sheet に書き込む関数のエントリポイントも更新します。

446e7c7c992c2d8a.png

次に、関数本体を に変更します。

  1. Cloud Storage と Sheets API を
  2. csv2sheet 関数を async としてマークします。
  3. Cloud Storage イベント メタデータから fileName を取得し、作成する新しいシートの名前を取得します。
const {google} = require("googleapis");
const {Storage} = require("@google-cloud/storage")

exports.csv2sheet = async (data, context) => {
  var fileName = data.name;
  // basic check that this is a *.csv file, etc...
  if (!fileName.endsWith(".csv")) {
    console.log("Not a .csv file, ignoring.");
    return;
  }
  // define name of new sheet  
  const sheetName = fileName.slice(0, -4);

  // TODO!
};

ここでの async は、後ほど説明するように await を使用するために必要です。

この関数を作成する際、重要なオプションには次のようなものがあります(画面下部にある [詳細] リンクをクリック)。

  • プルダウンを使用して、上記のサービス アカウントを選択します
  • SPREADSHEET_ID という環境変数を定義します。これは、前に作成したスプレッドシート ドキュメントと一致する必要があります。

fd22d1873bcb8c66.png

最後の設定ステップとして、package.json のコンテンツに Cloud Storage と Google Sheet API の 2 つの依存関係を指定します(コンソールのインライン エディタの PACKAGE.JSON タブを使用します)。

{
    "name": "csv2sheet",
    "version": "0.0.42",
    "dependencies": {
        "googleapis": "^51.0.0",
        "@google-cloud/storage": "^5.0.1"
    }
}

説明どおりにすべて設定したら [作成]をクリックします!1 分後に関数が作成され、デプロイされます。

6. 認証と Sheets API を設定する

インライン エディタを使用して Cloud Functions の関数でさらにコードを記述する前に、適切な Storage スコープとスプレッドシート スコープで Google Client API の作成をブロックする必要があります(これは async 関数の一部です)。

コンソールの関数エディタで、[編集] をクリックします。csv2sheet 関数の本文に次のコードを追加します。

// block on auth + getting the sheets API object
const auth = await google.auth.getClient({
  scopes: [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/devstorage.read_only"
  ]
});

そこから Sheets API クライアントを作成できます。

const sheetsAPI = google.sheets({version: 'v4', auth});

7. Sheets API を使用して空のシートを作成する

Sheets API クライアントを使用すると、ドキュメント内にシンプルな新しいシートを作成できますが、先に進む前に、用語について簡単に触れておきます。

  • スプレッドシートは実際のドキュメントであり、その識別子(上記で説明。ドキュメントの URL に表示されます)で参照されます。
  • シートはドキュメント内のタブの 1 つで、名前(タブ名)またはシートの作成時に生成された識別子で参照できます。

これを念頭に置いて、次の関数では、Sheets API クライアントを使用して、26 列、2,000 行の空のシートを 2 番目(通常はデフォルトの「Sheet1」の後)に作成し、最初の行を固定します(インライン エディタを使用して関数に追加します)。

function addEmptySheet(sheetsAPI, sheetName) {
  return new Promise((resolve, reject) => {
    const emptySheetParams = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            addSheet: {
              properties: {
                title: sheetName,
                index: 1,
                gridProperties: {
                  rowCount: 2000,
                  columnCount: 26,
                  frozenRowCount: 1
                }
              }
            }
          }
        ]
      }
    };
    sheetsAPI.spreadsheets.batchUpdate( emptySheetParams, function(err, response) {
        if (err) {
          reject("The Sheets API returned an error: " + err);
        } else {
          const sheetId = response.data.replies[0].addSheet.properties.sheetId;
          console.log("Created empty sheet: " + sheetId);
          resolve(sheetId);
        }
      }
    );
  });
}

スプレッドシートへの参照をハードコードする代わりに、以前に作成した SPREADSHEET_ID 環境変数を使用していることに注意してください。

この特定のシートに対する以降のリクエストのために、sheetId を記憶する必要があります。また、シート名は一意である必要があり、sheetName という名前のシートがすでに存在する場合は作成に失敗します。

Sheets API の batchUpdate 関数はドキュメントを操作する一般的な方法です。詳しくはこちらをご覧ください。

8. ストレージ CSV ファイルからデータを読み取る

データをダンプする場所ができたので、インライン エディタで Cloud Functions の関数をさらに開発し、Cloud Storage API を使用して、アップロードしたファイルから実際のデータを取得し、文字列に保存します。

function readCSVContent(sheetsAPI, file, sheetName) {
  return new Promise((resolve, reject) => {
    const storage = new Storage();
    let fileContents = new Buffer('');
    storage.bucket(file.bucket).file(file.name).createReadStream()
    .on('error', function(err) {
      reject('The Storage API returned an error: ' + err);
    })
    .on('data', function(chunk) {
      fileContents = Buffer.concat([fileContents, chunk]);
    })  
    .on('end', function() {
      let content = fileContents.toString('utf8');
      console.log("CSV content read as string : " + content );
      resolve(content);
    });
  });
}

9. 新しく作成したシートにデータを入力する

次に、同じ Sheet Client API と先ほど収集したデータを使用して、作成したシートにデータを入力します。これを機に、シートの列にスタイルも追加します(最上段のフォントサイズを変更して、太字にします)。

function populateAndStyle(sheetsAPI, theData, sheetId) {
  return new Promise((resolve, reject) => {
    // Using 'batchUpdate' allows for multiple 'requests' to be sent in a single batch.
    // Populate the sheet referenced by its ID with the data received (a CSV string)
    // Style: set first row font size to 11 and to Bold. Exercise left for the reader: resize columns
    const dataAndStyle = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            pasteData: {
              coordinate: {
                sheetId: sheetId,
                rowIndex: 0,
                columnIndex: 0
              },
              data: theData,
              delimiter: ","
            }
          },
          {
            repeatCell: {
              range: {
                sheetId: sheetId,
                startRowIndex: 0,
                endRowIndex: 1
              },
              cell: {
                userEnteredFormat: {
                  textFormat: {
                    fontSize: 11,
                    bold: true
                  }
                }
              },
              fields: "userEnteredFormat(textFormat)"
            }
          }       
        ]
      }
    };
        
    sheetsAPI.spreadsheets.batchUpdate(dataAndStyle, function(err, response) {
      if (err) {
        reject("The Sheets API returned an error: " + err);
      } else {
        console.log(sheetId + " sheet populated with " + theData.length + " rows and column style set.");
        resolve();
      }
    });    
  });
}

このコードを Cloud Functions に追加すると、99% 完了です。

データとスタイル設定の両方が複数の requests として 1 つの Sheets API の batchUpdate 呼び出しに統合されている点に注目してください。これにより、より効率的でアトミックな更新が可能になります。

また、作成したシートのサイズと一致する編集範囲も定義しています。つまり、26 列(シートの作成時に使用された columnCount 値)を超えるコンテンツは、この特定のコードでは失敗します。

問題がなければ、以下を行うことができます。

  1. 更新した関数を保存する
  2. バケットに CSV ファイルをドロップします。
  3. スプレッドシートに対応するデータがポップアップ表示されます。

10. すべてを組み合わせてフローをテストする

先ほど説明した関数の呼び出しは、元の csv2sheet 関数で連続したブロッキング呼び出しとして実行できます。

  const sheetId = await addEmptySheet(sheetsAPI, sheetName);
  const theData = await readCSVContent(sheetsAPI, data, sheetName);
  await populateAndStyle(sheetsAPI, theData, sheetId);

関数のソースコード全体が必要な場合は、こちらから入手できます(すべてを 1 セットにまとめた方が簡単でしょう)。

すべてを整えたら、CSV ファイルを適切なバケットにアップロードし、スプレッドシートがファイルのコンテンツを含む新しいシートに更新されるのを確認します。お手元にない場合は、CSV ファイルのサンプルをご利用ください。

1efae021942e64fa.png

バケットに複数のファイルをアップロードして、どうなるか見てみましょう。

11. これで、インフラストラクチャを破棄する時間

冗談です。破棄するためのインフラストラクチャはありません。すべてサーバーレスで行われています。

必要に応じて、Cloud Functions の関数と作成したバケット、またはプロジェクト全体を削除できます。

12. 次のステップ

この Codelab は終了です。適切な API を使用して Google スプレッドシートを更新するため、Cloud Functions の関数で Cloud Storage バケットへのアップロードをリッスンする手順を説明します。

フォローアップ情報として以下をご確認ください。

この Codelab で問題が発生した場合は、左下にあるリンクを使用してお気軽にお知らせください。

フィードバックの提供にご協力をお願いいたします。