Fix warning: assigned but unused variable
[arvados.git] / lib / google / api_client / request.rb
index da3fb6a65a1049c826a975c12ce9559a179423be..d043e001628cdfc7000d095848518225e5f84c97 100644 (file)
 # limitations under the License.
 
 require 'faraday'
-require 'faraday/utils'
-require 'multi_json'
+require 'faraday/request/multipart'
 require 'compat/multi_json'
 require 'addressable/uri'
 require 'stringio'
 require 'google/api_client/discovery'
+require 'google/api_client/logging'
 
 module Google
   class APIClient
@@ -26,8 +26,10 @@ module Google
     ##
     # Represents an API request.
     class Request
+      include Google::APIClient::Logging
+
       MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
-      
+
       # @return [Hash] Request parameters
       attr_reader :parameters
       # @return [Hash] Additional HTTP headers
@@ -42,7 +44,7 @@ module Google
       attr_accessor :authenticated
       # @return [#read, #to_str] Request body
       attr_accessor :body
-      
+
       ##
       # Build a request
       #
@@ -52,7 +54,7 @@ module Google
       # @option options [Google::APIClient::Method] :api_method
       #   API method to invoke. Either :api_method or :uri must be specified
       # @option options [TrueClass, FalseClass] :authenticated
-      #   True if request should include credentials. Implicitly true if 
+      #   True if request should include credentials. Implicitly true if
       #   unspecified and :authorization present
       # @option options [#generate_signed_request] :authorization
       #   OAuth credentials
@@ -68,18 +70,20 @@ module Google
       # @option options [String, Symbol] :http_method
       #   HTTP method when requesting a URI
       def initialize(options={})
-        @parameters = Hash[options[:parameters] || {}]
+        @parameters = Faraday::Utils::ParamsHash.new
         @headers = Faraday::Utils::Headers.new
+
+        self.parameters.merge!(options[:parameters]) unless options[:parameters].nil?
         self.headers.merge!(options[:headers]) unless options[:headers].nil?
         self.api_method = options[:api_method]
         self.authenticated = options[:authenticated]
         self.authorization = options[:authorization]
-        
+
         # These parameters are handled differently because they're not
         # parameters to the API method, but rather to the API system.
         self.parameters['key'] ||= options[:key] if options[:key]
         self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
-        
+
         if options[:media]
           self.initialize_media_upload(options)
         elsif options[:body]
@@ -90,13 +94,13 @@ module Google
         else
           self.body = ''
         end
-        
+
         unless self.api_method
           self.http_method = options[:http_method] || 'GET'
           self.uri = options[:uri]
         end
       end
-      
+
       # @!attribute [r] upload_type
       # @return [String] protocol used for upload
       def upload_type
@@ -128,7 +132,7 @@ module Google
             "Expected Google::APIClient::Method, got #{new_api_method.class}."
         end
       end
-      
+
       # @!attribute uri
       # @return [Addressable::URI] URI to send request
       def uri
@@ -145,26 +149,34 @@ module Google
       #
       # @api private
       #
-      # @param [Faraday::Connection] connection 
+      # @param [Faraday::Connection] connection
       #   the connection to transmit with
-      # 
-      # @return [Google::APIClient::Result] 
+      # @param [TrueValue,FalseValue] is_retry
+      #   True if request has been previous sent
+      #
+      # @return [Google::APIClient::Result]
       #   result of API request
-      def send(connection)
-        http_response = connection.app.call(self.to_env(connection))        
+      def send(connection, is_retry = false)
+        self.body.rewind if is_retry && self.body.respond_to?(:rewind)
+        env = self.to_env(connection)
+        logger.debug  { "#{self.class} Sending API request #{env[:method]} #{env[:url].to_s} #{env[:request_headers]}" }
+        http_response = connection.app.call(env)
         result = self.process_http_response(http_response)
-        
+
+        logger.debug { "#{self.class} Result: #{result.status} #{result.headers}" }
+
         # Resumamble slightly different than other upload protocols in that it requires at least
         # 2 requests.
-        if self.upload_type == 'resumable'
-          upload =  result.resumable_upload
+        if result.status == 200 && self.upload_type == 'resumable' && self.media
+          upload = result.resumable_upload
           unless upload.complete?
+            logger.debug { "#{self.class} Sending upload body" }
             result = upload.send(connection)
           end
         end
         return result
       end
-      
+
       # Convert to an HTTP request. Returns components in order of method, URI,
       # request headers, and body
       #
@@ -172,15 +184,16 @@ module Google
       #
       # @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
       def to_http_request
-        request = ( 
-          if self.uri
+        request = (
+          if self.api_method
+            self.api_method.generate_request(self.parameters, self.body, self.headers)
+          elsif self.uri
             unless self.parameters.empty?
               self.uri.query = Addressable::URI.form_encode(self.parameters)
             end
             [self.http_method, self.uri.to_s, self.headers, self.body]
-          else
-            self.api_method.generate_request(self.parameters, self.body, self.headers)
           end)
+        return request
       end
 
       ##
@@ -204,7 +217,7 @@ module Google
         end
         return options
       end
-      
+
       ##
       # Prepares the request for execution, building a hash of parts
       # suitable for sending to Faraday::Connection.
@@ -219,7 +232,7 @@ module Google
       def to_env(connection)
         method, uri, headers, body = self.to_http_request
         http_request = connection.build_request(method) do |req|
-          req.url(uri)
+          req.url(uri.to_s)
           req.headers.update(headers)
           req.body = body
         end
@@ -231,9 +244,9 @@ module Google
           )
         end
 
-        request_env = http_request.to_env(connection)
+        http_request.to_env(connection)
       end
-      
+
       ##
       # Convert HTTP response to an API Result
       #
@@ -247,9 +260,9 @@ module Google
       def process_http_response(response)
         Result.new(self, response)
       end
-      
+
       protected
-      
+
       ##
       # Adjust headers & body for media uploads
       #
@@ -269,14 +282,15 @@ module Google
         self.media = options[:media]
         case self.upload_type
         when "media"
-          if options[:body] || options[:body_object] 
+          if options[:body] || options[:body_object]
             raise ArgumentError, "Can not specify body & body object for simple uploads"
           end
           self.headers['Content-Type'] ||= self.media.content_type
+          self.headers['Content-Length'] ||= self.media.length.to_s
           self.body = self.media
         when "multipart"
-          unless options[:body_object] 
-            raise ArgumentError, "Multipart requested but no body object"              
+          unless options[:body_object]
+            raise ArgumentError, "Multipart requested but no body object"
           end
           metadata = StringIO.new(serialize_body(options[:body_object]))
           build_multipart([Faraday::UploadIO.new(metadata, 'application/json', 'file.json'), self.media])
@@ -286,13 +300,13 @@ module Google
           self.headers['X-Upload-Content-Length'] = file_length.to_s
           if options[:body_object]
             self.headers['Content-Type'] ||= 'application/json'
-            self.body = serialize_body(options[:body_object]) 
+            self.body = serialize_body(options[:body_object])
           else
             self.body = ''
           end
         end
       end
-      
+
       ##
       # Assemble a multipart message from a set of parts
       #
@@ -304,19 +318,19 @@ module Google
       #   MIME type of the message
       # @param [String] boundary
       #   Boundary for separating each part of the message
-      def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY) 
-        env = {
-          :request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"},
-          :request => { :boundary => boundary }
-        }
+      def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
+        env = Faraday::Env.new
+        env.request = Faraday::RequestOptions.new
+        env.request.boundary = boundary
+        env.request_headers = {'Content-Type' => "#{mime_type};boundary=#{boundary}"}
         multipart = Faraday::Request::Multipart.new
         self.body = multipart.create_multipart(env, parts.map {|part| [nil, part]})
         self.headers.update(env[:request_headers])
       end
-      
+
       ##
       # Serialize body object to JSON
-      # 
+      #
       # @api private
       #
       # @param [#to_json,#to_hash] body
@@ -326,7 +340,7 @@ module Google
       #   JSON
       def serialize_body(body)
         return body.to_json if body.respond_to?(:to_json)
-        return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
+        return MultiJson.dump(body.to_hash) if body.respond_to?(:to_hash)
         raise TypeError, 'Could not convert body object to JSON.' +
                          'Must respond to :to_json or :to_hash.'
       end