promoted-ruby-client 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0f6d46482500c45c759f6b2965ae13a76aea2b3b0d18c069e31055fe985146f
4
- data.tar.gz: 7527ed13e8306bc4f9b53fd1add1a12c478c261be30b0b0dc50d16e146f88f10
3
+ metadata.gz: '06709e6306f4f601ce1f67c7a75926ab585fd47af125995491909c2ddcef85e8'
4
+ data.tar.gz: c6e3a18424e31abf4a35000e03335251c709bcde886e20b5a4d4a5f660414382
5
5
  SHA512:
6
- metadata.gz: fa5105d3d057f9e949dca0fa46a47c5d701e8dabec37f1d64619969827a580994245a6a83b5fbce399be06201f29d8758c2829e1d76b066bee8a4e8ea62fc892
7
- data.tar.gz: c6d29aeb9721ad09de063c21afd529495ff8c7eb3422886010124e85fc654fe711efeeb35d5b1a873f4f0ca4d56c064a119069f698f080a203daf797309ad8a9
6
+ metadata.gz: 4d323360ed9e65a00fa9da40f3645288ea93f68c1f4e7a29c1779b3f21ba5430bfd278534669839f7e35e7ddc370a70d4d3b5b7152a8ce4b9b17097ed297d924
7
+ data.tar.gz: fc75f38fbd9d8c14f8c8930b2459b4eed3e912c57360bbf383478fbad4c614abef09a76bfa9fd818173367e168b24a730e13143c56a02c3191cd1f862c33e0e6
data/Gemfile CHANGED
@@ -9,7 +9,6 @@ gem 'faraday', '~> 1.4.1'
9
9
  gem 'faraday_middleware'
10
10
  gem 'faraday-net_http'
11
11
  gem 'concurrent-ruby', require: 'concurrent'
12
- gem 'byebug'
13
12
 
14
13
  group :development do
15
14
  gem 'ruby-debug-ide', group: :development
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- promoted-ruby-client (0.1.1)
4
+ promoted-ruby-client (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -9,7 +9,6 @@ GEM
9
9
  ast (2.4.2)
10
10
  backport (1.2.0)
11
11
  benchmark (0.1.1)
12
- byebug (11.1.3)
13
12
  concurrent-ruby (1.1.9)
14
13
  debase (0.2.5.beta2)
15
14
  debase-ruby_core_source (>= 0.10.12)
@@ -111,7 +110,6 @@ PLATFORMS
111
110
 
112
111
  DEPENDENCIES
113
112
  bundler (~> 1.17)
114
- byebug
115
113
  concurrent-ruby
116
114
  debase (>= 0.2.5.beta2)
117
115
  faraday (~> 1.4.1)
@@ -126,4 +124,4 @@ DEPENDENCIES
126
124
  solargraph
127
125
 
128
126
  BUNDLED WITH
129
- 1.17.2
127
+ 1.17.3
data/README.md CHANGED
@@ -35,7 +35,7 @@ The `prepare_for_logging` call assumes the client has already handled pagination
35
35
  ## Example to run the client
36
36
 
37
37
  Install our Ruby client.
38
- `promoted-ruby-client (0.1.1)`
38
+ `promoted-ruby-client (0.1.2)`
39
39
 
40
40
  Or
41
41
 
data/dev.md CHANGED
@@ -5,5 +5,5 @@
5
5
  2. Get credentials for deployment from 1password.
6
6
  3. Modify `promoted-ruby-client.gemspec`'s push block.
7
7
  4. Run `gem build promoted-ruby-client.gemspec` to generate `gem`.
8
- 5. Run (using new output) `gem push promoted-ruby-client-0.1.1.gem`
8
+ 5. Run (using new output) `gem push promoted-ruby-client-0.1.2.gem`
9
9
  6. Update README with new version.
@@ -18,12 +18,23 @@ module Promoted
18
18
  attr_reader :perform_checks, :default_only_log, :delivery_timeout_millis, :metrics_timeout_millis, :should_apply_treatment_func,
19
19
  :default_request_headers
20
20
 
21
+ # A common compact method implementation.
22
+ def self.copy_and_remove_properties
23
+ Proc.new do |insertion|
24
+ insertion = Hash[insertion]
25
+ insertion.delete(:properties)
26
+ insertion
27
+ end
28
+ end
29
+
21
30
  def initialize (params={})
22
31
  @perform_checks = true
23
32
  if params[:perform_checks] != nil
24
33
  @perform_checks = params[:perform_checks]
25
34
  end
26
35
 
36
+ @logger = params[:logger] # Example: Logger.new(STDERR, :progname => "promotedai")
37
+
27
38
  @default_request_headers = params[:default_request_headers] || {}
28
39
  @default_request_headers['x-api-key'] = params[:api_key] || ''
29
40
 
@@ -47,6 +58,7 @@ module Promoted
47
58
 
48
59
  @http_client = FaradayHTTPClient.new
49
60
  @pool = Concurrent::CachedThreadPool.new
61
+ @validator = Promoted::Ruby::Client::Validator.new
50
62
  end
51
63
 
52
64
  def close
@@ -62,7 +74,11 @@ module Promoted
62
74
  @http_client.send(endpoint, timeout_millis, payload, use_headers)
63
75
  end
64
76
  else
65
- @http_client.send(endpoint, timeout_millis, payload, use_headers)
77
+ begin
78
+ @http_client.send(endpoint, timeout_millis, payload, use_headers)
79
+ rescue Faraday::Error => err
80
+ raise EndpointError.new(err)
81
+ end
66
82
  end
67
83
  end
68
84
 
@@ -72,9 +88,7 @@ module Promoted
72
88
  delivery_request_builder = RequestBuilder.new
73
89
  delivery_request_builder.set_request_params(args)
74
90
 
75
- if perform_checks?
76
- Promoted::Ruby::Client::Settings.check_that_log_ids_not_set!(args)
77
- end
91
+ perform_common_checks!(args) if @perform_checks
78
92
 
79
93
  pre_delivery_fillin_fields delivery_request_builder
80
94
 
@@ -85,16 +99,24 @@ module Promoted
85
99
  only_log = delivery_request_builder.only_log != nil ? delivery_request_builder.only_log : @default_only_log
86
100
  if !only_log
87
101
  cohort_membership_to_log = delivery_request_builder.new_cohort_membership_to_log
88
- end
102
+
103
+ if should_apply_treatment(cohort_membership_to_log)
104
+ delivery_request_params = delivery_request_builder.delivery_request_params
89
105
 
90
- if should_apply_treatment(cohort_membership_to_log)
91
- delivery_request_params = delivery_request_builder.delivery_request_params
92
-
93
- # Call Delivery API
94
- response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, headers)
95
-
96
- response_insertions = delivery_request_builder.fill_details_from_response(response[:insertion])
97
- insertions_from_promoted = true;
106
+ # Call Delivery API
107
+ begin
108
+ response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, headers)
109
+ rescue StandardError => err
110
+ # Currently we don't propagate errors to the SDK caller, but rather default to returning
111
+ # the request insertions.
112
+ @logger.error("Error calling delivery: " + err.message) if @logger
113
+ end
114
+
115
+ if response != nil && response[:insertion] != nil
116
+ response_insertions = delivery_request_builder.fill_details_from_response(response[:insertion])
117
+ insertions_from_promoted = true;
118
+ end
119
+ end
98
120
  end
99
121
 
100
122
  request_to_log = nil
@@ -141,21 +163,49 @@ module Promoted
141
163
  return client_response
142
164
  end
143
165
 
144
- def add_missing_ids_on_insertions! request, insertions
145
- insertions.each do |insertion|
146
- insertion[:insertion_id] = SecureRandom.uuid if not insertion[:insertion_id]
147
- insertion[:session_id] = request[:session_id] if request[:session_id]
148
- insertion[:request_id] = request[:request_id] if request[:request_id]
166
+ def prepare_for_logging args, headers={}
167
+ args = Promoted::Ruby::Client::Util.translate_args(args)
168
+
169
+ log_request_builder = RequestBuilder.new
170
+
171
+ # Note: This method expects as JSON (string keys) but internally, RequestBuilder
172
+ # transforms and works with symbol keys.
173
+ log_request_builder.set_request_params(args)
174
+ if @perform_checks
175
+ perform_common_checks! args
176
+
177
+ if @shadow_traffic_delivery_percent > 0 && args[:insertion_page_type] != Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'] then
178
+ raise ShadowTrafficInsertionPageType
179
+ end
180
+ end
181
+
182
+ pre_delivery_fillin_fields log_request_builder
183
+
184
+ if should_send_as_shadow_traffic?
185
+ deliver_shadow_traffic args, headers
149
186
  end
150
- end
151
187
 
152
- def perform_checks?
153
- @perform_checks
188
+ log_request_builder.log_request_params
154
189
  end
155
190
 
156
191
  # Sends a log request (previously created by a call to prepare_for_logging) to the metrics endpoint.
157
192
  def send_log_request log_request_params, headers={}
158
- send_request(log_request_params, @metrics_endpoint, @metrics_timeout_millis, headers)
193
+ begin
194
+ send_request(log_request_params, @metrics_endpoint, @metrics_timeout_millis, headers)
195
+ rescue StandardError => err
196
+ # Currently we don't propagate errors to the SDK caller.
197
+ @logger.error("Error from metrics: " + err.message) if @logger
198
+ end
199
+ end
200
+
201
+ private
202
+
203
+ def add_missing_ids_on_insertions! request, insertions
204
+ insertions.each do |insertion|
205
+ insertion[:insertion_id] = SecureRandom.uuid if not insertion[:insertion_id]
206
+ insertion[:session_id] = request[:session_id] if request[:session_id]
207
+ insertion[:request_id] = request[:request_id] if request[:request_id]
208
+ end
159
209
  end
160
210
 
161
211
  def should_send_as_shadow_traffic?
@@ -176,29 +226,14 @@ module Promoted
176
226
  send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, headers, true)
177
227
  end
178
228
 
179
- def prepare_for_logging args, headers={}
180
- args = Promoted::Ruby::Client::Util.translate_args(args)
181
-
182
- log_request_builder = RequestBuilder.new
183
-
184
- # Note: This method expects as JSON (string keys) but internally, RequestBuilder
185
- # transforms and works with symbol keys.
186
- log_request_builder.set_request_params(args)
187
- if perform_checks?
188
- Promoted::Ruby::Client::Settings.check_that_log_ids_not_set!(args)
189
-
190
- if @shadow_traffic_delivery_percent > 0 && args[:insertion_page_type] != Promoted::Ruby::Client::INSERTION_PAGING_TYPE['UNPAGED'] then
191
- raise ShadowTrafficInsertionPageType
192
- end
193
- end
194
-
195
- pre_delivery_fillin_fields log_request_builder
196
-
197
- if should_send_as_shadow_traffic?
198
- deliver_shadow_traffic args, headers
229
+ def perform_common_checks!(req)
230
+ begin
231
+ @validator.check_that_log_ids_not_set!(req)
232
+ @validator.validate_metrics_request!(req)
233
+ rescue StandardError => err
234
+ @logger.error(err) if @logger
235
+ raise
199
236
  end
200
-
201
- log_request_builder.log_request_params
202
237
  end
203
238
 
204
239
  def should_apply_treatment(cohort_membership)
@@ -217,15 +252,6 @@ module Promoted
217
252
  log_request_builder.timing[:client_log_timestamp] = Time.now.to_i
218
253
  end
219
254
  end
220
-
221
- # A common compact method implementation.
222
- def self.copy_and_remove_properties
223
- Proc.new do |insertion|
224
- insertion = Hash[insertion]
225
- insertion.delete(:properties)
226
- insertion
227
- end
228
- end
229
255
  end
230
256
  end
231
257
  end
@@ -234,7 +260,6 @@ end
234
260
  # dependent /libs
235
261
  require "promoted/ruby/client/request_builder"
236
262
  require "promoted/ruby/client/sampler"
237
- require "promoted/ruby/client/settings"
238
263
  require "promoted/ruby/client/util"
239
- require 'byebug'
264
+ require "promoted/ruby/client/validator"
240
265
  require 'securerandom'
@@ -1,40 +1,21 @@
1
1
  module Promoted
2
2
  module Ruby
3
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 full_insertion.'
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
4
+ class ShadowTrafficInsertionPageType < StandardError
23
5
  def message
24
- 'Insertion.insertionId should not be set'
6
+ 'Insertions must be unpaged when shadow traffic is on'
25
7
  end
26
8
  end
27
9
 
28
- class InsertionContentId < StandardError
29
- def message
30
- 'Insertion.contentId should be set'
10
+ class EndpointError < StandardError
11
+ attr_reader :cause
12
+ def initialize(cause)
13
+ @cause = cause
14
+ super('Error calling Promoted.ai endpoint')
31
15
  end
32
16
  end
33
17
 
34
- class ShadowTrafficInsertionPageType < StandardError
35
- def message
36
- 'Insertions must be unpaged when shadow traffic is on'
37
- end
18
+ class ValidationError < StandardError
38
19
  end
39
20
  end
40
21
  end
@@ -11,6 +11,7 @@ module Promoted
11
11
  f.request :json
12
12
  f.request :retry, max: 3
13
13
  f.adapter :net_http
14
+ f.use Faraday::Response::RaiseError # raises on 4xx and 5xx responses
14
15
  end
15
16
  end
16
17
 
@@ -22,8 +23,12 @@ module Promoted
22
23
  req.body = request.to_json
23
24
  end
24
25
 
25
- # TODO: Check response code, rescue on ParserError, etc.
26
- JSON.parse(response.body, :symbolize_names => true)
26
+ norm_headers = response.headers.transform_keys(&:downcase)
27
+ if norm_headers["content-type"] != nil && norm_headers["content-type"].start_with?("application/json")
28
+ JSON.parse(response.body, :symbolize_names => true)
29
+ else
30
+ response.body
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -14,6 +14,7 @@ module Promoted
14
14
  def set_request_params args = {}
15
15
  @request = args[:request] || {}
16
16
  @experiment = args[:experiment]
17
+ @only_log = args[:only_log]
17
18
  @session_id = request[:session_id]
18
19
  @platform_id = request[:platform_id]
19
20
  @client_info = request[:client_info] || {}
@@ -23,7 +24,6 @@ module Promoted
23
24
  @request_id = SecureRandom.uuid
24
25
  @user_info = request[:user_info] || { :user_id => nil, :log_user_id => nil}
25
26
  @timing = request[:timing] || { :client_log_timestamp => Time.now.to_i }
26
- @only_log = request[:only_log]
27
27
  @to_compact_metrics_insertion_func = args[:to_compact_metrics_insertion_func]
28
28
  @to_compact_delivery_insertion_func = args[:to_compact_delivery_insertion_func]
29
29
  end
@@ -77,35 +77,6 @@ module Promoted
77
77
  filled_in_copy
78
78
  end
79
79
 
80
- def validate_request_params
81
- # TODO
82
- end
83
-
84
- def to_compact_metrics_insertion_func
85
- @to_compact_metrics_insertion_func
86
- end
87
-
88
- def to_compact_delivery_insertion_func
89
- @to_compact_delivery_insertion_func
90
- end
91
-
92
- # A list of the response Insertions. This client expects lists to be truncated
93
- # already to request.paging.size. If not truncated, this client will truncate
94
- # the list.
95
- def insertion
96
- @insertion
97
- end
98
-
99
- # A way to turn off logging. Defaults to true.
100
- def enabled?
101
- @enabled
102
- end
103
-
104
- # Default values to use on DeliveryRequests.
105
- def default_request_values
106
- @default_request_values
107
- end
108
-
109
80
  def log_request_params(include_insertions: true, include_request: true)
110
81
  params = {
111
82
  user_info: user_info,
@@ -154,6 +125,14 @@ module Promoted
154
125
  @insertion
155
126
  end
156
127
 
128
+ private
129
+
130
+ # A list of the response Insertions. This client expects lists to be truncated
131
+ # already to request.paging.size. If not truncated, this client will truncate
132
+ # the list.
133
+ def insertion
134
+ @insertion
135
+ end
157
136
  end
158
137
  end
159
138
  end
@@ -0,0 +1,158 @@
1
+ module Promoted
2
+ module Ruby
3
+ module Client
4
+ class Validator
5
+ def validate_user_info!(ui)
6
+ validate_fields!(
7
+ ui,
8
+ "user info",
9
+ [
10
+ {
11
+ :name => :user_id,
12
+ :type => String
13
+ },
14
+ {
15
+ :name => :log_user_id,
16
+ :type => String
17
+ }
18
+ ]
19
+ )
20
+ end
21
+
22
+ def validate_insertion!(ins)
23
+ validate_fields!(
24
+ ins,
25
+ "insertion",
26
+ [
27
+ {
28
+ :name => :platform_id,
29
+ :type => Integer
30
+ },
31
+ {
32
+ :name => :insertion_id,
33
+ :type => String
34
+ },
35
+ {
36
+ :name => :request_id,
37
+ :type => String
38
+ },
39
+ {
40
+ :name => :view_id,
41
+ :type => String
42
+ },
43
+ {
44
+ :name => :session_id,
45
+ :type => String
46
+ },
47
+ {
48
+ :name => :content_id,
49
+ :type => String
50
+ },
51
+ {
52
+ :name => :position,
53
+ :type => Integer
54
+ },
55
+ {
56
+ :name => :delivery_score,
57
+ :type => Integer
58
+ }
59
+ ]
60
+ )
61
+
62
+ if ins[:user_info] then
63
+ self.validate_user_info! ins[:user_info]
64
+ end
65
+ end
66
+
67
+ def validate_request!(req)
68
+ validate_fields!(
69
+ req,
70
+ "request",
71
+ [
72
+ {
73
+ :name => :platform_id,
74
+ :type => Integer
75
+ },
76
+ {
77
+ :name => :request_id,
78
+ :type => String
79
+ },
80
+ {
81
+ :name => :view_id,
82
+ :type => String
83
+ },
84
+ {
85
+ :name => :session_id,
86
+ :type => String
87
+ },
88
+ {
89
+ :name => :insertion,
90
+ :type => Array
91
+ }
92
+ ]
93
+ )
94
+
95
+ if req[:insertion] then
96
+ req[:insertion].each {|ins|
97
+ validate_insertion! ins
98
+ }
99
+ end
100
+
101
+ if req[:user_info] then
102
+ validate_user_info! req[:user_info]
103
+ end
104
+ end
105
+
106
+ def validate_metrics_request!(metrics_req)
107
+ validate_fields!(
108
+ metrics_req,
109
+ "metrics request",
110
+ [
111
+ {
112
+ :name => :request,
113
+ :required => true
114
+ },
115
+ {
116
+ :name => :full_insertion,
117
+ :required => true,
118
+ :type => Array
119
+ }
120
+ ]
121
+ )
122
+
123
+ validate_request!(metrics_req[:request])
124
+ metrics_req[:full_insertion].each {|ins|
125
+ validate_insertion! ins
126
+ }
127
+ end
128
+
129
+ def check_that_log_ids_not_set! req
130
+ raise ValidationError.new("Request.requestId should not be set") if req.dig(:request, :request_id)
131
+ raise ValidationError.new("Do not set Request.insertion. Set full_insertion.") if req[:insertion]
132
+
133
+ req[:full_insertion].each do |insertion_hash|
134
+ raise ValidationError.new("Insertion.requestId should not be set") if insertion_hash[:request_id]
135
+ raise ValidationError.new("'Insertion.insertionId should not be set") if insertion_hash[:insertion_id]
136
+ raise ValidationError.new("Insertion.deliveryScore should not be set") if insertion_hash[:delivery_score]
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def validate_fields!(obj, obj_name, fields)
143
+ fields.each {|field|
144
+ if field[:required] then
145
+ raise ValidationError.new(field[:name].to_s + " is required on " + obj_name) if !obj.has_key?(field[:name])
146
+ end
147
+
148
+ if field[:type] && obj.has_key?(field[:name]) then
149
+ raise ValidationError.new(field[:name].to_s + " should be a " + field[:type].to_s) if !obj[field[:name]].is_a?(field[:type])
150
+ end
151
+ }
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ require "promoted/ruby/client/errors"
@@ -1,7 +1,7 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
6
6
  end
7
7
  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.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - scottmcmaster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-25 00:00:00.000000000 Z
11
+ date: 2021-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -77,8 +77,8 @@ files:
77
77
  - lib/promoted/ruby/client/faraday_http_client.rb
78
78
  - lib/promoted/ruby/client/request_builder.rb
79
79
  - lib/promoted/ruby/client/sampler.rb
80
- - lib/promoted/ruby/client/settings.rb
81
80
  - lib/promoted/ruby/client/util.rb
81
+ - lib/promoted/ruby/client/validator.rb
82
82
  - lib/promoted/ruby/client/version.rb
83
83
  - promoted-ruby-client.gemspec
84
84
  homepage: https://github.com/promotedai/promoted-ruby-client
@@ -1,21 +0,0 @@
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"