promoted-ruby-client 0.1.20 → 0.1.24

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 340cf31fbc7680c1b9abb8df8dd1226e4a13fed103e21815afb82fb43b85f481
4
- data.tar.gz: d5448efcb375ac090847ac6c37fcda3b57babf0dca6043d82967e8ecdb42ae52
3
+ metadata.gz: 99f637dce3d3f85f13e8937bf586e6e9f5a11891b262e84a6e66d57a44bc6b09
4
+ data.tar.gz: 0c517b9e7b0049c6ae873c90db9536eae6b153367114cc50af887f36efcf79f2
5
5
  SHA512:
6
- metadata.gz: df18a2f73308fd9771b55a8762592a4041059b5d5fe72e0de07fa9566eb8627647eef75276a401c6f9da7d03bfc7158deb94bfd6b5824d22635a22b7d11848f0
7
- data.tar.gz: 7ed68c19270dcd13b8ed2347138aa845dd767d19bc257312e1a001f0e858879e75186142b9719b31e9c132d95fc7d48049baaf3e57b442f39f2b969cf2c835b8
6
+ metadata.gz: 640878d97d82424619a60ebf451db0f1501d15e625adfc2c53f3a47511e2661cb4d61bc8788d5b42f93c11f778411bc3bad59d5bab3fd787270a9b015f9968c3
7
+ data.tar.gz: e4a99864846f7d542e773d84897f9ce3ff9db842115d0ebd835f604a7763bebfa7c875656377e22f57442637f71a7d0663b098379e2603c5b6852f9696e626ce
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- promoted-ruby-client (0.1.20)
4
+ promoted-ruby-client (0.1.24)
5
5
  concurrent-ruby (~> 1)
6
6
  faraday (>= 0.9.0)
7
7
  faraday_middleware (>= 0.9.0)
data/README.md CHANGED
@@ -49,6 +49,7 @@ Name | Type | Description
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
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).
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.
52
53
  ```:default_request_headers``` | Hash | Additional headers to send on the request beyond ```x-api-key```. Defaults to {}
53
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.
54
55
  ```: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.
@@ -62,6 +63,7 @@ Field Name | Type | Optional? | Description
62
63
  ---------- | ---- | --------- | -----------
63
64
  ```:user_id``` | String | Yes | The platform user id, cleared from Promoted logs.
64
65
  ```:log_user_id``` | String | Yes | A different user id (presumably a UUID) disconnected from the platform user id, good for working with unauthenticated users or implementing right-to-be-forgotten.
66
+ ```:is_internal_user``` | Boolean | Yes | If this user is a test user or not, defaults to false.
65
67
 
66
68
  ---
67
69
  ### CohortMembership
@@ -93,6 +95,8 @@ Field Name | Type | Optional? | Description
93
95
  ```:insertion_id``` | String | Yes | Generated by the SDK (*do not set*)
94
96
  ```:request_id``` | String | Yes | Generated by the SDK when needed (*do not set*)
95
97
  ```:content_id``` | String | No | Identifier for the content to be shown, must be set.
98
+ ```:retrieval_rank``` | Number | Yes | Optional original ranking of this content item.
99
+ ```:retrieval_score``` | Number | Yes | Optional original quality score of this content item.
96
100
  ```:properties``` | Properties | Yes | Any additional custom properties to associate. For v1 integrations, it is fine not to fill in all the properties.
97
101
  ---
98
102
  ### Size
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.20.gem`
7
+ 5. Run (using new output) `gem push promoted-ruby-client-0.1.24.gem`
8
8
  6. Update README with new version.
@@ -57,7 +57,7 @@ module Promoted
57
57
  params = {
58
58
  user_info: user_info,
59
59
  timing: timing,
60
- client_info: @client_info.merge({ :client_type => Promoted::Ruby::Client::CLIENT_TYPE['PLATFORM_SERVER'] }),
60
+ client_info: merge_client_info_defaults,
61
61
  device: @device,
62
62
  platform_id: @platform_id,
63
63
  view_id: @view_id,
@@ -100,12 +100,12 @@ module Promoted
100
100
  filled_in_copy
101
101
  end
102
102
 
103
- def log_request_params(include_insertions: true, include_request: true)
103
+ def log_request_params(include_delivery_log:, exec_server:)
104
104
  params = {
105
105
  user_info: user_info,
106
106
  timing: timing,
107
- client_info: @client_info,
108
- device: @device
107
+ client_info: merge_client_info_defaults,
108
+ device: @device,
109
109
  }
110
110
 
111
111
  if @experiment
@@ -113,14 +113,20 @@ module Promoted
113
113
  end
114
114
 
115
115
  # Log request allows for multiple requests but here we only send one.
116
- if include_request
116
+ if include_delivery_log
117
117
  request[:request_id] = request[:request_id] || @id_generator.newID
118
- params[:request] = [request]
119
- end
120
118
 
121
- if include_insertions
122
- params[:insertion] = compact_metrics_properties if include_insertions
123
- add_missing_ids_on_insertions! request, params[:insertion]
119
+ params[:delivery_log] = [{
120
+ execution: {
121
+ execution_server: exec_server
122
+ },
123
+ request: request,
124
+ response: {
125
+ insertion: insertions_with_compact_metrics_properties
126
+ }
127
+ }]
128
+
129
+ add_missing_ids_on_insertions! request, params[:delivery_log][0][:response][:insertion]
124
130
  end
125
131
 
126
132
  params.clean!
@@ -161,7 +167,7 @@ module Promoted
161
167
  end
162
168
 
163
169
  # TODO: This looks overly complicated.
164
- def compact_metrics_properties
170
+ def insertions_with_compact_metrics_properties
165
171
  @insertion = [] # insertion should be set according to the compact insertion
166
172
  paging = request[:paging] || {}
167
173
  size = paging[:size] ? paging[:size].to_i : 0
@@ -178,7 +184,6 @@ module Promoted
178
184
  insertion_obj = Hash[insertion_obj]
179
185
  insertion_obj[:user_info] = user_info
180
186
  insertion_obj[:timing] = timing
181
- insertion_obj[:insertion_id] = @id_generator.newID
182
187
  insertion_obj[:request_id] = request_id
183
188
  insertion_obj[:position] = offset + index
184
189
  insertion_obj = compact_one_insertion(insertion_obj, @to_compact_metrics_properties_func)
@@ -187,18 +192,31 @@ module Promoted
187
192
  @insertion
188
193
  end
189
194
 
195
+ def add_missing_insertion_ids! insertions
196
+ insertions.each do |insertion|
197
+ insertion[:insertion_id] = @id_generator.newID if not insertion[:insertion_id]
198
+ end
199
+ end
200
+
190
201
  def client_request_id
191
202
  request[:client_request_id]
192
203
  end
193
204
 
194
205
  private
195
206
 
207
+ def merge_client_info_defaults
208
+ return @client_info.merge({
209
+ :client_type => Promoted::Ruby::Client::CLIENT_TYPE['PLATFORM_SERVER'],
210
+ :traffic_type => Promoted::Ruby::Client::TRAFFIC_TYPE['PRODUCTION']
211
+ })
212
+ end
213
+
196
214
  def add_missing_ids_on_insertions! request, insertions
197
215
  insertions.each do |insertion|
198
- insertion[:insertion_id] = @id_generator.newID if not insertion[:insertion_id]
199
216
  insertion[:session_id] = request[:session_id] if request[:session_id]
200
217
  insertion[:request_id] = request[:request_id] if request[:request_id]
201
218
  end
219
+ add_missing_insertion_ids! insertions
202
220
  end
203
221
 
204
222
  # A list of the response Insertions. This client expects lists to be truncated
@@ -14,6 +14,10 @@ module Promoted
14
14
  {
15
15
  :name => :log_user_id,
16
16
  :type => String
17
+ },
18
+ {
19
+ :name => :is_internal_user,
20
+ :type => [TrueClass, FalseClass]
17
21
  }
18
22
  ]
19
23
  )
@@ -55,6 +59,14 @@ module Promoted
55
59
  {
56
60
  :name => :delivery_score,
57
61
  :type => Integer
62
+ },
63
+ {
64
+ :name => :retrieval_rank,
65
+ :type => Integer
66
+ },
67
+ {
68
+ :name => :retrieval_score,
69
+ :type => Float
58
70
  }
59
71
  ]
60
72
  )
@@ -133,7 +145,6 @@ module Promoted
133
145
  req[:full_insertion].each do |insertion_hash|
134
146
  raise ValidationError.new("Insertion.requestId should not be set") if insertion_hash[:request_id]
135
147
  raise ValidationError.new("'Insertion.insertionId should not be set") if insertion_hash[:insertion_id]
136
- raise ValidationError.new("Insertion.deliveryScore should not be set") if insertion_hash[:delivery_score]
137
148
  end
138
149
  end
139
150
 
@@ -147,7 +158,11 @@ module Promoted
147
158
 
148
159
  # If a field is provided as non-nil, it should be of the correct type.
149
160
  if field[:type] && obj.has_key?(field[:name]) && obj[field[:name]] != nil then
150
- raise ValidationError.new(field[:name].to_s + " should be a " + field[:type].to_s) if !obj[field[:name]].is_a?(field[:type])
161
+ if field[:type].is_a?(Array) then
162
+ raise ValidationError.new(field[:name].to_s + " should be one of " + field[:type].to_s) if !field[:type].include?(obj[field[:name]].class)
163
+ else
164
+ raise ValidationError.new(field[:name].to_s + " should be a " + field[:type].to_s) if !obj[field[:name]].is_a?(field[:type])
165
+ end
151
166
  end
152
167
  }
153
168
  end
@@ -1,7 +1,7 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- VERSION = "0.1.20"
4
+ VERSION = "0.1.24"
5
5
  end
6
6
  end
7
7
  end
@@ -19,7 +19,8 @@ module Promoted
19
19
  class Error < StandardError; end
20
20
 
21
21
  attr_reader :perform_checks, :default_only_log, :delivery_timeout_millis, :metrics_timeout_millis, :should_apply_treatment_func,
22
- :default_request_headers, :http_client, :logger, :shadow_traffic_delivery_percent, :async_shadow_traffic
22
+ :default_request_headers, :http_client, :logger, :shadow_traffic_delivery_percent, :async_shadow_traffic,
23
+ :send_shadow_traffic_for_control
23
24
 
24
25
  attr_accessor :request_logging_on, :enabled
25
26
 
@@ -79,6 +80,11 @@ module Promoted
79
80
  @async_shadow_traffic = params[:async_shadow_traffic] || false
80
81
  end
81
82
 
83
+ @send_shadow_traffic_for_control = true
84
+ if params[:send_shadow_traffic_for_control] != nil
85
+ @send_shadow_traffic_for_control = params[:send_shadow_traffic_for_control] || false
86
+ end
87
+
82
88
  @pool = nil
83
89
  if @async_shadow_traffic
84
90
  # Thread pool to process delivery of shadow traffic. Will silently drop excess requests beyond the queue
@@ -161,9 +167,8 @@ module Promoted
161
167
  cohort_membership_to_log = delivery_request_builder.new_cohort_membership_to_log
162
168
 
163
169
  if should_apply_treatment(cohort_membership_to_log)
164
- delivery_request_params = delivery_request_builder.delivery_request_params
165
-
166
- # Call Delivery API
170
+ # Call Delivery API to get insertions to use
171
+ delivery_request_params = delivery_request_builder.delivery_request_params
167
172
  begin
168
173
  response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers)
169
174
  rescue StandardError => err
@@ -172,20 +177,25 @@ module Promoted
172
177
  deliver_err = true
173
178
  @logger.error("Error calling delivery: " + err.message) if @logger
174
179
  end
175
-
176
- insertions_from_delivery = (response != nil && !deliver_err);
177
- response_insertions = delivery_request_builder.fill_details_from_response(
178
- response && response[:insertion] || [])
180
+ elsif @send_shadow_traffic_for_control
181
+ # Call Delivery API to send shadow traffic. This will create the request params with traffic type set.
182
+ deliver_shadow_traffic args, headers
179
183
  end
184
+
185
+ insertions_from_delivery = (response != nil && !deliver_err);
186
+ response_insertions = delivery_request_builder.fill_details_from_response(
187
+ response && response[:insertion] || [])
180
188
  end
181
189
 
182
190
  request_to_log = nil
183
191
  if !insertions_from_delivery then
184
192
  request_to_log = delivery_request_builder.request
185
- response_insertions = @pager.apply_paging(delivery_request_builder.full_insertion, Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'], delivery_request_builder.request[:paging])
193
+ response_insertions = build_sdk_response_insertions(delivery_request_builder)
186
194
  end
187
195
 
188
196
  log_req = nil
197
+ exec_server = (insertions_from_delivery ? Promoted::Ruby::Client::EXECUTION_SERVER['API'] : Promoted::Ruby::Client::EXECUTION_SERVER['SDK'])
198
+
189
199
  # We only return a log request if there's a request or cohort to log.
190
200
  if request_to_log || cohort_membership_to_log
191
201
  log_request_builder = RequestBuilder.new
@@ -204,14 +214,14 @@ module Promoted
204
214
  # On a successful delivery request, we don't log the insertions
205
215
  # or the request since they are logged on the server-side.
206
216
  log_req = log_request_builder.log_request_params(
207
- include_insertions: !insertions_from_delivery,
208
- include_request: !insertions_from_delivery)
217
+ include_delivery_log: !insertions_from_delivery,
218
+ exec_server: exec_server)
209
219
  end
210
220
 
211
221
  client_response = {
212
222
  insertion: response_insertions,
213
223
  log_request: log_req,
214
- execution_server: insertions_from_delivery ? Promoted::Ruby::Client::EXECUTION_SERVER['API'] : Promoted::Ruby::Client::EXECUTION_SERVER['SDK'],
224
+ execution_server: exec_server,
215
225
  client_request_id: delivery_request_builder.client_request_id
216
226
  }
217
227
  return client_response
@@ -250,7 +260,9 @@ module Promoted
250
260
  deliver_shadow_traffic args, headers
251
261
  end
252
262
 
253
- log_request_builder.log_request_params
263
+ log_request_builder.log_request_params(
264
+ include_delivery_log: true,
265
+ exec_server: Promoted::Ruby::Client::EXECUTION_SERVER['SDK'])
254
266
  end
255
267
 
256
268
  ##
@@ -266,6 +278,14 @@ module Promoted
266
278
 
267
279
  private
268
280
 
281
+ ##
282
+ # Creates response insertions for SDK-side delivery, when we don't get response insertions from Delivery API.
283
+ def build_sdk_response_insertions delivery_request_builder
284
+ response_insertions = @pager.apply_paging(delivery_request_builder.full_insertion, Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'], delivery_request_builder.request[:paging])
285
+ delivery_request_builder.add_missing_insertion_ids! response_insertions
286
+ return response_insertions
287
+ end
288
+
269
289
  def do_warmup
270
290
  if !@delivery_endpoint
271
291
  # Warmup only supported when delivery is enabled.
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.20
4
+ version: 0.1.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - scottmcmaster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-19 00:00:00.000000000 Z
11
+ date: 2021-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
169
  - !ruby/object:Gem::Version
170
170
  version: '0'
171
171
  requirements: []
172
- rubygems_version: 3.2.24
172
+ rubygems_version: 3.0.3
173
173
  signing_key:
174
174
  specification_version: 4
175
175
  summary: A Ruby Client to contact Promoted APIs.