module IqairAirvisual class Client include ::IqairAirvisual::Constants include ::IqairAirvisual::Community # TODO: Air quality guidelines # TODO: Units breakdown # TODO: Return codes # https://api-docs.iqair.com/?version=latest # Below are a few example of return codes you may get. This list is not exhaustive. # success: returned when JSON file was generated successfully. # call_limit_reached: returned when minute/monthly limit is reached. # api_key_expired: returned when API key is expired. # incorrect_api_key: returned when using wrong API key. # ip_location_failed: returned when service is unable to locate IP address of request. # no_nearest_station: returned when there is no nearest station within specified radius. # feature_not_available: returned when call requests a feature that is not available in chosen subscription plan. # too_many_requests: returned when more than 10 calls per second are made. attr_reader :api_key, :base_path, :port, :login_response, :raw_cookie, :expiry def initialize(api_key:, base_path: BASE_PATH, port: 80) @api_key = api_key @base_path = base_path @port = port end def self.compatible_api_version 'v2' end # This is the version of the API docs this client was built off-of def self.api_version 'v2 2021-04-18' end private def authorise_and_send(http_method:, path:, payload: {}, params: {}) params.merge!({ key: api_key }) start_time = get_micro_second_time response = HTTParty.send( http_method.to_sym, construct_base_path(path, params), body: payload, headers: { 'Content-Type': 'application/json' }, port: port, format: :json ) end_time = get_micro_second_time construct_response_object(response, path, start_time, end_time) end def construct_response_object(response, path, start_time, end_time) { 'body' => parse_body(response, path), 'headers' => response.headers, 'metadata' => construct_metadata(response, start_time, end_time) } end def construct_metadata(response, start_time, end_time) total_time = end_time - start_time { 'start_time' => start_time, 'end_time' => end_time, 'total_time' => total_time } end def body_is_present?(response) !body_is_missing?(response) end def body_is_missing?(response) response.body.nil? || response.body.empty? end def parse_body(response, path) parsed_response = JSON.parse(response.body) # Purposely not using HTTParty if parsed_response.dig(path.to_s) parsed_response.dig(path.to_s) else parsed_response end rescue JSON::ParserError => _e response.body end def parse_cookie(set_cookie_str, key) value = nil expiry = nil set_cookie_str.each do |item| if item.include?(key) value = item.split('; ').first.split('=').last if item.include?('Expires') || item.include?('expires') item.split('; ').each do |sub_item| if sub_item.include?('Expires') || sub_item.include?('expires') expiry = Time.parse(sub_item.gsub('expires=', '').gsub('Expires=', '')) end end end end end [value, expiry] end def process_cookies # Cookies are always a single string separated by spaces raw_cookie.map { |item| item.split('; ').first }.join('; ') end def get_micro_second_time (Time.now.to_f * 1000000).to_i end def construct_base_path(path, params) constructed_path = "#{base_path}/#{path}" if params == {} constructed_path else "#{constructed_path}?#{process_params(params)}" end end def process_params(params) params.keys.map { |key| "#{key}=#{params[key]}" }.join('&') end end end