jomz-google-api-client 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.md +144 -0
  2. data/CONTRIBUTING.md +32 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE +202 -0
  5. data/README.md +192 -0
  6. data/Rakefile +46 -0
  7. data/lib/cacerts.pem +2183 -0
  8. data/lib/compat/multi_json.rb +16 -0
  9. data/lib/google/api_client.rb +672 -0
  10. data/lib/google/api_client/auth/compute_service_account.rb +28 -0
  11. data/lib/google/api_client/auth/file_storage.rb +87 -0
  12. data/lib/google/api_client/auth/installed_app.rb +122 -0
  13. data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
  14. data/lib/google/api_client/auth/key_utils.rb +93 -0
  15. data/lib/google/api_client/auth/pkcs12.rb +41 -0
  16. data/lib/google/api_client/batch.rb +323 -0
  17. data/lib/google/api_client/client_secrets.rb +176 -0
  18. data/lib/google/api_client/discovery.rb +19 -0
  19. data/lib/google/api_client/discovery/api.rb +300 -0
  20. data/lib/google/api_client/discovery/media.rb +77 -0
  21. data/lib/google/api_client/discovery/method.rb +363 -0
  22. data/lib/google/api_client/discovery/resource.rb +156 -0
  23. data/lib/google/api_client/discovery/schema.rb +121 -0
  24. data/lib/google/api_client/environment.rb +42 -0
  25. data/lib/google/api_client/errors.rb +60 -0
  26. data/lib/google/api_client/gzip.rb +28 -0
  27. data/lib/google/api_client/logging.rb +32 -0
  28. data/lib/google/api_client/media.rb +259 -0
  29. data/lib/google/api_client/railtie.rb +16 -0
  30. data/lib/google/api_client/reference.rb +27 -0
  31. data/lib/google/api_client/request.rb +351 -0
  32. data/lib/google/api_client/result.rb +253 -0
  33. data/lib/google/api_client/service.rb +233 -0
  34. data/lib/google/api_client/service/batch.rb +103 -0
  35. data/lib/google/api_client/service/request.rb +144 -0
  36. data/lib/google/api_client/service/resource.rb +40 -0
  37. data/lib/google/api_client/service/result.rb +162 -0
  38. data/lib/google/api_client/service/simple_file_store.rb +151 -0
  39. data/lib/google/api_client/service/stub_generator.rb +59 -0
  40. data/lib/google/api_client/service_account.rb +18 -0
  41. data/lib/google/api_client/version.rb +31 -0
  42. data/lib/google/inflection.rb +28 -0
  43. data/spec/fixtures/files/privatekey.p12 +0 -0
  44. data/spec/fixtures/files/sample.txt +33 -0
  45. data/spec/fixtures/files/secret.pem +19 -0
  46. data/spec/google/api_client/batch_spec.rb +249 -0
  47. data/spec/google/api_client/discovery_spec.rb +652 -0
  48. data/spec/google/api_client/gzip_spec.rb +86 -0
  49. data/spec/google/api_client/media_spec.rb +179 -0
  50. data/spec/google/api_client/request_spec.rb +30 -0
  51. data/spec/google/api_client/result_spec.rb +203 -0
  52. data/spec/google/api_client/service_account_spec.rb +164 -0
  53. data/spec/google/api_client/service_spec.rb +586 -0
  54. data/spec/google/api_client/simple_file_store_spec.rb +137 -0
  55. data/spec/google/api_client_spec.rb +253 -0
  56. data/spec/spec_helper.rb +56 -0
  57. data/tasks/gem.rake +97 -0
  58. data/tasks/git.rake +45 -0
  59. data/tasks/metrics.rake +22 -0
  60. data/tasks/spec.rake +57 -0
  61. data/tasks/wiki.rake +82 -0
  62. data/tasks/yard.rake +29 -0
  63. metadata +309 -0
@@ -0,0 +1,16 @@
1
+ require 'google/api_client/logging'
2
+
3
+ module Google
4
+ class APIClient
5
+
6
+ ##
7
+ # Optional support class for Rails. Currently replaces the built-in logger
8
+ # with Rails' application log.
9
+ #
10
+ class Railtie < Rails::Railtie
11
+ initializer 'google-api-client' do |app|
12
+ Google::APIClient.logger = Rails.logger
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'google/api_client/request'
16
+
17
+ module Google
18
+ class APIClient
19
+ ##
20
+ # Subclass of Request for backwards compatibility with pre-0.5.0 versions of the library
21
+ #
22
+ # @deprecated
23
+ # use {Google::APIClient::Request} instead
24
+ class Reference < Request
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,351 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'faraday'
16
+ require 'faraday/request/multipart'
17
+ require 'multi_json'
18
+ require 'compat/multi_json'
19
+ require 'addressable/uri'
20
+ require 'stringio'
21
+ require 'google/api_client/discovery'
22
+ require 'google/api_client/logging'
23
+
24
+ module Google
25
+ class APIClient
26
+
27
+ ##
28
+ # Represents an API request.
29
+ class Request
30
+ include Google::APIClient::Logging
31
+
32
+ MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
33
+
34
+ # @return [Hash] Request parameters
35
+ attr_reader :parameters
36
+ # @return [Hash] Additional HTTP headers
37
+ attr_reader :headers
38
+ # @return [Google::APIClient::Method] API method to invoke
39
+ attr_reader :api_method
40
+ # @return [Google::APIClient::UploadIO] File to upload
41
+ attr_accessor :media
42
+ # @return [#generated_authenticated_request] User credentials
43
+ attr_accessor :authorization
44
+ # @return [TrueClass,FalseClass] True if request should include credentials
45
+ attr_accessor :authenticated
46
+ # @return [#read, #to_str] Request body
47
+ attr_accessor :body
48
+
49
+ ##
50
+ # Build a request
51
+ #
52
+ # @param [Hash] options
53
+ # @option options [Hash, Array] :parameters
54
+ # Request parameters for the API method.
55
+ # @option options [Google::APIClient::Method] :api_method
56
+ # API method to invoke. Either :api_method or :uri must be specified
57
+ # @option options [TrueClass, FalseClass] :authenticated
58
+ # True if request should include credentials. Implicitly true if
59
+ # unspecified and :authorization present
60
+ # @option options [#generate_signed_request] :authorization
61
+ # OAuth credentials
62
+ # @option options [Google::APIClient::UploadIO] :media
63
+ # File to upload, if media upload request
64
+ # @option options [#to_json, #to_hash] :body_object
65
+ # Main body of the API request. Typically hash or object that can
66
+ # be serialized to JSON
67
+ # @option options [#read, #to_str] :body
68
+ # Raw body to send in POST/PUT requests
69
+ # @option options [String, Addressable::URI] :uri
70
+ # URI to request. Either :api_method or :uri must be specified
71
+ # @option options [String, Symbol] :http_method
72
+ # HTTP method when requesting a URI
73
+ def initialize(options={})
74
+ @parameters = Faraday::Utils::ParamsHash.new
75
+ @headers = Faraday::Utils::Headers.new
76
+
77
+ self.parameters.merge!(options[:parameters]) unless options[:parameters].nil?
78
+ self.headers.merge!(options[:headers]) unless options[:headers].nil?
79
+ self.api_method = options[:api_method]
80
+ self.authenticated = options[:authenticated]
81
+ self.authorization = options[:authorization]
82
+
83
+ # These parameters are handled differently because they're not
84
+ # parameters to the API method, but rather to the API system.
85
+ self.parameters['key'] ||= options[:key] if options[:key]
86
+ self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
87
+
88
+ if options[:media]
89
+ self.initialize_media_upload(options)
90
+ elsif options[:body]
91
+ self.body = options[:body]
92
+ elsif options[:body_object]
93
+ self.headers['Content-Type'] ||= 'application/json'
94
+ self.body = serialize_body(options[:body_object])
95
+ else
96
+ self.body = ''
97
+ end
98
+
99
+ unless self.api_method
100
+ self.http_method = options[:http_method] || 'GET'
101
+ self.uri = options[:uri]
102
+ end
103
+ end
104
+
105
+ # @!attribute [r] upload_type
106
+ # @return [String] protocol used for upload
107
+ def upload_type
108
+ return self.parameters['uploadType'] || self.parameters['upload_type']
109
+ end
110
+
111
+ # @!attribute http_method
112
+ # @return [Symbol] HTTP method if invoking a URI
113
+ def http_method
114
+ return @http_method ||= self.api_method.http_method.to_s.downcase.to_sym
115
+ end
116
+
117
+ def http_method=(new_http_method)
118
+ if new_http_method.kind_of?(Symbol)
119
+ @http_method = new_http_method.to_s.downcase.to_sym
120
+ elsif new_http_method.respond_to?(:to_str)
121
+ @http_method = new_http_method.to_s.downcase.to_sym
122
+ else
123
+ raise TypeError,
124
+ "Expected String or Symbol, got #{new_http_method.class}."
125
+ end
126
+ end
127
+
128
+ def api_method=(new_api_method)
129
+ if new_api_method.nil? || new_api_method.kind_of?(Google::APIClient::Method)
130
+ @api_method = new_api_method
131
+ else
132
+ raise TypeError,
133
+ "Expected Google::APIClient::Method, got #{new_api_method.class}."
134
+ end
135
+ end
136
+
137
+ # @!attribute uri
138
+ # @return [Addressable::URI] URI to send request
139
+ def uri
140
+ return @uri ||= self.api_method.generate_uri(self.parameters)
141
+ end
142
+
143
+ def uri=(new_uri)
144
+ @uri = Addressable::URI.parse(new_uri)
145
+ @parameters.update(@uri.query_values) unless @uri.query_values.nil?
146
+ end
147
+
148
+
149
+ # Transmits the request with the given connection
150
+ #
151
+ # @api private
152
+ #
153
+ # @param [Faraday::Connection] connection
154
+ # the connection to transmit with
155
+ # @param [TrueValue,FalseValue] is_retry
156
+ # True if request has been previous sent
157
+ #
158
+ # @return [Google::APIClient::Result]
159
+ # result of API request
160
+ def send(connection, is_retry = false)
161
+ self.body.rewind if is_retry && self.body.respond_to?(:rewind)
162
+ env = self.to_env(connection)
163
+ logger.debug { "#{self.class} Sending API request #{env[:method]} #{env[:url].to_s} #{env[:request_headers]}" }
164
+ http_response = connection.app.call(env)
165
+ result = self.process_http_response(http_response)
166
+
167
+ logger.debug { "#{self.class} Result: #{result.status} #{result.headers}" }
168
+
169
+ # Resumamble slightly different than other upload protocols in that it requires at least
170
+ # 2 requests.
171
+ if result.status == 200 && self.upload_type == 'resumable'
172
+ upload = result.resumable_upload
173
+ unless upload.complete?
174
+ logger.debug { "#{self.class} Sending upload body" }
175
+ result = upload.send(connection)
176
+ end
177
+ end
178
+ return result
179
+ end
180
+
181
+ # Convert to an HTTP request. Returns components in order of method, URI,
182
+ # request headers, and body
183
+ #
184
+ # @api private
185
+ #
186
+ # @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
187
+ def to_http_request
188
+ request = (
189
+ if self.api_method
190
+ self.api_method.generate_request(self.parameters, self.body, self.headers)
191
+ elsif self.uri
192
+ unless self.parameters.empty?
193
+ self.uri.query = Addressable::URI.form_encode(self.parameters)
194
+ end
195
+ [self.http_method, self.uri.to_s, self.headers, self.body]
196
+ end)
197
+ return request
198
+ end
199
+
200
+ ##
201
+ # Hashified verison of the API request
202
+ #
203
+ # @return [Hash]
204
+ def to_hash
205
+ options = {}
206
+ if self.api_method
207
+ options[:api_method] = self.api_method
208
+ options[:parameters] = self.parameters
209
+ else
210
+ options[:http_method] = self.http_method
211
+ options[:uri] = self.uri
212
+ end
213
+ options[:headers] = self.headers
214
+ options[:body] = self.body
215
+ options[:media] = self.media
216
+ unless self.authorization.nil?
217
+ options[:authorization] = self.authorization
218
+ end
219
+ return options
220
+ end
221
+
222
+ ##
223
+ # Prepares the request for execution, building a hash of parts
224
+ # suitable for sending to Faraday::Connection.
225
+ #
226
+ # @api private
227
+ #
228
+ # @param [Faraday::Connection] connection
229
+ # Connection for building the request
230
+ #
231
+ # @return [Hash]
232
+ # Encoded request
233
+ def to_env(connection)
234
+ method, uri, headers, body = self.to_http_request
235
+ http_request = connection.build_request(method) do |req|
236
+ req.url(uri.to_s)
237
+ req.headers.update(headers)
238
+ req.body = body
239
+ end
240
+
241
+ if self.authorization.respond_to?(:generate_authenticated_request)
242
+ http_request = self.authorization.generate_authenticated_request(
243
+ :request => http_request,
244
+ :connection => connection
245
+ )
246
+ end
247
+
248
+ request_env = http_request.to_env(connection)
249
+ end
250
+
251
+ ##
252
+ # Convert HTTP response to an API Result
253
+ #
254
+ # @api private
255
+ #
256
+ # @param [Faraday::Response] response
257
+ # HTTP response
258
+ #
259
+ # @return [Google::APIClient::Result]
260
+ # Processed API response
261
+ def process_http_response(response)
262
+ Result.new(self, response)
263
+ end
264
+
265
+ protected
266
+
267
+ ##
268
+ # Adjust headers & body for media uploads
269
+ #
270
+ # @api private
271
+ #
272
+ # @param [Hash] options
273
+ # @option options [Hash, Array] :parameters
274
+ # Request parameters for the API method.
275
+ # @option options [Google::APIClient::UploadIO] :media
276
+ # File to upload, if media upload request
277
+ # @option options [#to_json, #to_hash] :body_object
278
+ # Main body of the API request. Typically hash or object that can
279
+ # be serialized to JSON
280
+ # @option options [#read, #to_str] :body
281
+ # Raw body to send in POST/PUT requests
282
+ def initialize_media_upload(options)
283
+ self.media = options[:media]
284
+ case self.upload_type
285
+ when "media"
286
+ if options[:body] || options[:body_object]
287
+ raise ArgumentError, "Can not specify body & body object for simple uploads"
288
+ end
289
+ self.headers['Content-Type'] ||= self.media.content_type
290
+ self.headers['Content-Length'] ||= self.media.length.to_s
291
+ self.body = self.media
292
+ when "multipart"
293
+ unless options[:body_object]
294
+ raise ArgumentError, "Multipart requested but no body object"
295
+ end
296
+ metadata = StringIO.new(serialize_body(options[:body_object]))
297
+ build_multipart([Faraday::UploadIO.new(metadata, 'application/json', 'file.json'), self.media])
298
+ when "resumable"
299
+ file_length = self.media.length
300
+ self.headers['X-Upload-Content-Type'] = self.media.content_type
301
+ self.headers['X-Upload-Content-Length'] = file_length.to_s
302
+ if options[:body_object]
303
+ self.headers['Content-Type'] ||= 'application/json'
304
+ self.body = serialize_body(options[:body_object])
305
+ else
306
+ self.body = ''
307
+ end
308
+ end
309
+ end
310
+
311
+ ##
312
+ # Assemble a multipart message from a set of parts
313
+ #
314
+ # @api private
315
+ #
316
+ # @param [Array<[#read,#to_str]>] parts
317
+ # Array of parts to encode.
318
+ # @param [String] mime_type
319
+ # MIME type of the message
320
+ # @param [String] boundary
321
+ # Boundary for separating each part of the message
322
+ def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
323
+ env = Faraday::Env.new
324
+ env.request = Faraday::RequestOptions.new
325
+ env.request.boundary = boundary
326
+ env.request_headers = {'Content-Type' => "#{mime_type};boundary=#{boundary}"}
327
+ multipart = Faraday::Request::Multipart.new
328
+ self.body = multipart.create_multipart(env, parts.map {|part| [nil, part]})
329
+ self.headers.update(env[:request_headers])
330
+ end
331
+
332
+ ##
333
+ # Serialize body object to JSON
334
+ #
335
+ # @api private
336
+ #
337
+ # @param [#to_json,#to_hash] body
338
+ # object to serialize
339
+ #
340
+ # @return [String]
341
+ # JSON
342
+ def serialize_body(body)
343
+ return body.to_json if body.respond_to?(:to_json)
344
+ return MultiJson.dump(body.to_hash) if body.respond_to?(:to_hash)
345
+ raise TypeError, 'Could not convert body object to JSON.' +
346
+ 'Must respond to :to_json or :to_hash.'
347
+ end
348
+
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,253 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Google
17
+ class APIClient
18
+ ##
19
+ # This class wraps a result returned by an API call.
20
+ class Result
21
+ extend Forwardable
22
+
23
+ ##
24
+ # Init the result
25
+ #
26
+ # @param [Google::APIClient::Request] request
27
+ # The original request
28
+ # @param [Faraday::Response] response
29
+ # Raw HTTP Response
30
+ def initialize(request, response)
31
+ @request = request
32
+ @response = response
33
+ @media_upload = reference if reference.kind_of?(ResumableUpload)
34
+ end
35
+
36
+ # @return [Google::APIClient::Request] Original request object
37
+ attr_reader :request
38
+ # @return [Faraday::Response] HTTP response
39
+ attr_reader :response
40
+ # @!attribute [r] reference
41
+ # @return [Google::APIClient::Request] Original request object
42
+ # @deprecated See {#request}
43
+ alias_method :reference, :request # For compatibility with pre-beta clients
44
+
45
+ # @!attribute [r] status
46
+ # @return [Fixnum] HTTP status code
47
+ # @!attribute [r] headers
48
+ # @return [Hash] HTTP response headers
49
+ # @!attribute [r] body
50
+ # @return [String] HTTP response body
51
+ def_delegators :@response, :status, :headers, :body
52
+
53
+ # @!attribute [r] resumable_upload
54
+ # @return [Google::APIClient::ResumableUpload] For resuming media uploads
55
+ def resumable_upload
56
+ @media_upload ||= (
57
+ options = self.reference.to_hash.merge(
58
+ :uri => self.headers['location'],
59
+ :media => self.reference.media
60
+ )
61
+ Google::APIClient::ResumableUpload.new(options)
62
+ )
63
+ end
64
+
65
+ ##
66
+ # Get the content type of the response
67
+ # @!attribute [r] media_type
68
+ # @return [String]
69
+ # Value of content-type header
70
+ def media_type
71
+ _, content_type = self.headers.detect do |h, v|
72
+ h.downcase == 'Content-Type'.downcase
73
+ end
74
+ if content_type
75
+ return content_type[/^([^;]*);?.*$/, 1].strip.downcase
76
+ else
77
+ return nil
78
+ end
79
+ end
80
+
81
+ ##
82
+ # Check if request failed
83
+ #
84
+ # @!attribute [r] error?
85
+ # @return [TrueClass, FalseClass]
86
+ # true if result of operation is an error
87
+ def error?
88
+ return self.response.status >= 400
89
+ end
90
+
91
+ ##
92
+ # Check if request was successful
93
+ #
94
+ # @!attribute [r] success?
95
+ # @return [TrueClass, FalseClass]
96
+ # true if result of operation was successful
97
+ def success?
98
+ return !self.error?
99
+ end
100
+
101
+ ##
102
+ # Extracts error messages from the response body
103
+ #
104
+ # @!attribute [r] error_message
105
+ # @return [String]
106
+ # error message, if available
107
+ def error_message
108
+ if self.data?
109
+ if self.data.respond_to?(:error) &&
110
+ self.data.error.respond_to?(:message)
111
+ # You're going to get a terrible error message if the response isn't
112
+ # parsed successfully as an error.
113
+ return self.data.error.message
114
+ elsif self.data['error'] && self.data['error']['message']
115
+ return self.data['error']['message']
116
+ end
117
+ end
118
+ return self.body
119
+ end
120
+
121
+ ##
122
+ # Check for parsable data in response
123
+ #
124
+ # @!attribute [r] data?
125
+ # @return [TrueClass, FalseClass]
126
+ # true if body can be parsed
127
+ def data?
128
+ !(self.body.nil? || self.body.empty? || self.media_type != 'application/json')
129
+ end
130
+
131
+ ##
132
+ # Return parsed version of the response body.
133
+ #
134
+ # @!attribute [r] data
135
+ # @return [Object, Hash, String]
136
+ # Object if body parsable from API schema, Hash if JSON, raw body if unable to parse
137
+ def data
138
+ return @data ||= (begin
139
+ if self.data?
140
+ media_type = self.media_type
141
+ data = self.body
142
+ case media_type
143
+ when 'application/json'
144
+ data = MultiJson.load(data)
145
+ # Strip data wrapper, if present
146
+ data = data['data'] if data.has_key?('data')
147
+ else
148
+ raise ArgumentError,
149
+ "Content-Type not supported for parsing: #{media_type}"
150
+ end
151
+ if @request.api_method && @request.api_method.response_schema
152
+ # Automatically parse using the schema designated for the
153
+ # response of this API method.
154
+ data = @request.api_method.response_schema.new(data)
155
+ data
156
+ else
157
+ # Otherwise, return the raw unparsed value.
158
+ # This value must be indexable like a Hash.
159
+ data
160
+ end
161
+ end
162
+ end)
163
+ end
164
+
165
+ ##
166
+ # Get the token used for requesting the next page of data
167
+ #
168
+ # @!attribute [r] next_page_token
169
+ # @return [String]
170
+ # next page token
171
+ def next_page_token
172
+ if self.data.respond_to?(:next_page_token)
173
+ return self.data.next_page_token
174
+ elsif self.data.respond_to?(:[])
175
+ return self.data["nextPageToken"]
176
+ else
177
+ raise TypeError, "Data object did not respond to #next_page_token."
178
+ end
179
+ end
180
+
181
+ ##
182
+ # Build a request for fetching the next page of data
183
+ #
184
+ # @return [Google::APIClient::Request]
185
+ # API request for retrieving next page
186
+ def next_page
187
+ merged_parameters = Hash[self.reference.parameters].merge({
188
+ self.page_token_param => self.next_page_token
189
+ })
190
+ # Because Requests can be coerced to Hashes, we can merge them,
191
+ # preserving all context except the API method parameters that we're
192
+ # using for pagination.
193
+ return Google::APIClient::Request.new(
194
+ Hash[self.reference].merge(:parameters => merged_parameters)
195
+ )
196
+ end
197
+
198
+ ##
199
+ # Get the token used for requesting the previous page of data
200
+ #
201
+ # @!attribute [r] prev_page_token
202
+ # @return [String]
203
+ # previous page token
204
+ def prev_page_token
205
+ if self.data.respond_to?(:prev_page_token)
206
+ return self.data.prev_page_token
207
+ elsif self.data.respond_to?(:[])
208
+ return self.data["prevPageToken"]
209
+ else
210
+ raise TypeError, "Data object did not respond to #next_page_token."
211
+ end
212
+ end
213
+
214
+ ##
215
+ # Build a request for fetching the previous page of data
216
+ #
217
+ # @return [Google::APIClient::Request]
218
+ # API request for retrieving previous page
219
+ def prev_page
220
+ merged_parameters = Hash[self.reference.parameters].merge({
221
+ self.page_token_param => self.prev_page_token
222
+ })
223
+ # Because Requests can be coerced to Hashes, we can merge them,
224
+ # preserving all context except the API method parameters that we're
225
+ # using for pagination.
226
+ return Google::APIClient::Request.new(
227
+ Hash[self.reference].merge(:parameters => merged_parameters)
228
+ )
229
+ end
230
+
231
+ ##
232
+ # Pagination scheme used by this request/response
233
+ #
234
+ # @!attribute [r] pagination_type
235
+ # @return [Symbol]
236
+ # currently always :token
237
+ def pagination_type
238
+ return :token
239
+ end
240
+
241
+ ##
242
+ # Name of the field that contains the pagination token
243
+ #
244
+ # @!attribute [r] page_token_param
245
+ # @return [String]
246
+ # currently always 'pageToken'
247
+ def page_token_param
248
+ return "pageToken"
249
+ end
250
+
251
+ end
252
+ end
253
+ end