:::: MENU ::::

SpreadsheetをLambdaからGoogle API叩いて取得する

Pocket

定期的に spreadsheet を取得しにいくものを作りたい。
よくある要件なのだがハマったところがあったのでひととおりメモしておく。

サンプル一式

Githubにサンプル上げましたので、よかったらどうぞ

14kw/export-spreadsheet: Export Google spreadsheet using Lambda

認証

Service accountの作成

Google APIを叩くときによく使われるのはOAuth Clientだが、今回はユーザーによる認証ではなくシステム利用を目的としているのでService accountを作成する。

認証情報 – API とサービス – Google Cloud Platform

今回はLambdaから叩くので適当な名前をつける。
このメールアドレスがGoogleユーザー名のような扱いになるので、本稼働させる場合はなんのService accountか分かる名前にしましょう。

オプション: Service accountへの権限委任

Perform G Suite Domain-Wide Delegation of Authority  |  Directory API  |  Google Developers

今回は必要ないので設定していませんが、Service accountがユーザーの認証なしにユーザーデータにアクセスできるようにすることができます。
Admin APIを使いたいときもこれを利用します。

サービス アカウント – IAM と管理 – Google Cloud Platform

G Suite管理画面からService accountにどのSCOPEを許可するか設定することで、委任の範囲を指定することができます。

Admin console – Google Admin

LambdaからGoogle APIを呼ぶ

Service accountの Credential はSecret Managerに入れてLambdaから呼ぶようにしています。
初めて使ってみたんですが便利ですねSecret Manager。

Secret Managerから GetSecretValue すると復号化も勝手にやってくれますが、暗号化したKMSキーに対して kms:Decrypt の権限が必要になります。Roleへの権限付け忘れに注意。

基本的なシークレットを作成する – AWS Secrets Manager

実際に呼ぶのは公式の google-api-python-client 使っています。
Lambda Layer好きなのでLayerとして登録してLambdaから使いました。

cache_discovery=False を付けないとエラーになったので付けてます。
このあたりはよくわかってない。

            credentials = service_account.Credentials.from_service_account_info(
                service_account_info, scopes=SCOPES)
            self.service = build('sheets', 'v4', credentials=credentials,
                                 cache_discovery=False)

定期実行CloudWatch EventはSAMでテンプレートを書いているので数行追加するだけでOK。
素のCFnテンプレートのほうが慣れてるんですがSAMもいいですね。

      Events:
        HourlyEvent:
          Type: Schedule
          Properties:
            Schedule: rate(10 minutes)

Sheet Response

辞書型で返してほしかったので、こんな感じで返るようにしています。

{'id': '1001', 'name': 'hogehoge', 'address': 'shinjuku', 'mail': 'hogehoge@example.com'}
{'id': '1002', 'name': 'fugafuga', 'address': 'meguro', 'mail': 'fugafuga@example.com'}

Drive Response

Spreadsheetはファイルとして出力するときは mineType を指定する必要があるので text/csv として出力。
Downloadしてからcsvを呼んでいるのでこんな感じ。

OrderedDict([('id', '1001'), ('name', 'hogehoge'), ('address', 'shinjuku'), ('mail', 'hogehoge@example.com')])
OrderedDict([('id', '1002'), ('name', 'fugafuga'), ('address', 'meguro'), ('mail', 'fugafuga@example.com')])

TeamDrive対応

fileId を決め打ちで export_media するときは関係ないのだが、 list や get などをするときは TeamDrive 特有のパラメータが必要になってくる。

listで取得するときなにかとTeam系パラメータを付ける必要があった、 teamDriveId は任意だったはず。
なにが必須でなにが任意なのかわからなくてハマった。

results = self.service.files().list(
    pageSize=50,
    fields="nextPageToken, files(id, name, webContentLink)",
    supportsTeamDrives=True,
    includeTeamDriveItems=True,
    corpora="teamDrive",
    teamDriveId=teamdrive_id).execute()

get のときは supportsTeamDrives が必要。
export_media はいらないのに……パラメータの必須度がちぐはぐしている。

metadata = self.service.files().get(
    fileId=file_id,
    supportsTeamDrives=True).execute()
Pocket