1 # Copyright 2010 Google Inc.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 require 'faraday/utils'
18 require 'compat/multi_json'
19 require 'addressable/uri'
21 require 'google/api_client/discovery'
23 # TODO - needs some serious cleanup
28 MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
30 def initialize(options={})
32 self.connection = options[:connection] || Faraday.default_connection
33 self.authorization = options[:authorization]
34 self.api_method = options[:api_method]
36 self.parameters = options[:parameters] || {}
37 # These parameters are handled differently because they're not
38 # parameters to the API method, but rather to the API system.
39 self.parameters['key'] ||= options[:key] if options[:key]
40 self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
42 self.headers = options[:headers] || {}
44 self.initialize_media_upload
46 self.body = options[:body]
47 elsif options[:body_object]
48 self.headers['Content-Type'] ||= 'application/json'
49 self.body = serialize_body(options[:body_object])
53 unless self.api_method
54 self.http_method = options[:http_method] || 'GET'
55 self.uri = options[:uri]
56 unless self.parameters.empty?
57 self.uri.query = Addressable::URI.form_encode(self.parameters)
62 def initialize_media_upload
63 self.media = options[:media]
66 if options[:body] || options[:body_object]
67 raise ArgumentError, "Can not specify body & body object for simple uploads"
69 self.headers['Content-Type'] ||= self.media.content_type
70 self.body = self.media
72 unless options[:body_object]
73 raise ArgumentError, "Multipart requested but no body object"
75 metadata = StringIO.new(serialize_body(options[:body_object]))
77 :request_headers => {'Content-Type' => "multipart/related;boundary=#{MULTIPART_BOUNDARY}"},
78 :request => { :boundary => MULTIPART_BOUNDARY }
80 multipart = Faraday::Request::Multipart.new
81 self.body = multipart.create_multipart(env, [
82 [nil,Faraday::UploadIO.new(metadata, 'application/json', 'file.json')],
84 self.headers.update(env[:request_headers])
86 file_length = self.media.length
87 self.headers['X-Upload-Content-Type'] = self.media.content_type
88 self.headers['X-Upload-Content-Length'] = file_length.to_s
89 if options[:body_object]
90 self.headers['Content-Type'] ||= 'application/json'
91 self.body = serialize_body(options[:body_object])
96 raise ArgumentError, "Invalid uploadType for media"
100 def serialize_body(body)
101 return body.to_json if body.respond_to?(:to_json)
102 return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
103 raise TypeError, 'Could not convert body object to JSON.' +
104 'Must respond to :to_json or :to_hash.'
116 return self.parameters['uploadType'] || self.parameters['upload_type']
120 return @authorization
123 def authorization=(new_authorization)
124 @authorization = new_authorization
131 def connection=(new_connection)
132 if new_connection.kind_of?(Faraday::Connection)
133 @connection = new_connection
136 "Expected Faraday::Connection, got #{new_connection.class}."
144 def api_method=(new_api_method)
145 if new_api_method.kind_of?(Google::APIClient::Method) ||
146 new_api_method == nil
147 @api_method = new_api_method
150 "Expected Google::APIClient::Method, got #{new_api_method.class}."
158 def parameters=(new_parameters)
159 @parameters = Hash[new_parameters]
171 return @headers ||= {}
174 def headers=(new_headers)
175 if new_headers.kind_of?(Array) || new_headers.kind_of?(Hash)
176 @headers = new_headers
178 raise TypeError, "Expected Hash or Array, got #{new_headers.class}."
183 return @http_method ||= self.api_method.http_method
186 def http_method=(new_http_method)
187 if new_http_method.kind_of?(Symbol)
188 @http_method = new_http_method.to_s.downcase.to_sym
189 elsif new_http_method.respond_to?(:to_str)
190 @http_method = new_http_method.to_s.downcase.to_sym
193 "Expected String or Symbol, got #{new_http_method.class}."
198 return @uri ||= self.api_method.generate_uri(self.parameters)
202 @uri = Addressable::URI.parse(new_uri)
208 self.api_method.generate_request(
209 self.parameters, self.body, self.headers, :connection => self.connection
212 self.connection.build_request(self.http_method) do |req|
213 req.url(self.uri.to_str)
214 req.headers.update(self.headers)
219 if self.authorization.respond_to?(:generate_authenticated_request)
220 request = self.authorization.generate_authenticated_request(
222 :connection => self.connection
231 options[:api_method] = self.api_method
232 options[:parameters] = self.parameters
234 options[:http_method] = self.http_method
235 options[:uri] = self.uri
237 options[:headers] = self.headers
238 options[:body] = self.body
239 options[:connection] = self.connection
240 unless self.authorization.nil?
241 options[:authorization] = self.authorization