def do_request(method, path, obj = nil, options = {})
      # hatt clients pass in path possibly including query params.
      # Faraday needs the query and path seperately.
      parsed_uri = URI.parse make_path(path)
      # faraday needs the request params as a hash.
      # this turns out to be non-trivial
      query_hash = if parsed_uri.query
                     cgi_hash = CGI.parse(parsed_uri.query)
                     # this next line accounts for one param having multiple values
                     cgi_hash.each_with_object({}) { |(k, v), h| h[k] = v[1] ? v : v.first; }
                   end

      req_headers = make_headers(options)

      body = if options[:form]
               URI.encode_www_form obj
             else
               jsonify(obj)
      end


      log_request(method, parsed_uri, query_hash, req_headers, body)

      # doing it this way avoids problem with OPTIONS method: https://github.com/lostisland/faraday/issues/305
      response = nil
      metrics_obj = { method: method, service: @name, path: parsed_uri.path }
      ActiveSupport::Notifications.instrument('request.hatt', metrics_obj) do
        response = @faraday_connection.run_request(method, nil, nil, nil) do |req|
          req.path = parsed_uri.path
          req.params = metrics_obj[:params] = query_hash if query_hash

          req.headers = req_headers
          req.body = body
          req.options[:timeout] = options.fetch(:timeout, @default_timeout)
        end
        metrics_obj[:response] = response
      end

      logger.info "Request status: (#{response.status}) #{@name}: #{method.to_s.upcase} #{path}"
      @last_request = {
        method: method,
        path: parsed_uri.path,
        query: parsed_uri.query,
        headers: req_headers,
        body: body
      }
      @last_response = response

      response_obj = objectify response.body

      log_response(response, response_obj)

      raise RequestException.new(nil, response) unless response.status >= 200 && response.status < 300

      response_obj
    end
def do_request(method, path, obj = nil, options = {})
      # hatt clients pass in path possibly including query params.
      # Faraday needs the query and path seperately.
      parsed_uri = URI.parse make_path(path)
      # faraday needs the request params as a hash.
      # this turns out to be non-trivial
      query_hash = if parsed_uri.query
                     cgi_hash = CGI.parse(parsed_uri.query)
                     # this next line accounts for one param having multiple values
                     cgi_hash.each_with_object({}) { |(k, v), h| h[k] = v[1] ? v : v.first; }
                   end

      req_headers = make_headers(options)

      body = if options[:form]
               URI.encode_www_form obj
             else
               jsonify(obj)
      end


      log_request(method, parsed_uri, query_hash, req_headers, body)

      # doing it this way avoids problem with OPTIONS method: https://github.com/lostisland/faraday/issues/305
      response = nil
      metrics_obj = { method: method, service: @name, path: parsed_uri.path }
      ActiveSupport::Notifications.instrument('request.hatt', metrics_obj) do
        response = @faraday_connection.run_request(method, nil, nil, nil) do |req|
          req.path = parsed_uri.path
          req.params = metrics_obj[:params] = query_hash if query_hash

          req.headers = req_headers
          req.body = body
          req.options[:timeout] = options.fetch(:timeout, @default_timeout)
        end
        metrics_obj[:response] = response
      end

      logger.info "Request status: (#{response.status}) #{@name}: #{method.to_s.upcase} #{path}"
      @last_request = {
        method: method,
        path: parsed_uri.path,
        query: parsed_uri.query,
        headers: req_headers,
        body: body
      }
      @last_response = response

      response_obj = objectify response.body

      log_response(response, response_obj)

      raise RequestException.new(nil, response) unless response.status >= 200 && response.status < 300

      response_obj
    end
do_request performs the actual request, and does associated logging
 options can include:
 - :timeout, which specifies num secs the request should timeout in
   (this turns out to be kind of annoying to implement)