なぜやるか
GCPは無料枠が大きく、小規模のサービスであればDataStoreは無料枠で使い続けられると思います。( http://my-android-server.appspot.com/ は、実際に無料枠で収まっています。)
Google App Engineも小規模であれば無料で使い続けられますが、利用できる言語に制限があります。
Rubyをやりたくなったので、Herokuへの移行を検討していますが、DataStoreはそのまま使い続けたかったので、DataStoreを外部から利用する方法を試しました。
GCP上でサービスアカウントを作成
https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=my-android-server
※my-android-server
の部分は、プロジェクトに合わせて変える。
「サービスアカウントを作成」をクリック
「サービスアカウント名」を適当に入力、
「役割」にて必要な権限(今回であれば「編集者」と「データストアオーナー」)を選択し、
「新しい秘密鍵の提供」にチェックを入れ、「作成」をクリックします。
※「データストアオーナー」だけだと、権限のエラーが出ました。。。けど、今「編集者」を外しても動作する。謎。
JSONファイルがダウンロードされるので、Railsアプリケーションの任意の場所に保管します。
今回は、直下にcertディレクトリを作成し、そこに配置しました。
(ファイル名はmy-android-server-000000000000.json
として進めます。)
Herokuで使うための準備
用意したcertディレクトリをGitにコミットしたくなかったので、Herokuの環境変数として登録することにしました。
heroku config:add GCP_KEY="$(cat cert/my-android-server-000000000000.json)"
これで、jsonの内容を環境変数から取得できるようになります。
Railsアプリケーションの実装
https://developers.google.com/api-client-library/ruby/auth/service-accounts
https://cloud.google.com/datastore/reference/rest/v1beta3/projects/lookup
を参考に実装していきます。
とりあえず、controllerにべた書きしました。(ホントはlibディレクトリの中とかで抽象化したほうが良いのかな?)
require 'googleauth'
require 'google/apis/datastore_v1'
class WelcomeController < ApplicationController
# https://github.com/google/google-api-ruby-client/blob/master/generated/google/apis/datastore_v1/classes.rb
ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
Datastore = Google::Apis::DatastoreV1
RunQueryRequest = Google::Apis::DatastoreV1::RunQueryRequest
GqlQuery = Google::Apis::DatastoreV1::GqlQuery
GqlQueryParameter = Google::Apis::DatastoreV1::GqlQueryParameter
Value = Google::Apis::DatastoreV1::Value
def index
datastore = Google::Apis::DatastoreV1::DatastoreService.new
dummyFile = DummyFile.new(ENV['GCP_KEY']) if ENV['GCP_KEY'].present?
datastore.authorization = ServiceAccountCredentials.make_creds(
json_key_io: dummyFile || File.open('cert/my-android-server-000000000000.json'),
scope: [
Datastore::AUTH_CLOUD_PLATFORM,
Datastore::AUTH_DATASTORE
]
)
query = GqlQuery.new(
query_string: 'SELECT * FROM GcmModel LIMIT @limit',
named_bindings: {
limit: GqlQueryParameter.new(value: Value.new(integer_value: '5'))
}
)
request = RunQueryRequest.new(gql_query: query)
result = datastore.run_project_query('my-android-server', request)
render json: result.batch.entity_results[0].entity.as_json
end
class DummyFile
attr_accessor :read
def initialize(read)
@read = read
end
end
end
my-android-server
というプロジェクトの、GcmModelを5件取得して、1件目をjson形式で返しています。
DummyFile
あたりが上手く実装できなくて、GCP_KEY
が存在する場合はそこから情報を読み取り、それ以外(ローカル)ではファイルから読み取るようにしています。
内部的にread
を実行していたので、read
というプロパティを持たせることで回避しました。
結果
下記のようなjsonを取得できます。
{
"properties": {
"registrationId": {
"string_value": "XXXX"
},
"registDate": {
"timestamp_value": "2012-10-08T21:59:59.661960Z"
}
},
"key": {
"partition_id": {
"project_id": "my-android-server"
},
"path": [
{
"kind": "GcmModel",
"id": "111111"
}
]
}
}
GcmModelとしては、registrationId
とregistDate
があります。
XXX_value
というのがあるのが、ちょっと面倒ですね。