paypal-rest-api 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +320 -44
  3. data/VERSION +1 -1
  4. data/lib/paypal-api/access_token.rb +16 -2
  5. data/lib/paypal-api/api_collection.rb +29 -0
  6. data/lib/paypal-api/api_collections/authorized_payments.rb +4 -4
  7. data/lib/paypal-api/api_collections/captured_payments.rb +2 -2
  8. data/lib/paypal-api/api_collections/catalog_products.rb +2 -2
  9. data/lib/paypal-api/api_collections/disputes.rb +188 -0
  10. data/lib/paypal-api/api_collections/invoice_templates.rb +82 -0
  11. data/lib/paypal-api/api_collections/invoices.rb +196 -0
  12. data/lib/paypal-api/api_collections/orders.rb +7 -7
  13. data/lib/paypal-api/api_collections/payout_items.rb +47 -0
  14. data/lib/paypal-api/api_collections/payouts.rb +47 -0
  15. data/lib/paypal-api/api_collections/referenced_payout_items.rb +47 -0
  16. data/lib/paypal-api/api_collections/referenced_payouts.rb +47 -0
  17. data/lib/paypal-api/api_collections/refunds.rb +1 -1
  18. data/lib/paypal-api/api_collections/shipment_tracking.rb +2 -2
  19. data/lib/paypal-api/api_collections/subscription_plans.rb +106 -0
  20. data/lib/paypal-api/api_collections/subscriptions.rb +16 -98
  21. data/lib/paypal-api/api_collections/user_info.rb +47 -0
  22. data/lib/paypal-api/api_collections/users.rb +96 -0
  23. data/lib/paypal-api/api_collections/webhook_events.rb +78 -0
  24. data/lib/paypal-api/api_collections/webhook_lookups.rb +68 -0
  25. data/lib/paypal-api/api_collections/webhooks.rb +5 -108
  26. data/lib/paypal-api/client.rb +129 -5
  27. data/lib/paypal-api/config.rb +6 -2
  28. data/lib/paypal-api/request.rb +53 -21
  29. data/lib/paypal-api/request_executor.rb +81 -66
  30. data/lib/paypal-api/response.rb +42 -5
  31. data/lib/paypal-api/webhook_verifier/certs_cache.rb +75 -0
  32. data/lib/paypal-api/webhook_verifier.rb +104 -0
  33. data/lib/paypal-api.rb +120 -12
  34. metadata +17 -3
@@ -46,7 +46,7 @@ module PaypalAPI
46
46
  # @macro request
47
47
  #
48
48
  def show(webhook_id, query: nil, body: nil, headers: nil)
49
- client.get("/v1/notifications/webhooks/#{webhook_id}", query: query, body: body, headers: headers)
49
+ client.get("/v1/notifications/webhooks/#{encode(webhook_id)}", query: query, body: body, headers: headers)
50
50
  end
51
51
 
52
52
  #
@@ -58,7 +58,7 @@ module PaypalAPI
58
58
  # @macro request
59
59
  #
60
60
  def update(webhook_id, query: nil, body: nil, headers: nil)
61
- client.patch("/v1/notifications/webhooks/#{webhook_id}", query: query, body: body, headers: headers)
61
+ client.patch("/v1/notifications/webhooks/#{encode(webhook_id)}", query: query, body: body, headers: headers)
62
62
  end
63
63
 
64
64
  #
@@ -70,7 +70,7 @@ module PaypalAPI
70
70
  # @macro request
71
71
  #
72
72
  def delete(webhook_id, query: nil, body: nil, headers: nil)
73
- client.delete("/v1/notifications/webhooks/#{webhook_id}", query: query, body: body, headers: headers)
73
+ client.delete("/v1/notifications/webhooks/#{encode(webhook_id)}", query: query, body: body, headers: headers)
74
74
  end
75
75
 
76
76
  #
@@ -81,54 +81,8 @@ module PaypalAPI
81
81
  # @param webhook_id [String] Webhook ID
82
82
  # @macro request
83
83
  #
84
- def list_event_types(webhook_id, query: nil, body: nil, headers: nil)
85
- client.get("/v1/notifications/webhooks/#{webhook_id}/event-types", query: query, body: body, headers: headers)
86
- end
87
-
88
- #
89
- # Create webhook lookup
90
- #
91
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-lookup_post
92
- #
93
- # @macro request
94
- #
95
- def create_lookup(query: nil, body: nil, headers: nil)
96
- client.post("/v1/notifications/webhooks-lookup", query: query, body: body, headers: headers)
97
- end
98
-
99
- #
100
- # List webhook lookups
101
- #
102
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-lookup_list
103
- #
104
- # @macro request
105
- #
106
- def list_lookups(query: nil, body: nil, headers: nil)
107
- client.get("/v1/notifications/webhooks-lookup", query: query, body: body, headers: headers)
108
- end
109
-
110
- #
111
- # Show webhook lookup details
112
- #
113
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-lookup_get
114
- #
115
- # @param webhook_lookup_id [String] Webhook lookup ID
116
- # @macro request
117
- #
118
- def show_lookup(webhook_lookup_id, query: nil, body: nil, headers: nil)
119
- client.get("/v1/notifications/webhooks-lookup/#{webhook_lookup_id}", query: query, body: body, headers: headers)
120
- end
121
-
122
- #
123
- # Delete webhook lookup
124
- #
125
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-lookup_delete
126
- #
127
- # @param webhook_lookup_id [String] Webhook lookup ID
128
- # @macro request
129
- #
130
- def delete_lookup(webhook_lookup_id, query: nil, body: nil, headers: nil)
131
- client.delete("/v1/notifications/webhooks-lookup/#{webhook_lookup_id}", query: query, body: body, headers: headers)
84
+ def event_types(webhook_id, query: nil, body: nil, headers: nil)
85
+ client.get("/v1/notifications/webhooks/#{encode(webhook_id)}/event-types", query: query, body: body, headers: headers)
132
86
  end
133
87
 
134
88
  #
@@ -141,63 +95,6 @@ module PaypalAPI
141
95
  def verify(query: nil, body: nil, headers: nil)
142
96
  client.post("/v1/notifications/verify-webhook-signature", query: query, body: body, headers: headers)
143
97
  end
144
-
145
- #
146
- # Lists available events to which any webhook can subscribe
147
- #
148
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-event-types_list
149
- #
150
- # @macro request
151
- #
152
- def list_available_events(query: nil, body: nil, headers: nil)
153
- client.get("/v1/notifications/webhooks-event-types", query: query, body: body, headers: headers)
154
- end
155
-
156
- #
157
- # List event notifications
158
- #
159
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-events_list
160
- #
161
- # @macro request
162
- #
163
- def list_events(query: nil, body: nil, headers: nil)
164
- client.get("/v1/notifications/webhooks-events", query: query, body: body, headers: headers)
165
- end
166
-
167
- #
168
- # Show event notification details
169
- #
170
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-events_get
171
- #
172
- # @param event_id [String] Event ID
173
- # @macro request
174
- #
175
- def show_event(event_id, query: nil, body: nil, headers: nil)
176
- client.get("/v1/notifications/webhooks-events/#{event_id}", query: query, body: body, headers: headers)
177
- end
178
-
179
- #
180
- # Resend event notification
181
- #
182
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks-events_resend
183
- #
184
- # @param event_id [String] Event ID
185
- # @macro request
186
- #
187
- def resend_event(event_id, query: nil, body: nil, headers: nil)
188
- client.post("/v1/notifications/webhooks-events/#{event_id}/resend", query: query, body: body, headers: headers)
189
- end
190
-
191
- #
192
- # Simulate webhook event
193
- #
194
- # @see https://developer.paypal.com/docs/api/webhooks/v1/#simulate-event_post
195
- #
196
- # @macro request
197
- #
198
- def simulate_event(query: nil, body: nil, headers: nil)
199
- client.post("/v1/notifications/simulate-event", query: query, body: body, headers: headers)
200
- end
201
98
  end
202
99
 
203
100
  include APIs
@@ -5,9 +5,10 @@ module PaypalAPI
5
5
  # PaypalAPI Client
6
6
  #
7
7
  class Client
8
- attr_reader :config
8
+ attr_reader :config, :callbacks
9
9
 
10
10
  # Initializes Client
11
+ # @api public
11
12
  #
12
13
  # @param client_id [String] PayPal client id
13
14
  # @param client_secret [String] PayPal client secret
@@ -17,18 +18,49 @@ module PaypalAPI
17
18
  #
18
19
  # @return [Client] Initialized client
19
20
  #
20
- def initialize(client_id:, client_secret:, live: nil, http_opts: nil, retries: nil)
21
+ def initialize(client_id:, client_secret:, live: nil, http_opts: nil, retries: nil, cache: nil)
21
22
  @config = PaypalAPI::Config.new(
22
23
  client_id: client_id,
23
24
  client_secret: client_secret,
24
25
  live: live,
25
26
  http_opts: http_opts,
26
- retries: retries
27
+ retries: retries,
28
+ cache: cache
27
29
  )
28
30
 
31
+ @callbacks = {
32
+ before: [],
33
+ after_success: [],
34
+ after_fail: [],
35
+ after_network_error: []
36
+ }.freeze
37
+
29
38
  @access_token = nil
30
39
  end
31
40
 
41
+ # Registers callback
42
+ #
43
+ # @param callback_name [Symbol] Callback name.
44
+ # Allowed values: :before, :after_success, :after_faile, :after_network_error
45
+ #
46
+ # @param block [Proc] Block that must be call
47
+ # For `:before` callback proc should accept 2 params -
48
+ # request [Request], context [Hash]
49
+ #
50
+ # For `:after_success` callback proc should accept 3 params -
51
+ # request [Request], context [Hash], response [Response]
52
+ #
53
+ # For `:after_fail` callback proc should accept 3 params -
54
+ # request [Request], context [Hash], error [StandardError]
55
+ #
56
+ # For `:after_network_error` callback proc should accept 3 params -
57
+ # request [Request], context [Hash], response [Response]
58
+ #
59
+ # @return [void]
60
+ def add_callback(callback_name, &block)
61
+ callbacks.fetch(callback_name) << block
62
+ end
63
+
32
64
  #
33
65
  # Checks cached access token is expired and returns it or generates new one
34
66
  #
@@ -44,16 +76,48 @@ module PaypalAPI
44
76
  # @return [AccessToken] new AccessToken object
45
77
  #
46
78
  def refresh_access_token
79
+ requested_at = Time.now
47
80
  response = authentication.generate_access_token
48
81
 
49
82
  @access_token = AccessToken.new(
50
- requested_at: response.requested_at,
83
+ requested_at: requested_at,
51
84
  expires_in: response.fetch(:expires_in),
52
85
  access_token: response.fetch(:access_token),
53
86
  token_type: response.fetch(:token_type)
54
87
  )
55
88
  end
56
89
 
90
+ #
91
+ # Verifies Webhook
92
+ # It requires one-time request to download and cache certificate.
93
+ # If local verification returns false it tries to verify webhook online.
94
+ #
95
+ # @api public
96
+ # @example
97
+ #
98
+ # class Webhooks::PaypalController < ApplicationController
99
+ # def create
100
+ # webhook_id = ENV['PAYPAL_WEBHOOK_ID'] # PayPal registered webhook ID for current URL
101
+ # headers = request.headers # must be a Hash
102
+ # body = request.raw_post # must be a raw String body
103
+ #
104
+ # webhook_is_valid = PaypalAPI.verify_webhook(webhook_id: webhook_id, headers: headers, body: body)
105
+ # webhook_is_valid ? handle_webhook_event(body) : log_error(webhook_id, headers, body)
106
+ #
107
+ # head :no_content
108
+ # end
109
+ # end
110
+ #
111
+ # @param webhook_id [String] webhook_id provided by PayPal when webhook was registered
112
+ # @param headers [Hash,#[]] webhook request headers
113
+ # @param raw_body [String] webhook request raw body string
114
+ #
115
+ # @return [Boolean] webhook event is valid
116
+ #
117
+ def verify_webhook(webhook_id:, headers:, raw_body:)
118
+ WebhookVerifier.new(self).call(webhook_id: webhook_id, headers: headers, raw_body: raw_body)
119
+ end
120
+
57
121
  # @!macro [new] request
58
122
  # @param path [String] Request path
59
123
  # @param query [Hash, nil] Request query parameters
@@ -127,16 +191,51 @@ module PaypalAPI
127
191
  CatalogProducts.new(self)
128
192
  end
129
193
 
194
+ # @return [Disputes] Disputes APIs collection
195
+ def disputes
196
+ Disputes.new(self)
197
+ end
198
+
199
+ # @return [InvoiceTemplates] InvoiceTemplates APIs collection
200
+ def invoice_templates
201
+ InvoiceTemplates.new(self)
202
+ end
203
+
204
+ # @return [Invoices] Invoices APIs collection
205
+ def invoices
206
+ Invoices.new(self)
207
+ end
208
+
130
209
  # @return [Orders] Orders APIs collection
131
210
  def orders
132
211
  Orders.new(self)
133
212
  end
134
213
 
214
+ # @return [PayoutItems] PayoutItems APIs collection
215
+ def payout_items
216
+ PayoutItems.new(self)
217
+ end
218
+
219
+ # @return [Payouts] Payouts APIs collection
220
+ def payouts
221
+ Payouts.new(self)
222
+ end
223
+
135
224
  # @return [Redunds] Refunds APIs collection
136
225
  def refunds
137
226
  Refunds.new(self)
138
227
  end
139
228
 
229
+ # @return [ReferencedPayoutItems] ReferencedPayoutItems APIs collection
230
+ def referenced_payout_items
231
+ ReferencedPayoutItems.new(self)
232
+ end
233
+
234
+ # @return [ReferencedPayouts] ReferencedPayouts APIs collection
235
+ def referenced_payouts
236
+ ReferencedPayouts.new(self)
237
+ end
238
+
140
239
  # @return [ShipmentTracking] Shipment Tracking APIs collection
141
240
  def shipment_tracking
142
241
  ShipmentTracking.new(self)
@@ -147,16 +246,41 @@ module PaypalAPI
147
246
  Subscriptions.new(self)
148
247
  end
149
248
 
249
+ # @return [SubscriptionPlans] Subscription Plans APIs collection
250
+ def subscription_plans
251
+ SubscriptionPlans.new(self)
252
+ end
253
+
254
+ # @return [UserInfo] User Info APIs collection
255
+ def user_info
256
+ UserInfo.new(self)
257
+ end
258
+
259
+ # @return [Users] Users Management APIs collection
260
+ def users
261
+ Users.new(self)
262
+ end
263
+
150
264
  # @return [Webhooks] Webhooks APIs collection
151
265
  def webhooks
152
266
  Webhooks.new(self)
153
267
  end
154
268
 
269
+ # @return [WebhookEvents] Webhook Events APIs collection
270
+ def webhook_lookups
271
+ WebhookLookups.new(self)
272
+ end
273
+
274
+ # @return [WebhookEvents] Webhook Lookups APIs collection
275
+ def webhook_events
276
+ WebhookEvents.new(self)
277
+ end
278
+
155
279
  private
156
280
 
157
281
  def execute_request(http_method, path, query: nil, body: nil, headers: nil)
158
282
  request = Request.new(self, http_method, path, query: query, body: body, headers: headers)
159
- RequestExecutor.call(request)
283
+ RequestExecutor.new(self, request).call
160
284
  end
161
285
  end
162
286
  end
@@ -18,7 +18,7 @@ module PaypalAPI
18
18
  retries: {enabled: true, count: 3, sleep: [0.25, 0.75, 1.5].freeze}.freeze
19
19
  }.freeze
20
20
 
21
- attr_reader :client_id, :client_secret, :live, :http_opts, :retries
21
+ attr_reader :client_id, :client_secret, :live, :http_opts, :retries, :certs_cache
22
22
 
23
23
  # Initializes Config
24
24
  #
@@ -27,15 +27,19 @@ module PaypalAPI
27
27
  # @param live [Boolean] PayPal live/sandbox mode
28
28
  # @param http_opts [Hash] Net::Http opts for all requests
29
29
  # @param retries [Hash] Retries configuration
30
+ # @param cache [#read, nil] Application cache to store certificates to validate webhook events locally.
31
+ # Must respond to #read(key) and #write(key, expires_in: Integer)
30
32
  #
31
33
  # @return [Client] Initialized config object
32
34
  #
33
- def initialize(client_id:, client_secret:, live: nil, http_opts: nil, retries: nil)
35
+ def initialize(client_id:, client_secret:, live: nil, http_opts: nil, retries: nil, cache: nil)
34
36
  @client_id = client_id
35
37
  @client_secret = client_secret
36
38
  @live = with_default(:live, live)
37
39
  @http_opts = with_default(:http_opts, http_opts)
38
40
  @retries = with_default(:retries, retries)
41
+ @certs_cache = WebhookVerifier::CertsCache.new(cache)
42
+
39
43
  freeze
40
44
  end
41
45
 
@@ -19,8 +19,8 @@ module PaypalAPI
19
19
  # @return [Net::HTTPRequest] Generated Net::HTTPRequest
20
20
  attr_reader :http_request
21
21
 
22
- # @return [Time, nil] Time when request was sent
23
- attr_accessor :requested_at
22
+ # @return [Hash, nil, Object] Custom context that can be set/changed in callbacks
23
+ attr_accessor :context
24
24
 
25
25
  # rubocop:disable Metrics/ParameterLists
26
26
 
@@ -40,48 +40,80 @@ module PaypalAPI
40
40
  def initialize(client, request_type, path, body: nil, query: nil, headers: nil)
41
41
  @client = client
42
42
  @http_request = build_http_request(request_type, path, body: body, query: query, headers: headers)
43
- @requested_at = nil
43
+ @context = nil
44
44
  end
45
45
  # rubocop:enable Metrics/ParameterLists
46
46
 
47
+ # @return [String] HTTP request method name
48
+ def method
49
+ http_request.method
50
+ end
51
+
52
+ # @return [String] HTTP request method name
53
+ def path
54
+ http_request.path
55
+ end
56
+
57
+ # @return [URI] HTTP request URI
58
+ def uri
59
+ http_request.uri
60
+ end
61
+
62
+ # @return [String] HTTP request body
63
+ def body
64
+ http_request.body
65
+ end
66
+
67
+ # @return [Hash] HTTP request headers
68
+ def headers
69
+ http_request.each_header.to_h
70
+ end
71
+
47
72
  private
48
73
 
49
74
  def build_http_request(request_type, path, body:, query:, headers:)
50
- uri = request_uri(path, query)
51
- http_request = request_type.new(uri)
75
+ uri = build_http_uri(path, query)
76
+ http_request = request_type.new(uri, "accept-encoding" => nil)
52
77
 
53
- add_headers(http_request, headers || {})
54
- add_body(http_request, body)
78
+ build_http_headers(http_request, body, headers || {})
79
+ build_http_body(http_request, body)
55
80
 
56
81
  http_request
57
82
  end
58
83
 
59
- def add_headers(http_request, headers)
60
- headers.each { |key, value| http_request[key] = value }
84
+ def build_http_headers(http_request, body, headers)
85
+ headers = normalize_headers(headers)
86
+
87
+ unless headers.key?("authorization")
88
+ http_request["authorization"] = client.access_token.authorization_string
89
+ end
90
+
91
+ unless headers.key?("content-type")
92
+ http_request["content-type"] = "application/json" if body
93
+ end
61
94
 
62
- http_request["content-type"] ||= "application/json"
63
- http_request["authorization"] ||= client.access_token.authorization_string
64
- http_request["paypal-request-id"] ||= SecureRandom.uuid if idempotent?(http_request)
95
+ unless headers.key?("paypal-request-id")
96
+ http_request["paypal-request-id"] = SecureRandom.uuid unless http_request.is_a?(Net::HTTP::Get)
97
+ end
98
+
99
+ headers.each { |key, value| http_request[key] = value }
65
100
  end
66
101
 
67
- def add_body(http_request, body)
102
+ def build_http_body(http_request, body)
68
103
  return unless body
69
104
 
70
- json?(http_request) ? http_request.body = JSON.dump(body) : http_request.set_form_data(body)
105
+ is_json = http_request["content-type"].include?("json")
106
+ is_json ? http_request.body = JSON.dump(body) : http_request.set_form_data(body)
71
107
  end
72
108
 
73
- def request_uri(path, query)
109
+ def build_http_uri(path, query)
74
110
  uri = URI.join(client.config.url, path)
75
111
  uri.query = URI.encode_www_form(query) if query && !query.empty?
76
112
  uri
77
113
  end
78
114
 
79
- def idempotent?(http_request)
80
- http_request.method != Net::HTTP::Get::METHOD
81
- end
82
-
83
- def json?(http_request)
84
- http_request["content-type"].include?("json")
115
+ def normalize_headers(headers)
116
+ headers.empty? ? headers : headers.transform_keys { |key| key.to_s.downcase }
85
117
  end
86
118
  end
87
119
  end
@@ -5,91 +5,106 @@ module PaypalAPI
5
5
  # Executes PaypalAPI::Request and returns PaypalAPI::Response or raises PaypalAPI::Error
6
6
  #
7
7
  class RequestExecutor
8
- # List of Net::HTTP responses that must be retried
9
- RETRYABLE_RESPONSES = [
10
- Net::HTTPServerError, # 5xx
11
- Net::HTTPConflict, # 409
12
- Net::HTTPTooManyRequests # 429
13
- ].freeze
14
-
15
- class << self
16
- #
17
- # Executes prepared Request, handles retries and preparation of errors
18
- #
19
- # @param [Request] request
20
- #
21
- # @return [Response] Response
22
- #
23
- def call(request)
24
- http_response = execute(request)
25
- response = Response.new(http_response, requested_at: request.requested_at)
26
- raise FailedRequestErrorBuilder.call(request: request, response: response) unless http_response.is_a?(Net::HTTPSuccess)
8
+ attr_reader :client, :request, :http_opts, :context, :retries, :callbacks, :callbacks_context
9
+
10
+ def initialize(client, request)
11
+ @client = client
12
+ @request = request
13
+ @http_opts = {use_ssl: request.uri.is_a?(URI::HTTPS), **client.config.http_opts}
14
+ @retries = client.config.retries
15
+ @callbacks = client.callbacks
16
+ @callbacks_context = {retries_count: retries[:count]}
17
+ end
27
18
 
28
- response
29
- end
19
+ #
20
+ # Executes prepared Request, handles retries and preparation of errors
21
+ #
22
+ # @return [Response] Response
23
+ #
24
+ def call
25
+ response = execute_request
26
+ raise FailedRequestErrorBuilder.call(request: request, response: response) if response.failed?
27
+
28
+ response
29
+ end
30
+
31
+ private
30
32
 
31
- private
33
+ def execute_request(retry_number: 0)
34
+ callbacks_context[:retry_number] = retry_number
32
35
 
33
- def execute(request, retry_number: 0)
34
- http_response = execute_http_request(request)
35
- rescue *NetworkErrorBuilder::ERRORS => error
36
- retry_on_network_error(request, error, retry_number)
36
+ run_callbacks(:before)
37
+ response = execute_net_http_request
38
+ rescue *NetworkErrorBuilder::ERRORS => error
39
+ will_retry = !retries_limit_reached?(retry_number)
40
+ callbacks_context[:will_retry] = will_retry
41
+ run_callbacks(:after_network_error, error)
42
+ raise NetworkErrorBuilder.call(request: request, error: error) unless will_retry
43
+
44
+ retry_request(retry_number)
45
+ else
46
+ if response.success?
47
+ callbacks_context.delete(:will_retry)
48
+ run_callbacks(:after_success, response)
49
+ response
37
50
  else
38
- retryable?(request, http_response, retry_number) ? retry_request(request, retry_number) : http_response
51
+ will_retry = retryable?(response, retry_number)
52
+ callbacks_context[:will_retry] = will_retry
53
+ run_callbacks(:after_fail, response)
54
+ will_retry ? retry_request(retry_number) : response
39
55
  end
56
+ end
40
57
 
41
- def execute_http_request(request)
42
- http_request = request.http_request
43
- http_opts = request.client.config.http_opts
44
- uri = http_request.uri
45
- request.requested_at = Time.now
58
+ def execute_net_http_request
59
+ uri = request.uri
46
60
 
47
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, **http_opts) do |http|
61
+ http_response =
62
+ Net::HTTP.start(uri.hostname, uri.port, **http_opts) do |http|
48
63
  http.max_retries = 0 # we have custom retries logic
49
- http.request(http_request)
64
+ http.request(request.http_request)
50
65
  end
51
- end
52
66
 
53
- def retry_on_network_error(request, error, retry_number)
54
- raise NetworkErrorBuilder.call(request: request, error: error) if retries_limit_reached?(request, retry_number)
67
+ Response.new(http_response, request: request)
68
+ end
55
69
 
56
- retry_request(request, retry_number)
57
- end
70
+ def retry_request(current_retry_number)
71
+ sleep_time = retry_sleep_seconds(current_retry_number)
72
+ sleep(sleep_time) if sleep_time.positive?
73
+ execute_request(retry_number: current_retry_number + 1)
74
+ end
58
75
 
59
- def retry_request(request, current_retry_number)
60
- sleep(retry_sleep_seconds(request, current_retry_number))
61
- execute(request, retry_number: current_retry_number + 1)
62
- end
76
+ def retries_limit_reached?(retry_number)
77
+ retry_number >= retries[:count]
78
+ end
63
79
 
64
- def retries_limit_reached?(request, retry_number)
65
- retry_number >= request.client.config.retries[:count]
66
- end
80
+ def retry_sleep_seconds(current_retry_number)
81
+ seconds_per_retry = retries[:sleep]
82
+ seconds_per_retry[current_retry_number] || seconds_per_retry.last || 1
83
+ end
67
84
 
68
- def retry_sleep_seconds(request, current_retry_number)
69
- seconds_per_retry = request.client.config.retries[:sleep]
70
- seconds_per_retry[current_retry_number] || seconds_per_retry.last || 1
71
- end
85
+ def retryable?(response, retry_number)
86
+ response.failed? &&
87
+ !retries_limit_reached?(retry_number) &&
88
+ retryable_request?(response)
89
+ end
72
90
 
73
- def retryable?(request, http_response, retry_number)
74
- !http_response.is_a?(Net::HTTPSuccess) &&
75
- !retries_limit_reached?(request, retry_number) &&
76
- retryable_request?(request, http_response)
77
- end
91
+ def retryable_request?(response)
92
+ return true if response.retryable?
78
93
 
79
- def retryable_request?(request, http_response)
80
- return true if RETRYABLE_RESPONSES.any? { |retryable_class| http_response.is_a?(retryable_class) }
94
+ retry_unauthorized?(response)
95
+ end
81
96
 
82
- retry_unauthorized?(request, http_response)
83
- end
97
+ def retry_unauthorized?(response)
98
+ return false unless response.unauthorized? # 401
99
+ return false if request.path == Authentication::PATH # it's already an Authentication request
84
100
 
85
- def retry_unauthorized?(request, http_response)
86
- return false unless http_response.is_a?(Net::HTTPUnauthorized) # 401
87
- return false if http_response.uri.path == Authentication::PATH # it's already an Authentication request
101
+ # set new access-token
102
+ request.http_request["authorization"] = client.refresh_access_token.authorization_string
103
+ true
104
+ end
88
105
 
89
- # set new access-token
90
- request.http_request["authorization"] = request.client.refresh_access_token.authorization_string
91
- true
92
- end
106
+ def run_callbacks(callback_name, resp = nil)
107
+ callbacks[callback_name].each { |callback| callback.call(request, context, resp) }
93
108
  end
94
109
  end
95
110
  end