promoted-ruby-client 0.1.15 → 0.1.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/push-pull-requests_check.yml +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +71 -5
- data/dev.md +1 -1
- data/lib/promoted/ruby/client.rb +62 -18
- data/lib/promoted/ruby/client/constants.rb +7 -0
- data/lib/promoted/ruby/client/errors.rb +6 -0
- data/lib/promoted/ruby/client/faraday_http_client.rb +10 -2
- data/lib/promoted/ruby/client/pager.rb +17 -7
- data/lib/promoted/ruby/client/request_builder.rb +50 -17
- data/lib/promoted/ruby/client/util.rb +26 -3
- data/lib/promoted/ruby/client/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b8a39c8af4d21ae9987db29f0041e57d54de87da119b799e89091069a0bfbc2
|
4
|
+
data.tar.gz: 7b3932706882a896f330b0bd0a3d05671111e80b97fac4403e355ae30e537750
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3146dc2f31afa84fa2b332b1cdb5ca145e7aa51140ae14503a8ca8a4aebc96bb59c4313ee10884b85ba819c930f40f9dd88e7d88454a39a9748b42f3e7d827e5
|
7
|
+
data.tar.gz: 903cb0dca7f83acfa605db3be223833ffebe78d0e530b1e98599ed2b8b15e2b5babffb1e40704789bab45f42f3d8372a46e2263733428da2e9fea90860003cb0
|
@@ -0,0 +1,16 @@
|
|
1
|
+
name: Pull-Requests Check
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
Test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v2
|
10
|
+
- uses: ruby/setup-ruby@v1
|
11
|
+
with:
|
12
|
+
ruby-version: 2.5.1
|
13
|
+
bundler-cache: true
|
14
|
+
|
15
|
+
- name: Build and test with rspec
|
16
|
+
run: bundle exec rspec spec
|
data/Gemfile.lock
CHANGED
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
|
|
@@ -91,7 +94,66 @@ Field Name | Type | Optional? | Description
|
|
91
94
|
```:request_id``` | String | Yes | Generated by the SDK when needed (*do not set*)
|
92
95
|
```:content_id``` | String | No | Identifier for the content to be shown, must be set.
|
93
96
|
```:properties``` | Properties | Yes | Any additional custom properties to associate. For v1 integrations, it is fine not to fill in all the properties.
|
97
|
+
---
|
98
|
+
### Size
|
99
|
+
User's screen dimensions.
|
100
|
+
Field Name | Type | Optional? | Description
|
101
|
+
---------- | ---- | --------- | -----------
|
102
|
+
```:width``` | Integer | No | Screen width
|
103
|
+
```:height``` | Integer | No | Screen height
|
104
|
+
---
|
105
|
+
|
106
|
+
### ClientHints
|
107
|
+
Alternative to user-agent strings. See https://raw.githubusercontent.com/snowplow/iglu-central/master/schemas/org.ietf/http_client_hints/jsonschema/1-0-0
|
108
|
+
Field Name | Type | Optional? | Description
|
109
|
+
---------- | ---- | --------- | -----------
|
110
|
+
```:is_mobile``` | Boolean | Yes | Mobile flag
|
111
|
+
```:brand``` | Array of ClientBrandHint | Yes |
|
112
|
+
```:architecture``` | String | Yes |
|
113
|
+
```:model``` | String | Yes |
|
114
|
+
```:platform``` | String | Yes |
|
115
|
+
```:platform_version``` | String | Yes |
|
116
|
+
```:ua_full_version``` | String | Yes |
|
117
|
+
|
118
|
+
---
|
119
|
+
### ClientBrandHint
|
120
|
+
See https://raw.githubusercontent.com/snowplow/iglu-central/master/schemas/org.ietf/http_client_hints/jsonschema/1-0-0
|
121
|
+
Field Name | Type | Optional? | Description
|
122
|
+
---------- | ---- | --------- | -----------
|
123
|
+
```:brand``` | String | Yes | Mobile flag
|
124
|
+
```:version``` | String | Yes |
|
94
125
|
|
126
|
+
---
|
127
|
+
### Location
|
128
|
+
Information about the user's location.
|
129
|
+
Field Name | Type | Optional? | Description
|
130
|
+
---------- | ---- | --------- | -----------
|
131
|
+
```:latitude``` | Float | No | Location latitude
|
132
|
+
```:longitude``` | Float | No | Location longitude
|
133
|
+
```:accuracy_in_meters``` | Integer | Yes | Location accuracy if available
|
134
|
+
```:client_hints``` | ClientHints | Yes | HTTP client hints structure
|
135
|
+
---
|
136
|
+
|
137
|
+
### Browser
|
138
|
+
Information about the user's browser.
|
139
|
+
Field Name | Type | Optional? | Description
|
140
|
+
---------- | ---- | --------- | -----------
|
141
|
+
```:user_agent``` | String | Yes | Browser user agent string
|
142
|
+
```:viewport_size``` | Size | Yes | Size of the browser viewport
|
143
|
+
```:
|
144
|
+
---
|
145
|
+
### Device
|
146
|
+
Information about the user's device.
|
147
|
+
Field Name | Type | Optional? | Description
|
148
|
+
---------- | ---- | --------- | -----------
|
149
|
+
```:device_type``` | one of (`UNKNOWN_DEVICE_TYPE`, `DESKTOP`, `MOBILE`, `TABLET`) | Yes | Type of device
|
150
|
+
```:brand``` | String | Yes | "Apple, "google", Samsung", etc.
|
151
|
+
```:manufacturer``` | String | Yes | "Apple", "HTC", Motorola", "HUAWEI", etc.
|
152
|
+
```:identifier``` | String | Yes | Android: android.os.Build.MODEL; iOS: iPhoneXX,YY, etc.
|
153
|
+
```:screen``` | Screen | Yes | Screen dimensions
|
154
|
+
```:ip_address``` | String | Yes | Originating IP address
|
155
|
+
```:location``` | Location | Yes | Location information
|
156
|
+
```:browser``` | Browser | Yes | Browser information
|
95
157
|
---
|
96
158
|
### Paging
|
97
159
|
#### TODO
|
@@ -105,6 +167,7 @@ Field Name | Type | Optional? | Description
|
|
105
167
|
```: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)).
|
106
168
|
```:properties``` | Properties | Yes | Any additional custom properties to associate.
|
107
169
|
```:paging``` | Paging | Yes | Paging parameters (see TODO)
|
170
|
+
```:device``` | Device | Yes | Device information (as available)
|
108
171
|
---
|
109
172
|
### MetricsRequest
|
110
173
|
Input to ```prepare_for_logging```
|
@@ -139,6 +202,8 @@ Field Name | Type | Optional? | Description
|
|
139
202
|
---------- | ---- | --------- | -----------
|
140
203
|
```: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).
|
141
204
|
```: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.
|
205
|
+
```:client_request_id``` | String | Yes | Client-generated request id sent to Delivery API and may be useful for logging and debugging.
|
206
|
+
```:execution_server``` | one of 'API' or 'SDK' | Yes | Indicates if response insertions on a delivery request came from the API or the SDK.
|
142
207
|
---
|
143
208
|
|
144
209
|
### PromotedClient
|
@@ -216,12 +281,13 @@ metrics_request = {
|
|
216
281
|
}
|
217
282
|
|
218
283
|
# OPTIONAL: You can pass a custom function to "compact" insertions before metrics logging.
|
219
|
-
# Note that the PromotedClient has a class method helper,
|
220
|
-
|
221
|
-
|
222
|
-
|
284
|
+
# Note that the PromotedClient has a class method helper, remove_all_properties, that does
|
285
|
+
# an implementation of this, returning nil to remove ALL properties.
|
286
|
+
to_compact_metrics_properties_func = Proc.new do |properties|
|
287
|
+
properties[:struct].delete(:active)
|
288
|
+
properties
|
223
289
|
end
|
224
|
-
# metrics_request[:
|
290
|
+
# metrics_request[:to_compact_metrics_properties_func] = to_compact_metrics_properties_func
|
225
291
|
|
226
292
|
# Create a client
|
227
293
|
client = Promoted::Ruby::Client::PromotedClient.new
|
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.
|
7
|
+
5. Run (using new output) `gem push promoted-ruby-client-0.1.19.gem`
|
8
8
|
6. Update README with new version.
|
data/lib/promoted/ruby/client.rb
CHANGED
@@ -30,12 +30,10 @@ module Promoted
|
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
# A common compact method implementation.
|
34
|
-
def self.
|
35
|
-
Proc.new do |
|
36
|
-
|
37
|
-
insertion.delete(:properties)
|
38
|
-
insertion
|
33
|
+
# A common compact properties method implementation.
|
34
|
+
def self.remove_all_properties
|
35
|
+
Proc.new do |properties|
|
36
|
+
nil
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -73,7 +71,7 @@ module Promoted
|
|
73
71
|
@delivery_timeout_millis = params[:delivery_timeout_millis] || DEFAULT_DELIVERY_TIMEOUT_MILLIS
|
74
72
|
@metrics_timeout_millis = params[:metrics_timeout_millis] || DEFAULT_METRICS_TIMEOUT_MILLIS
|
75
73
|
|
76
|
-
@http_client = FaradayHTTPClient.new
|
74
|
+
@http_client = FaradayHTTPClient.new(@logger)
|
77
75
|
@validator = Promoted::Ruby::Client::Validator.new
|
78
76
|
|
79
77
|
@async_shadow_traffic = true
|
@@ -97,6 +95,10 @@ module Promoted
|
|
97
95
|
if params[:enabled] != nil
|
98
96
|
@enabled = params[:enabled] || false
|
99
97
|
end
|
98
|
+
|
99
|
+
if params[:warmup]
|
100
|
+
do_warmup
|
101
|
+
end
|
100
102
|
end
|
101
103
|
|
102
104
|
##
|
@@ -109,9 +111,9 @@ module Promoted
|
|
109
111
|
end
|
110
112
|
|
111
113
|
##
|
112
|
-
# Make a delivery request.
|
114
|
+
# Make a delivery request. If @perform_checks is set, input validation will occur and possibly raise errors.
|
113
115
|
def deliver args, headers={}
|
114
|
-
args = Promoted::Ruby::Client::Util.
|
116
|
+
args = Promoted::Ruby::Client::Util.translate_hash(args)
|
115
117
|
|
116
118
|
# Respect the enabled state
|
117
119
|
if !@enabled
|
@@ -124,7 +126,16 @@ module Promoted
|
|
124
126
|
delivery_request_builder = RequestBuilder.new
|
125
127
|
delivery_request_builder.set_request_params(args)
|
126
128
|
|
127
|
-
|
129
|
+
# perform_checks raises errors.
|
130
|
+
if @perform_checks
|
131
|
+
perform_common_checks!(args)
|
132
|
+
|
133
|
+
if args[:insertion_page_type] == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED'] then
|
134
|
+
err = DeliveryInsertionPageType.new
|
135
|
+
@logger.error(err) if @logger
|
136
|
+
raise err
|
137
|
+
end
|
138
|
+
end
|
128
139
|
|
129
140
|
delivery_request_builder.ensure_client_timestamp
|
130
141
|
|
@@ -135,10 +146,15 @@ module Promoted
|
|
135
146
|
only_log = delivery_request_builder.only_log != nil ? delivery_request_builder.only_log : @default_only_log
|
136
147
|
deliver_err = false
|
137
148
|
|
138
|
-
|
149
|
+
begin
|
150
|
+
@pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
|
151
|
+
rescue InvalidPagingError => err
|
139
152
|
# Invalid input, log and do SDK-side delivery.
|
140
|
-
@logger.warn(
|
141
|
-
|
153
|
+
@logger.warn(err) if @logger
|
154
|
+
return {
|
155
|
+
insertion: err.default_insertions_page
|
156
|
+
# No log request returned when no response insertions due to invalid paging
|
157
|
+
}
|
142
158
|
end
|
143
159
|
|
144
160
|
if !only_log
|
@@ -194,7 +210,9 @@ module Promoted
|
|
194
210
|
|
195
211
|
client_response = {
|
196
212
|
insertion: response_insertions,
|
197
|
-
log_request: log_req
|
213
|
+
log_request: log_req,
|
214
|
+
execution_server: insertions_from_delivery ? Promoted::Ruby::Client::EXECUTION_SERVER['API'] : Promoted::Ruby::Client::EXECUTION_SERVER['SDK'],
|
215
|
+
client_request_id: delivery_request_builder.client_request_id
|
198
216
|
}
|
199
217
|
return client_response
|
200
218
|
end
|
@@ -203,7 +221,7 @@ module Promoted
|
|
203
221
|
# Generate a log request for a subsequent call to send_log_request
|
204
222
|
# or for logging via alternative means.
|
205
223
|
def prepare_for_logging args, headers={}
|
206
|
-
args = Promoted::Ruby::Client::Util.
|
224
|
+
args = Promoted::Ruby::Client::Util.translate_hash(args)
|
207
225
|
|
208
226
|
if !@enabled
|
209
227
|
return {
|
@@ -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
|
|
@@ -300,13 +334,22 @@ module Promoted
|
|
300
334
|
delivery_request_builder = RequestBuilder.new
|
301
335
|
delivery_request_builder.set_request_params args
|
302
336
|
|
303
|
-
delivery_request_params = delivery_request_builder.delivery_request_params
|
337
|
+
delivery_request_params = delivery_request_builder.delivery_request_params
|
304
338
|
delivery_request_params[:client_info][:traffic_type] = Promoted::Ruby::Client::TRAFFIC_TYPE['SHADOW']
|
305
339
|
|
340
|
+
begin
|
341
|
+
@pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
|
342
|
+
rescue InvalidPagingError => err
|
343
|
+
# Invalid input, log and skip.
|
344
|
+
@logger.warn("Shadow traffic call failed with invalid paging #{err}") if @logger
|
345
|
+
return
|
346
|
+
end
|
347
|
+
|
306
348
|
# Call Delivery API and log/ignore errors.
|
307
349
|
start_time = Time.now
|
350
|
+
response = nil
|
308
351
|
begin
|
309
|
-
send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
|
352
|
+
response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
|
310
353
|
rescue StandardError => err
|
311
354
|
@logger.warn("Shadow traffic call failed with #{err}") if @logger
|
312
355
|
return
|
@@ -314,7 +357,8 @@ module Promoted
|
|
314
357
|
|
315
358
|
if !@async_shadow_traffic
|
316
359
|
ellapsed_time = Time.now - start_time
|
317
|
-
|
360
|
+
insertions = response ? response[:insertion] : []
|
361
|
+
@logger.info("Shadow traffic call completed in #{ellapsed_time.to_f * 1000} ms with #{insertions.length} insertions") if @logger
|
318
362
|
end
|
319
363
|
end
|
320
364
|
|
@@ -32,6 +32,13 @@ module Promoted
|
|
32
32
|
CLIENT_TYPE = {'UNKNOWN_REQUEST_CLIENT' => 'UNKNOWN_REQUEST_CLIENT',
|
33
33
|
'PLATFORM_SERVER' => 'PLATFORM_SERVER',
|
34
34
|
'PLATFORM_CLIENT' => 'PLATFORM_CLIENT'}
|
35
|
+
|
36
|
+
EXECUTION_SERVER = {'API' => 'API', 'SDK' => 'SDK'}
|
37
|
+
|
38
|
+
DEVICE_TYPE = {'UNKNOWN_DEVICE_TYPE' => 'UNKNOWN_DEVICE_TYPE',
|
39
|
+
'DESKTOP' => 'DESKTOP',
|
40
|
+
'MOBILE' => 'MOBILE',
|
41
|
+
'TABLET' => 'TABLET'}
|
35
42
|
end
|
36
43
|
end
|
37
44
|
end
|
@@ -1,15 +1,19 @@
|
|
1
1
|
require 'faraday'
|
2
2
|
require 'faraday_middleware'
|
3
|
+
require 'promoted/ruby/client/util'
|
3
4
|
|
4
5
|
module Promoted
|
5
6
|
module Ruby
|
6
7
|
module Client
|
7
8
|
class FaradayHTTPClient
|
8
9
|
|
9
|
-
def initialize
|
10
|
+
def initialize(logger = nil)
|
10
11
|
@conn = Faraday.new do |f|
|
11
12
|
f.request :json
|
12
13
|
f.request :retry, max: 3
|
14
|
+
if logger
|
15
|
+
f.response :logger, logger, { headers: false, bodies: true, log_level: :debug }
|
16
|
+
end
|
13
17
|
f.use Faraday::Response::RaiseError # raises on 4xx and 5xx responses
|
14
18
|
f.adapter :net_http_persistent
|
15
19
|
end
|
@@ -25,11 +29,15 @@ module Promoted
|
|
25
29
|
|
26
30
|
norm_headers = response.headers.transform_keys(&:downcase)
|
27
31
|
if norm_headers["content-type"] != nil && norm_headers["content-type"].start_with?("application/json")
|
28
|
-
JSON.parse(response.body
|
32
|
+
Promoted::Ruby::Client::Util.translate_hash(JSON.parse(response.body))
|
29
33
|
else
|
30
34
|
response.body
|
31
35
|
end
|
32
36
|
end
|
37
|
+
|
38
|
+
def get(endpoint)
|
39
|
+
@conn.get(endpoint).body
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
35
43
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
@@ -2,9 +2,9 @@ module Promoted
|
|
2
2
|
module Ruby
|
3
3
|
module Client
|
4
4
|
class RequestBuilder
|
5
|
-
attr_reader :session_id, :only_log, :experiment, :client_info,
|
6
|
-
:view_id, :insertion, :
|
7
|
-
:request_id, :full_insertion, :use_case, :request, :
|
5
|
+
attr_reader :session_id, :only_log, :experiment, :client_info, :device,
|
6
|
+
:view_id, :insertion, :to_compact_delivery_properties_func,
|
7
|
+
:request_id, :full_insertion, :use_case, :request, :to_compact_metrics_properties_func
|
8
8
|
|
9
9
|
attr_accessor :timing, :user_info, :platform_id
|
10
10
|
|
@@ -24,13 +24,14 @@ module Promoted
|
|
24
24
|
@session_id = request[:session_id]
|
25
25
|
@platform_id = request[:platform_id]
|
26
26
|
@client_info = request[:client_info] || {}
|
27
|
+
@device = request[:device] || {}
|
27
28
|
@view_id = request[:view_id]
|
28
29
|
@use_case = Promoted::Ruby::Client::USE_CASES[request[:use_case]] || Promoted::Ruby::Client::USE_CASES['UNKNOWN_USE_CASE']
|
29
30
|
@full_insertion = args[:full_insertion]
|
30
31
|
@user_info = request[:user_info] || { :user_id => nil, :log_user_id => nil}
|
31
32
|
@timing = request[:timing] || { :client_log_timestamp => Time.now.to_i }
|
32
|
-
@
|
33
|
-
@
|
33
|
+
@to_compact_metrics_properties_func = args[:to_compact_metrics_properties_func]
|
34
|
+
@to_compact_delivery_properties_func = args[:to_compact_delivery_properties_func]
|
34
35
|
|
35
36
|
# If the user didn't create a client request id, we do it for them.
|
36
37
|
request[:client_request_id] = request[:client_request_id] || @id_generator.newID
|
@@ -52,11 +53,12 @@ module Promoted
|
|
52
53
|
end
|
53
54
|
|
54
55
|
# Only used in delivery
|
55
|
-
def delivery_request_params
|
56
|
+
def delivery_request_params
|
56
57
|
params = {
|
57
58
|
user_info: user_info,
|
58
59
|
timing: timing,
|
59
60
|
client_info: @client_info.merge({ :client_type => Promoted::Ruby::Client::CLIENT_TYPE['PLATFORM_SERVER'] }),
|
61
|
+
device: @device,
|
60
62
|
platform_id: @platform_id,
|
61
63
|
view_id: @view_id,
|
62
64
|
session_id: @session_id,
|
@@ -64,9 +66,9 @@ module Promoted
|
|
64
66
|
search_query: request[:search_query],
|
65
67
|
properties: request[:properties],
|
66
68
|
paging: request[:paging],
|
67
|
-
client_request_id:
|
69
|
+
client_request_id: client_request_id
|
68
70
|
}
|
69
|
-
params[:insertion] =
|
71
|
+
params[:insertion] = insertions_with_compact_props(@to_compact_delivery_properties_func)
|
70
72
|
|
71
73
|
params.clean!
|
72
74
|
end
|
@@ -80,7 +82,10 @@ module Promoted
|
|
80
82
|
end
|
81
83
|
|
82
84
|
props = @full_insertion.each_with_object({}) do |insertion, hash|
|
83
|
-
|
85
|
+
if insertion.has_key?(:properties)
|
86
|
+
# Don't add nil properties to response insertions.
|
87
|
+
hash[insertion[:content_id]] = insertion[:properties]
|
88
|
+
end
|
84
89
|
end
|
85
90
|
|
86
91
|
filled_in_copy = []
|
@@ -99,10 +104,14 @@ module Promoted
|
|
99
104
|
params = {
|
100
105
|
user_info: user_info,
|
101
106
|
timing: timing,
|
102
|
-
|
103
|
-
|
107
|
+
client_info: @client_info,
|
108
|
+
device: @device
|
104
109
|
}
|
105
110
|
|
111
|
+
if @experiment
|
112
|
+
params[:cohort_membership] = [@experiment]
|
113
|
+
end
|
114
|
+
|
106
115
|
# Log request allows for multiple requests but here we only send one.
|
107
116
|
if include_request
|
108
117
|
request[:request_id] = request[:request_id] || @id_generator.newID
|
@@ -110,18 +119,38 @@ module Promoted
|
|
110
119
|
end
|
111
120
|
|
112
121
|
if include_insertions
|
113
|
-
params[:insertion] =
|
122
|
+
params[:insertion] = compact_metrics_properties if include_insertions
|
114
123
|
add_missing_ids_on_insertions! request, params[:insertion]
|
115
124
|
end
|
116
125
|
|
117
126
|
params.clean!
|
118
127
|
end
|
119
128
|
|
120
|
-
def
|
121
|
-
if
|
129
|
+
def compact_one_insertion(insertion, compact_func)
|
130
|
+
return insertion if (!compact_func || !insertion[:properties])
|
131
|
+
|
132
|
+
# Only need a copy if there are properties to compact.
|
133
|
+
compact_insertion = insertion.dup
|
134
|
+
|
135
|
+
# Let the custom function work with a deep copy of the properties.
|
136
|
+
# There's really no way to work with a shallow copy and still be able
|
137
|
+
# to restore the correct insertion properties after a call to delivery.
|
138
|
+
new_props = Marshal.load(Marshal.dump(insertion[:properties]))
|
139
|
+
compact_insertion[:properties] = compact_func.call(new_props)
|
140
|
+
compact_insertion.clean!
|
141
|
+
return compact_insertion
|
142
|
+
end
|
143
|
+
|
144
|
+
def insertions_with_compact_props(compact_func)
|
145
|
+
if !compact_func
|
146
|
+
# Nothing to do, avoid copying the whole array.
|
122
147
|
full_insertion
|
123
148
|
else
|
124
|
-
|
149
|
+
compact_insertions = Array.new(full_insertion.length)
|
150
|
+
full_insertion.each_with_index {|insertion, index|
|
151
|
+
compact_insertions[index] = compact_one_insertion(insertion, compact_func)
|
152
|
+
}
|
153
|
+
compact_insertions
|
125
154
|
end
|
126
155
|
end
|
127
156
|
|
@@ -132,7 +161,7 @@ module Promoted
|
|
132
161
|
end
|
133
162
|
|
134
163
|
# TODO: This looks overly complicated.
|
135
|
-
def
|
164
|
+
def compact_metrics_properties
|
136
165
|
@insertion = [] # insertion should be set according to the compact insertion
|
137
166
|
paging = request[:paging] || {}
|
138
167
|
size = paging[:size] ? paging[:size].to_i : 0
|
@@ -152,12 +181,16 @@ module Promoted
|
|
152
181
|
insertion_obj[:insertion_id] = @id_generator.newID
|
153
182
|
insertion_obj[:request_id] = request_id
|
154
183
|
insertion_obj[:position] = offset + index
|
155
|
-
insertion_obj =
|
184
|
+
insertion_obj = compact_one_insertion(insertion_obj, @to_compact_metrics_properties_func)
|
156
185
|
@insertion << insertion_obj.clean!
|
157
186
|
end
|
158
187
|
@insertion
|
159
188
|
end
|
160
189
|
|
190
|
+
def client_request_id
|
191
|
+
request[:client_request_id]
|
192
|
+
end
|
193
|
+
|
161
194
|
private
|
162
195
|
|
163
196
|
def add_missing_ids_on_insertions! request, insertions
|
@@ -2,16 +2,39 @@ module Promoted
|
|
2
2
|
module Ruby
|
3
3
|
module Client
|
4
4
|
module Util
|
5
|
-
def self.
|
5
|
+
def self.translate_array(arr)
|
6
|
+
sym_arr = Array.new(arr.length)
|
7
|
+
arr.each_with_index do |v, i|
|
8
|
+
new_v = v
|
9
|
+
case v
|
10
|
+
when Hash
|
11
|
+
new_v = translate_hash(v)
|
12
|
+
when Array
|
13
|
+
new_v = translate_array(v)
|
14
|
+
end
|
15
|
+
sym_arr[i] = new_v
|
16
|
+
end
|
17
|
+
sym_arr
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.translate_hash(args)
|
6
21
|
sym_hash = {}
|
7
22
|
args.each do |k, v|
|
8
|
-
|
23
|
+
new_key = k.to_s.to_underscore.to_sym
|
24
|
+
case v
|
25
|
+
when Hash
|
26
|
+
sym_hash[new_key] = translate_hash(v)
|
27
|
+
when Array
|
28
|
+
sym_hash[new_key] = translate_array(v)
|
29
|
+
else
|
30
|
+
sym_hash[new_key] = v
|
31
|
+
end
|
9
32
|
end
|
10
33
|
sym_hash
|
11
34
|
rescue => e
|
12
35
|
raise 'Unable to parse args. Please pass correct arguments. Must be JSON'
|
13
36
|
end
|
14
|
-
|
37
|
+
end
|
15
38
|
end
|
16
39
|
end
|
17
40
|
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.
|
4
|
+
version: 0.1.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- scottmcmaster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -123,6 +123,7 @@ extensions: []
|
|
123
123
|
extra_rdoc_files:
|
124
124
|
- README.md
|
125
125
|
files:
|
126
|
+
- ".github/workflows/push-pull-requests_check.yml"
|
126
127
|
- ".gitignore"
|
127
128
|
- Gemfile
|
128
129
|
- Gemfile.lock
|
@@ -167,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
168
|
- !ruby/object:Gem::Version
|
168
169
|
version: '0'
|
169
170
|
requirements: []
|
170
|
-
rubygems_version: 3.
|
171
|
+
rubygems_version: 3.2.24
|
171
172
|
signing_key:
|
172
173
|
specification_version: 4
|
173
174
|
summary: A Ruby Client to contact Promoted APIs.
|