Martin91/rong_cloud

View on GitHub
lib/rong_cloud/request.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'net/http'
require 'json'
require 'rong_cloud/signature'
require 'logger'

module RongCloud
  # Handle api request, based on Ruby's built-in Net::HTTP,
  # support both HTTP and HTTPS
  #
  module Request
    include Signature

    # @param path [String] The api endpoint
    # @param params [Hash] api params
    # @param content_type [Symbol] required formatting for request data, :form_data or :json
    # @return [Hash] the parsed response represented by JSON
    # @raise [RongCloud::BadRequest] when request params is invalid or other situations
    def request(path, params = nil, content_type = :form_data)
      uri = get_uri(path)
      req = initialize_request(uri, params, content_type)

      use_ssl = uri.scheme == 'https'
      timeout = RongCloud::Configuration.timeout
      request_options = { use_ssl: use_ssl, open_timeout: timeout, read_timeout: timeout }

      res = Net::HTTP.start(uri.hostname, uri.port, request_options) do |http|
        inspect_debug_informations(req, uri, request_options)
        http.request(req)
      end

      inspect_debug_informations(res)
      handle_response(res)
    end

    private
    # construct the api endpoint url
    #
    # @param path [String] the relative path for api endpoint,e.g. "/user/getToken" or "user/getToken"
    # @return [URI] full url for the endpoint
    #
    def get_uri(path)
      url = "#{RongCloud::Configuration.host}/#{path.gsub(/^\//, "")}"
      URI(url.end_with?(".json") ? url : "#{url}.json")
    end

    # construct a new request
    #
    # @param uri [URI] the relative path for api endpoint
    # @param params [Hash] the request params
    # @param content_type [Symbol] required formatting for request data, :form_data or :json
    # @return [Net::HTTP::Post] the request object
    #
    def initialize_request(uri, params, content_type)
      req = Net::HTTP::Post.new(uri)
      signed_headers.each { |header, value| req[header] = value }

      case content_type
      when :form_data
        req.set_form_data(params) if params.respond_to?(:map)
      when :json
        req.body = params.to_json
        req["Content-Type"] = "application/json"
      end

      req
    end

    # parse the raw response
    #
    # @param res [HTTPResponse] the response
    # @return [Hash] response data parsed as JSON
    # @raise [RongCloud::BadRequest] when params are invalid or other situations
    #
    def handle_response(res)
      json = JSON.parse(res.body)
      case res
      when Net::HTTPOK
        json
      else
        error = (HTTP_CODE_TO_ERRORS_MAP[res.code] || RequestError).new(json["errorMessage"])
        error.business_code = json["code"]
        raise error
      end
    end

    def inspect_debug_informations(*objects)
      return unless RongCloud::Configuration.debug_mode

      objects.each do |obj|
        begin
          case
          when obj.is_a?(URI::Generic)
            logger.debug("Requesting #{obj.hostname}:#{obj.port}")
          when obj.is_a?(Net::HTTPGenericRequest)
            logger.debug("request method: #{obj.method}, headers: #{obj.to_hash}, raw body: #{obj.body}")
          when obj.is_a?(Net::HTTPResponse)
            logger.debug("response headers: #{obj.to_hash}, raw body: #{obj.body}")
          else
            logger.debug(obj.inspect)
          end
        rescue
        end
      end
    end

    def logger
      @logger ||= ::Logger.new("log/rong_cloud_debug.log")
    end
  end
end