google-api-client 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +11 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +80 -0
- data/README.md +152 -45
- data/Rakefile +2 -2
- data/bin/google-api +2 -9
- data/lib/compat/multi_json.rb +2 -3
- data/lib/google/api_client.rb +87 -278
- data/lib/google/api_client/auth/jwt_asserter.rb +139 -0
- data/lib/google/api_client/auth/pkcs12.rb +48 -0
- data/lib/google/api_client/batch.rb +164 -136
- data/lib/google/api_client/client_secrets.rb +45 -1
- data/lib/google/api_client/discovery/api.rb +7 -8
- data/lib/google/api_client/discovery/method.rb +20 -27
- data/lib/google/api_client/discovery/resource.rb +16 -10
- data/lib/google/api_client/discovery/schema.rb +2 -0
- data/lib/google/api_client/media.rb +76 -64
- data/lib/google/api_client/reference.rb +7 -285
- data/lib/google/api_client/request.rb +336 -0
- data/lib/google/api_client/result.rb +147 -55
- data/lib/google/api_client/service_account.rb +2 -120
- data/lib/google/api_client/version.rb +2 -3
- data/spec/google/api_client/batch_spec.rb +9 -10
- data/spec/google/api_client/discovery_spec.rb +184 -114
- data/spec/google/api_client/media_spec.rb +27 -39
- data/spec/google/api_client/result_spec.rb +30 -11
- data/spec/google/api_client/service_account_spec.rb +38 -6
- data/spec/google/api_client_spec.rb +48 -32
- data/spec/spec_helper.rb +46 -0
- data/tasks/gem.rake +1 -0
- metadata +36 -70
@@ -12,294 +12,16 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
16
|
-
gem 'faraday', '~> 0.8.1'
|
17
|
-
require 'faraday'
|
18
|
-
require 'faraday/utils'
|
19
|
-
require 'multi_json'
|
20
|
-
require 'compat/multi_json'
|
21
|
-
require 'addressable/uri'
|
22
|
-
require 'stringio'
|
23
|
-
require 'google/api_client/discovery'
|
24
|
-
|
25
|
-
# TODO - needs some serious cleanup
|
15
|
+
require 'google/api_client/request'
|
26
16
|
|
27
17
|
module Google
|
28
18
|
class APIClient
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# omitted.
|
36
|
-
@client = options[:client]
|
37
|
-
@version = options[:version] || 'v1'
|
38
|
-
|
39
|
-
self.connection = options[:connection] || Faraday.default_connection
|
40
|
-
self.authorization = options[:authorization]
|
41
|
-
self.api_method = options[:api_method]
|
42
|
-
self.parameters = options[:parameters] || {}
|
43
|
-
# These parameters are handled differently because they're not
|
44
|
-
# parameters to the API method, but rather to the API system.
|
45
|
-
if self.parameters.kind_of?(Array)
|
46
|
-
if options[:key]
|
47
|
-
self.parameters.reject! { |k, _| k == 'key' }
|
48
|
-
self.parameters << ['key', options[:key]]
|
49
|
-
end
|
50
|
-
if options[:user_ip]
|
51
|
-
self.parameters.reject! { |k, _| k == 'userIp' }
|
52
|
-
self.parameters << ['userIp', options[:user_ip]]
|
53
|
-
end
|
54
|
-
elsif self.parameters.kind_of?(Hash)
|
55
|
-
self.parameters['key'] ||= options[:key] if options[:key]
|
56
|
-
self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
|
57
|
-
# Convert to Array, because they're easier to work with when
|
58
|
-
# repeated parameters are an issue.
|
59
|
-
self.parameters = self.parameters.to_a
|
60
|
-
else
|
61
|
-
raise TypeError,
|
62
|
-
"Expected Array or Hash, got #{self.parameters.class}."
|
63
|
-
end
|
64
|
-
self.headers = options[:headers] || {}
|
65
|
-
if options[:media]
|
66
|
-
self.media = options[:media]
|
67
|
-
upload_type = self.parameters.find { |(k, _)| ['uploadType', 'upload_type'].include?(k) }.last
|
68
|
-
case upload_type
|
69
|
-
when "media"
|
70
|
-
if options[:body] || options[:body_object]
|
71
|
-
raise ArgumentError,
|
72
|
-
"Can not specify body & body object for simple uploads."
|
73
|
-
end
|
74
|
-
self.headers['Content-Type'] ||= self.media.content_type
|
75
|
-
self.body = self.media
|
76
|
-
when "multipart"
|
77
|
-
unless options[:body_object]
|
78
|
-
raise ArgumentError, "Multipart requested but no body object."
|
79
|
-
end
|
80
|
-
# This is all a bit of a hack due to Signet requiring body to be a
|
81
|
-
# string. Ideally, update Signet to delay serialization so we can
|
82
|
-
# just pass streams all the way down through to the HTTP library.
|
83
|
-
metadata = StringIO.new(serialize_body(options[:body_object]))
|
84
|
-
env = {
|
85
|
-
:request_headers => {
|
86
|
-
'Content-Type' =>
|
87
|
-
"multipart/related;boundary=#{MULTIPART_BOUNDARY}"
|
88
|
-
},
|
89
|
-
:request => {:boundary => MULTIPART_BOUNDARY}
|
90
|
-
}
|
91
|
-
multipart = Faraday::Request::Multipart.new
|
92
|
-
self.body = multipart.create_multipart(env, [
|
93
|
-
[nil, Faraday::UploadIO.new(
|
94
|
-
metadata, 'application/json', 'file.json'
|
95
|
-
)],
|
96
|
-
[nil, self.media]])
|
97
|
-
self.headers.update(env[:request_headers])
|
98
|
-
when "resumable"
|
99
|
-
file_length = self.media.length
|
100
|
-
self.headers['X-Upload-Content-Type'] = self.media.content_type
|
101
|
-
self.headers['X-Upload-Content-Length'] = file_length.to_s
|
102
|
-
if options[:body_object]
|
103
|
-
self.headers['Content-Type'] ||= 'application/json'
|
104
|
-
self.body = serialize_body(options[:body_object])
|
105
|
-
else
|
106
|
-
self.body = ''
|
107
|
-
end
|
108
|
-
else
|
109
|
-
raise ArgumentError, "Invalid uploadType for media."
|
110
|
-
end
|
111
|
-
elsif options[:body]
|
112
|
-
self.body = options[:body]
|
113
|
-
elsif options[:body_object]
|
114
|
-
self.headers['Content-Type'] ||= 'application/json'
|
115
|
-
self.body = serialize_body(options[:body_object])
|
116
|
-
else
|
117
|
-
self.body = ''
|
118
|
-
end
|
119
|
-
unless self.api_method
|
120
|
-
self.http_method = options[:http_method] || 'GET'
|
121
|
-
self.uri = options[:uri]
|
122
|
-
unless self.parameters.empty?
|
123
|
-
query_values = (self.uri.query_values(Array) || [])
|
124
|
-
self.uri.query = Addressable::URI.form_encode(
|
125
|
-
(query_values + self.parameters).sort
|
126
|
-
)
|
127
|
-
self.uri.query = nil if self.uri.query == ""
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def serialize_body(body)
|
133
|
-
return body.to_json if body.respond_to?(:to_json)
|
134
|
-
return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
|
135
|
-
raise TypeError, 'Could not convert body object to JSON.' +
|
136
|
-
'Must respond to :to_json or :to_hash.'
|
137
|
-
end
|
138
|
-
|
139
|
-
def media
|
140
|
-
return @media
|
141
|
-
end
|
142
|
-
|
143
|
-
def media=(media)
|
144
|
-
@media = (media)
|
145
|
-
end
|
146
|
-
|
147
|
-
def authorization
|
148
|
-
return @authorization
|
149
|
-
end
|
150
|
-
|
151
|
-
def authorization=(new_authorization)
|
152
|
-
@authorization = new_authorization
|
153
|
-
end
|
154
|
-
|
155
|
-
def connection
|
156
|
-
return @connection
|
157
|
-
end
|
158
|
-
|
159
|
-
def connection=(new_connection)
|
160
|
-
if new_connection.kind_of?(Faraday::Connection)
|
161
|
-
@connection = new_connection
|
162
|
-
else
|
163
|
-
raise TypeError,
|
164
|
-
"Expected Faraday::Connection, got #{new_connection.class}."
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def api_method
|
169
|
-
return @api_method
|
170
|
-
end
|
171
|
-
|
172
|
-
def api_method=(new_api_method)
|
173
|
-
if new_api_method.kind_of?(Google::APIClient::Method) ||
|
174
|
-
new_api_method == nil
|
175
|
-
@api_method = new_api_method
|
176
|
-
elsif new_api_method.respond_to?(:to_str) ||
|
177
|
-
new_api_method.kind_of?(Symbol)
|
178
|
-
unless @client
|
179
|
-
raise ArgumentError,
|
180
|
-
"API method lookup impossible without client instance."
|
181
|
-
end
|
182
|
-
new_api_method = new_api_method.to_s
|
183
|
-
# This method of guessing the API is unreliable. This will fail for
|
184
|
-
# APIs where the first segment of the RPC name does not match the
|
185
|
-
# service name. However, this is a fallback mechanism anyway.
|
186
|
-
# Developers should be passing in a reference to the method, rather
|
187
|
-
# than passing in a string or symbol. This should raise an error
|
188
|
-
# in the case of a mismatch.
|
189
|
-
api = new_api_method[/^([^.]+)\./, 1]
|
190
|
-
@api_method = @client.discovered_method(
|
191
|
-
new_api_method, api, @version
|
192
|
-
)
|
193
|
-
if @api_method
|
194
|
-
# Ditch the client reference, we won't need it again.
|
195
|
-
@client = nil
|
196
|
-
else
|
197
|
-
raise ArgumentError, "API method could not be found."
|
198
|
-
end
|
199
|
-
else
|
200
|
-
raise TypeError,
|
201
|
-
"Expected Google::APIClient::Method, got #{new_api_method.class}."
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
def parameters
|
206
|
-
return @parameters
|
207
|
-
end
|
208
|
-
|
209
|
-
def parameters=(new_parameters)
|
210
|
-
# No type-checking needed, the Method class handles this.
|
211
|
-
@parameters = new_parameters
|
212
|
-
end
|
213
|
-
|
214
|
-
def body
|
215
|
-
return @body
|
216
|
-
end
|
217
|
-
|
218
|
-
def body=(new_body)
|
219
|
-
if new_body.respond_to?(:to_str)
|
220
|
-
@body = new_body.to_str
|
221
|
-
elsif new_body.respond_to?(:read)
|
222
|
-
@body = new_body.read()
|
223
|
-
elsif new_body.respond_to?(:inject)
|
224
|
-
@body = (new_body.inject(StringIO.new) do |accu, chunk|
|
225
|
-
accu.write(chunk)
|
226
|
-
accu
|
227
|
-
end).string
|
228
|
-
else
|
229
|
-
raise TypeError,
|
230
|
-
"Expected body to be String, IO, or Enumerable chunks."
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def headers
|
235
|
-
return @headers ||= {}
|
236
|
-
end
|
237
|
-
|
238
|
-
def headers=(new_headers)
|
239
|
-
if new_headers.kind_of?(Array) || new_headers.kind_of?(Hash)
|
240
|
-
@headers = new_headers
|
241
|
-
else
|
242
|
-
raise TypeError, "Expected Hash or Array, got #{new_headers.class}."
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def http_method
|
247
|
-
return @http_method ||= self.api_method.http_method
|
248
|
-
end
|
249
|
-
|
250
|
-
def http_method=(new_http_method)
|
251
|
-
if new_http_method.kind_of?(Symbol)
|
252
|
-
@http_method = new_http_method.to_s.upcase
|
253
|
-
elsif new_http_method.respond_to?(:to_str)
|
254
|
-
@http_method = new_http_method.to_str.upcase
|
255
|
-
else
|
256
|
-
raise TypeError,
|
257
|
-
"Expected String or Symbol, got #{new_http_method.class}."
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def uri
|
262
|
-
return @uri ||= self.api_method.generate_uri(self.parameters)
|
263
|
-
end
|
264
|
-
|
265
|
-
def uri=(new_uri)
|
266
|
-
@uri = Addressable::URI.parse(new_uri)
|
267
|
-
end
|
268
|
-
|
269
|
-
def to_request
|
270
|
-
if self.api_method
|
271
|
-
return self.api_method.generate_request(
|
272
|
-
self.parameters, self.body, self.headers,
|
273
|
-
:connection => self.connection
|
274
|
-
)
|
275
|
-
else
|
276
|
-
return self.connection.build_request(
|
277
|
-
self.http_method.to_s.downcase.to_sym
|
278
|
-
) do |req|
|
279
|
-
req.url(Addressable::URI.parse(self.uri).normalize.to_s)
|
280
|
-
req.headers = Faraday::Utils::Headers.new(self.headers)
|
281
|
-
req.body = self.body
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def to_hash
|
287
|
-
options = {}
|
288
|
-
if self.api_method
|
289
|
-
options[:api_method] = self.api_method
|
290
|
-
options[:parameters] = self.parameters
|
291
|
-
else
|
292
|
-
options[:http_method] = self.http_method
|
293
|
-
options[:uri] = self.uri
|
294
|
-
end
|
295
|
-
options[:headers] = self.headers
|
296
|
-
options[:body] = self.body
|
297
|
-
options[:connection] = self.connection
|
298
|
-
unless self.authorization.nil?
|
299
|
-
options[:authorization] = self.authorization
|
300
|
-
end
|
301
|
-
return options
|
302
|
-
end
|
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
|
303
25
|
end
|
304
26
|
end
|
305
27
|
end
|
@@ -0,0 +1,336 @@
|
|
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/utils'
|
17
|
+
require 'multi_json'
|
18
|
+
require 'compat/multi_json'
|
19
|
+
require 'addressable/uri'
|
20
|
+
require 'stringio'
|
21
|
+
require 'google/api_client/discovery'
|
22
|
+
|
23
|
+
module Google
|
24
|
+
class APIClient
|
25
|
+
|
26
|
+
##
|
27
|
+
# Represents an API request.
|
28
|
+
class Request
|
29
|
+
MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
|
30
|
+
|
31
|
+
# @return [Hash] Request parameters
|
32
|
+
attr_reader :parameters
|
33
|
+
# @return [Hash] Additional HTTP headers
|
34
|
+
attr_reader :headers
|
35
|
+
# @return [Google::APIClient::Method] API method to invoke
|
36
|
+
attr_reader :api_method
|
37
|
+
# @return [Google::APIClient::UploadIO] File to upload
|
38
|
+
attr_accessor :media
|
39
|
+
# @return [#generated_authenticated_request] User credentials
|
40
|
+
attr_accessor :authorization
|
41
|
+
# @return [TrueClass,FalseClass] True if request should include credentials
|
42
|
+
attr_accessor :authenticated
|
43
|
+
# @return [#read, #to_str] Request body
|
44
|
+
attr_accessor :body
|
45
|
+
|
46
|
+
##
|
47
|
+
# Build a request
|
48
|
+
#
|
49
|
+
# @param [Hash] options
|
50
|
+
# @option options [Hash, Array] :parameters
|
51
|
+
# Request parameters for the API method.
|
52
|
+
# @option options [Google::APIClient::Method] :api_method
|
53
|
+
# API method to invoke. Either :api_method or :uri must be specified
|
54
|
+
# @option options [TrueClass, FalseClass] :authenticated
|
55
|
+
# True if request should include credentials. Implicitly true if
|
56
|
+
# unspecified and :authorization present
|
57
|
+
# @option options [#generate_signed_request] :authorization
|
58
|
+
# OAuth credentials
|
59
|
+
# @option options [Google::APIClient::UploadIO] :media
|
60
|
+
# File to upload, if media upload request
|
61
|
+
# @option options [#to_json, #to_hash] :body_object
|
62
|
+
# Main body of the API request. Typically hash or object that can
|
63
|
+
# be serialized to JSON
|
64
|
+
# @option options [#read, #to_str] :body
|
65
|
+
# Raw body to send in POST/PUT requests
|
66
|
+
# @option options [String, Addressable::URI] :uri
|
67
|
+
# URI to request. Either :api_method or :uri must be specified
|
68
|
+
# @option options [String, Symbol] :http_method
|
69
|
+
# HTTP method when requesting a URI
|
70
|
+
def initialize(options={})
|
71
|
+
@parameters = Hash[options[:parameters] || {}]
|
72
|
+
@headers = Faraday::Utils::Headers.new
|
73
|
+
self.headers.merge!(options[:headers]) unless options[:headers].nil?
|
74
|
+
self.api_method = options[:api_method]
|
75
|
+
self.authenticated = options[:authenticated]
|
76
|
+
self.authorization = options[:authorization]
|
77
|
+
|
78
|
+
# These parameters are handled differently because they're not
|
79
|
+
# parameters to the API method, but rather to the API system.
|
80
|
+
self.parameters['key'] ||= options[:key] if options[:key]
|
81
|
+
self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
|
82
|
+
|
83
|
+
if options[:media]
|
84
|
+
self.initialize_media_upload(options)
|
85
|
+
elsif options[:body]
|
86
|
+
self.body = options[:body]
|
87
|
+
elsif options[:body_object]
|
88
|
+
self.headers['Content-Type'] ||= 'application/json'
|
89
|
+
self.body = serialize_body(options[:body_object])
|
90
|
+
else
|
91
|
+
self.body = ''
|
92
|
+
end
|
93
|
+
|
94
|
+
unless self.api_method
|
95
|
+
self.http_method = options[:http_method] || 'GET'
|
96
|
+
self.uri = options[:uri]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# @!attribute [r] upload_type
|
101
|
+
# @return [String] protocol used for upload
|
102
|
+
def upload_type
|
103
|
+
return self.parameters['uploadType'] || self.parameters['upload_type']
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!attribute http_method
|
107
|
+
# @return [Symbol] HTTP method if invoking a URI
|
108
|
+
def http_method
|
109
|
+
return @http_method ||= self.api_method.http_method.to_s.downcase.to_sym
|
110
|
+
end
|
111
|
+
|
112
|
+
def http_method=(new_http_method)
|
113
|
+
if new_http_method.kind_of?(Symbol)
|
114
|
+
@http_method = new_http_method.to_s.downcase.to_sym
|
115
|
+
elsif new_http_method.respond_to?(:to_str)
|
116
|
+
@http_method = new_http_method.to_s.downcase.to_sym
|
117
|
+
else
|
118
|
+
raise TypeError,
|
119
|
+
"Expected String or Symbol, got #{new_http_method.class}."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def api_method=(new_api_method)
|
124
|
+
if new_api_method.nil? || new_api_method.kind_of?(Google::APIClient::Method)
|
125
|
+
@api_method = new_api_method
|
126
|
+
else
|
127
|
+
raise TypeError,
|
128
|
+
"Expected Google::APIClient::Method, got #{new_api_method.class}."
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @!attribute uri
|
133
|
+
# @return [Addressable::URI] URI to send request
|
134
|
+
def uri
|
135
|
+
return @uri ||= self.api_method.generate_uri(self.parameters)
|
136
|
+
end
|
137
|
+
|
138
|
+
def uri=(new_uri)
|
139
|
+
@uri = Addressable::URI.parse(new_uri)
|
140
|
+
@parameters.update(@uri.query_values) unless @uri.query_values.nil?
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Transmits the request with the given connection
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
#
|
148
|
+
# @param [Faraday::Connection] connection
|
149
|
+
# the connection to transmit with
|
150
|
+
#
|
151
|
+
# @return [Google::APIClient::Result]
|
152
|
+
# result of API request
|
153
|
+
def send(connection)
|
154
|
+
http_response = connection.app.call(self.to_env(connection))
|
155
|
+
result = self.process_http_response(http_response)
|
156
|
+
|
157
|
+
# Resumamble slightly different than other upload protocols in that it requires at least
|
158
|
+
# 2 requests.
|
159
|
+
if self.upload_type == 'resumable'
|
160
|
+
upload = result.resumable_upload
|
161
|
+
unless upload.complete?
|
162
|
+
result = upload.send(connection)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
return result
|
166
|
+
end
|
167
|
+
|
168
|
+
# Convert to an HTTP request. Returns components in order of method, URI,
|
169
|
+
# request headers, and body
|
170
|
+
#
|
171
|
+
# @api private
|
172
|
+
#
|
173
|
+
# @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
|
174
|
+
def to_http_request
|
175
|
+
request = (
|
176
|
+
if self.uri
|
177
|
+
unless self.parameters.empty?
|
178
|
+
self.uri.query = Addressable::URI.form_encode(self.parameters)
|
179
|
+
end
|
180
|
+
[self.http_method, self.uri.to_s, self.headers, self.body]
|
181
|
+
else
|
182
|
+
self.api_method.generate_request(self.parameters, self.body, self.headers)
|
183
|
+
end)
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Hashified verison of the API request
|
188
|
+
#
|
189
|
+
# @return [Hash]
|
190
|
+
def to_hash
|
191
|
+
options = {}
|
192
|
+
if self.api_method
|
193
|
+
options[:api_method] = self.api_method
|
194
|
+
options[:parameters] = self.parameters
|
195
|
+
else
|
196
|
+
options[:http_method] = self.http_method
|
197
|
+
options[:uri] = self.uri
|
198
|
+
end
|
199
|
+
options[:headers] = self.headers
|
200
|
+
options[:body] = self.body
|
201
|
+
options[:media] = self.media
|
202
|
+
unless self.authorization.nil?
|
203
|
+
options[:authorization] = self.authorization
|
204
|
+
end
|
205
|
+
return options
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Prepares the request for execution, building a hash of parts
|
210
|
+
# suitable for sending to Faraday::Connection.
|
211
|
+
#
|
212
|
+
# @api private
|
213
|
+
#
|
214
|
+
# @param [Faraday::Connection] connection
|
215
|
+
# Connection for building the request
|
216
|
+
#
|
217
|
+
# @return [Hash]
|
218
|
+
# Encoded request
|
219
|
+
def to_env(connection)
|
220
|
+
method, uri, headers, body = self.to_http_request
|
221
|
+
http_request = connection.build_request(method) do |req|
|
222
|
+
req.url(uri)
|
223
|
+
req.headers.update(headers)
|
224
|
+
req.body = body
|
225
|
+
end
|
226
|
+
|
227
|
+
if self.authorization.respond_to?(:generate_authenticated_request)
|
228
|
+
http_request = self.authorization.generate_authenticated_request(
|
229
|
+
:request => http_request,
|
230
|
+
:connection => connection
|
231
|
+
)
|
232
|
+
end
|
233
|
+
|
234
|
+
request_env = http_request.to_env(connection)
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# Convert HTTP response to an API Result
|
239
|
+
#
|
240
|
+
# @api private
|
241
|
+
#
|
242
|
+
# @param [Faraday::Response] response
|
243
|
+
# HTTP response
|
244
|
+
#
|
245
|
+
# @return [Google::APIClient::Result]
|
246
|
+
# Processed API response
|
247
|
+
def process_http_response(response)
|
248
|
+
Result.new(self, response)
|
249
|
+
end
|
250
|
+
|
251
|
+
protected
|
252
|
+
|
253
|
+
##
|
254
|
+
# Adjust headers & body for media uploads
|
255
|
+
#
|
256
|
+
# @api private
|
257
|
+
#
|
258
|
+
# @param [Hash] options
|
259
|
+
# @option options [Hash, Array] :parameters
|
260
|
+
# Request parameters for the API method.
|
261
|
+
# @option options [Google::APIClient::UploadIO] :media
|
262
|
+
# File to upload, if media upload request
|
263
|
+
# @option options [#to_json, #to_hash] :body_object
|
264
|
+
# Main body of the API request. Typically hash or object that can
|
265
|
+
# be serialized to JSON
|
266
|
+
# @option options [#read, #to_str] :body
|
267
|
+
# Raw body to send in POST/PUT requests
|
268
|
+
def initialize_media_upload(options)
|
269
|
+
self.media = options[:media]
|
270
|
+
case self.upload_type
|
271
|
+
when "media"
|
272
|
+
if options[:body] || options[:body_object]
|
273
|
+
raise ArgumentError, "Can not specify body & body object for simple uploads"
|
274
|
+
end
|
275
|
+
self.headers['Content-Type'] ||= self.media.content_type
|
276
|
+
self.body = self.media
|
277
|
+
when "multipart"
|
278
|
+
unless options[:body_object]
|
279
|
+
raise ArgumentError, "Multipart requested but no body object"
|
280
|
+
end
|
281
|
+
metadata = StringIO.new(serialize_body(options[:body_object]))
|
282
|
+
build_multipart([Faraday::UploadIO.new(metadata, 'application/json', 'file.json'), self.media])
|
283
|
+
when "resumable"
|
284
|
+
file_length = self.media.length
|
285
|
+
self.headers['X-Upload-Content-Type'] = self.media.content_type
|
286
|
+
self.headers['X-Upload-Content-Length'] = file_length.to_s
|
287
|
+
if options[:body_object]
|
288
|
+
self.headers['Content-Type'] ||= 'application/json'
|
289
|
+
self.body = serialize_body(options[:body_object])
|
290
|
+
else
|
291
|
+
self.body = ''
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
##
|
297
|
+
# Assemble a multipart message from a set of parts
|
298
|
+
#
|
299
|
+
# @api private
|
300
|
+
#
|
301
|
+
# @param [Array<[#read,#to_str]>] parts
|
302
|
+
# Array of parts to encode.
|
303
|
+
# @param [String] mime_type
|
304
|
+
# MIME type of the message
|
305
|
+
# @param [String] boundary
|
306
|
+
# Boundary for separating each part of the message
|
307
|
+
def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
|
308
|
+
env = {
|
309
|
+
:request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"},
|
310
|
+
:request => { :boundary => boundary }
|
311
|
+
}
|
312
|
+
multipart = Faraday::Request::Multipart.new
|
313
|
+
self.body = multipart.create_multipart(env, parts.map {|part| [nil, part]})
|
314
|
+
self.headers.update(env[:request_headers])
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# Serialize body object to JSON
|
319
|
+
#
|
320
|
+
# @api private
|
321
|
+
#
|
322
|
+
# @param [#to_json,#to_hash] body
|
323
|
+
# object to serialize
|
324
|
+
#
|
325
|
+
# @return [String]
|
326
|
+
# JSON
|
327
|
+
def serialize_body(body)
|
328
|
+
return body.to_json if body.respond_to?(:to_json)
|
329
|
+
return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
|
330
|
+
raise TypeError, 'Could not convert body object to JSON.' +
|
331
|
+
'Must respond to :to_json or :to_hash.'
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|