タイトルが長い
RailsアプリケーションからGoogle Calendar APIでカレンダー上のイベントに対して更新処理を行い、そのレスポンスから特定の属性を取り出して取り回すような処理を追加した。
レスポンスで受け取ったJSONをgoogle-api-ruby-clientがGoogle::API::V3::Eventクラスのオブジェクトに変換してしまうため、スタブリクエストのレスポンスもruby-clientで変換させるか同様の構造を持ったオブジェクトにする必要があった。
TL;DR
- google-api-ruby-clientを利用してGoogleAPIにリクエストした場合、レスポンスはJSON形式で返ってきたあとgoogle-api-ruby-clientで変換される
- stub_requestからの戻り値のヘッダーに
'X-Goog-Upload-Status': 'final'
を加えると実際のAPIからのレスポンスと同様の形式になる
スタブ化したリクエストと実際にAPIにリクエストした場合のレスポンスの違い
下記のコードはgoogle-api-ruby-clientを利用してリクエストし、レスポンスからイベントのIDを取得しようとしている
リクエスト
service = Google::Apis::CalendarV3::CalendarService.new
service.authorization = access_token
response = service.update_event('primary', event_id, body)
event_id = response.id
このとき response
は以下のようになっている。
#<Google::Apis::CalendarV3::Event:0x000055c94da58f18
@attachments=[],
@attendees=[#<Google::Apis::CalendarV3::EventAttendee:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com", @organizer=true, @self=true>],
@created=Mon, 01 Jan 2020 00:00:00 +0000,
@creator=#<Google::Apis::CalendarV3::Event::Creator:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com", @self=true>,
@description="",
@end=#<Google::Apis::CalendarV3::EventDateTime:xxxxxxxxxxxxxxxxxxx>,
@etag="xxxxxxxxxxxxxxxxxxx",
@id="abcdefghijklmnopqrstu",
@organizer=#<Google::Apis::CalendarV3::Event::Organizer:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com">,
@start=#<Google::Apis::CalendarV3::EventDateTime:xxxxxxxxxxxxxxxxxxx>,
@updated=Mon, 01 Jan 2020 00:00:00 +0000>
このため、response
オブジェクトから ゲッターメソッドの id
で値を取得できる。
一方で大抵の場合、RSpec内でAPIレスポンスは下記のように定義されているだろう。
let(:response) do
{
id: 'abcdefghijklmnopqrstu',
start: { date_time: Time.zone.today },
end: { date_time: Time.zone.today },
html_link: 'https://www.google.com/calendar/event?eid=xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
i_cal_uid: 'xxxxxxxxxxxxxxxxxxx@google.com',
summary: 'event name',
updated: Time.zone.today,
...
}.to_json
end
この場合イベントIDを取得するためには JSON.parse(response)['id']
のようにする必要があり、 response.id
はNoMethodErrorとなる。
対応策
このJSON形式のレスポンスをEventクラスのオブジェクトとして返させるためには以下のように 'X-Goog-Upload-Status': 'final'
をレスポンスのヘッダーに付与する必要がある。
stub_request(
:put, url
).to_return(
status: 201,
body: response,
headers: {
content_type: 'application/json',
'X-Goog-Upload-Status': 'final'
}
)
google-api-ruby-clientのテストスペック内でもレスポンスに同様のヘッダー情報が付与されている。
たまたま同僚がすぐに記事やGitHubのissueを見つけてくれたおかげで解決したけどそうじゃなかったら3日はハマってた気がする。