erp_integration 0.53.1 → 0.55.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94641315e041baf5dfa5ade8f7d4802f53bf70cff09f3fadd61b2232f2b541ca
4
- data.tar.gz: b32375c29eed88ee070426ea42465d36cb8c8dd309512a41cf54649fc956940d
3
+ metadata.gz: 865d21dfcb8c0caaed6bb6e76151e019424294a04c5a553c68459cbda871278b
4
+ data.tar.gz: 5da7b05a69bc74a8dcfa508ebe2b7b544e5902b26398b6da552fd139f20170f1
5
5
  SHA512:
6
- metadata.gz: 4e87ddefd4bc53228dd37ba8f6e1651d9f160401898f44f9d16b740b3e78d5aab6f1e2cc415b6ab31066393e7ac8babf272c17107bc7b113005f67ec5c8395c2
7
- data.tar.gz: 9c0f725849676df52f149df431367079cb9652afcb07ad55b295433e96124f251a292e91f98df08d70f9fe986b1da25b1a8e497d6ccfddb2a04bd8529fd801b4
6
+ metadata.gz: ea5e4f2a3f0e8bec393964d050ef9db305d1b7859032989d6899750306eba46efba5627e741fc469b95c215a23a42e6f6fcdee7f418f1ddf3da2f276cb201629
7
+ data.tar.gz: 71db26f1be6f150c53f34591d7ca46f7f89805fb65b5f44258c6dac86067bb8752feae8a08c11bf5cc4f6c096e7b3749b511583d1e3cf536921463e0d829e385
data/README.md CHANGED
@@ -26,7 +26,7 @@ To configure the gem, create an initializer and add the following lines:
26
26
  # config/initializers/erp_integration.rb
27
27
  ErpIntegration.configure do |config|
28
28
  config.fulfil_api_keys = '<your-api-key>'
29
- config.fulfil_merchant_id = '<your-merchant-id>'
29
+ config.fulfil_base_url = '<your-base-url>'
30
30
  end
31
31
  ```
32
32
 
@@ -35,7 +35,7 @@ You can configure multiple API keys, to enable rotation mechanism (see ["API key
35
35
  # config/initializers/erp_integration.rb
36
36
  ErpIntegration.configure do |config|
37
37
  config.fulfil_api_keys = ['<your-api-key1>', '<your-api-key2>']
38
- config.fulfil_merchant_id = '<your-merchant-id>'
38
+ config.fulfil_base_url = '<your-base-url>'
39
39
  end
40
40
  ```
41
41
 
@@ -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.
@@ -14,13 +14,13 @@ module ErpIntegration
14
14
  class Configuration
15
15
  # The `fulfil_api_keys` sets a single or a list of API keys to use
16
16
  # in the `FulfilClient` to authorize the requests to the Fulfil API endpoints.
17
- # @return [Array<Atring>] The API keys for Fulfil.
17
+ # @return [Array<String>] The API keys for Fulfil.
18
18
  attr_accessor :fulfil_api_keys
19
19
 
20
- # The `fulfil_merchant_id` is used by the `FulfilClient` to connect to
20
+ # The `fulfil_base_url` is used by the `FulfilClient` to connect to
21
21
  # the right Fulfil API endpoints.
22
- # @return [String] The merchant ID for Fulfil.
23
- attr_accessor :fulfil_merchant_id
22
+ # @return [String] The base URL for Fulfil.
23
+ attr_accessor :fulfil_base_url
24
24
 
25
25
  # Allows configuring an adapter for the `BillOfMaterial` resource. When
26
26
  # none is configured, it will default to Fulfil.
@@ -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.
@@ -28,7 +28,7 @@ module ErpIntegration
28
28
  def self.client
29
29
  Client.new(
30
30
  api_keys_pool: api_keys_pool,
31
- merchant_id: config.fulfil_merchant_id,
31
+ base_url: config.fulfil_base_url,
32
32
  logger: config.logger
33
33
  )
34
34
  end
@@ -3,21 +3,15 @@
3
3
  module ErpIntegration
4
4
  module Fulfil
5
5
  class Client
6
- attr_reader :api_keys_pool, :merchant_id
6
+ attr_reader :api_keys_pool, :base_url
7
7
  attr_writer :connection, :faraday_adapter, :rotate_statuses
8
8
 
9
- def initialize(api_keys_pool:, merchant_id:, logger: nil)
9
+ def initialize(api_keys_pool:, base_url:, logger: nil)
10
10
  @api_keys_pool = api_keys_pool
11
- @merchant_id = merchant_id
11
+ @base_url = base_url.strip
12
12
  @logger = logger
13
13
  end
14
14
 
15
- # Generates the url prefix for the Faraday connection client.
16
- # @return [String] The base url for the Fulfil HTTP client
17
- def base_url
18
- "https://#{merchant_id}.fulfil.io/"
19
- end
20
-
21
15
  # Sets the default adapter for the Faraday Connection.
22
16
  # @return [Symbol] The default Faraday adapter
23
17
  def faraday_adapter
@@ -50,11 +44,13 @@ module ErpIntegration
50
44
 
51
45
  %i[delete get patch put post].each do |action_name|
52
46
  define_method(action_name) do |path, options = {}|
53
- if api_key.nil? || merchant_id.nil?
54
- raise ErpIntegration::Error, 'The Fulfil API key and/or Merchant ID are missing.'
47
+ if api_key.nil? || base_url.nil?
48
+ raise ErpIntegration::Error, 'The Fulfil API key and/or base URL are missing.'
55
49
  end
56
50
 
57
- 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
58
54
  end
59
55
  end
60
56
 
@@ -70,6 +66,10 @@ module ErpIntegration
70
66
  api_keys_pool.current_key
71
67
  end
72
68
 
69
+ def rate_limiter
70
+ RateLimiter.find_by_api_key(api_key)
71
+ end
72
+
73
73
  def default_headers
74
74
  {
75
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.53.1'
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erp_integration
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.53.1
4
+ version: 0.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Vermaas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-30 00:00:00.000000000 Z
11
+ date: 2024-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -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