sift 1.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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