droidlabs/motion-prime

View on GitHub
motion-prime/api_client.rb

Summary

Maintainability
C
7 hrs
Test Coverage
class ApiClient
  attr_accessor :access_token

  def initialize(options = {})
    self.access_token = options[:access_token]
    AFMotion::Client.build_shared(config.base) do
      header "Accept", "application/json"
      response_serializer AFJSONResponseSerializer.serializerWithReadingOptions(NSJSONReadingMutableContainers)
    end
  end

  def request_params(data)
    params = data.clone

    if config.http_auth?
      params.merge!(credentials: config.http_auth.to_hash)
    end
    if config.sign_request?
      params.delete(:sign)
      signature = RmDigest::MD5.hexdigest(
        config.signature_secret + params.keys.map(&:to_s).sort.join
      )
      params.merge!(sign: signature)
    end
    params
  end

  def authenticate(username = nil, password = nil, data = nil, &block)
    data ||= {
      grant_type: "password",
      username: username,
      password: password,
      client_id: config.client_id,
      client_secret: config.client_secret
    }
    use_callback = block_given?
    AFMotion::Client.shared.post(config.auth_path, request_params(data)) do |response|
      auth_data = response.object

      self.access_token = auth_data[:access_token] if auth_data
      block.call(auth_data, response.operation.response.try(:statusCode)) if use_callback
    end
    true
  end

  def page_url(path)
    path_with_base(path, config.base)
  end

  def resource_url(path)
    base = config.resource_base? ? config.resource_base : config.base
    path_with_base(path, base)
  end

  def request(method, path, params = {}, options = {}, &block)
    files = params.delete(:_files)
    data = request_params(params.merge(access_token: access_token))

    if !options.has_key?(:allow_queue) && config.default_methods_queue.include?(method.to_sym)
      options[:allow_queue] = true
    end

    if !options.has_key?(:allow_cache) && config.default_methods_cache.include?(method.to_sym)
      options[:allow_cache] = true
    end

    if allow_cache?(method, path, options)
      cached_request!(method, path, data, files, options, &block)
    else
      request!(method, path, data, files, options, &block)
    end
  end

  def request!(method, path, data, files = nil, options = {}, &block)
    use_callback = block_given?
    path_with_base = path_with_base(path, config.api_namespace)
    client_method = files.present? ? :"multipart_#{method}" : method
    AFMotion::Client.shared.send client_method, path_with_base, data do |response, form_data, progress|
      if form_data && files.present?
        append_files_to_data(files, form_data)
      elsif progress
        # handle progress
      elsif !response.success? && allow_queue?(method, path_with_base, options)
        queue(method: method, path: path, params: data)
      elsif response.operation.response.nil?
        block.call if use_callback
      else
        prepared_response = prepare_response_object(response.object)
        block.call(prepared_response, response.operation.response.statusCode) if use_callback
        process_queue if config.allow_queue?
      end
    end
  end

  def cached_request!(method, path, data, files = nil, options = {}, &block)
    use_callback = block_given?
    params = data.map { |key, value| "#{key}=#{value}" }.join('&')
    cache_key = [method, path, params].join(' ')
    response = read_cache(cache_key)
    if response && use_callback
      block.call(prepare_response_object(response), 200)
    else
      request!(method, path, data, files, options) do |response, status|
        write_cache(cache_key, response)
        block.call(response, status) if use_callback
      end
    end
  end

  def get(path, params = {}, options = {}, &block)
    request(:get, path, params, options, &block)
  end

  def put(path, params = {}, options = {}, &block)
    request(:put, path, params, options, &block)
  end

  def post(path, params = {}, options = {}, &block)
    request(:post, path, params, options, &block)
  end

  def delete(path, params = {}, options = {}, &block)
    request(:delete, path, params, options, &block)
  end

  def queue(item)
    queue_json = user_defaults['api_client_queue']
    queue_list = MotionPrime::JSON.parse(queues) if queue_list.present?
    queue_list ||= []
    queue_list.push(item)
    user_defaults['api_client_queue'] = MotionPrime::JSON.generate(queue_list)
  end

  # TODO: temporary solution, add real caching system here
  def read_cache(key)
    @cache ||= {}
    @cache[key]
  end

  # TODO: temporary solution, add real caching system here
  def write_cache(key, data)
    @cache ||= {}
    @cache[key] = data
  end

  protected
    def path_with_base(path, base)
      path.to_s.starts_with?('http') ? path : "#{base}#{path}"
    end

    def allow_queue?(method, path, options)
      options[:allow_queue] && config.allow_queue?
    end

    def allow_cache?(method, path, options)
      options[:allow_cache] && config.allow_cache?
    end

    def process_queue
      queue_json = user_defaults['api_client_queue']
      queue_list = MotionPrime::JSON.parse(queue_json) if queue_json.present?
      user_defaults['api_client_queue'] = nil
      Array.wrap(queue_list).each do |item|
        request(item[:method], item[:path], item[:params].clone.symbolize_keys)
      end
    end

    def config
      MotionPrime::Config.api_client
    end

    def user_defaults
      @user_defaults ||= NSUserDefaults.standardUserDefaults
    end

    def append_files_to_data(files, data)
      files.each do |file|
        name = file[:name]
        file_name = file[:file_name] || "avatar.png"
        mime_type = file[:mime_type] || "image/jpeg"
        data.appendPartWithFileData(
          file[:data], name: name, fileName: file_name, mimeType: mime_type
        )
      end
      data
    end

    def prepare_response_object(object)
      if object.is_a?(Hash)
        object.with_indifferent_access
      elsif object.is_a?(Array)
        object.map{ |obj| prepare_response_object(obj) }
      else
        object
      end
    end
end