class ShopifyAPI::Clients::HttpClient
Constants
- RETRY_WAIT_TIME
Public Class Methods
Source
# File lib/shopify_api/clients/http_client.rb, line 12 def initialize(base_path:, session: nil) session ||= Context.active_session raise Errors::NoActiveSessionError, "No passed or active session" unless session api_host = Context.api_host @base_uri = T.let("https://#{api_host || session.shop}", String) @base_uri_and_path = T.let("#{@base_uri}#{base_path}", String) user_agent_prefix = Context.user_agent_prefix.nil? ? "" : "#{Context.user_agent_prefix} | " @headers = T.let({ "User-Agent": "#{user_agent_prefix}Shopify API Library v#{VERSION} | Ruby #{RUBY_VERSION}", "Accept": "application/json", }, T::Hash[T.any(Symbol, String), T.untyped]) @headers["Host"] = session.shop unless api_host.nil? unless session.access_token.nil? || T.must(session.access_token).empty? @headers["X-Shopify-Access-Token"] = T.cast(session.access_token, String) end end
Public Instance Methods
Source
# File lib/shopify_api/clients/http_client.rb, line 36 def request(request, response_as_struct: false) request.verify headers = @headers headers["Content-Type"] = T.must(request.body_type) if request.body_type headers = headers.merge(T.must(request.extra_headers)) if request.extra_headers parsed_uri = URI(request_url(request)) headers = append_first_party_development_headers(headers, parsed_uri) tries = 0 response = HttpResponse.new(code: 0, headers: {}, body: "") while tries < request.tries tries += 1 res = T.cast(HTTParty.send( request.http_method, parsed_uri.to_s, headers: headers, query: request.query, body: request.body.class == Hash ? T.unsafe(request.body).to_json : request.body, ), HTTParty::Response) begin body = res.body.nil? || res.body.empty? ? {} : JSON.parse(res.body) rescue JSON::ParserError raise if res.code.to_i < 500 body = res.body end if response_as_struct && body.is_a?(Hash) json_body = body.to_json body = JSON.parse(json_body, object_class: OpenStruct) end response = HttpResponse.new(code: res.code.to_i, headers: res.headers.to_h, body: body) if response.headers["x-shopify-api-deprecated-reason"] reason = T.must(response.headers["x-shopify-api-deprecated-reason"])[0] Context.logger.warn("Deprecated request to Shopify API at #{request.path}, received reason: #{reason}") end break if response.ok? error_message = serialized_error(response) unless [429, 500].include?(response.code) raise ShopifyAPI::Errors::HttpResponseError.new(response: response), error_message end if tries == request.tries raise ShopifyAPI::Errors::HttpResponseError.new(response: response), error_message if request.tries == 1 raise ShopifyAPI::Errors::MaxHttpRetriesExceededError.new(response: response), "Exceeded maximum retry count of #{request.tries}. Last message: #{error_message}" end if response.code == 500 || response.headers["retry-after"].nil? sleep(RETRY_WAIT_TIME) else sleep(T.must(response.headers["retry-after"])[0].to_i) end end response end
Protected Instance Methods
Source
# File lib/shopify_api/clients/http_client.rb, line 107 def request_url(request) "#{@base_uri_and_path}/#{request.path}" end
Source
# File lib/shopify_api/clients/http_client.rb, line 112 def serialized_error(response) body = {} body["errors"] = response.body["errors"] if response.body["errors"] if response.headers["x-request-id"] id = T.must(response.headers["x-request-id"])[0] body["error_reference"] = "If you report this error, please include this id: #{id}." end body.to_json end
Private Instance Methods
Source
# File lib/shopify_api/clients/http_client.rb, line 131 def append_first_party_development_headers(headers, parsed_uri) return headers unless defined?(DevServer) return headers unless headers["Host"]&.include?(".my.shop.dev") || parsed_uri.host&.include?(".my.shop.dev") return headers if headers["x-forwarded-host"]&.include?(".my.shop.dev") # These headers are only used for first party applications in development mode headers["x-forwarded-host"] = headers["Host"] || parsed_uri.host headers["Host"] = T.unsafe( Object.const_get("DevServer::Core"), # rubocop:disable Sorbet/ConstantsFromStrings ).new.host!(:app) headers end