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'
27 MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
29 attr_reader :parameters, :headers
30 attr_accessor :api_client, :connection, :api_method, :version ,:media, :authorization, :authenticated, :body
32 def initialize(options={})
33 @parameters = Hash[options[:parameters] || {}]
34 @headers = Faraday::Utils::Headers.new
35 self.api_client = options[:api_client]
36 self.headers.merge!(options[:headers]) unless options[:headers].nil?
37 self.api_method = options[:api_method]
38 self.version = options[:version]
39 self.authenticated = options[:authenticated]
40 self.authorization = options[:authorization]
42 # These parameters are handled differently because they're not
43 # parameters to the API method, but rather to the API system.
44 self.parameters['key'] ||= options[:key] if options[:key]
45 self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
48 self.initialize_media_upload(options)
50 self.body = options[:body]
51 elsif options[:body_object]
52 self.headers['Content-Type'] ||= 'application/json'
53 self.body = serialize_body(options[:body_object])
58 unless self.api_method
59 self.http_method = options[:http_method] || 'GET'
60 self.uri = options[:uri]
65 return self.parameters['uploadType'] || self.parameters['upload_type']
69 return @http_method ||= self.api_method.http_method.to_s.downcase.to_sym
72 def http_method=(new_http_method)
73 if new_http_method.kind_of?(Symbol)
74 @http_method = new_http_method.to_s.downcase.to_sym
75 elsif new_http_method.respond_to?(:to_str)
76 @http_method = new_http_method.to_s.downcase.to_sym
79 "Expected String or Symbol, got #{new_http_method.class}."
84 return @uri ||= self.api_method.generate_uri(self.parameters)
88 @uri = Addressable::URI.parse(new_uri)
89 @parameters.update(@uri.query_values) unless @uri.query_values.nil?
93 response = connection.app.call(self.to_env(connection))
94 self.process_http_response(response)
99 self.headers['User-Agent'] ||= '' + self.api_client.user_agent unless self.api_client.user_agent.nil?
100 self.parameters['key'] ||= self.api_client.key unless self.api_client.key.nil?
101 self.parameters['userIp'] ||= self.api_client.user_ip unless self.api_client.user_ip.nil?
102 self.api_method = self.api_client.resolve_method(self.api_method, self.version) unless self.api_method.nil?
106 unless self.parameters.empty?
107 self.uri.query = Addressable::URI.form_encode(self.parameters)
109 [self.http_method, self.uri.to_s, self.headers, self.body]
111 self.api_method.generate_request(self.parameters, self.body, self.headers)
118 options[:api_method] = self.api_method
119 options[:parameters] = self.parameters
121 options[:http_method] = self.http_method
122 options[:uri] = self.uri
124 options[:headers] = self.headers
125 options[:body] = self.body
126 options[:media] = self.media
127 unless self.authorization.nil?
128 options[:authorization] = self.authorization
133 def to_env(connection)
134 method, uri, headers, body = self.to_http_request
135 http_request = connection.build_request(method) do |req|
137 req.headers.update(headers)
141 if self.authorization.respond_to?(:generate_authenticated_request)
142 http_request = self.authorization.generate_authenticated_request(
143 :request => http_request,
144 :connection => connection
148 request_env = http_request.to_env(connection)
151 def process_http_response(response)
152 Result.new(self, response)
157 def initialize_media_upload(options)
158 self.media = options[:media]
159 case self.upload_type
161 if options[:body] || options[:body_object]
162 raise ArgumentError, "Can not specify body & body object for simple uploads"
164 self.headers['Content-Type'] ||= self.media.content_type
165 self.body = self.media
167 unless options[:body_object]
168 raise ArgumentError, "Multipart requested but no body object"
170 metadata = StringIO.new(serialize_body(options[:body_object]))
171 build_multipart([Faraday::UploadIO.new(metadata, 'application/json', 'file.json'), self.media])
173 file_length = self.media.length
174 self.headers['X-Upload-Content-Type'] = self.media.content_type
175 self.headers['X-Upload-Content-Length'] = file_length.to_s
176 if options[:body_object]
177 self.headers['Content-Type'] ||= 'application/json'
178 self.body = serialize_body(options[:body_object])
185 def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
187 :request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"},
188 :request => { :boundary => boundary }
190 multipart = Faraday::Request::Multipart.new
191 self.body = multipart.create_multipart(env, parts.map {|part| [nil, part]})
192 self.headers.update(env[:request_headers])
195 def serialize_body(body)
196 return body.to_json if body.respond_to?(:to_json)
197 return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
198 raise TypeError, 'Could not convert body object to JSON.' +
199 'Must respond to :to_json or :to_hash.'
204 class Reference < Request