sift 1.1.0 → 4.0.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.
data/lib/sift/client.rb CHANGED
@@ -1,161 +1,745 @@
1
- require 'httparty'
2
- require 'multi_json'
1
+ require "httparty"
2
+ require "multi_json"
3
+ require "base64"
4
+
5
+ require_relative "./client/decision"
6
+ require_relative "./error"
3
7
 
4
8
  module Sift
5
9
 
6
10
  # Represents the payload returned from a call through the track API
7
11
  #
8
12
  class Response
9
- attr_reader :json
10
- attr_reader :http_status_code
11
- attr_reader :api_status
12
- attr_reader :api_error_message
13
- attr_reader :original_request
13
+ attr_reader :body,
14
+ :http_class,
15
+ :http_status_code,
16
+ :api_status,
17
+ :api_error_message,
18
+ :request,
19
+ :api_error_description,
20
+ :api_error_issues
14
21
 
15
22
  # Constructor
16
23
  #
17
- # == Parameters:
18
- # http_response
24
+ # ==== Parameters:
25
+ #
26
+ # http_response::
19
27
  # The HTTP body text returned from the API call. The body is expected to be
20
28
  # a JSON object that can be decoded into status, message and request
21
29
  # sections.
22
30
  #
23
- def initialize(http_response, http_response_code)
24
- @json = MultiJson.load(http_response)
25
- @original_request = MultiJson.load(@json["request"].to_s) if @json["request"]
31
+ def initialize(http_response, http_response_code, http_raw_response)
26
32
  @http_status_code = http_response_code
27
- @api_status = @json["status"].to_i
28
- @api_error_message = @json["error_message"].to_s
33
+ @http_raw_response = http_raw_response
34
+
35
+ # only set these variables if a message-body is expected.
36
+ if not @http_raw_response.kind_of? Net::HTTPNoContent
37
+
38
+ begin
39
+ @body = MultiJson.load(http_response) unless http_response.nil?
40
+ rescue
41
+ if @http_status_code == 200
42
+ raise TypeError.new
43
+ end
44
+ end
45
+
46
+ if not @body.nil?
47
+ @request = MultiJson.load(@body["request"].to_s) if @body["request"]
48
+ @api_status = @body["status"].to_i if @body["status"]
49
+ @api_error_message = @body["error_message"]
50
+
51
+ if @body["error"]
52
+ @api_error_message = @body["error"]
53
+ @api_error_description = @body["description"]
54
+ @api_error_issues = @body["issues"] || {}
55
+ end
56
+ end
57
+ end
29
58
  end
30
59
 
31
60
  # Helper method returns true if and only if the response from the API call was
32
61
  # successful
33
62
  #
34
- # == Returns:
35
- # true on success; false otherwise
63
+ # ==== Returns:
64
+ #
65
+ # true on success; false otherwise
66
+ #
36
67
  def ok?
37
- 0 == @api_status.to_i
68
+ if @http_raw_response.kind_of? Net::HTTPNoContent
69
+ #if there is no content expected, use HTTP code
70
+ 204 == @http_status_code
71
+ else
72
+ # otherwise use API status
73
+ @http_raw_response.kind_of? Net::HTTPOK and 0 == @api_status.to_i
74
+ end
75
+ end
76
+
77
+
78
+ # DEPRECATED
79
+ # Getter method for deprecated 'json' member variable.
80
+ def json
81
+ @body
82
+ end
83
+
84
+ # DEPRECATED
85
+ # Getter method for deprecated 'original_request' member variable.
86
+ def original_request
87
+ @request
38
88
  end
39
89
  end
40
90
 
41
91
  # This class wraps accesses through the API
42
92
  #
43
93
  class Client
44
- API_ENDPOINT = "https://api.siftscience.com"
45
- API_TIMEOUT = 2
94
+ API_ENDPOINT = ENV["SIFT_RUBY_API_URL"] || 'https://api.siftscience.com'
95
+ API3_ENDPOINT = ENV["SIFT_RUBY_API3_URL"] || 'https://api3.siftscience.com'
46
96
 
47
97
  include HTTParty
48
98
  base_uri API_ENDPOINT
49
- default_timeout API_TIMEOUT
99
+
100
+ attr_reader :api_key, :account_id
101
+
102
+ def self.build_auth_header(api_key)
103
+ { "Authorization" => "Basic #{Base64.encode64(api_key)}" }
104
+ end
105
+
106
+ def self.user_agent
107
+ "sift-ruby/#{VERSION}"
108
+ end
50
109
 
51
110
  # Constructor
52
111
  #
53
- # == Parameters:
54
- # api_key
55
- # The Sift Science API key associated with your customer account. This parameter
56
- # cannot be nil or blank.
57
- # path
58
- # The path to the event API, e.g., "/v201/events"
112
+ # ==== Parameters:
113
+ #
114
+ # opts (optional)::
115
+ # A Hash of optional parameters for this Client --
116
+ #
117
+ # :api_key::
118
+ # The Sift Science API key associated with your account.
119
+ # Sift.api_key is used if this parameter is not set.
59
120
  #
60
- def initialize(api_key, path = Sift.current_rest_api_path)
61
- @api_key = api_key.to_s
62
- @path = path
63
- raise(RuntimeError, "api_key is required") if @api_key.nil? || @api_key.empty?
121
+ # :account_id::
122
+ # The ID of your Sift Science account. Sift.account_id is
123
+ # used if this parameter is not set.
124
+ #
125
+ # :timeout::
126
+ # The number of seconds to wait before failing a request. By
127
+ # default this is configured to 2 seconds.
128
+ #
129
+ # :version::
130
+ # The version of the Events API, Score API, and Labels API to call.
131
+ # By default, version 205.
132
+ #
133
+ # :path::
134
+ # The URL path to use for Events API path. By default, the
135
+ # official path of the specified-version of the Events API.
136
+ #
137
+ #
138
+ def initialize(opts = {})
139
+ @api_key = opts[:api_key] || Sift.api_key
140
+ @account_id = opts[:account_id] || Sift.account_id
141
+ @version = opts[:version] || API_VERSION
142
+ @timeout = opts[:timeout] || 2 # 2-second timeout by default
143
+ @path = opts[:path] || Sift.rest_api_path(@version)
144
+
145
+ raise("api_key") if !@api_key.is_a?(String) || @api_key.empty?
146
+ raise("path must be a non-empty string") if !@path.is_a?(String) || @path.empty?
147
+ end
148
+
149
+ def user_agent
150
+ "SiftScience/v#{@version} sift-ruby/#{VERSION}"
64
151
  end
65
152
 
66
- # Tracks an event and associated properties through the Sift Science API. This call
67
- # is blocking.
153
+
154
+ # Sends an event to the Sift Science Events API.
68
155
  #
69
- # == Parameters:
70
- # event
71
- # The name of the event to send. This can be either a reserved event name, like
72
- # $transaction or $label or a custom event name (that does not start with a $).
73
- # This parameter must be specified.
156
+ # See https://siftscience.com/developers/docs/ruby/events-api .
74
157
  #
75
- # properties
76
- # A hash of name-value pairs that specify the event-specific attributes to track.
77
- # This parameter must be specified.
158
+ # ==== Parameters:
159
+ #
160
+ # event::
161
+ # The name of the event to send. This can be either a reserved
162
+ # event name, like $transaction or $label or a custom event name
163
+ # (that does not start with a $). This parameter must be
164
+ # specified.
165
+ #
166
+ # properties::
167
+ # A hash of name-value pairs that specify the event-specific
168
+ # attributes to track. This parameter must be specified.
169
+ #
170
+ # opts (optional)::
171
+ # A Hash of optional parameters for the request --
172
+ #
173
+ # :return_score::
174
+ # If true, requests that the response include a score for this
175
+ # user, computed using the submitted event. See
176
+ # https://siftscience.com/developers/docs/ruby/score-api/synchronous-scores
78
177
  #
79
- # timeout (optional)
80
- # The number of seconds to wait before failing the request. By default this is
81
- # configured to 2 seconds (see above). This parameter is optional.
178
+ # :abuse_types::
179
+ # List of abuse types, specifying for which abuse types a
180
+ # score should be returned (if scoring was requested). By
181
+ # default, a score is returned for every abuse type to which
182
+ # you are subscribed.
82
183
  #
83
- # path (optional)
84
- # Overrides the default API path with a different URL.
184
+ # :return_action::
185
+ # If true, requests that the response include any actions
186
+ # triggered as a result of the tracked event.
85
187
  #
188
+ # :return_workflow_status::
189
+ # If true, requests that the response include the status of
190
+ # any workflow run as a result of the tracked event. See
191
+ # https://siftscience.com/developers/docs/ruby/workflows-api/workflow-decisions
86
192
  #
87
- # == Returns:
88
- # In the case of an HTTP error (timeout, broken connection, etc.), this
89
- # method returns nil; otherwise, a Response object is returned and captures
90
- # the status message and status code. In general, you can ignore the returned
91
- # result, though.
193
+ # :timeout::
194
+ # Overrides the timeout (in seconds) for this call.
92
195
  #
93
- def track(event, properties = {}, timeout = nil, path = nil)
196
+ # :api_key::
197
+ # Overrides the API key for this call.
198
+ #
199
+ # :version::
200
+ # Overrides the version of the Events API to call.
201
+ #
202
+ # :path::
203
+ # Overrides the URI path for this API call.
204
+ #
205
+ # ==== Returns:
206
+ #
207
+ # In the case of a network error (timeout, broken connection, etc.),
208
+ # this method propagates the exception, otherwise, a Response object is
209
+ # returned that captures the status message and status code.
210
+ #
211
+ def track(event, properties = {}, opts = {})
212
+ api_key = opts[:api_key] || @api_key
213
+ version = opts[:version] || @version
214
+ path = opts[:path] || (version && Sift.rest_api_path(version)) || @path
215
+ timeout = opts[:timeout] || @timeout
216
+ return_score = opts[:return_score]
217
+ return_action = opts[:return_action]
218
+ return_workflow_status = opts[:return_workflow_status]
219
+ force_workflow_run = opts[:force_workflow_run]
220
+ abuse_types = opts[:abuse_types]
221
+
222
+ raise("event must be a non-empty string") if (!event.is_a? String) || event.empty?
223
+ raise("properties cannot be empty") if properties.empty?
224
+ raise("api_key cannot be empty") if api_key.empty?
94
225
 
95
- raise(RuntimeError, "event must be a string") if event.nil? || event.to_s.empty?
96
- raise(RuntimeError, "properties cannot be empty") if properties.empty?
226
+ query = {}
227
+ query["return_score"] = "true" if return_score
228
+ query["return_action"] = "true" if return_action
229
+ query["return_workflow_status"] = "true" if return_workflow_status
230
+ query["force_workflow_run"] = "true" if force_workflow_run
231
+ query["abuse_types"] = abuse_types.join(",") if abuse_types
97
232
 
98
- path ||= @path
99
233
  options = {
100
- :body => MultiJson.dump(properties.merge({"$type" => event,
101
- "$api_key" => @api_key})),
234
+ :body => MultiJson.dump(delete_nils(properties).merge({"$type" => event,
235
+ "$api_key" => api_key})),
236
+ :headers => {"User-Agent" => user_agent},
237
+ :query => query
102
238
  }
103
239
  options.merge!(:timeout => timeout) unless timeout.nil?
104
- begin
105
- response = self.class.post(path, options)
106
- Response.new(response.body, response.code)
107
- rescue StandardError => e
108
- Sift.warn("Failed to track event: " + e.to_s)
109
- Sift.warn(e.backtrace)
110
- end
240
+
241
+ response = self.class.post(path, options)
242
+ Response.new(response.body, response.code, response.response)
111
243
  end
112
244
 
113
- # Retrieves a user's fraud score from the Sift Science API. This call
114
- # is blocking.
245
+
246
+ # Retrieves a user's fraud score from the Sift Science API.
247
+ #
248
+ # See https://siftscience.com/developers/docs/ruby/score-api/score-api .
249
+ #
250
+ # ==== Parameters:
115
251
  #
116
- # == Parameters:
117
- # user_id
252
+ # user_id::
118
253
  # A user's id. This id should be the same as the user_id used in
119
254
  # event calls.
120
255
  #
121
- # == Returns:
122
- # A Response object is returned and captures the status message and
123
- # status code. In general, you can ignore the returned result, though.
256
+ # opts (optional)::
257
+ # A Hash of optional parameters for the request --
258
+ #
259
+ # :abuse_types::
260
+ # List of abuse types, specifying for which abuse types a
261
+ # score should be returned. By default, a score is returned
262
+ # for every abuse type to which you are subscribed.
263
+ #
264
+ # :api_key::
265
+ # Overrides the API key for this call.
266
+ #
267
+ # :timeout::
268
+ # Overrides the timeout (in seconds) for this call.
124
269
  #
125
- def score(user_id)
270
+ # :version::
271
+ # Overrides the version of the Events API to call.
272
+ #
273
+ # ==== Returns:
274
+ #
275
+ # A Response object containing a status code, status message, and,
276
+ # if successful, the user's score(s).
277
+ #
278
+ def score(user_id, opts = {})
279
+ abuse_types = opts[:abuse_types]
280
+ api_key = opts[:api_key] || @api_key
281
+ timeout = opts[:timeout] || @timeout
282
+ version = opts[:version] || @version
283
+
284
+ raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
285
+ raise("Bad api_key parameter") if api_key.empty?
126
286
 
127
- raise(RuntimeError, "user_id must be a string") if user_id.nil? || user_id.to_s.empty?
287
+ query = {}
288
+ query["api_key"] = api_key
289
+ query["abuse_types"] = abuse_types.join(",") if abuse_types
128
290
 
129
- response = self.class.get('/v203/score/' + user_id)
130
- Response.new(response.body, response.code)
291
+ options = {
292
+ :headers => {"User-Agent" => user_agent},
293
+ :query => query
294
+ }
295
+ options.merge!(:timeout => timeout) unless timeout.nil?
131
296
 
297
+ response = self.class.get(Sift.score_api_path(user_id, version), options)
298
+ Response.new(response.body, response.code, response.response)
132
299
  end
133
300
 
134
- # Labels a user as either good or bad. This call is blocking.
301
+
302
+ # Fetches the latest score(s) computed for the specified user and abuse types.
303
+ #
304
+ # As opposed to client.score() and client.rescore_user(), this *does not* compute
305
+ # a new score for the user; it simply fetches the latest score(s) which have computed.
306
+ # These scores may be arbitrarily old.
307
+ #
308
+ # See https://siftscience.com/developers/docs/ruby/score-api/get-score for more details.
309
+ #
310
+ # ==== Parameters:
135
311
  #
136
- # == Parameters:
137
- # user_id
312
+ # user_id::
138
313
  # A user's id. This id should be the same as the user_id used in
139
314
  # event calls.
140
315
  #
141
- # properties
316
+ # opts (optional)::
317
+ # A Hash of optional parameters for the request --
318
+ #
319
+ # :abuse_types::
320
+ # List of abuse types, specifying for which abuse types a
321
+ # score should be returned. By default, a score is returned
322
+ # for every abuse type to which you are subscribed.
323
+ #
324
+ # :api_key::
325
+ # Overrides the API key for this call.
326
+ #
327
+ # :timeout::
328
+ # Overrides the timeout (in seconds) for this call.
329
+ #
330
+ # ==== Returns:
331
+ #
332
+ # A Response object containing a status code, status message, and,
333
+ # if successful, the user's score(s).
334
+ #
335
+ def get_user_score(user_id, opts = {})
336
+ abuse_types = opts[:abuse_types]
337
+ api_key = opts[:api_key] || @api_key
338
+ timeout = opts[:timeout] || @timeout
339
+
340
+ raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
341
+ raise("Bad api_key parameter") if api_key.empty?
342
+
343
+ query = {}
344
+ query["api_key"] = api_key
345
+ query["abuse_types"] = abuse_types.join(",") if abuse_types
346
+
347
+ options = {
348
+ :headers => {"User-Agent" => user_agent},
349
+ :query => query
350
+ }
351
+ options.merge!(:timeout => timeout) unless timeout.nil?
352
+
353
+ response = self.class.get(Sift.user_score_api_path(user_id, @version), options)
354
+ Response.new(response.body, response.code, response.response)
355
+ end
356
+
357
+
358
+ # Rescores the specified user for the specified abuse types and returns the resulting score(s).
359
+ #
360
+ # See https://siftscience.com/developers/docs/ruby/score-api/rescore for more details.
361
+ #
362
+ # ==== Parameters:
363
+ #
364
+ # user_id::
365
+ # A user's id. This id should be the same as the user_id used in
366
+ # event calls.
367
+ #
368
+ # opts (optional)::
369
+ # A Hash of optional parameters for the request --
370
+ #
371
+ # :abuse_types::
372
+ # List of abuse types, specifying for which abuse types a
373
+ # score should be returned. By default, a score is returned
374
+ # for every abuse type to which you are subscribed.
375
+ #
376
+ # :api_key::
377
+ # Overrides the API key for this call.
378
+ #
379
+ # :timeout::
380
+ # Overrides the timeout (in seconds) for this call.
381
+ #
382
+ # ==== Returns:
383
+ #
384
+ # A Response object containing a status code, status message, and,
385
+ # if successful, the user's score(s).
386
+ #
387
+ def rescore_user(user_id, opts = {})
388
+ abuse_types = opts[:abuse_types]
389
+ api_key = opts[:api_key] || @api_key
390
+ timeout = opts[:timeout] || @timeout
391
+
392
+ raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
393
+ raise("Bad api_key parameter") if api_key.empty?
394
+
395
+ query = {}
396
+ query["api_key"] = api_key
397
+ query["abuse_types"] = abuse_types.join(",") if abuse_types
398
+
399
+ options = {
400
+ :headers => {"User-Agent" => user_agent},
401
+ :query => query
402
+ }
403
+ options.merge!(:timeout => timeout) unless timeout.nil?
404
+
405
+ response = self.class.post(Sift.user_score_api_path(user_id, @version), options)
406
+ Response.new(response.body, response.code, response.response)
407
+ end
408
+
409
+
410
+ # Labels a user.
411
+ #
412
+ # See https://siftscience.com/developers/docs/ruby/labels-api/label-user .
413
+ #
414
+ # ==== Parameters:
415
+ #
416
+ # user_id::
417
+ # A user's id. This id should be the same as the user_id used in
418
+ # event calls.
419
+ #
420
+ # properties::
142
421
  # A hash of name-value pairs that specify the label attributes.
143
422
  # This parameter must be specified.
144
423
  #
145
- # timeout (optional)
146
- # The number of seconds to wait before failing the request. By default this is
147
- # configured to 2 seconds (see above). This parameter is optional.
424
+ # opts (optional)::
425
+ # A Hash of optional parameters for the request --
426
+ #
427
+ # :api_key::
428
+ # Overrides the API key for this call.
429
+ #
430
+ # :timeout::
431
+ # Overrides the timeout (in seconds) for this call.
432
+ #
433
+ # :version::
434
+ # Overrides the version of the Events API to call.
435
+ #
436
+ # ==== Returns:
437
+ #
438
+ # In the case of a connection error (timeout, broken connection,
439
+ # etc.), this method returns nil; otherwise, a Response object is
440
+ # returned that captures the status message and status code.
441
+ #
442
+ def label(user_id, properties = {}, opts = {})
443
+ api_key = opts[:api_key] || @api_key
444
+ timeout = opts[:timeout] || @timeout
445
+ version = opts[:version] || @version
446
+ path = Sift.users_label_api_path(user_id, version)
447
+
448
+ raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
449
+
450
+ track("$label", delete_nils(properties),
451
+ :path => path, :api_key => api_key, :timeout => timeout)
452
+ end
453
+
454
+
455
+ # Unlabels a user.
456
+ #
457
+ # See https://siftscience.com/developers/docs/ruby/labels-api/unlabel-user .
458
+ #
459
+ # ==== Parameters:
460
+ #
461
+ # user_id::
462
+ # A user's id. This id should be the same as the user_id used in
463
+ # event calls.
464
+ #
465
+ # opts (optional)::
466
+ # A Hash of optional parameters for this request --
467
+ #
468
+ # :abuse_type::
469
+ # The abuse type for which the user should be unlabeled. If
470
+ # omitted, the user is unlabeled for all abuse types.
471
+ #
472
+ # :api_key::
473
+ # Overrides the API key for this call.
474
+ #
475
+ # :timeout::
476
+ # Overrides the timeout (in seconds) for this call.
477
+ #
478
+ # :version::
479
+ # Overrides the version of the Events API to call.
480
+ #
481
+ # ==== Returns:
482
+ #
483
+ # A Response object is returned with only an http code of 204.
484
+ #
485
+ def unlabel(user_id, opts = {})
486
+ abuse_type = opts[:abuse_type]
487
+ api_key = opts[:api_key] || @api_key
488
+ timeout = opts[:timeout] || @timeout
489
+ version = opts[:version] || @version
490
+
491
+ raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
492
+
493
+ query = {}
494
+ query[:api_key] = api_key
495
+ query[:abuse_type] = abuse_type if abuse_type
496
+
497
+ options = {
498
+ :headers => {},
499
+ :query => query
500
+ }
501
+ options.merge!(:timeout => timeout) unless timeout.nil?
502
+
503
+ response = self.class.delete(Sift.users_label_api_path(user_id, version), options)
504
+ Response.new(response.body, response.code, response.response)
505
+ end
506
+
507
+
508
+ # Gets the status of a workflow run.
509
+ #
510
+ # See https://siftscience.com/developers/docs/ruby/workflows-api/workflow-status .
511
+ #
512
+ # ==== Parameters
513
+ #
514
+ # run_id::
515
+ # The ID of a workflow run.
516
+ #
517
+ # opts (optional)::
518
+ # A Hash of optional parameters for this request --
148
519
  #
149
- # == Returns:
150
- # A Response object is returned and captures the status message and
151
- # status code. In general, you can ignore the returned result, though.
520
+ # :account_id::
521
+ # Overrides the API key for this call.
152
522
  #
153
- def label(user_id, properties = {}, timeout = nil)
523
+ # :api_key::
524
+ # Overrides the API key for this call.
525
+ #
526
+ # :timeout::
527
+ # Overrides the timeout (in seconds) for this call.
528
+ #
529
+ def get_workflow_status(run_id, opts = {})
530
+ account_id = opts[:account_id] || @account_id
531
+ api_key = opts[:api_key] || @api_key
532
+ timeout = opts[:timeout] || @timeout
533
+
534
+ options = {
535
+ :headers => { "User-Agent" => user_agent },
536
+ :basic_auth => { :username => api_key, :password => "" }
537
+ }
538
+ options.merge!(:timeout => timeout) unless timeout.nil?
539
+
540
+ uri = API3_ENDPOINT + Sift.workflow_status_path(account_id, run_id)
541
+ response = self.class.get(uri, options)
542
+ Response.new(response.body, response.code, response.response)
543
+ end
154
544
 
155
- raise(RuntimeError, "user_id must be a string") if user_id.nil? || user_id.to_s.empty?
156
545
 
157
- path = Sift.current_users_label_api_path(user_id)
158
- track("$label", properties, timeout, path)
546
+ # Gets the decision status of a user.
547
+ #
548
+ # See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
549
+ #
550
+ # ==== Parameters
551
+ #
552
+ # user_id::
553
+ # The ID of user.
554
+ #
555
+ # opts (optional)::
556
+ # A Hash of optional parameters for this request --
557
+ #
558
+ # :account_id::
559
+ # Overrides the API key for this call.
560
+ #
561
+ # :api_key::
562
+ # Overrides the API key for this call.
563
+ #
564
+ # :timeout::
565
+ # Overrides the timeout (in seconds) for this call.
566
+ #
567
+ def get_user_decisions(user_id, opts = {})
568
+ account_id = opts[:account_id] || @account_id
569
+ api_key = opts[:api_key] || @api_key
570
+ timeout = opts[:timeout] || @timeout
571
+
572
+ options = {
573
+ :headers => { "User-Agent" => user_agent },
574
+ :basic_auth => { :username => api_key, :password => "" }
575
+ }
576
+ options.merge!(:timeout => timeout) unless timeout.nil?
577
+
578
+ uri = API3_ENDPOINT + Sift.user_decisions_api_path(account_id, user_id)
579
+ response = self.class.get(uri, options)
580
+ Response.new(response.body, response.code, response.response)
581
+ end
582
+
583
+
584
+ # Gets the decision status of an order.
585
+ #
586
+ # See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
587
+ #
588
+ # ==== Parameters
589
+ #
590
+ # order_id::
591
+ # The ID of an order.
592
+ #
593
+ # opts (optional)::
594
+ # A Hash of optional parameters for this request --
595
+ #
596
+ # :account_id::
597
+ # Overrides the API key for this call.
598
+ #
599
+ # :api_key::
600
+ # Overrides the API key for this call.
601
+ #
602
+ # :timeout::
603
+ # Overrides the timeout (in seconds) for this call.
604
+ #
605
+ def get_order_decisions(order_id, opts = {})
606
+ account_id = opts[:account_id] || @account_id
607
+ api_key = opts[:api_key] || @api_key
608
+ timeout = opts[:timeout] || @timeout
609
+
610
+ options = {
611
+ :headers => { "User-Agent" => user_agent },
612
+ :basic_auth => { :username => api_key, :password => "" }
613
+ }
614
+ options.merge!(:timeout => timeout) unless timeout.nil?
615
+
616
+ uri = API3_ENDPOINT + Sift.order_decisions_api_path(account_id, order_id)
617
+ response = self.class.get(uri, options)
618
+ Response.new(response.body, response.code, response.response)
619
+ end
620
+
621
+ # Gets the decision status of a session.
622
+ #
623
+ # See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
624
+ #
625
+ # ==== Parameters
626
+ #
627
+ # user_id::
628
+ # The ID of the user in the session.
629
+ #
630
+ # session_id::
631
+ # The ID of a session.
632
+ #
633
+ # opts (optional)::
634
+ # A Hash of optional parameters for this request --
635
+ #
636
+ # :account_id::
637
+ # Overrides the account id for this call.
638
+ #
639
+ # :api_key::
640
+ # Overrides the API key for this call.
641
+ #
642
+ # :timeout::
643
+ # Overrides the timeout (in seconds) for this call.
644
+ #
645
+ def get_session_decisions(user_id, session_id, opts = {})
646
+ account_id = opts[:account_id] || @account_id
647
+ api_key = opts[:api_key] || @api_key
648
+ timeout = opts[:timeout] || @timeout
649
+
650
+ options = {
651
+ :headers => { "User-Agent" => user_agent },
652
+ :basic_auth => { :username => api_key, :password => "" }
653
+ }
654
+ options.merge!(:timeout => timeout) unless timeout.nil?
655
+
656
+ uri = API3_ENDPOINT + Sift.session_decisions_api_path(account_id, user_id, session_id)
657
+ response = self.class.get(uri, options)
658
+ Response.new(response.body, response.code, response.response)
659
+ end
660
+
661
+ # Gets the decision status of a piece of content.
662
+ #
663
+ # See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
664
+ #
665
+ # ==== Parameters
666
+ #
667
+ # user_id::
668
+ # The ID of the owner of the content.
669
+ #
670
+ # content_id::
671
+ # The ID of a piece of content.
672
+ #
673
+ # opts (optional)::
674
+ # A Hash of optional parameters for this request --
675
+ #
676
+ # :account_id::
677
+ # Overrides the API key for this call.
678
+ #
679
+ # :api_key::
680
+ # Overrides the API key for this call.
681
+ #
682
+ # :timeout::
683
+ # Overrides the timeout (in seconds) for this call.
684
+ #
685
+ def get_content_decisions(user_id, content_id, opts = {})
686
+ account_id = opts[:account_id] || @account_id
687
+ api_key = opts[:api_key] || @api_key
688
+ timeout = opts[:timeout] || @timeout
689
+
690
+ options = {
691
+ :headers => { "User-Agent" => user_agent },
692
+ :basic_auth => { :username => api_key, :password => "" }
693
+ }
694
+ options.merge!(:timeout => timeout) unless timeout.nil?
695
+
696
+ uri = API3_ENDPOINT + Sift.content_decisions_api_path(account_id, user_id, content_id)
697
+ response = self.class.get(uri, options)
698
+ Response.new(response.body, response.code, response.response)
699
+ end
700
+
701
+ def decisions(opts = {})
702
+ decision_instance.list(opts)
703
+ end
704
+
705
+ def decisions!(opts = {})
706
+ handle_response(decisions(opts))
707
+ end
708
+
709
+ def apply_decision(configs = {})
710
+ decision_instance.apply_to(configs)
711
+ end
712
+
713
+ def apply_decision!(configs = {})
714
+ handle_response(apply_decision(configs))
715
+ end
716
+
717
+ private
718
+
719
+ def handle_response(response)
720
+ if response.ok?
721
+ response.body
722
+ else
723
+ raise ApiError.new(response.api_error_message, response)
724
+ end
725
+ end
726
+
727
+ def decision_instance
728
+ @decision_instance ||= Decision.new(api_key, account_id)
729
+ end
730
+
731
+ def delete_nils(properties)
732
+ properties.delete_if do |k, v|
733
+ case v
734
+ when nil
735
+ true
736
+ when Hash
737
+ delete_nils(v)
738
+ false
739
+ else
740
+ false
741
+ end
742
+ end
159
743
  end
160
744
  end
161
745
  end