promoted-ruby-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +57 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +21 -0
- data/README.md +197 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/promoted/ruby/client.rb +71 -0
- data/lib/promoted/ruby/client/errors.rb +35 -0
- data/lib/promoted/ruby/client/options.rb +206 -0
- data/lib/promoted/ruby/client/settings.rb +21 -0
- data/lib/promoted/ruby/client/version.rb +7 -0
- data/promoted-ruby-client.gemspec +34 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4494d298c8907ddb04588f1430d93ff2e83dd1978df0cfcb419df63db0cf302a
|
4
|
+
data.tar.gz: ab8105ad6c2dc8c8fc6992f5a312b240dcda3f13624fa3258fc769685ba9bdf3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 97a95326b32122f9d81292881c139cd0902f98c92dded7015d8ab7483c83b322f1e668f999da981a59e7c686523c74bf2ece6443e3dab26307cf44470f6a0cb9
|
7
|
+
data.tar.gz: 703316787c03c1e30eec766fda2f2247d357b8ad4e8c9b99765421a7d0fc119f998ba2cbb342044e229ad8067672a174a2f31f8cf62ce9206f545ba005fc1a11
|
data/.gitignore
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
# Ignore Byebug command history file.
|
17
|
+
.byebug_history
|
18
|
+
|
19
|
+
## Specific to RubyMotion:
|
20
|
+
.dat*
|
21
|
+
.repl_history
|
22
|
+
build/
|
23
|
+
*.bridgesupport
|
24
|
+
build-iPhoneOS/
|
25
|
+
build-iPhoneSimulator/
|
26
|
+
|
27
|
+
## Specific to RubyMotion (use of CocoaPods):
|
28
|
+
#
|
29
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
30
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
31
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
32
|
+
#
|
33
|
+
# vendor/Pods/
|
34
|
+
|
35
|
+
## Documentation cache and generated files:
|
36
|
+
/.yardoc/
|
37
|
+
/_yardoc/
|
38
|
+
/doc/
|
39
|
+
/rdoc/
|
40
|
+
|
41
|
+
## Environment normalization:
|
42
|
+
/.bundle/
|
43
|
+
/vendor/bundle
|
44
|
+
/lib/bundler/man/
|
45
|
+
|
46
|
+
# for a library or gem, you might want to ignore these files since the code is
|
47
|
+
# intended to run in multiple environments; otherwise, check them in:
|
48
|
+
# Gemfile.lock
|
49
|
+
# .ruby-version
|
50
|
+
# .ruby-gemset
|
51
|
+
|
52
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
53
|
+
.rvmrc
|
54
|
+
.rspec_status
|
55
|
+
|
56
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
57
|
+
# .rubocop-https?--*
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
promoted-ruby-client (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (11.1.3)
|
10
|
+
diff-lcs (1.4.4)
|
11
|
+
faraday (1.4.1)
|
12
|
+
faraday-excon (~> 1.1)
|
13
|
+
faraday-net_http (~> 1.0)
|
14
|
+
faraday-net_http_persistent (~> 1.1)
|
15
|
+
multipart-post (>= 1.2, < 3)
|
16
|
+
ruby2_keywords (>= 0.0.4)
|
17
|
+
faraday-excon (1.1.0)
|
18
|
+
faraday-net_http (1.0.1)
|
19
|
+
faraday-net_http_persistent (1.1.0)
|
20
|
+
multipart-post (2.1.1)
|
21
|
+
rake (10.5.0)
|
22
|
+
rspec (3.10.0)
|
23
|
+
rspec-core (~> 3.10.0)
|
24
|
+
rspec-expectations (~> 3.10.0)
|
25
|
+
rspec-mocks (~> 3.10.0)
|
26
|
+
rspec-core (3.10.1)
|
27
|
+
rspec-support (~> 3.10.0)
|
28
|
+
rspec-expectations (3.10.1)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.10.0)
|
31
|
+
rspec-mocks (3.10.2)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.10.0)
|
34
|
+
rspec-support (3.10.2)
|
35
|
+
ruby2_keywords (0.0.4)
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
ruby
|
39
|
+
|
40
|
+
DEPENDENCIES
|
41
|
+
bundler (~> 1.17)
|
42
|
+
byebug
|
43
|
+
faraday (~> 1.4.1)
|
44
|
+
promoted-ruby-client!
|
45
|
+
rake (~> 10.0)
|
46
|
+
rspec (~> 3.0)
|
47
|
+
|
48
|
+
BUNDLED WITH
|
49
|
+
1.17.2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Promoted
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# promoted-ruby-client
|
2
|
+
|
3
|
+
Ruby client designed for calling Promoted's Delivery and Metrics API.
|
4
|
+
|
5
|
+
This version of the library only supports preparing objects for logging. TODO - support Delivery API.
|
6
|
+
|
7
|
+
## Expected pseudo-code flow for Metrics logging
|
8
|
+
|
9
|
+
This example is for the integration where we do not want to modify the list of items to include `insertionId`. TODO - add this example too.
|
10
|
+
|
11
|
+
```
|
12
|
+
def get_items args
|
13
|
+
items = retrieve_items((args))
|
14
|
+
async_log_request(items)
|
15
|
+
return items
|
16
|
+
end
|
17
|
+
|
18
|
+
# Done async
|
19
|
+
def log_request items
|
20
|
+
log_request = Promoted::Ruby::Client.prepare_for_logging(input)
|
21
|
+
# Send JSON to Metrics API.
|
22
|
+
log_to_promoted_event_api(log_request)
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
## Naming details
|
27
|
+
|
28
|
+
`fullInsertion` - for `prepare_for_logging`, this is the current page of `Insertion`s with full `Insertion.properties` filled with the item details. For v1 integrations, it is fine to not fill in the full properties.
|
29
|
+
|
30
|
+
## Pagination
|
31
|
+
|
32
|
+
The `prepare_for_logging` call assumes the client has already handled pagination. It needs a `Request.paging.from` to be passed in for the number of items deep that the page is.
|
33
|
+
|
34
|
+
## Example to run the client
|
35
|
+
|
36
|
+
1. Clone the repo on your local machine
|
37
|
+
2. `cd promoted-ruby-client`
|
38
|
+
3. `bundle`
|
39
|
+
4. `irb -Ilib -rpromoted/ruby/client`
|
40
|
+
|
41
|
+
A console will launch with the library loaded. Here is example code to use.
|
42
|
+
|
43
|
+
```
|
44
|
+
products = [
|
45
|
+
{
|
46
|
+
id: "123",
|
47
|
+
type: "SHOE",
|
48
|
+
name: "Blue shoe",
|
49
|
+
total_sales: 1000
|
50
|
+
},
|
51
|
+
{
|
52
|
+
id: "124",
|
53
|
+
type: "SHIRT",
|
54
|
+
name: "Green shirt",
|
55
|
+
total_sales: 800
|
56
|
+
},
|
57
|
+
{
|
58
|
+
id: "125",
|
59
|
+
type: "DRESS",
|
60
|
+
name: "Red dress",
|
61
|
+
total_sales: 1200
|
62
|
+
}
|
63
|
+
]
|
64
|
+
|
65
|
+
# Converts the Products to a list of Insertions.
|
66
|
+
def to_insertions products
|
67
|
+
@to_insertions = []
|
68
|
+
products.each_with_index do |product, index|
|
69
|
+
@to_insertions << {
|
70
|
+
content_id: product[:id],
|
71
|
+
properties: {
|
72
|
+
struct: {
|
73
|
+
product: product.reject { |k, v| [:id].include? k }
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
@to_insertions
|
79
|
+
end
|
80
|
+
|
81
|
+
request_input = {
|
82
|
+
request: {
|
83
|
+
user_info: { user_id: "912", log_user_id: "912191"},
|
84
|
+
use_case: "FEED",
|
85
|
+
paging: {
|
86
|
+
from: 10,
|
87
|
+
size: 5
|
88
|
+
},
|
89
|
+
properties: {
|
90
|
+
struct: {
|
91
|
+
active: true
|
92
|
+
}
|
93
|
+
}
|
94
|
+
},
|
95
|
+
fullInsertion: to_insertions(products)
|
96
|
+
}
|
97
|
+
|
98
|
+
log_request = Promoted::Ruby::Client.prepare_for_logging(request_input)
|
99
|
+
log_request.to_json
|
100
|
+
```
|
101
|
+
|
102
|
+
`log_request.to_json` returns a result that looks like the following
|
103
|
+
```
|
104
|
+
=> "{\"user_info\":{\"user_id\":\"912\",\"log_user_id\":\"912191\"},\"timing\":{\"client_log_timestamp\":1623306198},\"request\":[{\"user_info\":{\"user_id\":\"912\",\"log_user_id\":\"912191\"},\"use_case\":\"FEED\",\"paging\":{\"from\":10,\"size\":10},\"properties\":{\"struct\":{\"active\":true}}}],\"insertion\":[{\"content_id\":\"123\",\"properties\":{\"struct\":{\"product\":{\"type\":\"SHOE\",\"name\":\"Blue shoe\",\"total_sales\":1000}}},\"user_info\":{\"user_id\":\"912\",\"log_user_id\":\"912191\"},\"timing\":{\"client_log_timestamp\":1623306198},\"insertion_id\":\"a87e1b57-a574-424f-8af6-10e0250aa7ab\",\"request_id\":\"54ff4884-2192-4180-8c72-a805a436980f\",\"position\":10},{\"content_id\":\"124\",\"properties\":{\"struct\":{\"product\":{\"type\":\"SHIRT\",\"name\":\"Green shirt\",\"total_sales\":800}}},\"user_info\":{\"user_id\":\"912\",\"log_user_id\":\"912191\"},\"timing\":{\"client_log_timestamp\":1623306198},\"insertion_id\":\"4495f72a-8101-4cb8-94ce-4db76839b8b6\",\"request_id\":\"54ff4884-2192-4180-8c72-a805a436980f\",\"position\":11},{\"content_id\":\"125\",\"properties\":{\"struct\":{\"product\":{\"type\":\"DRESS\",\"name\":\"Red dress\",\"total_sales\":1200}}},\"user_info\":{\"user_id\":\"912\",\"log_user_id\":\"912191\"},\"timing\":{\"client_log_timestamp\":1623306198},\"insertion_id\":\"d1e4f3f6-1783-4059-8fab-fdf2ba343cdf\",\"request_id\":\"54ff4884-2192-4180-8c72-a805a436980f\",\"position\":12}]}"
|
105
|
+
```
|
106
|
+
|
107
|
+
## Other input syntaxes
|
108
|
+
|
109
|
+
The client should also work if Hash rocket too.
|
110
|
+
```
|
111
|
+
products = [
|
112
|
+
{
|
113
|
+
"id"=>"123",
|
114
|
+
"type"=>"SHOE",
|
115
|
+
"name"=>"Blue shoe",
|
116
|
+
"totalSales"=>1000
|
117
|
+
},
|
118
|
+
{
|
119
|
+
"id"=>"124",
|
120
|
+
"type"=>"SHIRT",
|
121
|
+
"name"=>"Green shirt",
|
122
|
+
"totalSales"=>800
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"id"=>"125",
|
126
|
+
"type"=>"DRESS",
|
127
|
+
"name"=>"Red dress",
|
128
|
+
"totalSales"=>1200
|
129
|
+
}
|
130
|
+
]
|
131
|
+
|
132
|
+
input = {
|
133
|
+
"request"=>{
|
134
|
+
"user_info"=>{"user_id"=> "912", "log_user_id"=> "912191"},
|
135
|
+
"use_case"=>"FEED",
|
136
|
+
"properties"=>{
|
137
|
+
"struct"=>{
|
138
|
+
"active"=>true
|
139
|
+
}
|
140
|
+
}
|
141
|
+
},
|
142
|
+
"fullInsertion"=>to_insertions(products)
|
143
|
+
}
|
144
|
+
```
|
145
|
+
|
146
|
+
Or inlined full request.
|
147
|
+
```
|
148
|
+
input = {
|
149
|
+
"request"=>{
|
150
|
+
"user_info"=>{"user_id"=> "912", "log_user_id"=> "912191"},
|
151
|
+
"use_case"=>"FEED",
|
152
|
+
"properties"=>{
|
153
|
+
"struct"=>{
|
154
|
+
"active"=>true
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},
|
158
|
+
"fullInsertion"=>[
|
159
|
+
{
|
160
|
+
"contentId"=>"123",
|
161
|
+
"properties"=>{
|
162
|
+
"struct"=>{
|
163
|
+
"product"=>{
|
164
|
+
"type"=>"SHOE",
|
165
|
+
"name"=>"Blue shoe",
|
166
|
+
"totalSales"=>1000
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
},
|
171
|
+
{
|
172
|
+
"contentId"=>"124",
|
173
|
+
"properties"=>{
|
174
|
+
"struct"=>{
|
175
|
+
"product"=>{
|
176
|
+
"type"=>"SHIRT",
|
177
|
+
"name"=>"Green shirt",
|
178
|
+
"totalSales"=>800
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
},
|
183
|
+
{
|
184
|
+
"contentId"=>"125",
|
185
|
+
"properties"=>{
|
186
|
+
"struct"=>{
|
187
|
+
"product"=>{
|
188
|
+
"type"=>"DRESS",
|
189
|
+
"name"=>"Red dress",
|
190
|
+
"totalSales"=>1200
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
]
|
196
|
+
}
|
197
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "promoted/ruby/client"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require "promoted/ruby/client/version"
|
2
|
+
require 'faraday'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Promoted
|
6
|
+
module Ruby
|
7
|
+
module Client
|
8
|
+
class Error < StandardError; end
|
9
|
+
attr_accessor :options
|
10
|
+
BASE_URL = "http://wh12.lvh.me:3000"
|
11
|
+
DELIVERY_ENDPOINT = "#{BASE_URL}/deliver"
|
12
|
+
LOGGING_ENDPOINT = "#{BASE_URL}/log_request"
|
13
|
+
|
14
|
+
def self.send_request payload, endpoint=nil
|
15
|
+
endpoint ||= BASE_URL
|
16
|
+
response = Faraday.post(endpoint) do |req|
|
17
|
+
req.headers['Content-Type'] = 'application/json'
|
18
|
+
req.body = payload.to_json
|
19
|
+
end
|
20
|
+
response
|
21
|
+
end
|
22
|
+
|
23
|
+
def deliver payload={}
|
24
|
+
# TODO
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.log_request args={}, options={}
|
28
|
+
endpoint = options[:endpoint]
|
29
|
+
payload = prepare_for_logging(args)
|
30
|
+
send_request(payload, endpoint)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.prepare_for_logging args
|
34
|
+
options.set_request_params(args)
|
35
|
+
if options.perform_checks
|
36
|
+
Promoted::Ruby::Client::Settings.check_that_log_ids_not_set(args)
|
37
|
+
pre_delivery_fillin_fields
|
38
|
+
end
|
39
|
+
options.log_request_params
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.pre_delivery_fillin_fields
|
43
|
+
if !options.timing[:client_log_timestamp].present?
|
44
|
+
options.client_log_timestamp = Time.now.to_i
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.options
|
49
|
+
@options ||= Options.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.promoted_client_impl params={}
|
53
|
+
# Dummy implementation
|
54
|
+
# TODO will implement it in details
|
55
|
+
perform_checks = params[:perform_checks] || true
|
56
|
+
only_Log = params[:only_Log] || false
|
57
|
+
uuid = params[:uuid]
|
58
|
+
now_millis = params[:now_millis] || Time.now.to_i
|
59
|
+
delivery_timeout_millis = params[:delivery_timeout_millis] || DEFAULT_DELIVERY_TIMEOUT_MILLIS
|
60
|
+
metrics_timeout_millis = params[:metrics_timeout_millis] || DEFAULT_METRICS_TIMEOUT_MILLIS
|
61
|
+
should_apply_treatment = params[:should_apply_treatment] || false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# dependent /libs
|
68
|
+
require "promoted/ruby/client/options"
|
69
|
+
require "promoted/ruby/client/settings"
|
70
|
+
require 'byebug'
|
71
|
+
require 'securerandom'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Promoted
|
2
|
+
module Ruby
|
3
|
+
module Client
|
4
|
+
class RequestError < StandardError
|
5
|
+
def message
|
6
|
+
'Request.requestId should not be set'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class RequestInsertionError < StandardError
|
11
|
+
def message
|
12
|
+
'Do not set Request.insertion. Set fullInsertion.'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class InsertionRequestIdError < StandardError
|
17
|
+
def message
|
18
|
+
'Insertion.requestId should not be set'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class InsertionIdError < StandardError
|
23
|
+
def message
|
24
|
+
'Insertion.insertionId should not be set'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class InsertionContentId < StandardError
|
29
|
+
def message
|
30
|
+
'Insertion.contentId should be set'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Promoted
|
2
|
+
module Ruby
|
3
|
+
module Client
|
4
|
+
DELIVERY_TIMEOUT_MILLIS = 30000
|
5
|
+
DEFAULT_METRICS_TIMEOUT_MILLIS = 250
|
6
|
+
|
7
|
+
class Options
|
8
|
+
attr_accessor :delivery_timeout_millis, :session_id, :perform_checks,
|
9
|
+
:uuid, :metrics_timeout_millis, :now_millis, :should_apply_treatment,
|
10
|
+
:view_id, :user_id, :insertion, :client_log_timestamp,
|
11
|
+
:request_id, :full_insertion, :use_case, :request
|
12
|
+
|
13
|
+
def initialize()
|
14
|
+
# TODO
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_request_params args = {}
|
18
|
+
args = translate_args(args)
|
19
|
+
@request = args[:request]
|
20
|
+
@delivery_timeout_millis = args[:delivery_timeout_millis] || DELIVERY_TIMEOUT_MILLIS
|
21
|
+
@session_id = args[:session_id]
|
22
|
+
@user_id = args[:user_id]
|
23
|
+
@log_user_id = args[:log_user_id]
|
24
|
+
@view_id = args[:view_id]
|
25
|
+
@perform_checks = args[:perform_checks] || false
|
26
|
+
@only_Log = args[:only_Log] || false
|
27
|
+
@uuid = args[:uuid]
|
28
|
+
@use_case = args[:use_case] || 'FEED'
|
29
|
+
@now_millis = args[:now_millis] || Time.now.to_i
|
30
|
+
@metrics_timeout_millis = args[:metrics_timeout_millis] || DEFAULT_METRICS_TIMEOUT_MILLIS
|
31
|
+
@should_apply_treatment = args[:should_apply_treatment] || false
|
32
|
+
@full_insertion = args[:full_insertion]
|
33
|
+
@insertion = args[:insertion] || []
|
34
|
+
@client_log_timestamp = args[:client_log_timestamp] || Time.now.to_i
|
35
|
+
@request_id = SecureRandom.uuid
|
36
|
+
end
|
37
|
+
|
38
|
+
def translate_args(args)
|
39
|
+
args.transform_keys(&:to_s).transform_keys(&:to_underscore).transform_keys(&:to_sym)
|
40
|
+
rescue => e
|
41
|
+
raise 'Unable to parse args. Please pass correct arguments. Must be JSON'
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_request_params
|
45
|
+
# TODO
|
46
|
+
end
|
47
|
+
|
48
|
+
def request
|
49
|
+
@request
|
50
|
+
end
|
51
|
+
|
52
|
+
def client_log_timestamp
|
53
|
+
@client_log_timestamp
|
54
|
+
end
|
55
|
+
|
56
|
+
def view_id
|
57
|
+
@view_id
|
58
|
+
end
|
59
|
+
|
60
|
+
def user_id
|
61
|
+
return @user_id if @user_id
|
62
|
+
@user_id = request.dig(:user_info, :user_id)
|
63
|
+
@user_id ||= request.dig('user_info', 'user_id')
|
64
|
+
@user_id
|
65
|
+
end
|
66
|
+
|
67
|
+
def session_id
|
68
|
+
@session_id
|
69
|
+
end
|
70
|
+
|
71
|
+
# A list of the response Insertions. This client expects lists to be truncated
|
72
|
+
# already to request.paging.size. If not truncated, this client will truncate
|
73
|
+
# the list.
|
74
|
+
def insertion
|
75
|
+
@insertion
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_user_id
|
79
|
+
return @log_user_id if @log_user_id
|
80
|
+
@log_user_id = request.dig(:user_info, :log_user_id)
|
81
|
+
@log_user_id ||= request.dig('user_info', 'log_user_id')
|
82
|
+
@log_user_id
|
83
|
+
end
|
84
|
+
|
85
|
+
# A way to turn off logging. Defaults to true.
|
86
|
+
def enabled?
|
87
|
+
@enabled
|
88
|
+
end
|
89
|
+
|
90
|
+
# Performs extra dev checks. Safer but slower. Defaults to true.
|
91
|
+
def perform_checks?
|
92
|
+
@perform_checks
|
93
|
+
end
|
94
|
+
|
95
|
+
# Default values to use on DeliveryRequests.
|
96
|
+
def default_request_values
|
97
|
+
@default_request_values
|
98
|
+
end
|
99
|
+
|
100
|
+
# Required as a dependency so clients can load reduce dependency on multiple
|
101
|
+
# uuid libraries.
|
102
|
+
def uuid
|
103
|
+
@uuid
|
104
|
+
end
|
105
|
+
|
106
|
+
# Defaults to 250ms
|
107
|
+
def delivery_timeout_millis
|
108
|
+
@delivery_timeout_millis
|
109
|
+
end
|
110
|
+
|
111
|
+
# Defaults to 3000ms
|
112
|
+
def metrics_timeout_millis
|
113
|
+
@metrics_timeout_millis
|
114
|
+
end
|
115
|
+
|
116
|
+
# For testing. Allows for easy mocking of the clock.
|
117
|
+
def now_millis
|
118
|
+
@now_millis
|
119
|
+
end
|
120
|
+
|
121
|
+
def only_Log
|
122
|
+
@only_Log
|
123
|
+
end
|
124
|
+
|
125
|
+
def full_insertion
|
126
|
+
@full_insertion
|
127
|
+
end
|
128
|
+
|
129
|
+
def user_info
|
130
|
+
{
|
131
|
+
user_id: user_id,
|
132
|
+
log_user_id: log_user_id
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def timing
|
137
|
+
@timing = {
|
138
|
+
client_log_timestamp: client_log_timestamp
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
def request_id
|
143
|
+
@request_id
|
144
|
+
end
|
145
|
+
|
146
|
+
def log_request_params
|
147
|
+
{
|
148
|
+
user_info: user_info,
|
149
|
+
timing: timing,
|
150
|
+
request: [request],
|
151
|
+
insertion: compact_insertions
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
def request_params include_insertion: true
|
156
|
+
@request_params = {
|
157
|
+
user_info: user_info,
|
158
|
+
timing: timing,
|
159
|
+
request_id: request_id,
|
160
|
+
view_id: view_id,
|
161
|
+
session_id: session_id,
|
162
|
+
insertion: compact_insertions
|
163
|
+
}
|
164
|
+
@request_params.merge!({insertion: compact_insertions}) if include_insertion
|
165
|
+
@request_params
|
166
|
+
end
|
167
|
+
|
168
|
+
def compact_insertions
|
169
|
+
@compact_insertions = []
|
170
|
+
insertions_to_compact = full_insertion
|
171
|
+
paging = request[:paging] || {}
|
172
|
+
size = paging[:size]
|
173
|
+
unless size.nil? || size == 0
|
174
|
+
insertions_to_compact = insertions_to_compact[0..size-1]
|
175
|
+
end
|
176
|
+
from = paging[:from].to_i
|
177
|
+
insertions_to_compact.each_with_index do |insertion_obj, index|
|
178
|
+
# TODO - this does not look performant.
|
179
|
+
insertion_obj = insertion_obj.transform_keys{ |key| key.to_s.to_underscore.to_sym }
|
180
|
+
insertion_obj[:user_info] = user_info
|
181
|
+
insertion_obj[:timing] = timing
|
182
|
+
insertion_obj[:insertion_id] = SecureRandom.uuid # generate random UUID
|
183
|
+
insertion_obj[:request_id] = request_id
|
184
|
+
insertion_obj[:position] = from + index
|
185
|
+
@compact_insertions << insertion_obj
|
186
|
+
end
|
187
|
+
@compact_insertions
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class String
|
196
|
+
# Ruby mutation methods have the expectation to return self if a mutation occurred, nil otherwise. (see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-gsub-21)
|
197
|
+
def to_underscore!
|
198
|
+
gsub!(/(.)([A-Z])/,'\1_\2')
|
199
|
+
downcase!
|
200
|
+
end
|
201
|
+
|
202
|
+
def to_underscore
|
203
|
+
dup.tap { |s| s.to_underscore! }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
require 'securerandom'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Promoted
|
2
|
+
module Ruby
|
3
|
+
module Client
|
4
|
+
class Settings
|
5
|
+
|
6
|
+
def self.check_that_log_ids_not_set! options_hash
|
7
|
+
raise RequestError if options_hash.dig("request", "request_id")
|
8
|
+
raise RequestInsertionError if options_hash["insertion"]
|
9
|
+
|
10
|
+
options_hash["full_insertion"].each do |insertion_hash|
|
11
|
+
raise InsertionRequestIdError if insertion_hash["request_id"]
|
12
|
+
raise InsertionIdError if insertion_hash["insertion_id"]
|
13
|
+
end
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require "promoted/ruby/client/errors"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "promoted/ruby/client/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "promoted-ruby-client"
|
8
|
+
spec.version = Promoted::Ruby::Client::VERSION
|
9
|
+
spec.authors = ["danbosnichill"]
|
10
|
+
spec.email = ["dhill@promoted.ai"]
|
11
|
+
|
12
|
+
spec.summary = 'A Ruby Client to contact Promoted APIs.'
|
13
|
+
spec.description = 'This is primarily intended to be used when logging Requests and Insertions on a backend server.'
|
14
|
+
spec.homepage = 'https://github.com/promotedai/promoted-ruby-client'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/promotedai/promoted-ruby-client"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/promotedai/promoted-ruby-client/blob/master/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.extra_rdoc_files = ['README.md']
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
32
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: promoted-ruby-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- danbosnichill
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: This is primarily intended to be used when logging Requests and Insertions
|
56
|
+
on a backend server.
|
57
|
+
email:
|
58
|
+
- dhill@promoted.ai
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files:
|
62
|
+
- README.md
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- LICENSE
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/console
|
71
|
+
- bin/setup
|
72
|
+
- lib/promoted/ruby/client.rb
|
73
|
+
- lib/promoted/ruby/client/errors.rb
|
74
|
+
- lib/promoted/ruby/client/options.rb
|
75
|
+
- lib/promoted/ruby/client/settings.rb
|
76
|
+
- lib/promoted/ruby/client/version.rb
|
77
|
+
- promoted-ruby-client.gemspec
|
78
|
+
homepage: https://github.com/promotedai/promoted-ruby-client
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata:
|
82
|
+
homepage_uri: https://github.com/promotedai/promoted-ruby-client
|
83
|
+
source_code_uri: https://github.com/promotedai/promoted-ruby-client
|
84
|
+
changelog_uri: https://github.com/promotedai/promoted-ruby-client/blob/master/CHANGELOG.md
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubygems_version: 3.0.3
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: A Ruby Client to contact Promoted APIs.
|
104
|
+
test_files: []
|