promoted-ruby-client 3.0.0 → 4.0.1

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: ffd08d1b6e14b721172d2e30680541f1d087d14f7cdfe63dc96b60ada84b5b1a
4
- data.tar.gz: d21fa553ef8e746c642605bb2de051d4bdc9901b5837c1db6f1ca11fe358fa98
3
+ metadata.gz: d2581fc859bd3a30b8460858e6ab95ef4be27724f60ac02cff4e15c4579ea833
4
+ data.tar.gz: e87f89c047284f62cdd8d1975aae5c464e096f32c752352f5d83820b8e922ea4
5
5
  SHA512:
6
- metadata.gz: bf16053b5bef1560caf7e9a5eaf01b62dd4484d40069801310d4cc8b7eb0fdce91eb072ee4c8a1146188b5fc74a168b2769623379dfd8df2a9f493d1e214a012
7
- data.tar.gz: f814b279bd1585402a381e8269c9c4664964381702fb5fb597845990ace9e56fdb5b8e0d24d8b2d4f3373523f764798092bcb5c324f2ca90d173b4008035993f
6
+ metadata.gz: 98361d750d5bf3605b4acda0c974be206ae115fa973f8d4e57d723874d2fed40eb86b4f635ce22a502c5faf36e1e6ee12456f95b9d4fc8a5e763a0747393a20e
7
+ data.tar.gz: ebf6134571b25c908d9dba394286a31564517d0218d3d97975e5965a85d3607c91ff37f191cbb8d1dd2942bd9c47209cbb56315bd7ea3a4e92c92d5313220b36
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- promoted-ruby-client (2.0.2)
4
+ promoted-ruby-client (4.0.1)
5
5
  concurrent-ruby (~> 1)
6
6
  faraday (>= 0.9.0)
7
7
  faraday_middleware (>= 0.9.0)
@@ -52,13 +52,12 @@ GEM
52
52
  rexml
53
53
  kramdown-parser-gfm (1.1.0)
54
54
  kramdown (~> 2.0)
55
- language_server-protocol (3.17.0.3)
56
55
  multipart-post (2.3.0)
57
56
  net-http-persistent (4.0.2)
58
57
  connection_pool (~> 2.2)
59
- nokogiri (1.15.2-arm64-darwin)
58
+ nokogiri (1.15.4-arm64-darwin)
60
59
  racc (~> 1.4)
61
- nokogiri (1.15.2-x86_64-linux)
60
+ nokogiri (1.15.4-x86_64-linux)
62
61
  racc (~> 1.4)
63
62
  parallel (1.23.0)
64
63
  parser (3.2.2.3)
@@ -85,11 +84,10 @@ GEM
85
84
  diff-lcs (>= 1.2.0, < 2.0)
86
85
  rspec-support (~> 3.12.0)
87
86
  rspec-support (3.12.1)
88
- rubocop (1.53.1)
87
+ rubocop (1.50.2)
89
88
  json (~> 2.3)
90
- language_server-protocol (>= 3.17.0)
91
89
  parallel (~> 1.10)
92
- parser (>= 3.2.2.3)
90
+ parser (>= 3.2.0.0)
93
91
  rainbow (>= 2.2.2, < 4.0)
94
92
  regexp_parser (>= 1.8, < 3.0)
95
93
  rexml (>= 3.2.5, < 4.0)
@@ -142,4 +140,4 @@ DEPENDENCIES
142
140
  solargraph
143
141
 
144
142
  BUNDLED WITH
145
- 2.4.9
143
+ 2.4.19
data/README.md CHANGED
@@ -20,7 +20,7 @@ HTTP client for calling Promoted.
20
20
  ### [Net::HTTP::Persistent](https://github.com/drbrain/net-http-persistent)
21
21
  Faraday binding (provides connection pool support)
22
22
  ### [Concurrent Ruby](https://github.com/ruby-concurrency/concurrent-ruby)
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```
23
+ Provides a thread pool for making shadow traffic requests to Delivery API in the background on a subset of calls to ```deliver```
24
24
  ## Creating a Client
25
25
  ```rb
26
26
  client = Promoted::Ruby::Client::PromotedClient.new
@@ -48,7 +48,7 @@ Name | Type | Description
48
48
  ```:metrics_timeout_millis``` | Number | Timeout on the Metrics API call. Defaults to 3000.
49
49
  ```:perform_checks``` | Boolean | Whether or not to perform detailed input validation, defaults to true but may be disabled for performance
50
50
  ```:logger``` | Ruby Logger-compatible logger | Defaults to nil (no logging). Example: ```Logger.new(STDERR, :progname => 'promotedai')```
51
- ```:shadow_traffic_delivery_percent``` | Number between 0 and 1 | % of ```prepare_for_logging``` traffic that gets directed to Delivery API as "shadow traffic". Defaults to 0 (no shadow traffic).
51
+ ```:shadow_traffic_delivery_percent``` | Number between 0 and 1 | % of ```deliver``` traffic that gets directed to Delivery API as "shadow traffic". Defaults to 0 (no shadow traffic).
52
52
  ```:send_shadow_traffic_for_control``` | Boolean | If true, the ```deliver``` method will send shadow traffic for users in the CONTROL arm of an experiment. Defaults to true.
53
53
  ```:default_request_headers``` | Hash | Additional headers to send on the request beyond ```x-api-key```. Defaults to {}
54
54
  ```: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.
@@ -169,7 +169,14 @@ Field Name | Type | Optional? | Description
169
169
  ```:browser``` | Browser | Yes | Browser information
170
170
  ---
171
171
  ### Paging
172
- #### TODO
172
+ Paging parameters.
173
+
174
+ ```DeliveryRequest.retrieval_insertion_offset``` also impacts paging. That field indicate the offset of the retrieved insertions that are passed into ```Request.insertion```. See [detailed documentation on paging and ```retrieval_insertion_offset```](https://docs.promoted.ai/docs/ranking-requests#sending-even-more-request-insertions).
175
+
176
+ Field Name | Type | Optional? | Description
177
+ ---------- | ---- | --------- | -----------
178
+ ```:offset``` | Integer | Yes | The 0-based, starting index for the response page. This should be the global position.
179
+ ```:size``` | Integer | Yes | The number of items to return in a response page.
173
180
  ---
174
181
  ### Request
175
182
  A request for content insertions.
@@ -179,7 +186,7 @@ Field Name | Type | Optional? | Description
179
186
  ```:request_id``` | String | Yes | Generated by the SDK when needed (*do not set*)
180
187
  ```:use_case``` | String | Yes | One of the use case values, i.e. 'FEED' (see [constants.rb](https://github.com/promotedai/promoted-ruby-client/blob/main/lib/promoted/ruby/client/constants.rb)).
181
188
  ```:properties``` | Properties | Yes | Any additional custom properties to associate.
182
- ```:paging``` | Paging | Yes | Paging parameters (see TODO)
189
+ ```:paging``` | Paging | Yes | Paging parameters
183
190
  ```:device``` | Device | Yes | Device information (as available)
184
191
  ---
185
192
 
@@ -190,15 +197,12 @@ Field Name | Type | Optional? | Description
190
197
  ```:experiment``` | CohortMembership | Yes | A cohort to evaluation in experimentation.
191
198
  ```:request``` | Request | No | The underlying request for content.
192
199
  ```:only_log``` | Boolean | Yes | Defaults to false. Set to true to override whether Delivery API is called for this request.
200
+ ```:retrieval_insertion_offset``` | Integer | Yes | The index of the retrieved insertions set on ```Request.insertion``` list. If just sending the top-N from your retrieval, this is ```0```. If this is the next batch (e.g. ```500``` to ```999```), then the value is ```500```. This can be used to send multiple groups of retrieved insertions. This interacts with the ```Paging``` fields. [More detailed documentation on paging](https://docs.promoted.ai/docs/ranking-requests#sending-even-more-request-insertions).
193
201
  ---
194
202
 
195
203
  ### LogRequest
196
204
 
197
- Output of ```prepare_for_logging``` as well as an ouput of an SDK call to ```deliver```, input to ```send_log_request``` to log to Promoted
198
- Field Name | Type | Optional? | Description
199
- ---------- | ---- | --------- | -----------
200
- ```:request``` | Request | No | The underlying request for content to log.
201
- ```:insertion``` | [] of Insertion | No | The insertions, which are either the original request insertions or the insertions resulting from a call to ```deliver``` if such call occurred.
205
+ A log object that is sent as a RPC request to Promoted's Metrics API log endpoint. This is outputted from the ```deliver``` SDK call. Callers need to either input it into the ```send_log_request``` method or send it to the Metrics API directly. Clients should avoid manipulating this object directly.
202
206
  ---
203
207
 
204
208
  ### ClientResponse
@@ -214,18 +218,12 @@ Field Name | Type | Optional? | Description
214
218
  ### PromotedClient
215
219
  Method | Input | Output | Description
216
220
  ------ | ----- | ------ | -----------
217
- ```prepare_for_logging``` | MetricsRequest | LogRequest | Builds a request suitable for logging locally and/or to Promoted, either via a subsequent call to ```send_log_request``` in the SDK client or by using this structure to make the call yourself. Optionally, based on client configuration may send a random subset of requests to Delivery API as shadow traffic for integration purposes.
218
221
  ```send_log_request``` | LogRequest | n/a | Forwards a LogRequest to Promoted using an HTTP client.
219
- ```deliver``` | DeliveryRequest | ClientResponse | Makes a request (subject to experimentation) to Delivery API for insertions, which are then returned along with a LogRequest.
222
+ ```deliver``` | DeliveryRequest | ClientResponse | Depending on flags, either (1) makes a request (subject to experimentation) to Delivery API for insertions, which are then returned along with a LogRequest or (2) implements SDK-side paging and prepares log records that can be logged to Promoted using ```send_log_request``` or by using this structure to make the call yourself. Optionally, based on client configuration may send a random subset of requests to Delivery API as shadow traffic for integration purposes.
220
223
  ```close``` | n/a | n/a | Closes down the client at shutdown, currently this is just to drain the thread pool that handles shadow traffic.
221
224
  ---
222
225
 
223
226
  ## Metrics API
224
- ### Pagination
225
-
226
- The `prepare_for_logging` call assumes the client has already handled pagination. It needs a `Request.paging.offset` to be passed in for the number of items deep that the page is.
227
- TODO: Needs more details.
228
-
229
227
  ### Expected flow for Metrics logging
230
228
 
231
229
  ```rb
data/dev.md CHANGED
@@ -10,5 +10,5 @@ bundle exec rspec
10
10
  2. Get credentials for deployment from 1password.
11
11
  3. Modify `promoted-ruby-client.gemspec`'s push block.
12
12
  4. Run `gem build promoted-ruby-client.gemspec` to generate `gem`.
13
- 5. Run (using new output) `gem push promoted-ruby-client-3.0.0.gem`
14
- 6. Update README with new version.
13
+ 5. Run `bundle exec rspec`. This updates `Gemfile.lock`.
14
+ 6. Run (using new output) `gem push promoted-ruby-client-4.0.1.gem`
@@ -14,9 +14,6 @@ module Promoted
14
14
  'SELLER_CONTENT'=> 'SELLER_CONTENT',
15
15
  'DISCOVER'=> 'DISCOVER'}
16
16
 
17
- INSERTION_PAGING_TYPE = {'UNPAGED' => 'UNPAGED',
18
- 'PRE_PAGED' => 'PRE_PAGED'}
19
-
20
17
  COHORT_ARM = {'UNKNOWN_GROUP' => 'UNKNOWN_GROUP',
21
18
  'CONTROL' => 'CONTROL',
22
19
  'TREATMENT' => 'TREATMENT',
@@ -1,18 +1,6 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- class ShadowTrafficInsertionPageType < StandardError
5
- def message
6
- 'Insertions must be unpaged when shadow traffic is on'
7
- end
8
- end
9
-
10
- class DeliveryInsertionPageType < StandardError
11
- def message
12
- 'Delivery insertions must be unpaged'
13
- end
14
- end
15
-
16
4
  class EndpointError < StandardError
17
5
  attr_reader :cause
18
6
  def initialize(cause)
@@ -11,20 +11,30 @@ module Promoted
11
11
  end
12
12
 
13
13
  class Pager
14
- def validate_paging(insertions, paging)
15
- if paging && paging[:offset] && paging[:offset] >= insertions.length
16
- raise InvalidPagingError.new("Invalid page offset (insertion size #{insertions.length}, offset #{paging[:offset]})", [])
14
+ def validate_paging(insertions, retrieval_insertion_offset, paging)
15
+ if paging && paging[:offset]
16
+ offset, retrieval_insertion_offset, index = _sanitize_offsets(retrieval_insertion_offset, paging)
17
+ _validate_paging(insertions, retrieval_insertion_offset, offset, index)
17
18
  end
18
19
  end
19
20
 
20
- def apply_paging(insertions, insertion_page_type, paging = nil)
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
21
+ def _sanitize_offsets(retrieval_insertion_offset, paging)
22
+ offset = [0, paging[:offset]].max
23
+ retrieval_insertion_offset = [0, retrieval_insertion_offset].max
24
+ index = [0, offset - retrieval_insertion_offset].max
25
+ return [offset, retrieval_insertion_offset, index]
26
+ end
27
+
28
+ def _validate_paging(insertions, retrieval_insertion_offset, offset, index)
29
+ if offset < retrieval_insertion_offset
30
+ raise InvalidPagingError.new("Invalid page offset (retrieval_insertion_offset #{retrieval_insertion_offset}, offset #{offset})", [])
26
31
  end
32
+ if index >= insertions.length
33
+ raise InvalidPagingError.new("Invalid page offset (insertion size #{insertions.length}, index #{index})", [])
34
+ end
35
+ end
27
36
 
37
+ def apply_paging(insertions, retrieval_insertion_offset, paging = nil)
28
38
  if !paging
29
39
  paging = {
30
40
  :offset => 0,
@@ -32,14 +42,13 @@ module Promoted
32
42
  }
33
43
  end
34
44
 
35
- offset = [0, paging[:offset]].max
45
+ offset, retrieval_insertion_offset, index = _sanitize_offsets(retrieval_insertion_offset, paging)
36
46
 
37
- index = offset
38
- if insertion_page_type == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED']
39
- # When insertions are pre-paged, we don't use offset to
40
- # window into the provided insertions, although we do use it when
41
- # assigning positions.
42
- index = 0
47
+ begin
48
+ _validate_paging(insertions, retrieval_insertion_offset, offset, index)
49
+ rescue InvalidPagingError => err
50
+ # This is invalid input, stop it before it goes to the server.
51
+ return err.default_insertions_page
43
52
  end
44
53
 
45
54
  size = paging[:size]
@@ -1,7 +1,7 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- VERSION = "3.0.0"
4
+ VERSION = "4.0.1"
5
5
  SERVER_VERSION = "rb." + VERSION
6
6
  end
7
7
  end
@@ -54,6 +54,7 @@ module Promoted
54
54
 
55
55
  @sampler = Sampler.new
56
56
  @pager = Pager.new
57
+ @retrieval_insertion_offset = params[:retrieval_insertion_offset] || 0
57
58
 
58
59
  # HTTP Client creation
59
60
  @delivery_endpoint = params[:delivery_endpoint] || DEFAULT_DELIVERY_ENDPOINT
@@ -119,7 +120,7 @@ module Promoted
119
120
  # Respect the enabled state
120
121
  if !@enabled
121
122
  return {
122
- insertion: @pager.apply_paging(args[:request][:insertion], Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'], args[:request][:paging])
123
+ insertion: @pager.apply_paging(args[:request][:insertion], @retrieval_insertion_offset, args[:request][:paging])
123
124
  # No log request returned when disabled
124
125
  }
125
126
  end
@@ -134,16 +135,6 @@ module Promoted
134
135
  # perform_checks raises errors.
135
136
  if @perform_checks
136
137
  perform_common_checks!(args)
137
- if !only_log && args[:insertion_page_type] == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED'] then
138
- err = DeliveryInsertionPageType.new
139
- @logger.error(err) if @logger
140
- raise err
141
- end
142
-
143
- if should_send_shadow_traffic && args[:insertion_page_type] != Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'] then
144
- should_send_shadow_traffic = false
145
- @logger.error(ShadowTrafficInsertionPageType.new) if @logger
146
- end
147
138
  end
148
139
 
149
140
  delivery_request_builder.ensure_client_timestamp
@@ -161,7 +152,7 @@ module Promoted
161
152
  end
162
153
 
163
154
  begin
164
- @pager.validate_paging(delivery_request_builder.insertion, delivery_request_builder.request[:paging])
155
+ @pager.validate_paging(delivery_request_builder.insertion, @retrieval_insertion_offset, delivery_request_builder.request[:paging])
165
156
  rescue InvalidPagingError => err
166
157
  # Invalid input, log and do SDK-side delivery.
167
158
  @logger.warn(err) if @logger
@@ -234,7 +225,7 @@ module Promoted
234
225
  end
235
226
 
236
227
  ##
237
- # Sends a log request (previously created by a call to prepare_for_logging) to the metrics endpoint.
228
+ # Sends a log request to the metrics endpoint.
238
229
  def send_log_request log_request_params, headers={}
239
230
  begin
240
231
  send_request(log_request_params, @metrics_endpoint, @metrics_timeout_millis, @metrics_api_key, headers)
@@ -249,7 +240,7 @@ module Promoted
249
240
  ##
250
241
  # Creates response insertions for SDK-side delivery, when we don't get response insertions from Delivery API.
251
242
  def build_sdk_response_insertions delivery_request_builder
252
- response_insertions = @pager.apply_paging(delivery_request_builder.insertion, Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'], delivery_request_builder.request[:paging])
243
+ response_insertions = @pager.apply_paging(delivery_request_builder.insertion, @retrieval_insertion_offset, delivery_request_builder.request[:paging])
253
244
  delivery_request_builder.add_missing_insertion_ids! response_insertions
254
245
  return response_insertions
255
246
  end
@@ -317,7 +308,7 @@ module Promoted
317
308
 
318
309
  # Delivers shadow traffic from the given metrics args.
319
310
  # Assumes that the args have already been normalized since this
320
- # method should only be called from inside prepare_for_logging.
311
+ # method should only be called from inside deliver.
321
312
  def deliver_shadow_traffic args, headers
322
313
  delivery_request_builder = RequestBuilder.new
323
314
  delivery_request_builder.set_request_params args
@@ -326,7 +317,7 @@ module Promoted
326
317
  delivery_request_params[:client_info][:traffic_type] = Promoted::Ruby::Client::TRAFFIC_TYPE['SHADOW']
327
318
 
328
319
  begin
329
- @pager.validate_paging(delivery_request_builder.insertion, delivery_request_builder.request[:paging])
320
+ @pager.validate_paging(delivery_request_builder.insertion, @retrieval_insertion_offset, delivery_request_builder.request[:paging])
330
321
  rescue InvalidPagingError => err
331
322
  # Invalid input, log and skip.
332
323
  @logger.warn("Shadow traffic call failed with invalid paging #{err}") if @logger
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: 3.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - scottmcmaster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-08 00:00:00.000000000 Z
11
+ date: 2023-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday