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 +4 -4
- data/Gemfile.lock +6 -8
- data/README.md +14 -16
- data/dev.md +2 -2
- data/lib/promoted/ruby/client/constants.rb +0 -3
- data/lib/promoted/ruby/client/errors.rb +0 -12
- data/lib/promoted/ruby/client/pager.rb +25 -16
- data/lib/promoted/ruby/client/version.rb +1 -1
- data/lib/promoted/ruby/client.rb +7 -16
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2581fc859bd3a30b8460858e6ab95ef4be27724f60ac02cff4e15c4579ea833
|
4
|
+
data.tar.gz: e87f89c047284f62cdd8d1975aae5c464e096f32c752352f5d83820b8e922ea4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 (
|
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.
|
58
|
+
nokogiri (1.15.4-arm64-darwin)
|
60
59
|
racc (~> 1.4)
|
61
|
-
nokogiri (1.15.
|
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.
|
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.
|
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.
|
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 ```
|
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 ```
|
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
|
-
|
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
|
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
|
-
|
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 |
|
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
|
14
|
-
6.
|
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]
|
16
|
-
|
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
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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 =
|
45
|
+
offset, retrieval_insertion_offset, index = _sanitize_offsets(retrieval_insertion_offset, paging)
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
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]
|
data/lib/promoted/ruby/client.rb
CHANGED
@@ -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],
|
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
|
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,
|
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
|
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:
|
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-
|
11
|
+
date: 2023-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|