promoted-ruby-client 0.1.15 → 0.1.16

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: 99ee2c76dc1547104c6e40f5b05c1b7897fb609108d3009c686236ef5646f33b
4
- data.tar.gz: ff7dc6ed3901dac8112564687353c4942352b044d7b158ce2e9bd554843d777a
3
+ metadata.gz: a2c2ac1232cfaf7a98ca948a7949a420a6ab311c4b54d92ea8098e8b830e931b
4
+ data.tar.gz: 9ae8796dc993ecc11e01f1980a2200a7ee10dedb8287504a2bafcebec2f50105
5
5
  SHA512:
6
- metadata.gz: f7d4ab79296c610db92af6ba93d86023f254fef6d59753e991df0915b028b303a505e11cdce623345f1bc78414f30ecd72f8c237e3f2eb87a93f7c4b331ae690
7
- data.tar.gz: b612ddec96289bffe5b6eb2e9dff2b58f103d2008d53e8ff91069f2b6b13d24a179a25fbbe99315ea29b69cf227ad862da1eeb6204a05a288ab6a6caa0f23c3b
6
+ metadata.gz: d3df3068c668514c3666435fc1a4b25216223258fd305ba69e4cebf333f5026ff008f868a4bd7f3a15e748bd9b00ddb6dfbb9e0c0de7c72a9ed06ad4a175ddbe
7
+ data.tar.gz: 7adb59ff3806736008764d32a51176e1a064547259c8f4d7c9d800b4737e688d00b25593c07e5b2ecffc05daad1795830d17efb3f66284d6fc8f02b5045dd1f8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- promoted-ruby-client (0.1.15)
4
+ promoted-ruby-client (0.1.16)
5
5
  concurrent-ruby (~> 1)
6
6
  faraday (>= 0.9.0)
7
7
  faraday_middleware (>= 0.9.0)
data/README.md CHANGED
@@ -17,6 +17,8 @@ More information at [http://www.promoted.ai](http://www.promoted.ai)
17
17
 
18
18
  ### [Faraday](https://github.com/lostisland/faraday)
19
19
  HTTP client for calling Promoted.
20
+ ### [Net::HTTP::Persistent](https://github.com/drbrain/net-http-persistent)
21
+ Faraday binding (provides connection pool support)
20
22
  ### [Concurrent Ruby](https://github.com/ruby-concurrency/concurrent-ruby)
21
23
  Provides a thread pool for making shadow traffic requests to Delivery API in the background on a subset of calls to ```prepare_for_logging```
22
24
  ## Creating a Client
@@ -50,6 +52,7 @@ Name | Type | Description
50
52
  ```:default_request_headers``` | Hash | Additional headers to send on the request beyond ```x-api-key```. Defaults to {}
51
53
  ```:default_only_log``` | Boolean | If true, the ```deliver``` method will not direct traffic to Delivery API but rather return a request suitable for logging. Defaults to false.
52
54
  ```:should_apply_treatment_func``` | Proc | Called during delivery, accepts an experiment and returns a Boolean indicating whether the request should be considered part of the control group (false) or in the experiment (true). If nil, the default behavior of checking the experiement ```:arm``` is applied.
55
+ ```:warmup``` | Boolean | If true, the client will prime the `Net::HTTP::Persistent` connection pool on construction; this can make the first few calls to Promoted complete faster. Defaults to false.
53
56
 
54
57
  ## Data Types
55
58
 
data/dev.md CHANGED
@@ -4,5 +4,5 @@
4
4
  2. Get credentials for deployment from 1password.
5
5
  3. Modify `promoted-ruby-client.gemspec`'s push block.
6
6
  4. Run `gem build promoted-ruby-client.gemspec` to generate `gem`.
7
- 5. Run (using new output) `gem push promoted-ruby-client-0.1.15.gem`
7
+ 5. Run (using new output) `gem push promoted-ruby-client-0.1.16.gem`
8
8
  6. Update README with new version.
@@ -73,7 +73,7 @@ module Promoted
73
73
  @delivery_timeout_millis = params[:delivery_timeout_millis] || DEFAULT_DELIVERY_TIMEOUT_MILLIS
74
74
  @metrics_timeout_millis = params[:metrics_timeout_millis] || DEFAULT_METRICS_TIMEOUT_MILLIS
75
75
 
76
- @http_client = FaradayHTTPClient.new
76
+ @http_client = FaradayHTTPClient.new(@logger)
77
77
  @validator = Promoted::Ruby::Client::Validator.new
78
78
 
79
79
  @async_shadow_traffic = true
@@ -97,6 +97,10 @@ module Promoted
97
97
  if params[:enabled] != nil
98
98
  @enabled = params[:enabled] || false
99
99
  end
100
+
101
+ if params[:warmup]
102
+ do_warmup
103
+ end
100
104
  end
101
105
 
102
106
  ##
@@ -109,7 +113,7 @@ module Promoted
109
113
  end
110
114
 
111
115
  ##
112
- # Make a delivery request.
116
+ # Make a delivery request. If @perform_checks is set, input validation will occur and possibly raise errors.
113
117
  def deliver args, headers={}
114
118
  args = Promoted::Ruby::Client::Util.translate_args(args)
115
119
 
@@ -124,7 +128,16 @@ module Promoted
124
128
  delivery_request_builder = RequestBuilder.new
125
129
  delivery_request_builder.set_request_params(args)
126
130
 
127
- perform_common_checks!(args) if @perform_checks
131
+ # perform_checks raises errors.
132
+ if @perform_checks
133
+ perform_common_checks!(args)
134
+
135
+ if args[:insertion_page_type] == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED'] then
136
+ err = DeliveryInsertionPageType.new
137
+ @logger.error(err) if @logger
138
+ raise err
139
+ end
140
+ end
128
141
 
129
142
  delivery_request_builder.ensure_client_timestamp
130
143
 
@@ -135,10 +148,15 @@ module Promoted
135
148
  only_log = delivery_request_builder.only_log != nil ? delivery_request_builder.only_log : @default_only_log
136
149
  deliver_err = false
137
150
 
138
- if !@pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
151
+ begin
152
+ @pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
153
+ rescue InvalidPagingError => err
139
154
  # Invalid input, log and do SDK-side delivery.
140
- @logger.warn("Invalid paging parameters") if @logger
141
- only_log = true
155
+ @logger.warn(err) if @logger
156
+ return {
157
+ insertion: err.default_insertions_page
158
+ # No log request returned when no response insertions due to invalid paging
159
+ }
142
160
  end
143
161
 
144
162
  if !only_log
@@ -248,6 +266,22 @@ module Promoted
248
266
 
249
267
  private
250
268
 
269
+ def do_warmup
270
+ if !@delivery_endpoint
271
+ # Warmup only supported when delivery is enabled.
272
+ return
273
+ end
274
+
275
+ warmup_url = @delivery_endpoint.reverse.sub("/deliver".reverse, "/healthz".reverse).reverse
276
+ @logger.info("Warming up at #{warmup_url}") if @logger
277
+ 1.upto(20) do
278
+ resp = @http_client.get(warmup_url)
279
+ if resp != "ok"
280
+ @logger.warn("Got a failure warming up") if @logger
281
+ end
282
+ end
283
+ end
284
+
251
285
  def send_request payload, endpoint, timeout_millis, api_key, headers={}, send_async=false
252
286
  resp = nil
253
287
 
@@ -305,8 +339,9 @@ module Promoted
305
339
 
306
340
  # Call Delivery API and log/ignore errors.
307
341
  start_time = Time.now
342
+ response = nil
308
343
  begin
309
- send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
344
+ response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
310
345
  rescue StandardError => err
311
346
  @logger.warn("Shadow traffic call failed with #{err}") if @logger
312
347
  return
@@ -314,7 +349,8 @@ module Promoted
314
349
 
315
350
  if !@async_shadow_traffic
316
351
  ellapsed_time = Time.now - start_time
317
- @logger.info("Shadow traffic call completed in #{ellapsed_time.to_f * 1000} ms") if @logger
352
+ insertions = response ? response[:insertion] : []
353
+ @logger.info("Shadow traffic call completed in #{ellapsed_time.to_f * 1000} ms with #{insertions.length} insertions") if @logger
318
354
  end
319
355
  end
320
356
 
@@ -7,6 +7,12 @@ module Promoted
7
7
  end
8
8
  end
9
9
 
10
+ class DeliveryInsertionPageType < StandardError
11
+ def message
12
+ 'Delivery insertions must be unpaged'
13
+ end
14
+ end
15
+
10
16
  class EndpointError < StandardError
11
17
  attr_reader :cause
12
18
  def initialize(cause)
@@ -6,10 +6,13 @@ module Promoted
6
6
  module Client
7
7
  class FaradayHTTPClient
8
8
 
9
- def initialize
9
+ def initialize(logger = nil)
10
10
  @conn = Faraday.new do |f|
11
11
  f.request :json
12
12
  f.request :retry, max: 3
13
+ if logger
14
+ f.response :logger, logger, { headers: false, bodies: true, log_level: :debug }
15
+ end
13
16
  f.use Faraday::Response::RaiseError # raises on 4xx and 5xx responses
14
17
  f.adapter :net_http_persistent
15
18
  end
@@ -30,6 +33,10 @@ module Promoted
30
33
  response.body
31
34
  end
32
35
  end
36
+
37
+ def get(endpoint)
38
+ @conn.get(endpoint).body
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -1,18 +1,28 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
+ class InvalidPagingError < StandardError
5
+ attr_reader :default_insertions_page
6
+
7
+ def initialize(message, default_insertions_page)
8
+ super(message)
9
+ @default_insertions_page = default_insertions_page
10
+ end
11
+ end
12
+
4
13
  class Pager
5
14
  def validate_paging (insertions, paging)
6
- if paging && paging[:offset]
7
- return paging[:offset] < insertions.length
15
+ if paging && paging[:offset] && paging[:offset] >= insertions.length
16
+ raise InvalidPagingError.new("Invalid page offset (insertion size #{insertions.length}, offset #{paging[:offset]})", [])
8
17
  end
9
- return true
10
18
  end
11
19
 
12
20
  def apply_paging (insertions, insertion_page_type, paging = nil)
13
- # This is invalid input, stop it before it goes to the server.
14
- if !validate_paging(insertions, paging)
15
- return []
21
+ begin
22
+ validate_paging(insertions, paging)
23
+ rescue InvalidPagingError => err
24
+ # This is invalid input, stop it before it goes to the server.
25
+ return err.default_insertions_page
16
26
  end
17
27
 
18
28
  if !paging
@@ -54,4 +64,4 @@ module Promoted
54
64
  end
55
65
  end
56
66
  end
57
- end
67
+ end
@@ -99,10 +99,13 @@ module Promoted
99
99
  params = {
100
100
  user_info: user_info,
101
101
  timing: timing,
102
- cohort_membership: @experiment,
103
102
  client_info: @client_info
104
103
  }
105
104
 
105
+ if @experiment
106
+ params[:cohort_membership] = [@experiment]
107
+ end
108
+
106
109
  # Log request allows for multiple requests but here we only send one.
107
110
  if include_request
108
111
  request[:request_id] = request[:request_id] || @id_generator.newID
@@ -1,7 +1,7 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- VERSION = "0.1.15"
4
+ VERSION = "0.1.16"
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promoted-ruby-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - scottmcmaster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-24 00:00:00.000000000 Z
11
+ date: 2021-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -167,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubygems_version: 3.0.3
170
+ rubygems_version: 3.2.24
171
171
  signing_key:
172
172
  specification_version: 4
173
173
  summary: A Ruby Client to contact Promoted APIs.