eppo-server-sdk 0.2.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'validation'
4
- require_relative 'assignment_logger'
3
+ require_relative "validation"
4
+ require_relative "assignment_logger"
5
5
 
6
6
  module EppoClient
7
7
  # The class for configuring the Eppo client singleton
8
8
  class Config
9
9
  attr_reader :api_key, :assignment_logger, :base_url
10
10
 
11
- def initialize(api_key, assignment_logger: AssignmentLogger.new, base_url: 'https://fscdn.eppo.cloud/api')
11
+ def initialize(api_key, assignment_logger: AssignmentLogger.new, base_url: EppoClient::Core::DEFAULT_BASE_URL)
12
12
  @api_key = api_key
13
13
  @assignment_logger = assignment_logger
14
14
  @base_url = base_url
15
15
  end
16
16
 
17
17
  def validate
18
- EppoClient.validate_not_blank('api_key', @api_key)
18
+ EppoClient.validate_not_blank("api_key", @api_key)
19
19
  end
20
20
 
21
21
  # Hide instance variables (specifically api_key) from logs
@@ -8,23 +8,6 @@ module EppoClient
8
8
  end
9
9
  end
10
10
 
11
- # A custom error class for unauthorized requests
12
- class UnauthorizedError < StandardError
13
- def initialize(message)
14
- super("Unauthorized: #{message}")
15
- end
16
- end
17
-
18
- # A custom error class for HTTP requests
19
- class HttpRequestError < StandardError
20
- attr_reader :status_code
21
-
22
- def initialize(message, status_code)
23
- @status_code = status_code
24
- super("HttpRequestError: #{message}")
25
- end
26
- end
27
-
28
11
  # A custom error class for invalid values
29
12
  class InvalidValueError < StandardError
30
13
  def initialize(message)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'custom_errors'
3
+ require_relative "custom_errors"
4
4
 
5
5
  # The helper module to validate keys
6
6
  module EppoClient
7
7
  module_function
8
8
 
9
9
  def validate_not_blank(field_name, field_value)
10
- (field_value.nil? || field_value == '') && raise(
10
+ (field_value.nil? || field_value == "") && raise(
11
11
  EppoClient::InvalidValueError, "#{field_name} cannot be blank"
12
12
  )
13
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EppoClient
4
- VERSION = '0.2.5'
4
+ VERSION = "3.0.0"
5
5
  end
data/lib/eppo_client.rb CHANGED
@@ -1,53 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'eppo_client/assignment_logger'
4
- require_relative 'eppo_client/http_client'
5
- require_relative 'eppo_client/poller'
6
- require_relative 'eppo_client/config'
7
- require_relative 'eppo_client/client'
8
- require_relative 'eppo_client/constants'
9
- require_relative 'eppo_client/configuration_requestor'
10
- require_relative 'eppo_client/configuration_store'
11
- require_relative 'eppo_client/version'
3
+ require_relative "eppo_client/client"
4
+ require_relative "eppo_client/version"
12
5
 
13
- # This module scopes all the client SDK's classes and functions
6
+ # EppoClient is the main module for initializing the Eppo client.
7
+ # It provides a method to initialize the client with a given configuration.
14
8
  module EppoClient
15
- # rubocop:disable Metrics/MethodLength
16
- def initialize_client(config_requestor, assignment_logger)
17
- client = EppoClient::Client.instance
18
- !client.poller.nil? && client.shutdown
19
- client.config_requestor = config_requestor
20
- client.assignment_logger = assignment_logger
21
- client.poller = EppoClient::Poller.new(
22
- EppoClient::POLL_INTERVAL_MILLIS,
23
- EppoClient::POLL_JITTER_MILLIS,
24
- proc { client.config_requestor.fetch_and_store_configurations }
25
- )
26
- client.poller.start
27
- client
28
- end
29
- # rubocop:enable Metrics/MethodLength
30
-
31
- # rubocop:disable Metrics/MethodLength
32
9
  def init(config)
33
- config.validate
34
- sdk_params = EppoClient::SdkParams.new(config.api_key, 'ruby',
35
- EppoClient::VERSION)
36
- http_client = EppoClient::HttpClient.new(config.base_url,
37
- sdk_params.formatted)
38
- config_store = EppoClient::ConfigurationStore.new(
39
- EppoClient::MAX_CACHE_ENTRIES
40
- )
41
- config_store.lock.with_write_lock do
42
- EppoClient.initialize_client(
43
- EppoClient::ExperimentConfigurationRequestor.new(
44
- http_client, config_store
45
- ),
46
- config.assignment_logger
47
- )
48
- end
10
+ client = EppoClient::Client.instance
11
+ client.init(config)
49
12
  end
50
- # rubocop:enable Metrics/MethodLength
51
13
 
52
- module_function :init, :initialize_client
14
+ module_function :init
53
15
  end
@@ -0,0 +1,96 @@
1
+ # EppoClient is the main module for initializing the Eppo client.
2
+ # It provides a method to initialize the client with a given configuration.
3
+ module EppoClient
4
+ def self.init: (Config config) -> void
5
+
6
+ # The base assignment logger class to override
7
+ class AssignmentLogger
8
+ def log_assignment: (untyped assignment_event) -> void
9
+
10
+ def log_bandit_action: (untyped assignment_event) -> void
11
+ end
12
+
13
+ # The main client singleton
14
+ class Client
15
+ @assignment_logger: AssignmentLogger
16
+ @core: Core::Client
17
+
18
+ include Singleton
19
+
20
+ attr_accessor assignment_logger: AssignmentLogger
21
+
22
+ def self.instance: () -> Client
23
+
24
+ def init: (Config config) -> void
25
+
26
+ def shutdown: () -> void
27
+
28
+ def get_string_assignment: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, String default_value) -> String
29
+
30
+ def get_numeric_assignment: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, Numeric default_value) -> Numeric
31
+
32
+ def get_integer_assignment: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, Integer default_value) -> Integer
33
+
34
+ def get_boolean_assignment: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, bool default_value) -> bool
35
+
36
+ def get_json_assignment: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, Object default_value) -> Object
37
+
38
+ def get_bandit_action: (String flag_key, String subject_key, Hash[String, untyped] subject_attributes, Hash[String, untyped] actions, String default_variation) -> { variation: untyped, action: untyped }
39
+
40
+ private
41
+
42
+ # rubocop:disable Metrics/MethodLength
43
+ def get_assignment_inner: (untyped flag_key, String subject_key, untyped subject_attributes, untyped expected_type, untyped default_value) -> untyped
44
+
45
+ def log_assignment: (untyped event) -> void
46
+
47
+ def log_bandit_action: (untyped event) -> void
48
+
49
+ def enrich_event_metadata: (untyped event) -> void
50
+
51
+ def coerce_context_attributes: (untyped attributes) -> untyped
52
+ end
53
+
54
+ # The class for configuring the Eppo client singleton
55
+ class Config
56
+ @api_key: String
57
+ @assignment_logger: AssignmentLogger
58
+ @base_url: String
59
+
60
+ attr_reader api_key: String
61
+ attr_reader assignment_logger: AssignmentLogger
62
+ attr_reader base_url: String
63
+
64
+ def initialize: (String api_key, ?assignment_logger: AssignmentLogger, ?base_url: String) -> void
65
+
66
+ def validate: () -> void
67
+
68
+ # Hide instance variables (specifically api_key) from logs
69
+ def inspect: () -> ::String
70
+ end
71
+
72
+ # A custom error class for AssignmentLogger
73
+ class AssignmentLoggerError < StandardError
74
+ def initialize: (String message) -> void
75
+ end
76
+
77
+ # A custom error class for invalid values
78
+ class InvalidValueError < StandardError
79
+ def initialize: (String message) -> void
80
+ end
81
+
82
+ def self?.validate_not_blank: (String field_name, String field_value) -> void
83
+
84
+ VERSION: String
85
+ end
86
+
87
+ # Exposed from Rust
88
+ module EppoClient::Core
89
+ DEFAULT_BASE_URL: String
90
+ class Client
91
+ def self.new: (untyped config) -> Client
92
+ def shutdown: () -> void
93
+ def get_assignment: (String flag_key, String subject_key, untyped subject_attributes, String expected_type) -> untyped
94
+ def get_bandit_action: (String flag_key, String subject_key, untyped attributes, untyped actions, String default_variation) -> untyped
95
+ end
96
+ end
metadata CHANGED
@@ -1,171 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eppo-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eppo
8
- autorequire:
9
- bindir: bin
8
+ autorequire:
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-21 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: concurrent-ruby
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.1'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 1.1.9
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.1'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 1.1.9
33
- - !ruby/object:Gem::Dependency
34
- name: faraday
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '2.7'
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: 2.7.1
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '2.7'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: 2.7.1
53
- - !ruby/object:Gem::Dependency
54
- name: faraday-retry
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '2.0'
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: 2.0.0
63
- type: :runtime
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - "~>"
68
- - !ruby/object:Gem::Version
69
- version: '2.0'
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- version: 2.0.0
73
- - !ruby/object:Gem::Dependency
74
- name: rake
75
- requirement: !ruby/object:Gem::Requirement
76
- requirements:
77
- - - "~>"
78
- - !ruby/object:Gem::Version
79
- version: '13.0'
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: 13.0.6
83
- type: :development
84
- prerelease: false
85
- version_requirements: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '13.0'
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- version: 13.0.6
93
- - !ruby/object:Gem::Dependency
94
- name: rspec
95
- requirement: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - "~>"
98
- - !ruby/object:Gem::Version
99
- version: '3.12'
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: 3.12.0
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '3.12'
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- version: 3.12.0
113
- - !ruby/object:Gem::Dependency
114
- name: rubocop
115
- requirement: !ruby/object:Gem::Requirement
116
- requirements:
117
- - - "~>"
118
- - !ruby/object:Gem::Version
119
- version: 0.82.0
120
- type: :development
121
- prerelease: false
122
- version_requirements: !ruby/object:Gem::Requirement
123
- requirements:
124
- - - "~>"
125
- - !ruby/object:Gem::Version
126
- version: 0.82.0
127
- - !ruby/object:Gem::Dependency
128
- name: webmock
129
- requirement: !ruby/object:Gem::Requirement
130
- requirements:
131
- - - "~>"
132
- - !ruby/object:Gem::Version
133
- version: '3.18'
134
- - - ">="
135
- - !ruby/object:Gem::Version
136
- version: 3.18.1
137
- type: :development
138
- prerelease: false
139
- version_requirements: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - "~>"
142
- - !ruby/object:Gem::Version
143
- version: '3.18'
144
- - - ">="
145
- - !ruby/object:Gem::Version
146
- version: 3.18.1
147
- description:
148
- email: eppo-team@geteppo.com
11
+ date: 2024-07-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - eppo-team@geteppo.com
149
16
  executables: []
150
- extensions: []
17
+ extensions:
18
+ - ext/eppo_client/Cargo.toml
151
19
  extra_rdoc_files: []
152
20
  files:
21
+ - ".gitignore"
22
+ - ".rspec"
23
+ - ".rubocop.yml"
24
+ - Cargo.lock
25
+ - Cargo.toml
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - Steepfile
30
+ - ext/eppo_client/Cargo.toml
31
+ - ext/eppo_client/build.rs
32
+ - ext/eppo_client/extconf.rb
33
+ - ext/eppo_client/src/client.rs
34
+ - ext/eppo_client/src/lib.rs
153
35
  - lib/eppo_client.rb
154
36
  - lib/eppo_client/assignment_logger.rb
155
37
  - lib/eppo_client/client.rb
156
38
  - lib/eppo_client/config.rb
157
- - lib/eppo_client/configuration_requestor.rb
158
- - lib/eppo_client/configuration_store.rb
159
- - lib/eppo_client/constants.rb
160
39
  - lib/eppo_client/custom_errors.rb
161
- - lib/eppo_client/http_client.rb
162
- - lib/eppo_client/lru_cache.rb
163
- - lib/eppo_client/poller.rb
164
- - lib/eppo_client/rules.rb
165
- - lib/eppo_client/shard.rb
166
40
  - lib/eppo_client/validation.rb
167
- - lib/eppo_client/variation_type.rb
168
41
  - lib/eppo_client/version.rb
42
+ - sig/eppo_server_sdk.rbs
169
43
  homepage: https://github.com/Eppo-exp/ruby-sdk
170
44
  licenses:
171
45
  - MIT
@@ -175,7 +49,7 @@ metadata:
175
49
  homepage_uri: https://geteppo.com/
176
50
  source_code_uri: https://github.com/Eppo-exp/ruby-sdk
177
51
  wiki_uri: https://github.com/Eppo-exp/ruby-sdk/wiki
178
- post_install_message:
52
+ post_install_message:
179
53
  rdoc_options: []
180
54
  require_paths:
181
55
  - lib
@@ -183,15 +57,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
57
  requirements:
184
58
  - - ">="
185
59
  - !ruby/object:Gem::Version
186
- version: 3.0.6
60
+ version: 3.0.0
187
61
  required_rubygems_version: !ruby/object:Gem::Requirement
188
62
  requirements:
189
63
  - - ">="
190
64
  - !ruby/object:Gem::Version
191
- version: '0'
65
+ version: 3.3.11
192
66
  requirements: []
193
- rubygems_version: 3.4.6
194
- signing_key:
67
+ rubygems_version: 3.5.9
68
+ signing_key:
195
69
  specification_version: 4
196
70
  summary: Eppo SDK for Ruby
197
71
  test_files: []
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'custom_errors'
4
- require_relative 'constants'
5
-
6
- module EppoClient
7
- # A class for the variation object
8
- class VariationDto
9
- attr_reader :name, :value, :typed_value, :shard_range
10
-
11
- def initialize(name, value, typed_value, shard_range)
12
- @name = name
13
- @value = value
14
- @typed_value = typed_value
15
- @shard_range = shard_range
16
- end
17
- end
18
-
19
- # A class for the allocation object
20
- class AllocationDto
21
- attr_reader :percent_exposure, :variations
22
-
23
- def initialize(percent_exposure, variations)
24
- @percent_exposure = percent_exposure
25
- @variations = variations
26
- end
27
- end
28
-
29
- # A class for the experiment configuration object
30
- class ExperimentConfigurationDto
31
- attr_reader :subject_shards, :enabled, :name, :overrides,
32
- :typed_overrides, :rules, :allocations
33
-
34
- def initialize(exp_config)
35
- @subject_shards = exp_config['subjectShards']
36
- @enabled = exp_config['enabled']
37
- @name = exp_config['name'] || nil
38
- @overrides = exp_config['overrides'] || {}
39
- @typed_overrides = exp_config['typedOverrides'] || {}
40
- @rules = exp_config['rules'] || []
41
- @allocations = exp_config['allocations']
42
- end
43
- end
44
-
45
- # A class for getting exp configs from the local cache or API
46
- class ExperimentConfigurationRequestor
47
- attr_reader :config_store
48
-
49
- def initialize(http_client, config_store)
50
- @http_client = http_client
51
- @config_store = config_store
52
- end
53
-
54
- def get_configuration(experiment_key)
55
- @http_client.is_unauthorized && raise(EppoClient::UnauthorizedError,
56
- 'please check your API key')
57
- @config_store.retrieve_configuration(experiment_key)
58
- end
59
-
60
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
61
- def fetch_and_store_configurations
62
- configs = {}
63
- begin
64
- exp_configs = @http_client.get(EppoClient::RAC_ENDPOINT).fetch(
65
- 'flags', {}
66
- )
67
- # rubocop: disable Metrics/BlockLength
68
- exp_configs.each do |exp_key, exp_config|
69
- exp_config['allocations'].each do |k, v|
70
- exp_config['allocations'][k] = EppoClient::AllocationDto.new(
71
- v['percentExposure'],
72
- v['variations'].map do |var|
73
- EppoClient::VariationDto.new(
74
- var['name'], var['value'], var['typedValue'],
75
- EppoClient::ShardRange.new(var['shardRange']['start'],
76
- var['shardRange']['end'])
77
- )
78
- end
79
- )
80
- end
81
- exp_config['rules'] = exp_config['rules'].map do |rule|
82
- EppoClient::Rule.new(
83
- conditions: rule['conditions'].map do |condition|
84
- EppoClient::Condition.new(
85
- value: condition['value'],
86
- operator: condition['operator'],
87
- attribute: condition['attribute']
88
- )
89
- end,
90
- allocation_key: rule['allocationKey']
91
- )
92
- end
93
- configs[exp_key] = EppoClient::ExperimentConfigurationDto.new(
94
- exp_config
95
- )
96
- end
97
- # rubocop: enable Metrics/BlockLength
98
- @config_store.assign_configurations(configs)
99
- rescue EppoClient::HttpRequestError => e
100
- Logger.new($stdout).error(
101
- "Error retrieving assignment configurations: #{e}"
102
- )
103
- end
104
- configs
105
- end
106
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
107
- end
108
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'concurrent/atomic/read_write_lock'
4
-
5
- require_relative 'lru_cache'
6
-
7
- module EppoClient
8
- # A thread safe store for the configurations to ensure that retrievals pull from a single source of truth
9
- class ConfigurationStore
10
- attr_reader :lock, :cache
11
-
12
- def initialize(max_size)
13
- @cache = EppoClient::LRUCache.new(max_size)
14
- @lock = Concurrent::ReadWriteLock.new
15
- end
16
-
17
- def retrieve_configuration(key)
18
- @lock.with_read_lock { @cache[key] }
19
- end
20
-
21
- def assign_configurations(configs)
22
- @lock.with_write_lock do
23
- # Create a temporary new cache and populate it.
24
- new_cache = EppoClient::LRUCache.new(@cache.size)
25
- configs.each do |key, config|
26
- new_cache[key] = config
27
- end
28
-
29
- # Replace the old cache with the new one.
30
- # Performs an atomic swap.
31
- @cache = new_cache
32
- end
33
- end
34
- end
35
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger'
4
-
5
- module EppoClient
6
- # default level for logging
7
- DEFAULT_LOGGER_LEVEL = Logger::INFO
8
-
9
- # configuration cache constants
10
- MAX_CACHE_ENTRIES = 1000 # arbitrary; the caching library requires a max limit
11
-
12
- # poller constants
13
- SECOND_MILLIS = 1000
14
- MINUTE_MILLIS = 60 * SECOND_MILLIS
15
- POLL_JITTER_MILLIS = 30 * SECOND_MILLIS
16
- POLL_INTERVAL_MILLIS = 5 * MINUTE_MILLIS
17
-
18
- # the configs endpoint
19
- RAC_ENDPOINT = 'randomized_assignment/v3/config'
20
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday'
4
- require 'faraday/retry'
5
-
6
- require_relative 'custom_errors'
7
-
8
- REQUEST_TIMEOUT_SECONDS = 2
9
- # This applies only to failed DNS lookups and connection timeouts,
10
- # never to requests where data has made it to the server.
11
- MAX_RETRIES = 3
12
-
13
- module EppoClient
14
- # The SDK params object
15
- class SdkParams
16
- attr_reader :api_key, :sdk_name, :sdk_version
17
-
18
- def initialize(api_key, sdk_name, sdk_version)
19
- @api_key = api_key
20
- @sdk_name = sdk_name
21
- @sdk_version = sdk_version
22
- end
23
-
24
- # attributes are camelCase because that's what the backend endpoint expects
25
- def formatted
26
- {
27
- 'apiKey' => api_key,
28
- 'sdkName' => sdk_name,
29
- 'sdkVersion' => sdk_version
30
- }
31
- end
32
-
33
- # Hide instance variables (specifically api_key) from logs
34
- def inspect
35
- "#<EppoClient::SdkParams:#{object_id}>"
36
- end
37
- end
38
-
39
- # The http request client with retry/timeout behavior
40
- class HttpClient
41
- attr_reader :is_unauthorized
42
-
43
- @retry_options = {
44
- max: MAX_RETRIES,
45
- interval: 0.05,
46
- interval_randomness: 0.5,
47
- backoff_factor: 2,
48
- exceptions: ['Timeout::Error']
49
- }
50
-
51
- def initialize(base_url, sdk_params)
52
- @base_url = base_url
53
- @sdk_params = sdk_params
54
- @is_unauthorized = false
55
- end
56
-
57
- def get(resource)
58
- conn = Faraday::Connection.new(@base_url, params: @sdk_params) do |f|
59
- f.request :retry, @retry_options
60
- end
61
- conn.options.timeout = REQUEST_TIMEOUT_SECONDS
62
- response = conn.get(resource)
63
- @is_unauthorized = response.status == 401
64
- raise get_http_error(response.status, resource) if response.status != 200
65
-
66
- JSON.parse(response.body)
67
- end
68
-
69
- private
70
-
71
- def get_http_error(status_code, resource)
72
- EppoClient::HttpRequestError.new("HTTP #{status_code} error while requesting resource #{resource}", status_code)
73
- end
74
- end
75
- end