sift 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbaf2000cc0d9083f104b38c5e04ca271cc2cd9c
4
- data.tar.gz: c90f468af4bdfdf91a04b22faaa0ec7eb637fd29
3
+ metadata.gz: 57a679adbe12a556895633060cde177cf89fcb06
4
+ data.tar.gz: d3eff77b83c2ba5696c4c391f82d592a8df7443a
5
5
  SHA512:
6
- metadata.gz: bb51191b838c93928607ef420f8ffd5588bf1de49f3e2f5e6ade15b9ce3e9d4a8d4b043b62605716cd452f261f4d39a48aacc33938d8a97c0c41a18ebc243482
7
- data.tar.gz: 57fcde0d67394bacc175815dc12c60edf25782f75baad7174acc8bcded6d1d63a277aad4df2c1d7bceab2bcadbb7f520c772cad3c7be5c57c267dcff049ca059
6
+ metadata.gz: 5809b998556c975ff30faceacf5468a812e815dead3e836640343fe681c1d612192a9a26615f8a0c35c4c13a3cb9b232f3aff1bbf7649d6b3e6c8250cd7f2896
7
+ data.tar.gz: 8b941c79e41784d1452f8ae4fe8b1052f7f32ed3954f8afb16b16f3e7346a0bf6e5cfc71f6080c6475ff75a0fce78172a58e4f8ece8f73ab47385ffd57d3001a
@@ -0,0 +1,43 @@
1
+ version: 2
2
+
3
+ jobs:
4
+ build:
5
+ parallelism: 3 # run three instances of this job in parallel
6
+ docker:
7
+ - image: circleci/ruby:2.4.2-jessie-node
8
+ environment:
9
+ BUNDLE_JOBS: 3
10
+ BUNDLE_RETRY: 3
11
+ BUNDLE_PATH: vendor/bundle
12
+ steps:
13
+ - checkout
14
+
15
+ - run:
16
+ name: Which bundler?
17
+ command: bundle -v
18
+
19
+ - restore_cache:
20
+ keys:
21
+ - sift-bundle-{{ checksum "sift.gemspec" }}
22
+ - sift-bundle-
23
+
24
+ - run:
25
+ name: Bundle Install
26
+ command: bundle check || bundle install
27
+
28
+ - save_cache:
29
+ key: sift-bundle-{{ checksum "sift.gemspec" }}
30
+ paths:
31
+ - vendor/bundle
32
+
33
+ - run:
34
+ name: Run rspec in parallel
35
+ command: |
36
+ bundle exec rspec --profile 10 \
37
+ --format RspecJunitFormatter \
38
+ --out test_results/rspec.xml \
39
+ --format progress \
40
+ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
41
+
42
+ - store_test_results:
43
+ path: test_results
data/HISTORY CHANGED
@@ -1,8 +1,30 @@
1
- === 3.0.0.0 2018-03-05
2
- * adds support for v205 of Sift Science's APIs
3
- * v205 APIs are now called by default -- this is an incompatible change
4
- (use :version => 204 to call the previous API version)
5
- * Add content level decisions in Apply Decisions APIs.
1
+ === 4.0.0 2019-05-15
2
+ - Breaking change: Propagate exception in Client.track() previously we were silently swallowing exceptions and now we are bubbling them up. You will need to add exception handling code.
3
+ - Fix URL encoding
4
+ - Add Circle build
5
+
6
+ === 3.3.0 2018-07-31
7
+ - Add support for rescore_user and get_user_score APIs
8
+
9
+ === 3.2.0 2018-07-05
10
+ - Add new query parameter force_workflow_run
11
+
12
+ === 3.1.0 2018-06-04
13
+ - Adds support for get session decisions to [Decisions API](https://siftscience.com/developers/docs/curl/decisions-api)
14
+
15
+ === 3.0.1 2018-04-06
16
+ - Improved documentation on HISTORY and README.MD
17
+
18
+ === 3.0.0 2018-03-05
19
+ - Adds support for Sift Science API Version 205, including new [`$create_content`](https://siftscience.com/developers/docs/curl/events-api/reserved-events/create-content) and [`$update_content`](https://siftscience.com/developers/docs/curl/events-api/reserved-events/update-content) formats
20
+ - V205 APIs are now called -- **this is an incompatible change**
21
+ - (use `:version => 204` to call the previous API version)
22
+ - Adds support for content decisions to [Decisions API](https://siftscience.com/developers/docs/curl/decisions-api)
23
+
24
+ INCOMPATIBLE CHANGES INTRODUCED IN API V205:
25
+ - `$create_content` and `$update_content` have significantly changed, and the old format will be rejected
26
+ - `$send_message` and `$submit_review` events are no longer valid
27
+ - V205 improves server-side event data validation. In V204 and earlier, server-side validation accepted some events that did not conform to the published APIs in our [developer documentation](https://siftscience.com/developers/docs/curl/events-api). V205 does not modify existing event APIs other than those mentioned above, but may reject invalid event data that were previously accepted. **Please test your integration on V205 in sandbox before using in production.**
6
28
 
7
29
  === 2.2.1.0 2018-02-12
8
30
  * Add session level decisions in Apply Decisions APIs.
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # Sift Science Ruby bindings [![Build Status](https://travis-ci.org/SiftScience/sift-ruby.png?branch=master)](https://travis-ci.org/SiftScience/sift-ruby)
1
+ # Sift Ruby bindings
2
+
3
+ [![CircleCI](https://circleci.com/gh/SiftScience/sift-ruby.svg?style=svg)](https://circleci.com/gh/SiftScience/sift-ruby)
2
4
 
3
5
  ## Requirements
4
6
 
5
- * Ruby 1.9.3 or above.
7
+ * Ruby 2.0.0 or above.
6
8
 
7
9
 
8
10
  ## Installation
@@ -32,7 +34,7 @@ client = Sift::Client.new()
32
34
 
33
35
  ##### OR
34
36
 
35
- client = Sift::Client.new(api_key: '<your_api_key_here>', account_id: '<your_account_id_here>'
37
+ client = Sift::Client.new(api_key: '<your_api_key_here>', account_id: '<your_account_id_here>')
36
38
 
37
39
  ```
38
40
 
@@ -73,14 +75,14 @@ response = client.score(user_id)
73
75
 
74
76
  ## Decisions
75
77
 
76
- To learn more about the decisions endpoint visit our [developer docs](https://siftscience.com/developers/docs/ruby/decisions-api/get-decisions).
78
+ To learn more about the decisions endpoint visit our [developer docs](https://sift.com/developers/docs/ruby/decisions-api/get-decisions).
77
79
 
78
80
  ### List of Configured Decisions
79
81
 
80
82
  Get a list of your decisions.
81
83
 
82
84
  **Optional Params**
83
- - `entity_type`: `:user` or `:order` or `:session`
85
+ - `entity_type`: `:user` or `:order` or `:session` or `:content`
84
86
  - `abuse_types`: `["payment_abuse", "content_abuse", "content_abuse",
85
87
  "account_abuse", "legacy", "account_takeover"]`
86
88
 
@@ -117,7 +119,7 @@ end
117
119
 
118
120
  ### Apply a decision
119
121
 
120
- Applies a decision to an entity. Visit our [developer docs](http://siftscience.com/developers/docs/ruby/decisions-api/apply-decision) for more information.
122
+ Applies a decision to an entity. Visit our [developer docs](http://sift.com/developers/docs/ruby/decisions-api/apply-decision) for more information.
121
123
 
122
124
  **Required Params:**
123
125
  - `decision_id`, `source`, `user_id`
@@ -207,6 +209,9 @@ response = client.get_user_decisions('example_user_id')
207
209
  # Get the latest decisions for an order
208
210
  response = client.get_order_decisions('example_order_id')
209
211
 
212
+ # Get the latest decisions for a session
213
+ response = client.get_session_decisions('example_user_id', 'example_session_id')
214
+
210
215
  # Get the latest decisions for an content
211
216
  response = client.get_content_decisions('example_user_id', 'example_order_id')
212
217
  ```
@@ -1,5 +1,6 @@
1
- require_relative "./sift/version"
2
- require_relative "./sift/client"
1
+ require_relative './sift/version'
2
+ require_relative './sift/client'
3
+ require 'erb'
3
4
 
4
5
  module Sift
5
6
 
@@ -10,32 +11,49 @@ module Sift
10
11
 
11
12
  # Returns the Score API path for the specified user ID and API version
12
13
  def self.score_api_path(user_id, version=API_VERSION)
13
- "/v#{version}/score/#{URI.encode(user_id)}/"
14
+ "/v#{version}/score/#{ERB::Util.url_encode(user_id)}/"
15
+ end
16
+
17
+ # Returns the User Score API path for the specified user ID and API version
18
+ def self.user_score_api_path(user_id, version=API_VERSION)
19
+ "/v#{version}/users/#{ERB::Util.url_encode(user_id)}/score"
14
20
  end
15
21
 
16
22
  # Returns the users API path for the specified user ID and API version
17
23
  def self.users_label_api_path(user_id, version=API_VERSION)
18
- "/v#{version}/users/#{URI.encode(user_id)}/labels"
24
+ "/v#{version}/users/#{ERB::Util.url_encode(user_id)}/labels"
19
25
  end
20
26
 
21
27
  # Returns the path for the Workflow Status API
22
28
  def self.workflow_status_path(account_id, run_id)
23
- "/v3/accounts/#{account_id}/workflows/runs/#{run_id}"
29
+ "/v3/accounts/#{ERB::Util.url_encode(account_id)}" \
30
+ "/workflows/runs/#{ERB::Util.url_encode(run_id)}"
24
31
  end
25
32
 
26
33
  # Returns the path for User Decisions API
27
34
  def self.user_decisions_api_path(account_id, user_id)
28
- "/v3/accounts/#{account_id}/users/#{user_id}/decisions"
35
+ "/v3/accounts/#{ERB::Util.url_encode(account_id)}" \
36
+ "/users/#{ERB::Util.url_encode(user_id)}/decisions"
29
37
  end
30
38
 
31
39
  # Returns the path for Orders Decisions API
32
40
  def self.order_decisions_api_path(account_id, order_id)
33
- "/v3/accounts/#{account_id}/orders/#{order_id}/decisions"
41
+ "/v3/accounts/#{ERB::Util.url_encode(account_id)}" \
42
+ "/orders/#{ERB::Util.url_encode(order_id)}/decisions"
43
+ end
44
+
45
+ # Returns the path for Session Decisions API
46
+ def self.session_decisions_api_path(account_id, user_id, session_id)
47
+ "/v3/accounts/#{ERB::Util.url_encode(account_id)}" \
48
+ "/users/#{ERB::Util.url_encode(user_id)}" \
49
+ "/sessions/#{ERB::Util.url_encode(session_id)}/decisions"
34
50
  end
35
51
 
36
52
  # Returns the path for Content Decisions API
37
53
  def self.content_decisions_api_path(account_id, user_id, content_id)
38
- "/v3/accounts/#{account_id}/users/#{user_id}/content/#{content_id}/decisions"
54
+ "/v3/accounts/#{ERB::Util.url_encode(account_id)}" \
55
+ "/users/#{ERB::Util.url_encode(user_id)}" \
56
+ "/content/#{ERB::Util.url_encode(content_id)}/decisions"
39
57
  end
40
58
 
41
59
  # Module-scoped public API key
@@ -69,5 +87,4 @@ module Sift
69
87
  def self.fatal(msg)
70
88
  @logger.fatal(msg) if @logger
71
89
  end
72
-
73
90
  end
@@ -34,15 +34,25 @@ module Sift
34
34
 
35
35
  # only set these variables if a message-body is expected.
36
36
  if not @http_raw_response.kind_of? Net::HTTPNoContent
37
- @body = MultiJson.load(http_response) unless http_response.nil?
38
- @request = MultiJson.load(@body["request"].to_s) if @body["request"]
39
- @api_status = @body["status"].to_i if @body["status"]
40
- @api_error_message = @body["error_message"]
41
-
42
- if @body["error"]
43
- @api_error_message = @body["error"]
44
- @api_error_description = @body["description"]
45
- @api_error_issues = @body["issues"] || {}
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
46
56
  end
47
57
  end
48
58
  end
@@ -194,8 +204,8 @@ module Sift
194
204
  #
195
205
  # ==== Returns:
196
206
  #
197
- # In the case of a connection error (timeout, broken connection,
198
- # etc.), this method returns nil; otherwise, a Response object is
207
+ # In the case of a network error (timeout, broken connection, etc.),
208
+ # this method propagates the exception, otherwise, a Response object is
199
209
  # returned that captures the status message and status code.
200
210
  #
201
211
  def track(event, properties = {}, opts = {})
@@ -206,6 +216,7 @@ module Sift
206
216
  return_score = opts[:return_score]
207
217
  return_action = opts[:return_action]
208
218
  return_workflow_status = opts[:return_workflow_status]
219
+ force_workflow_run = opts[:force_workflow_run]
209
220
  abuse_types = opts[:abuse_types]
210
221
 
211
222
  raise("event must be a non-empty string") if (!event.is_a? String) || event.empty?
@@ -216,6 +227,7 @@ module Sift
216
227
  query["return_score"] = "true" if return_score
217
228
  query["return_action"] = "true" if return_action
218
229
  query["return_workflow_status"] = "true" if return_workflow_status
230
+ query["force_workflow_run"] = "true" if force_workflow_run
219
231
  query["abuse_types"] = abuse_types.join(",") if abuse_types
220
232
 
221
233
  options = {
@@ -226,14 +238,8 @@ module Sift
226
238
  }
227
239
  options.merge!(:timeout => timeout) unless timeout.nil?
228
240
 
229
- begin
230
- response = self.class.post(path, options)
231
- Response.new(response.body, response.code, response.response)
232
- rescue StandardError => e
233
- Sift.warn("Failed to track event: " + e.to_s)
234
- Sift.warn(e.backtrace)
235
- nil
236
- end
241
+ response = self.class.post(path, options)
242
+ Response.new(response.body, response.code, response.response)
237
243
  end
238
244
 
239
245
 
@@ -293,6 +299,114 @@ module Sift
293
299
  end
294
300
 
295
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:
311
+ #
312
+ # user_id::
313
+ # A user's id. This id should be the same as the user_id used in
314
+ # event calls.
315
+ #
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
+
296
410
  # Labels a user.
297
411
  #
298
412
  # See https://siftscience.com/developers/docs/ruby/labels-api/label-user .
@@ -504,6 +618,45 @@ module Sift
504
618
  Response.new(response.body, response.code, response.response)
505
619
  end
506
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
507
660
 
508
661
  # Gets the decision status of a piece of content.
509
662
  #
@@ -1,4 +1,4 @@
1
1
  module Sift
2
- VERSION = "3.0.0"
2
+ VERSION = "4.0.0"
3
3
  API_VERSION = "205"
4
4
  end
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gems that must be intalled for sift to compile and build
23
23
  s.add_development_dependency "rspec", "~> 3.5"
24
+ s.add_development_dependency "rspec_junit_formatter"
24
25
  s.add_development_dependency "pry"
25
26
  s.add_development_dependency "webmock", ">= 1.16.0", "< 2"
26
27
 
@@ -43,6 +43,38 @@ describe Sift::Client do
43
43
  }
44
44
  end
45
45
 
46
+ def user_score_response_json
47
+ {
48
+ :entity_type => "user",
49
+ :entity_id => "247019",
50
+ :scores => {
51
+ :payment_abuse => {
52
+ :score => 0.78
53
+ },
54
+ :content_abuse => {
55
+ :score => 0.11
56
+ }
57
+ },
58
+ :latest_decisions => {
59
+ :payment_abuse => {
60
+ :id => "user_looks_bad_payment_abuse",
61
+ :category => "block",
62
+ :source => "AUTOMATED_RULE",
63
+ :time => 1352201880,
64
+ :description => "Bad Fraudster"
65
+ }
66
+ },
67
+ :latest_labels => {
68
+ :payment_abuse => {
69
+ :is_bad => true,
70
+ :time => 1352201880
71
+ }
72
+ },
73
+ :status => 0,
74
+ :error_message => "OK"
75
+ }
76
+ end
77
+
46
78
  def action_response_json
47
79
  {
48
80
  :user_id => "247019",
@@ -140,29 +172,52 @@ describe Sift::Client do
140
172
  end
141
173
 
142
174
 
143
- it "Doesn't raise an exception on Net/HTTP errors" do
175
+ it "Handles parse exceptions for 500 status" do
144
176
  api_key = "foobar"
145
177
  event = "$transaction"
146
178
  properties = valid_transaction_properties
179
+ res = nil
180
+
181
+ stub_request(:any, /.*/).to_return(:body => "{123", :status => 500)
182
+
183
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.not_to raise_error
184
+ expect(res.http_status_code).to eq(500)
185
+ end
186
+
187
+
188
+ it "Handles parse exceptions for 200 status" do
189
+ api_key = "foobar"
190
+ event = "$transaction"
191
+ properties = valid_transaction_properties
192
+ res = nil
193
+
194
+ stub_request(:any, /.*/).to_return(:body => "{123", :status => 200)
195
+
196
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.to raise_error(TypeError)
197
+ end
198
+
199
+
200
+ it "Preserves response on HTTP errors but does not raise an exception" do
201
+ api_key = "foobar"
202
+ event = "$transaction"
203
+ properties = valid_transaction_properties
204
+ res = nil
147
205
 
148
206
  stub_request(:any, /.*/).to_return(:status => 401)
149
207
 
150
- # This method should just return nil -- the track call failed because
151
- # of an HTTP error
152
- expect(Sift::Client.new(:api_key => api_key).track(event, properties)).to eq(nil)
208
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.not_to raise_error
209
+ expect(res.http_status_code).to eq(401)
153
210
  end
154
211
 
155
212
 
156
- it "Returns nil when a StandardError occurs within the request" do
213
+ it "Propagates exception when a StandardError occurs within the request" do
157
214
  api_key = "foobar"
158
215
  event = "$transaction"
159
216
  properties = valid_transaction_properties
160
217
 
161
218
  stub_request(:any, /.*/).to_raise(StandardError)
162
219
 
163
- # This method should just return nil -- the track call failed because
164
- # a StandardError exception was thrown
165
- expect(Sift::Client.new(:api_key => api_key).track(event, properties)).to eq(nil)
220
+ expect { Sift::Client.new(:api_key => api_key).track(event, properties) }.to raise_error(StandardError)
166
221
  end
167
222
 
168
223
 
@@ -272,6 +327,88 @@ describe Sift::Client do
272
327
  end
273
328
 
274
329
 
330
+ it "Successfully executes client.get_user_score()" do
331
+ api_key = "foobar"
332
+ response_json = user_score_response_json
333
+
334
+ stub_request(:get, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
335
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
336
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
337
+ "content-length"=> "74"})
338
+
339
+ response = Sift::Client.new(:api_key => api_key).get_user_score(user_score_response_json[:entity_id])
340
+ expect(response.ok?).to eq(true)
341
+ expect(response.api_status).to eq(0)
342
+ expect(response.api_error_message).to eq("OK")
343
+
344
+ expect(response.body["entity_id"]).to eq("247019")
345
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
346
+ end
347
+
348
+
349
+ it "Successfully executes client.get_user_score() with abuse types specified" do
350
+ api_key = "foobar"
351
+ response_json = user_score_response_json
352
+
353
+ stub_request(:get, "https://api.siftscience.com/v205/users/247019/score" +
354
+ "?api_key=foobar&abuse_types=content_abuse,payment_abuse")
355
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
356
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
357
+ "content-length"=> "74"})
358
+
359
+ response = Sift::Client.new(:api_key => api_key).get_user_score(user_score_response_json[:entity_id],
360
+ :abuse_types => ["content_abuse",
361
+ "payment_abuse"])
362
+ expect(response.ok?).to eq(true)
363
+ expect(response.api_status).to eq(0)
364
+ expect(response.api_error_message).to eq("OK")
365
+
366
+ expect(response.body["entity_id"]).to eq("247019")
367
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
368
+ end
369
+
370
+
371
+ it "Successfully executes client.rescore_user()" do
372
+ api_key = "foobar"
373
+ response_json = user_score_response_json
374
+
375
+ stub_request(:post, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
376
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
377
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
378
+ "content-length"=> "74"})
379
+
380
+ response = Sift::Client.new(:api_key => api_key).rescore_user(user_score_response_json[:entity_id])
381
+ expect(response.ok?).to eq(true)
382
+ expect(response.api_status).to eq(0)
383
+ expect(response.api_error_message).to eq("OK")
384
+
385
+ expect(response.body["entity_id"]).to eq("247019")
386
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
387
+ end
388
+
389
+
390
+ it "Successfully executes client.rescore_user() with abuse types specified" do
391
+ api_key = "foobar"
392
+ response_json = user_score_response_json
393
+
394
+ stub_request(:post, "https://api.siftscience.com/v205/users/247019/score" +
395
+ "?api_key=foobar&abuse_types=content_abuse,payment_abuse")
396
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
397
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
398
+ "content-length"=> "74"})
399
+
400
+ response = Sift::Client.new(:api_key => api_key).rescore_user(user_score_response_json[:entity_id],
401
+ :abuse_types => ["content_abuse",
402
+ "payment_abuse"])
403
+ expect(response.ok?).to eq(true)
404
+ expect(response.api_status).to eq(0)
405
+ expect(response.api_error_message).to eq("OK")
406
+
407
+ expect(response.body["entity_id"]).to eq("247019")
408
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
409
+ end
410
+
411
+
275
412
  it "Successfully make a sync score request" do
276
413
  api_key = "foobar"
277
414
  response_json = {
@@ -363,7 +500,7 @@ describe Sift::Client do
363
500
  end
364
501
 
365
502
 
366
- it "Successfully make a user decisions request" do
503
+ it "Successfully make a user decisions request" do
367
504
  response_text = '{"decisions":{"content_abuse":{"decision":{"id":"user_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
368
505
 
369
506
  stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/decisions")
@@ -390,6 +527,18 @@ describe Sift::Client do
390
527
  expect(response.body["decisions"]["payment_abuse"]["decision"]["id"]).to eq("decision7")
391
528
  end
392
529
 
530
+ it "Successfully make a session decisions request" do
531
+ response_text = '{"decisions":{"account_takeover":{"decision":{"id":"session_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
532
+
533
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/sessions/example_session/decisions")
534
+ .to_return(:status => 200, :body => response_text, :headers => {})
535
+
536
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
537
+ response = client.get_session_decisions("example_user", "example_session")
538
+
539
+ expect(response.ok?).to eq(true)
540
+ expect(response.body["decisions"]["account_takeover"]["decision"]["id"]).to eq("session_decision")
541
+ end
393
542
 
394
543
  it "Successfully make an content decisions request" do
395
544
  response_text = '{"decisions":{"content_abuse":{"decision":{"id":"decision7"},"time":1468599638005,"webhook_succeeded":false}}}'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sift
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Sadaghiani
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-04-05 00:00:00.000000000 Z
13
+ date: 2019-05-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -26,6 +26,20 @@ dependencies:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
28
  version: '3.5'
29
+ - !ruby/object:Gem::Dependency
30
+ name: rspec_junit_formatter
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: pry
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -109,8 +123,8 @@ executables: []
109
123
  extensions: []
110
124
  extra_rdoc_files: []
111
125
  files:
126
+ - ".circleci/config.yml"
112
127
  - ".gitignore"
113
- - ".travis.yml"
114
128
  - Gemfile
115
129
  - HISTORY
116
130
  - LICENSE
@@ -158,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
172
  version: '0'
159
173
  requirements: []
160
174
  rubyforge_project: sift
161
- rubygems_version: 2.4.6
175
+ rubygems_version: 2.5.2.3
162
176
  signing_key:
163
177
  specification_version: 4
164
178
  summary: Sift Science Ruby API Gem
@@ -1,18 +0,0 @@
1
- language: ruby
2
- script: "bundle exec rake spec"
3
- rvm:
4
- - 1.9.3
5
- - 2.0.0
6
- - 2.1.5
7
- - 2.2.1
8
- before_install:
9
- - gem install bundler
10
- - bundle --version
11
- env:
12
- # None for now
13
- gemfile:
14
- - Gemfile
15
- services:
16
- # None for now
17
- notifications:
18
- # None for now