erp_integration 0.54.0 → 0.55.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 015dee8d29c0a8bcd763966f25c24b77efb9cd2e7ac4a1555aa104e9a708c2ec
4
- data.tar.gz: 92f5a27785677904bd7b0cd60fc5a033195ed99d64d9c16941fbb4d942db8b31
3
+ metadata.gz: 865d21dfcb8c0caaed6bb6e76151e019424294a04c5a553c68459cbda871278b
4
+ data.tar.gz: 5da7b05a69bc74a8dcfa508ebe2b7b544e5902b26398b6da552fd139f20170f1
5
5
  SHA512:
6
- metadata.gz: 526e86fb610c85d0b5c69f36e2cf1b1c396ff36e6f74c2db6d9764eae9eb1f076d9ed7771b5347ac3060bbdbd047777c1901c427351a280bcb26bf49b6edb269
7
- data.tar.gz: f876d9874077923090353f7e2c9d6766cae725fe1558345ba98f4d5cfc07495bb272f451205f04c6a957402fdf10bc756d69ad8792a55405c91a07ce856d5e4e
6
+ metadata.gz: ea5e4f2a3f0e8bec393964d050ef9db305d1b7859032989d6899750306eba46efba5627e741fc469b95c215a23a42e6f6fcdee7f418f1ddf3da2f276cb201629
7
+ data.tar.gz: 71db26f1be6f150c53f34591d7ca46f7f89805fb65b5f44258c6dac86067bb8752feae8a08c11bf5cc4f6c096e7b3749b511583d1e3cf536921463e0d829e385
data/README.md CHANGED
@@ -61,6 +61,47 @@ ErpIntegration::SalesOrder.api_keys_pool = '<your-api-key>'
61
61
  ErpIntegration::SalesOrder.api_keys_pool = ['<your-api-key1>', '<your-api-key2>']
62
62
  ```
63
63
 
64
+ ### Rate limiting
65
+
66
+ To manage the number of requests made to avoid rate limiting by the API provider, you can configure rate limiters in the ERP Integration gem. Each rate limiter should implement the `api_key_fragment` and `within_limit` methods.
67
+
68
+ ### Setting Up Rate Limiters
69
+ You can configure multiple rate limiters to be used for HTTP operations on the client.
70
+
71
+ ```ruby
72
+ # config/initializers/erp_integration.rb
73
+ ErpIntegration.configure do |config|
74
+ config.rate_limiters = [
75
+ MyRateLimiter.new(api_key_fragment: 'key1'),
76
+ MyRateLimiter.new(api_key_fragment: 'key2')
77
+ ]
78
+ end
79
+ ```
80
+
81
+ Each rate limiter must implement the following methods:
82
+ - `api_key_fragment`: This method should return a string that is used to identify the rate limiter by the API key.
83
+ - `within_limit`: This method should `yield` to the block if the rate limit is not exceeded.
84
+
85
+ ### Example Rate Limiter
86
+ Here is an example implementation of a rate limiter:
87
+
88
+ ```ruby
89
+ class MyRateLimiter
90
+ def initialize(api_key_fragment:)
91
+ @api_key_fragment = api_key_fragment
92
+ end
93
+
94
+ def api_key_fragment
95
+ @api_key_fragment
96
+ end
97
+
98
+ def within_limit
99
+ # Implement your rate limiting logic here
100
+ yield
101
+ end
102
+ end
103
+ ```
104
+
64
105
  ### Supported Query Methods
65
106
 
66
107
  After configuring the gem, one can easily query all the available ERP resources from the connected third-parties. In all cases, the API will return a collection of resources.
@@ -299,6 +299,18 @@ module ErpIntegration
299
299
  def product_option_adapter
300
300
  @product_option_adapter || :fulfil
301
301
  end
302
+
303
+ # Rate limiters that will be used for HTTP operations on Client
304
+ # to manage the number of requests made to avoid rate limiting by the API provider.
305
+ def rate_limiters
306
+ @rate_limiters ||= []
307
+ end
308
+
309
+ # Sets the rate limiters that will be used
310
+ # @raise [MissingMethodError] if the rate limiter object doesn't respond to the required methods.
311
+ def rate_limiters=(rate_limiters)
312
+ @rate_limiters = rate_limiters.each { |limiter| RateLimiter.validate!(limiter) }
313
+ end
302
314
  end
303
315
 
304
316
  # Returns ERP Integration's configuration.
@@ -48,7 +48,9 @@ module ErpIntegration
48
48
  raise ErpIntegration::Error, 'The Fulfil API key and/or base URL are missing.'
49
49
  end
50
50
 
51
- connection.public_send(action_name, "api/#{version}/#{path}", options).body
51
+ rate_limiter.within_limit do
52
+ connection.public_send(action_name, "api/#{version}/#{path}", options).body
53
+ end
52
54
  end
53
55
  end
54
56
 
@@ -64,6 +66,10 @@ module ErpIntegration
64
66
  api_keys_pool.current_key
65
67
  end
66
68
 
69
+ def rate_limiter
70
+ RateLimiter.find_by_api_key(api_key)
71
+ end
72
+
67
73
  def default_headers
68
74
  {
69
75
  'Accept': 'application/json',
@@ -43,6 +43,8 @@ module ErpIntegration
43
43
  @formatter.exception(exc) if @formatter.respond_to?(:exception)
44
44
  end
45
45
 
46
+ # @param [String] api_key
47
+ # @return [String] The last 4 characters of the API key
46
48
  def sanitize_api_key(api_key)
47
49
  api_key[-4..-1] if api_key
48
50
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErpIntegration
4
+ class RateLimiter
5
+ class MissingMethodError < StandardError; end
6
+ # The `api_key_fragment` method should return a string that is used to identify
7
+ # the rate limiter by the API key.
8
+ #
9
+ # The `within_limit` method should yield to the block if the rate limit is not exceeded.
10
+ REQUIRED_METHODS = %i[api_key_fragment within_limit].freeze
11
+
12
+ class << self
13
+ # Validates that the rate limiter object responds to the required methods.
14
+ # It requires the `api_key_fragment` and `within_limit` methods to be present.
15
+ #
16
+ # @raise [MissingMethodError]
17
+ # @param rate_limiter [Object]
18
+ def validate!(rate_limiter)
19
+ REQUIRED_METHODS.each do |method|
20
+ next if rate_limiter.respond_to?(method)
21
+
22
+ raise MissingMethodError, "'#{rate_limiter.class}##{method}' method is required."
23
+ end
24
+ end
25
+
26
+ # Finds the rate limiter by the API key.
27
+ # If the API key is not found, it returns an unlimited rate limiter.
28
+ #
29
+ # @param api_key [String]
30
+ # @return [Object] The rate limiter object.
31
+ def find_by_api_key(api_key)
32
+ rate_limiters[api_key] || unlimited
33
+ end
34
+
35
+ # Returns an unlimited rate limiter.
36
+ #
37
+ # @return [RateLimiter::Unlimited] The unlimited rate limiter object.
38
+ def unlimited
39
+ @unlimited ||= Unlimited.new
40
+ end
41
+
42
+ private
43
+
44
+ # The `rate_limiters` hash stores the rate limiter objects found by the API key.
45
+ #
46
+ # @return [Hash] The rate limiters hash.
47
+ def rate_limiters
48
+ @rate_limiters ||= Hash.new do |h, api_key|
49
+ h[api_key] = ErpIntegration.config.rate_limiters.find do |rate_limiter|
50
+ api_key.end_with?(rate_limiter.api_key_fragment)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # {Unlimited} is a rate limiter that allows all requests.
57
+ # When the rate limiter wasn't found by the API key, it defaults to this class.
58
+ # Or if the `rate_limiters` option is not set in the configuration.
59
+ class Unlimited
60
+ # Executes the block without any restrictions.
61
+ def within_limit
62
+ yield
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ErpIntegration
4
- VERSION = '0.54.0'
4
+ VERSION = '0.55.0'
5
5
  end
@@ -11,6 +11,7 @@ require 'json'
11
11
  require_relative 'erp_integration/version'
12
12
  require_relative 'erp_integration/errors'
13
13
  require_relative 'erp_integration/api_keys_pool'
14
+ require_relative 'erp_integration/rate_limiter'
14
15
  require_relative 'erp_integration/configuration'
15
16
 
16
17
  # Middleware
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erp_integration
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.54.0
4
+ version: 0.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Vermaas
@@ -329,6 +329,7 @@ files:
329
329
  - lib/erp_integration/purchase_order.rb
330
330
  - lib/erp_integration/purchase_order_line.rb
331
331
  - lib/erp_integration/purchase_request.rb
332
+ - lib/erp_integration/rate_limiter.rb
332
333
  - lib/erp_integration/resource.rb
333
334
  - lib/erp_integration/resources/errors.rb
334
335
  - lib/erp_integration/resources/persistence.rb
@@ -368,7 +369,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
368
369
  - !ruby/object:Gem::Version
369
370
  version: '0'
370
371
  requirements: []
371
- rubygems_version: 3.5.11
372
+ rubygems_version: 3.2.22
372
373
  signing_key:
373
374
  specification_version: 4
374
375
  summary: Connects Mejuri with third-party ERP vendors