この記事は Google Cloud Platform その1 Advent Calendar 2018 の8日目の記事です。
GCPには以下のような分析ツールが用意されておりますが、実際にBigQuery+DatastudioやBigQuery+Spreadsheetなど様々な組み合わせで様々な分析をされていることでしょう。
- BigQuery : データ分析、DWH
- Datalab: データ分析、機械学習
- Data Studio: データの可視化、共有
- Spreadsheet: データ分析、可視化、共有
BigQueryに貯めてあるデータをSpreadSheetやDatastudioで可視化するというのもとても便利ですが、最近社内でCloud Datastoreに貯めてあるデータをSpreadsheetで共有したいというニーズがあり、その際にGoogle Apps Script(GAS)を使ってとても簡単に実現出来たのでその方法・手順についてご紹介しようと思います。
事前にご準備いただくもの
- GCPプロジェクト
- Cloud Datastore及び抽出用のデータ
- サービスアカウントキー(使わない方法もご紹介します)
TL;DR
- GASからGoogle APIを叩く際の認可の方法として、Oauthとサービスアカウントの2種類ある
- Oauthが簡単で楽
- Datastore APIに限らずGoogleの提供する他のAPIでも適用可能
GASで使える認可のパターン
GASからGoogle APIを実行する場合、以下の2つの認可のパターンが考えられます。
- Oauthによる認可
- サービスアカウントを利用したJWTによる認可
通常のアプリケーションからAPIを叩く場合にはどちらか都合の良い方を選べばいいと思いますが、GASから叩く場合にはOauthを使うのが簡単です。
Oauthを使った認可パターン
大まかな流れは以下の通りです。
- マニフェストファイル(appsscript.js)でScopeを設定する
- ScriptApp.getOAuthToken()でアクセストークンを取得しAPIリクエストを送信する
1.マニフェストファイル(appsscript.js)でScopeを設定する
[表示]→[マニフェストファイルを表示]を選択すると左側のファイルビューでappsscript.jsが表示されると思います。そこで今回使用するScopeを設定します。
{
"timeZone": "Asia/Tokyo",
"dependencies": {
"libraries": [{
"userSymbol": "OAuth2",
"libraryId": {YOUR_LIBRARY_ID},
"version": "31",
"developmentMode": true
}]
},
"webapp": {
"access": "MYSELF",
"executeAs": "USER_ACCESSING"
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/datastore"
]
}
今回必要なのは”oauthScopes”の項目です。今回はDatastoreのAPIを実行するので”https://www.googleapis.com/auth/datastore”を追加します。
“https://www.googleapis.com/auth/script.external_request"は外部のAPIを叩くときに必要なScopeです。
2.マニフェストファイル(appsscript.js)でScopeを設定する
設定はこれだけでOKです。あとはコードを書いていきます。
function main() {
var result = query("select * from `{YOUR_DATA_KIND}`");
Logger.log(result);
}function query(queryString) {
var url = "https://datastore.googleapis.com/v1/projects/" +
{YOUR_PROJECT_ID}+ ":runQuery";
var response = sendRequest(url, createRequestOption("POST", {
"gqlQuery": {
"queryString": queryString
},
}));
return response;
}function sendRequest(url, fetchOptions) {
var res = UrlFetchApp.fetch(url, fetchOptions);
return JSON.parse(res);
}function createRequestOption(method, payload) {
return { "method": method,
"muteHttpExceptions": true,
"contentType": "application/json",
"payload": JSON.stringify(payload),
"headers": {
Authorization: "Bearer " + ScriptApp.getOAuthToken()
} };
}
説明の都合上実際のコードから多少端折った箇所もありますが大体こんな感じです。これだけでOauth認証を介してDatastore APIを実行することが出来ます。通常、アプリケーションからOauth認証でGoogle APIを実行する場合はClientIdとClientSecretが必要となりますが、GASの場合はGoogle承認済みのクライアントのため?、それらも必要ありません。
- YOUR_DATA_KIND: Datastoreから取得する対象のKind
- YOUR_PROJECT_ID: Datastoreの存在するプロジェクトのID
を適宜自分の環境に置き換えてお読みください。
先にも書いた通り、GASからGoogle APIを叩く場合は上記の方法がおすすめです。ただ、どうしても個人のアカウントに依存してしまうOauthでは要件を満たせない場合、少し複雑にはなりますがサービスアカウントを利用できますので、ご紹介します。
2.サービスアカウントを利用したJWTによる認可
サービスアカウントのキーはGCPコンソールから発行できます。
[IAM&admin]→[Service accounts]からAppengineのDefault Service Accountの[Create Key]を選択することでjsonのキーを発行出来ます。
そしてGASではそのファイル全てを使うわけではなく、中身に含まれているPrivate Keyのみを利用します。
{"type": "service_account","project_id": "{YOUR_PROJECT_ID}","private_key_id": "efcbbcc2e880e8b917712b4a18a0ebf655f0b12d","private_key": "{YOUR_PRIVATE_KEY}","client_email": "{YOUR_CLIENT_EMAIL}","client_id": "","auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/{YOUR_PROJECT_ID}%40appspot.gserviceaccount.com"}
JSONファイルの中身は上記のような内容になっているかと思います。
上記の{YOUR_PRIVATE_KEY}と{YOUR_CLIENT_EMAIL}にあたる文字列を控えておいてください。
では実際にGASのコードを見ていきます。
function main() {
var result = query("select * from `{YOUR_DATA_KIND}`");
Logger.log(result);
}function query(queryString) {
var url = "https://datastore.googleapis.com/v1/projects/" +
{YOUR_PROJECT_ID}+ ":runQuery";
var response = sendRequest(url, createRequestOption("POST", {
"gqlQuery": {
"queryString": queryString
},
}));
return response;
}function sendRequest(url, fetchOptions) {
var res = UrlFetchApp.fetch(url, fetchOptions);
return JSON.parse(res);
}function createRequestOption(method, payload) {
var service = getService();
if (service.hasAccess()) {
return { "method": method,
"muteHttpExceptions": true,
"contentType": "application/json",
"payload": JSON.stringify(payload),
"headers": { Authorization: "Bearer " + service.getAccessToken() } };
} else {
Logger.log(service.getLastError());
}
}function getService() {
return OAuth2.createService("Datastore:" + {YOUR_CLIENT_EMAIL})
.setTokenUrl("https://accounts.google.com/o/oauth2/token")
.setPrivateKey("YOUR_PRIVATE_KEY")
.setIssuer({YOUR_CLIENT_EMAIL})
.setPropertyStore(PropertiesService.getScriptProperties())
.setScope("https://www.googleapis.com/auth/datastore");
}
本来Private_keyからJWTを生成する部分を自前で実装しなくてはならないのですが、そのあたりの面倒をみてくれるライブラリをGoogleが用意してくれています。
このライブラリを利用することでJWTの生成、トークンの取得あたりの処理を自前で実装する必要がなくなります。
いずれの方式もほんの十数行のコードを書くだけでDatastoreからデータを抽出することができることが分かりました。今回はご紹介できませんでしたがSpreadsheetにも書き出すことも簡単にできます。
もちろん大規模なデータを扱う場合であったり、機械学習系のデータを扱う場合にはBigQueryであったりDatalabといった他の選択肢を選ぶ方が好ましいでしょう。ただ、もっと気軽にちょっとした分析を行う際の選択肢としてGAS(+Spreadsheet)の組み合わせも検討して頂き、本記事がその際の実装のお役に立てれば幸いです。
それでは良いGASライフを〜