promoted-ruby-client 0.1.15 → 0.1.19
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/.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.
|