シャッフル主任の進捗報告

興味のあるものを作ります。進捗を不定期にご報告します。

そうだ、教祖になろう。出エジプト記 第3章2節 LambdaからGoogleスプレッドシートにアクセスする

門の神「ズール」


前回の第3章1節 Cloud9でLambdaの開発環境を構築するではCloud9で開発環境をセットアップしました。 これでサーバサイドを実装できるようになったわけですが、いきなり全部作ると長いので少しずついきましょう。

まずはこのアプリケーションの外部記憶であるところのGoogleスプレッドシートへのアクセスを試してみます。 こういう構成です。

f:id:chief-shuffle:20191207104748p:plain

GoogleスプレッドシートへのアクセスはPythonのgspreadというパッケージがやってくれます。

まずは、Googleスプレッドシート側でアクセスに必要な認証情報を作成します。
今回、こちらのサイトを参考にさせていただきました。ありがとうございます。

qiita.com

Google API Consoleにログイン。

割り当てプロジェクトを作成します。
「+プロジェクトを作成」をクリック。

f:id:chief-shuffle:20191207095254j:plain

「プロジェクト名」を入力。「作成」をクリック。

f:id:chief-shuffle:20191207095441j:plain

左上のメニュー「APIとサービス」から「ライブラリ」を選択。

f:id:chief-shuffle:20191207095610j:plain

Google Drive API」を検索して「有効にする」。

f:id:chief-shuffle:20191207095913j:plain

f:id:chief-shuffle:20191207095733j:plain

さらに「Google Sheets API」を選択して「有効にする」。

f:id:chief-shuffle:20191207095933j:plain

f:id:chief-shuffle:20191207095844j:plain

認証情報を作成します。
実体は認証情報が書き込まれたJSONファイルです。
あとでサーバサイド処理で読み込んでGoogleスプレッドシートへの認証を行います。

「認証情報を作成」をクリック。

f:id:chief-shuffle:20191207100014j:plain

「使用するAPI」に「Google Sheets API」、「APIを呼び出す場所」に「ウェブサーバー」、「アクセスするデータの種類」に「アプリケーションデータ」、「いいえ、使用していません」を選択して「必要な認証情報」をクリック。

f:id:chief-shuffle:20191207100300j:plain

「サービスアカウント名」にわかりやすい名前を入力、「役割」を「Project」から「編集者」を選択、「キーのタイプ」に「JSON」を選択して「次へ」をクリック。

f:id:chief-shuffle:20191207131400j:plain

これでサービスアカウントキーのJSONファイルがダウンロードされました。

f:id:chief-shuffle:20191207100457j:plain

アクセスするGoogleスプレッドシートを共有設定します。
アクセスしたいGoogleスプレッドシートを開き、「共有」をクリック。

f:id:chief-shuffle:20191207100839j:plain

「他のユーザーとの共有」ダイアログの「ユーザー」欄にダウンロードしたJSONファイルの「client_email」のメールアドレスを入力します。
このメールアドレスは実在はせず、スプレッドシートを共有した相手を識別するためのIDのようです。

f:id:chief-shuffle:20191207101136j:plain

鍵の神「ビンツ」


さて、認証情報をサーバサイド処理に取り込みます。
ダウンロードしたJSONファイルをCloud9にアップロードします。
画面左部にドラッグ&ドロップ。

f:id:chief-shuffle:20191207101602j:plain

先ほどの参考サイトをもとにソースを変えてみます。
JSONファイルを使って認証したあとに特定の名前のスプレッドシートの1シート目のシート名を表示するプログラムです。

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '../site-packages'))
import datetime
import json
import gspread
from oauth2client.service_account import ServiceAccountCredentials

def lambda_handler(event, context):
    scope = ['https://www.googleapis.com/auth/drive']

    credentials = ServiceAccountCredentials.from_json_keyfile_name('./Shuffle Playism Project-e8939d859c3b.json', scope)
    gclient = gspread.authorize(credentials)
    spreadsheet = gclient.open('シャッフル再生教')
    worksheet = spreadsheet.sheet1

    return {
        'statusCode': 200,
        'body': json.dumps('WorkSheet name is ' + worksheet.title + ' at ' + str(datetime.datetime.now()))
    }

インポートしているgspreadはGoogleスプレッドシートAPI、oauth2clientはOAuth認証のパッケージです。
これらパッケージをPythonにインストールしておかないとUnable to importエラーが出ます。

ローカルPCならOSのパスが通っているpipコマンドを打てばいいのですが、Lambda関数だとウィザードが作成したvenvフォルダ配下にあるpipコマンドでインストールする必要があるようです。
※嘘でした。普通のpip3.6で問題ありません。コマンドを修正しましたが、キャプチャはそのままです。(2019/12/14)

Lambda関数フォルダの並びに「site-packages」フォルダを作ってそこにインストールしてみます。

cd server
pip3.6 install gspread oauth2client -t site-packages

f:id:chief-shuffle:20191207102313j:plain

インストールしたパッケージを読み込むためにソースの先頭で「site-packages」フォルダにパスを通します。

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '../site-packages'))

「Lambda(local)」で実行してみます。
API Gateway(local)」よりエラーの詳細が分かって便利です。

f:id:chief-shuffle:20191207102728j:plain

んーFileNotFoundErrorですね。ファイルが読み込めていないようです。

どうもLambdaは.py拡張子以外のファイルを自由に扱えないようです。
セキュリティ上、JSONファイルをS3やSecrets Managerに配備しておいてLambda実行時に取得するのがセオリーなのかもしれません。

破壊の神「ゴーザ」


「セオリーなど打ち壊してくれる!.pyファイルにしてしまえばよかろう!」

 

 

JSONファイルをsettings.pyというファイル名に変更し、内容の先頭に

ACCOUNT_KEY =

と付与しました。これでPythonのディクショナリ型宣言になります。

f:id:chief-shuffle:20191207104116j:plain

これをlambda_function.pyでインポートします。

sys.path.append(os.path.join(os.path.dirname(__file__), '.'))
import settings

また、認証情報作成部分のメソッド名をfrom_json_keyfile_nameからfrom_json_keyfile_dictに、第1引数をファイルパスからsettings.pyで定義したキー定数に変更します。

    credentials = ServiceAccountCredentials.from_json_keyfile_dict(settings.ACCOUNT_KEY, scope)

f:id:chief-shuffle:20191207104225j:plain

「Run」。
今度はちゃんとワークシート名「lifes」が取れました。

f:id:chief-shuffle:20191207104321j:plain

認証情報をソースとして扱っているのが不安ですが…

 

「よかろうなのだ!」

 

…では、セキュリティ情報はまとめてS3かSecrets Managerで管理する、をバックログに積んでおきましょう。

「誰かにお前は神かって聞かれたら、イエスって答えるもんだぜ」


快適コーディングライフのためにもうひと工夫してみましょう。
Cloud9ではデフォルトでPythonの自動フォーマットが利きません。

ので、自動フォーマッタパッケージ「yapf」を入れます。
こちらを参考にさせていただだきました。ありがとうございます。

qiita.com

またpipでインストールしていくわけですが、フォーマッタはLambda関数とか関係なくCloud9のインスタンス上で操作するので、通常のpipでインストールします。
画面下部のターミナルウィンドウで以下を実行。
まずpipをアップグレードしてからyapfをインストールして、試しにフォーマットしてみています。

cd (Lambdaアプリケーション名)
sudo python3.6 -m pip install --upgrade pip
sudo python3.6 -m pip install yapf
yapf -i (Lambda関数名)/lambda_function.py 

f:id:chief-shuffle:20191207111825j:plain

お、きれいに改行が入っています。

f:id:chief-shuffle:20191207111924j:plain

保存時に自動でフォーマットする設定は「AWS Cloud9」から「Preferences」を選択。
Python Support」の「Format Code on Save」をON、「Custom Code Formatter」を「yapf -i "$file"」と入力します。

f:id:chief-shuffle:20191207112203j:plain

これでソースを保存するたびに

f:id:chief-shuffle:20191207112227j:plain

こうじゃ!

f:id:chief-shuffle:20191207112239j:plain

あー気持ちいい。

次回はお待ちかね、サーバサイド処理を本格的に実装していきます。