-
Notifications
You must be signed in to change notification settings - Fork 474
/
http_client.rb
100 lines (77 loc) · 3.52 KB
/
http_client.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# typed: strict
# frozen_string_literal: true
module ShopifyAPI
module Clients
class HttpClient
extend T::Sig
RETRY_WAIT_TIME = 1
sig { params(base_path: String, session: T.nilable(Auth::Session)).void }
def initialize(base_path:, session: nil)
session ||= Context.active_session
raise Errors::NoActiveSessionError, "No passed or active session" unless session
@base_uri = T.let("https://#{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])
unless session.access_token.nil? || T.must(session.access_token).empty?
@headers["X-Shopify-Access-Token"] = T.cast(session.access_token, String)
end
end
sig { params(request: HttpRequest).returns(HttpResponse) }
def request(request)
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
tries = 0
response = HttpResponse.new(code: 0, headers: {}, body: "")
while tries < request.tries
tries += 1
res = T.cast(HTTParty.send(
request.http_method,
request_url(request),
headers: headers,
query: request.query,
body: request.body.class == Hash ? T.unsafe(request.body).to_json : request.body,
), HTTParty::Response)
body = res.body.empty? ? {} : JSON.parse(res.body)
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_messages = []
error_messages << response.body["errors"] if response.body["errors"]
if response.headers["x-request-id"]
id = T.must(response.headers["x-request-id"])[0]
error_messages << "If you report this error, please include this id: #{id}."
end
error_message = error_messages.join("\n")
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
sig { params(request: HttpRequest).returns(String) }
def request_url(request)
"#{@base_uri_and_path}/#{request.path}"
end
end
end
end