Google Cloud DataStoreをHeroku上のRailsアプリから使う

  • 1
    いいね
  • 0
    コメント

なぜやるか

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の部分は、プロジェクトに合わせて変える。

image

「サービスアカウントを作成」をクリック

image

「サービスアカウント名」を適当に入力、
「役割」にて必要な権限(今回であれば「編集者」と「データストアオーナー」)を選択し、
「新しい秘密鍵の提供」にチェックを入れ、「作成」をクリックします。

※「データストアオーナー」だけだと、権限のエラーが出ました。。。けど、今「編集者」を外しても動作する。謎。

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としては、registrationIdregistDateがあります。
XXX_valueというのがあるのが、ちょっと面倒ですね。