promoted-ruby-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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: []
|