promoted-ruby-client 1.0.0 → 2.0.0
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 +1 -1
- data/README.md +1 -1
- data/dev.md +7 -1
- data/lib/promoted/ruby/client/log_request_builder.rb +61 -0
- data/lib/promoted/ruby/client/pager.rb +8 -5
- data/lib/promoted/ruby/client/request_builder.rb +0 -82
- data/lib/promoted/ruby/client/util.rb +3 -3
- data/lib/promoted/ruby/client/validator.rb +18 -0
- data/lib/promoted/ruby/client/version.rb +1 -1
- data/lib/promoted/ruby/client.rb +16 -23
- data/promoted-ruby-client.gemspec +3 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e743a5f01479b23e4e68d12262e93549d58a11540c0cf448fe4316c277422373
|
4
|
+
data.tar.gz: 65b9c38eab75a844783f3595c05bd1ea6f1f668596f46be2ecd3e370b61a551f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68c1e692e7e5af9a3deb344fde3cbc0049e8aa6f775d524bc0a6196646e9736cbfdb269fedf4ae83a07ad24cdab94c7140e555709bf046d72530e2f151c252e1
|
7
|
+
data.tar.gz: d0ef6502f473fb279a5a1379234c88f1263d7448d66e7d56cc65794d9d6692826f86e8d4934a69f80aececdf8752c993c96044f9b4146bc02d8721ae2d9dc626
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -205,7 +205,7 @@ Field Name | Type | Optional? | Description
|
|
205
205
|
Output of ```deliver```, includes the insertions as well as a suitable ```LogRequest``` for forwarding to Metrics API.
|
206
206
|
Field Name | Type | Optional? | Description
|
207
207
|
---------- | ---- | --------- | -----------
|
208
|
-
```:insertion``` | [] of Insertion | No | The insertions, which are from Delivery API (when ```deliver``` was called, i.e. we weren't either only-log or part of an experiment) or the input insertions (when the other conditions don't hold).
|
208
|
+
```:insertion``` | [] of Insertion | No | The paged insertions, which are from Delivery API (when ```deliver``` was called, i.e. we weren't either only-log or part of an experiment) or the input insertions (when the other conditions don't hold).
|
209
209
|
```:log_request``` | LogRequest | Yes | A message suitable for logging to Metrics API via ```send_log_request```. If the call to ```deliver``` was made (i.e. the request was not part of the CONTROL arm of an experiment or marked to only log), ```:log_request``` will not be set, as you can assume logging was performed on the server-side by Promoted.
|
210
210
|
```:client_request_id``` | String | Yes | Client-generated request id sent to Delivery API and may be useful for logging and debugging.
|
211
211
|
```:execution_server``` | one of 'API' or 'SDK' | Yes | Indicates if response insertions on a delivery request came from the API or the SDK.
|
data/dev.md
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
# Run tests
|
2
|
+
|
3
|
+
```
|
4
|
+
bundle exec rspec
|
5
|
+
```
|
6
|
+
|
1
7
|
# Deploy
|
2
8
|
|
3
9
|
1. Update version number.
|
4
10
|
2. Get credentials for deployment from 1password.
|
5
11
|
3. Modify `promoted-ruby-client.gemspec`'s push block.
|
6
12
|
4. Run `gem build promoted-ruby-client.gemspec` to generate `gem`.
|
7
|
-
5. Run (using new output) `gem push promoted-ruby-client-
|
13
|
+
5. Run (using new output) `gem push promoted-ruby-client-2.0.0.gem`
|
8
14
|
6. Update README with new version.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Promoted
|
2
|
+
module Ruby
|
3
|
+
module Client
|
4
|
+
class LogRequestBuilder
|
5
|
+
attr_reader :request, :response_insertions, :experiment
|
6
|
+
attr_accessor :request, :response_insertions, :experiment
|
7
|
+
|
8
|
+
def initialize args = {}
|
9
|
+
if args[:id_generator]
|
10
|
+
@id_generator = args[:id_generator]
|
11
|
+
else
|
12
|
+
@id_generator = IdGenerator.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_request(include_delivery_log:, exec_server:)
|
17
|
+
log_req = {
|
18
|
+
platform_id: @request[:platform_id],
|
19
|
+
user_info: @request[:user_info],
|
20
|
+
# For now, we'll just use the request timestamp.
|
21
|
+
timing: @request[:timing],
|
22
|
+
client_info: @request[:client_info],
|
23
|
+
device: @request[:device],
|
24
|
+
}
|
25
|
+
|
26
|
+
if @experiment
|
27
|
+
log_req[:cohort_membership] = [@experiment]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Log request allows for multiple requests but here we only send one.
|
31
|
+
if include_delivery_log
|
32
|
+
request[:request_id] = request[:request_id] || @id_generator.newID
|
33
|
+
# Remove redundant fields from `request` since they're already on the LogRequest.
|
34
|
+
stripped_request = @request.clone
|
35
|
+
stripped_request.delete(:platform_id)
|
36
|
+
stripped_request.delete(:user_info)
|
37
|
+
stripped_request.delete(:timing)
|
38
|
+
stripped_request.delete(:device)
|
39
|
+
stripped_request.delete(:client_info)
|
40
|
+
|
41
|
+
log_req[:delivery_log] = [{
|
42
|
+
execution: {
|
43
|
+
execution_server: exec_server,
|
44
|
+
server_version: Promoted::Ruby::Client::SERVER_VERSION
|
45
|
+
},
|
46
|
+
request: stripped_request,
|
47
|
+
response: {
|
48
|
+
insertion: @response_insertions
|
49
|
+
}
|
50
|
+
}]
|
51
|
+
end
|
52
|
+
log_req.clean!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
require "promoted/ruby/client/constants"
|
60
|
+
require "promoted/ruby/client/extensions"
|
61
|
+
require "promoted/ruby/client/id_generator"
|
@@ -50,11 +50,14 @@ module Promoted
|
|
50
50
|
final_insertion_size = [size, insertions.length - index].min
|
51
51
|
insertion_page = Array.new(final_insertion_size)
|
52
52
|
0.upto(final_insertion_size - 1) {|i|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
request_insertion = insertions[index]
|
54
|
+
# Pager returns response insertions. Create a copy and fill in some of the fields.
|
55
|
+
response_insertion = Hash[]
|
56
|
+
response_insertion[:content_id] = request_insertion[:content_id]
|
57
|
+
response_insertion[:insertion_id] = request_insertion[:insertion_id]
|
58
|
+
# TODO - implement retrieval_start.
|
59
|
+
response_insertion[:position] = offset
|
60
|
+
insertion_page[i] = response_insertion
|
58
61
|
index = index + 1
|
59
62
|
offset = offset + 1
|
60
63
|
}
|
@@ -49,7 +49,6 @@ module Promoted
|
|
49
49
|
return @experiment
|
50
50
|
end
|
51
51
|
|
52
|
-
# Only used in delivery
|
53
52
|
def delivery_request_params
|
54
53
|
params = {
|
55
54
|
user_info: user_info,
|
@@ -70,93 +69,12 @@ module Promoted
|
|
70
69
|
params.clean!
|
71
70
|
end
|
72
71
|
|
73
|
-
# Only used in delivery
|
74
|
-
# Maps the response insertions to the full insertions and re-insert the properties bag
|
75
|
-
# to the responses.
|
76
|
-
def fill_details_from_response response_insertions
|
77
|
-
if !response_insertions then
|
78
|
-
response_insertions = []
|
79
|
-
end
|
80
|
-
|
81
|
-
props = @insertion.each_with_object({}) do |insertion, hash|
|
82
|
-
if insertion.has_key?(:properties)
|
83
|
-
# Don't add nil properties to response insertions.
|
84
|
-
hash[insertion[:content_id]] = insertion[:properties]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
filled_in_copy = []
|
89
|
-
response_insertions.each do |resp_insertion|
|
90
|
-
copied_insertion = resp_insertion.clone
|
91
|
-
if copied_insertion.has_key?(:content_id) && props.has_key?(copied_insertion[:content_id])
|
92
|
-
copied_insertion[:properties] = props[resp_insertion[:content_id]]
|
93
|
-
end
|
94
|
-
filled_in_copy << copied_insertion
|
95
|
-
end
|
96
|
-
|
97
|
-
filled_in_copy
|
98
|
-
end
|
99
|
-
|
100
|
-
def log_request_params(include_delivery_log:, exec_server:)
|
101
|
-
params = {
|
102
|
-
user_info: user_info,
|
103
|
-
timing: timing,
|
104
|
-
client_info: merge_client_info_defaults,
|
105
|
-
device: @device,
|
106
|
-
}
|
107
|
-
|
108
|
-
if @experiment
|
109
|
-
params[:cohort_membership] = [@experiment]
|
110
|
-
end
|
111
|
-
|
112
|
-
# Log request allows for multiple requests but here we only send one.
|
113
|
-
if include_delivery_log
|
114
|
-
request[:request_id] = request[:request_id] || @id_generator.newID
|
115
|
-
|
116
|
-
params[:delivery_log] = [{
|
117
|
-
execution: {
|
118
|
-
execution_server: exec_server,
|
119
|
-
server_version: Promoted::Ruby::Client::SERVER_VERSION
|
120
|
-
},
|
121
|
-
request: request,
|
122
|
-
response: {
|
123
|
-
insertion: response_insertion
|
124
|
-
}
|
125
|
-
}]
|
126
|
-
|
127
|
-
add_missing_insertion_ids! params[:delivery_log][0][:response][:insertion]
|
128
|
-
end
|
129
|
-
|
130
|
-
params.clean!
|
131
|
-
end
|
132
|
-
|
133
72
|
def ensure_client_timestamp
|
134
73
|
if timing[:client_log_timestamp].nil?
|
135
74
|
timing[:client_log_timestamp] = (Time.now.to_f * 1000).to_i
|
136
75
|
end
|
137
76
|
end
|
138
77
|
|
139
|
-
def response_insertion
|
140
|
-
@response_insertions = []
|
141
|
-
paging = request[:paging] || {}
|
142
|
-
size = paging[:size] ? paging[:size].to_i : 0
|
143
|
-
if size <= 0
|
144
|
-
size = insertion.length()
|
145
|
-
end
|
146
|
-
offset = paging[:offset] ? paging[:offset].to_i : 0
|
147
|
-
|
148
|
-
insertion.each_with_index do |insertion_obj, index|
|
149
|
-
# TODO - this does not look performant.
|
150
|
-
break if @response_insertions.length() >= size
|
151
|
-
response_insertion = Hash[]
|
152
|
-
response_insertion[:content_id] = insertion_obj[:content_id]
|
153
|
-
response_insertion[:position] = offset + index
|
154
|
-
response_insertion[:insertion_id] = insertion_obj[:insertion_id]
|
155
|
-
@response_insertions << response_insertion.clean!
|
156
|
-
end
|
157
|
-
@response_insertions
|
158
|
-
end
|
159
|
-
|
160
78
|
def add_missing_insertion_ids! insertions
|
161
79
|
insertions.each do |insertion|
|
162
80
|
insertion[:insertion_id] = @id_generator.newID if not insertion[:insertion_id]
|
@@ -116,6 +116,24 @@ module Promoted
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
def validate_response!(res)
|
120
|
+
validate_fields!(
|
121
|
+
res,
|
122
|
+
"response",
|
123
|
+
[
|
124
|
+
{
|
125
|
+
:name => :request_id,
|
126
|
+
:required => true,
|
127
|
+
:type => String
|
128
|
+
}
|
129
|
+
]
|
130
|
+
)
|
131
|
+
|
132
|
+
if !res.key?(:insertion) then
|
133
|
+
res[:insertion] = []
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
119
137
|
# TODO - delete?
|
120
138
|
def validate_metrics_request!(metrics_req)
|
121
139
|
validate_fields!(
|
data/lib/promoted/ruby/client.rb
CHANGED
@@ -135,10 +135,10 @@ module Promoted
|
|
135
135
|
if @perform_checks
|
136
136
|
perform_common_checks!(args)
|
137
137
|
if !only_log && args[:insertion_page_type] == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED'] then
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
138
|
+
err = DeliveryInsertionPageType.new
|
139
|
+
@logger.error(err) if @logger
|
140
|
+
raise err
|
141
|
+
end
|
142
142
|
|
143
143
|
if should_send_shadow_traffic && args[:insertion_page_type] != Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'] then
|
144
144
|
should_send_shadow_traffic = false
|
@@ -179,6 +179,7 @@ module Promoted
|
|
179
179
|
delivery_request_params = delivery_request_builder.delivery_request_params
|
180
180
|
begin
|
181
181
|
response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers)
|
182
|
+
@validator.validate_response!(response)
|
182
183
|
rescue StandardError => err
|
183
184
|
# Currently we don't propagate errors to the SDK caller, but rather default to returning
|
184
185
|
# the request insertions.
|
@@ -191,8 +192,7 @@ module Promoted
|
|
191
192
|
end
|
192
193
|
|
193
194
|
insertions_from_delivery = (response != nil && !deliver_err);
|
194
|
-
response_insertions =
|
195
|
-
response && response[:insertion] || [])
|
195
|
+
response_insertions = response && response[:insertion] || []
|
196
196
|
end
|
197
197
|
|
198
198
|
should_send_shadow_traffic &&= should_send_as_shadow_traffic?
|
@@ -201,33 +201,24 @@ module Promoted
|
|
201
201
|
deliver_shadow_traffic args, headers
|
202
202
|
end
|
203
203
|
|
204
|
-
request_to_log = nil
|
205
204
|
if !insertions_from_delivery then
|
206
|
-
request_to_log = delivery_request_builder.request
|
207
205
|
response_insertions = build_sdk_response_insertions(delivery_request_builder)
|
208
206
|
end
|
209
207
|
|
210
208
|
log_req = nil
|
211
209
|
exec_server = (insertions_from_delivery ? Promoted::Ruby::Client::EXECUTION_SERVER['API'] : Promoted::Ruby::Client::EXECUTION_SERVER['SDK'])
|
212
|
-
|
213
|
-
# We only return a log request if there's a request or cohort to log.
|
214
|
-
if request_to_log || cohort_membership_to_log
|
215
|
-
log_request_builder = RequestBuilder.new
|
216
|
-
log_request = {
|
217
|
-
:insertion => response_insertions,
|
218
|
-
:experiment => cohort_membership_to_log,
|
219
|
-
:request => request_to_log
|
220
|
-
}
|
221
|
-
log_request_builder.set_request_params(log_request)
|
222
210
|
|
223
|
-
|
224
|
-
|
225
|
-
log_request_builder
|
226
|
-
|
211
|
+
# We only return a log request if there's a request or cohort to log.
|
212
|
+
if !insertions_from_delivery || cohort_membership_to_log
|
213
|
+
log_request_builder = LogRequestBuilder.new
|
214
|
+
# TODO - make this more efficient.
|
215
|
+
log_request_builder.request = delivery_request_builder.delivery_request_params
|
216
|
+
log_request_builder.response_insertions = response_insertions
|
217
|
+
log_request_builder.experiment = cohort_membership_to_log
|
227
218
|
|
228
219
|
# On a successful delivery request, we don't log the insertions
|
229
220
|
# or the request since they are logged on the server-side.
|
230
|
-
log_req = log_request_builder.
|
221
|
+
log_req = log_request_builder.log_request(
|
231
222
|
include_delivery_log: !insertions_from_delivery,
|
232
223
|
exec_server: exec_server)
|
233
224
|
end
|
@@ -346,6 +337,7 @@ module Promoted
|
|
346
337
|
response = nil
|
347
338
|
begin
|
348
339
|
response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
|
340
|
+
@validator.validate_response!(response)
|
349
341
|
rescue StandardError => err
|
350
342
|
@logger.warn("Shadow traffic call failed with #{err}") if @logger
|
351
343
|
return
|
@@ -384,6 +376,7 @@ module Promoted
|
|
384
376
|
end
|
385
377
|
|
386
378
|
# dependent /libs
|
379
|
+
require "promoted/ruby/client/log_request_builder"
|
387
380
|
require "promoted/ruby/client/request_builder"
|
388
381
|
require "promoted/ruby/client/pager"
|
389
382
|
require "promoted/ruby/client/sampler"
|
@@ -14,7 +14,9 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = 'https://github.com/promotedai/promoted-ruby-client'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
|
-
#
|
17
|
+
# Uncomment if you want to push to a custom host
|
18
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
19
|
+
|
18
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
19
21
|
spec.metadata["source_code_uri"] = "https://github.com/promotedai/promoted-ruby-client"
|
20
22
|
spec.metadata["changelog_uri"] = "https://github.com/promotedai/promoted-ruby-client/blob/master/CHANGELOG.md"
|
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: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- scottmcmaster
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -139,6 +139,7 @@ files:
|
|
139
139
|
- lib/promoted/ruby/client/extensions.rb
|
140
140
|
- lib/promoted/ruby/client/faraday_http_client.rb
|
141
141
|
- lib/promoted/ruby/client/id_generator.rb
|
142
|
+
- lib/promoted/ruby/client/log_request_builder.rb
|
142
143
|
- lib/promoted/ruby/client/pager.rb
|
143
144
|
- lib/promoted/ruby/client/request_builder.rb
|
144
145
|
- lib/promoted/ruby/client/sampler.rb
|
@@ -153,7 +154,7 @@ metadata:
|
|
153
154
|
homepage_uri: https://github.com/promotedai/promoted-ruby-client
|
154
155
|
source_code_uri: https://github.com/promotedai/promoted-ruby-client
|
155
156
|
changelog_uri: https://github.com/promotedai/promoted-ruby-client/blob/master/CHANGELOG.md
|
156
|
-
post_install_message:
|
157
|
+
post_install_message:
|
157
158
|
rdoc_options: []
|
158
159
|
require_paths:
|
159
160
|
- lib
|
@@ -168,8 +169,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
169
|
- !ruby/object:Gem::Version
|
169
170
|
version: '0'
|
170
171
|
requirements: []
|
171
|
-
rubygems_version: 3.
|
172
|
-
signing_key:
|
172
|
+
rubygems_version: 3.4.9
|
173
|
+
signing_key:
|
173
174
|
specification_version: 4
|
174
175
|
summary: A Ruby Client to contact Promoted APIs.
|
175
176
|
test_files: []
|