sift 1.1.6.2 → 4.5.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.
Files changed (45) hide show
  1. checksums.yaml +5 -13
  2. data/.circleci/config.yml +105 -0
  3. data/.github/pull_request_template.md +12 -0
  4. data/.github/workflows/publishing_sift_ruby.yml +38 -0
  5. data/.gitignore +1 -0
  6. data/.jenkins/Jenkinsfile +103 -0
  7. data/HISTORY +104 -0
  8. data/README.md +351 -0
  9. data/examples/psp_merchant_management_apis.rb +105 -0
  10. data/examples/validation_apis.rb +47 -0
  11. data/lib/sift/client/decision/apply_to.rb +129 -0
  12. data/lib/sift/client/decision.rb +66 -0
  13. data/lib/sift/client.rb +845 -112
  14. data/lib/sift/error.rb +13 -0
  15. data/lib/sift/router.rb +41 -0
  16. data/lib/sift/utils/hash_getter.rb +15 -0
  17. data/lib/sift/validate/decision.rb +65 -0
  18. data/lib/sift/validate/primitive.rb +43 -0
  19. data/lib/sift/version.rb +2 -2
  20. data/lib/sift.rb +85 -11
  21. data/sift.gemspec +5 -3
  22. data/spec/fixtures/fake_responses.rb +16 -0
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/unit/client/decision/apply_to_spec.rb +262 -0
  25. data/spec/unit/client/decision_spec.rb +83 -0
  26. data/spec/unit/client_203_spec.rb +193 -0
  27. data/spec/unit/client_205_spec.rb +117 -0
  28. data/spec/unit/client_label_spec.rb +68 -11
  29. data/spec/unit/client_psp_merchant_spec.rb +133 -0
  30. data/spec/unit/client_spec.rb +556 -79
  31. data/spec/unit/client_validationapi_spec.rb +91 -0
  32. data/spec/unit/router_spec.rb +37 -0
  33. data/spec/unit/validate/decision_spec.rb +85 -0
  34. data/spec/unit/validate/primitive_spec.rb +73 -0
  35. data/test_integration_app/decisions_api/test_decisions_api.rb +31 -0
  36. data/test_integration_app/events_api/test_events_api.rb +843 -0
  37. data/test_integration_app/globals.rb +2 -0
  38. data/test_integration_app/main.rb +67 -0
  39. data/test_integration_app/psp_merchants_api/test_psp_merchant_api.rb +44 -0
  40. data/test_integration_app/score_api/test_score_api.rb +11 -0
  41. data/test_integration_app/verification_api/test_verification_api.rb +32 -0
  42. metadata +85 -28
  43. data/.travis.yml +0 -13
  44. data/README.rdoc +0 -85
  45. data/spec/unit/sift_spec.rb +0 -6
@@ -1,4 +1,5 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
1
+ require_relative "../spec_helper"
2
+ require "sift"
2
3
 
3
4
  describe Sift::Client do
4
5
 
@@ -42,134 +43,301 @@ describe Sift::Client do
42
43
  }
43
44
  end
44
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
+
78
+ def action_response_json
79
+ {
80
+ :user_id => "247019",
81
+ :score => 0.93,
82
+ :actions => [{
83
+ :action_id => "1234567890abcdefghijklmn",
84
+ :time => 1437421587052,
85
+ :triggers => [{
86
+ :triggerType => "FORMULA",
87
+ :source => "synchronous_action",
88
+ :trigger_id => "12345678900987654321abcd"
89
+ }],
90
+ :entity => {
91
+ :type => "USER",
92
+ :id => "23056"
93
+ }
94
+ },
95
+ {
96
+ :action_id => "12345678901234567890abcd",
97
+ :time => 1437421587410,
98
+ :triggers => [{
99
+ :triggerType => "FORMULA",
100
+ :source => "synchronous_action",
101
+ :trigger_id => "abcd12345678901234567890"
102
+ }],
103
+ :entity => {
104
+ :type => "ORDER",
105
+ :id => "order_at_ 1437421587009"
106
+ }
107
+ }],
108
+ :status => 0,
109
+ :error_message => "OK"
110
+ }
111
+ end
112
+
113
+ def warnings
114
+ {
115
+ :count => 1,
116
+ :items => [{
117
+ :message => 'Invalid currency'
118
+ }]
119
+ }
120
+ end
121
+
122
+ def percentile_response_json
123
+ {
124
+ :user_id => 'billy_jones_301',
125
+ :latest_labels => {},
126
+ :workflow_statuses => [],
127
+ :scores => {
128
+ :account_abuse => {
129
+ :score => 0.32787917675535705,
130
+ :reasons => [{
131
+ :name => 'Latest item product title',
132
+ :value => 'The Slanket Blanket-Texas Tea'
133
+ }],
134
+ :percentiles => {
135
+ :last_7_days => -1.0, :last_1_days => -1.0, :last_10_days => -1.0, :last_5_days => -1.0
136
+ }
137
+ },
138
+ :acontent_abuse => {
139
+ :score => 0.28056292905897995,
140
+ :reasons => [{
141
+ :name => 'timeSinceFirstEvent',
142
+ :value => '13.15 minutes'
143
+ }],
144
+ :percentiles => {
145
+ :last_7_days => -1.0, :last_1_days => -1.0, :last_10_days => -1.0, :last_5_days => -1.0
146
+ }
147
+ },
148
+ :payment_abuse => {
149
+ :score => 0.28610507028376797,
150
+ :reasons => [{
151
+ :name => 'Latest item currency code',
152
+ :value => 'USD'
153
+ }, {
154
+ :name => 'Latest item item ID',
155
+ :value => 'B004834GQO'
156
+ }, {
157
+ :name => 'Latest item product title',
158
+ :value => 'The Slanket Blanket-Texas Tea'
159
+ }],
160
+ :percentiles => {
161
+ :last_7_days => -1.0, :last_1_days => -1.0, :last_10_days => -1.0, :last_5_days => -1.0
162
+ }
163
+ },
164
+ :promotion_abuse => {
165
+ :score => 0.05731508921450917,
166
+ :percentiles => {
167
+ :last_7_days => -1.0, :last_1_days => -1.0, :last_10_days => -1.0, :last_5_days => -1.0
168
+ }
169
+ }
170
+ },
171
+ :status => 0,
172
+ :error_message => 'OK'
173
+ }
174
+ end
175
+
45
176
  def fully_qualified_api_endpoint
46
- Sift::Client::API_ENDPOINT + Sift.current_rest_api_path
177
+ Sift::Client::API_ENDPOINT + Sift.rest_api_path
47
178
  end
48
179
 
180
+
49
181
  it "Can instantiate client with blank api key if Sift.api_key set" do
50
182
  Sift.api_key = "test_global_api_key"
51
- Sift::Client.new().api_key.should eq(Sift.api_key)
183
+ expect(Sift::Client.new().api_key).to eq(Sift.api_key)
52
184
  end
53
185
 
186
+
54
187
  it "Parameter passed api key takes precedence over Sift.api_key" do
55
188
  Sift.api_key = "test_global_api_key"
56
189
  api_key = "test_local_api_key"
57
- Sift::Client.new(api_key).api_key.should eq(api_key)
190
+ expect(Sift::Client.new(:api_key => api_key).api_key).to eq(api_key)
58
191
  end
59
192
 
193
+
60
194
  it "Cannot instantiate client with nil, empty, non-string, or blank api key" do
61
- lambda { Sift::Client.new(nil) }.should raise_error
62
- lambda { Sift::Client.new("") }.should raise_error
63
- lambda { Sift::Client.new(123456) }.should raise_error
64
- lambda { Sift::Client.new() }.should raise_error
195
+ expect(lambda { Sift::Client.new(:api_key => nil) }).to raise_error(StandardError)
196
+ expect(lambda { Sift::Client.new(:api_key => "") }).to raise_error(StandardError)
197
+ expect(lambda { Sift::Client.new(:api_key => 123456) }).to raise_error(StandardError)
198
+ expect(lambda { Sift::Client.new() }).to raise_error(StandardError)
65
199
  end
66
200
 
67
- it "Cannot instantiate client with nil, empty, non-string, or blank path" do
68
- api_key = "test_local_api_key"
69
- lambda { Sift::Client.new(api_key, nil) }.should raise_error
70
- lambda { Sift::Client.new(api_key, "") }.should raise_error
71
- lambda { Sift::Client.new(api_key, 123456) }.should raise_error
201
+
202
+ it "Cannot instantiate client with empty, non-string, or blank path" do
203
+ expect(lambda { Sift::Client.new(:path => "") }).to raise_error(StandardError)
204
+ expect(lambda { Sift::Client.new(:path => 123456) }).to raise_error(StandardError)
72
205
  end
73
206
 
207
+
74
208
  it "Can instantiate client with non-default timeout" do
75
- lambda { Sift::Client.new("test_local_api_key", Sift.current_rest_api_path, 4) }.should_not raise_error
209
+ expect(lambda { Sift::Client.new(:api_key => "foo", :timeout => 4) })
210
+ .not_to raise_error
76
211
  end
77
212
 
213
+
78
214
  it "Track call must specify an event name" do
79
- lambda { Sift::Client.new("foo").track(nil) }.should raise_error
80
- lambda { Sift::Client.new("foo").track("") }.should raise_error
215
+ expect(lambda { Sift::Client.new().track(nil) }).to raise_error(StandardError)
216
+ expect(lambda { Sift::Client.new().track("") }).to raise_error(StandardError)
81
217
  end
82
218
 
219
+
83
220
  it "Must specify an event name" do
84
- lambda { Sift::Client.new("foo").track(nil) }.should raise_error
85
- lambda { Sift::Client.new("foo").track("") }.should raise_error
221
+ expect(lambda { Sift::Client.new().track(nil) }).to raise_error(StandardError)
222
+ expect(lambda { Sift::Client.new().track("") }).to raise_error(StandardError)
86
223
  end
87
224
 
225
+
88
226
  it "Must specify properties" do
89
227
  event = "custom_event_name"
90
- lambda { Sift::Client.new("foo").track(event) }.should raise_error
228
+ expect(lambda { Sift::Client.new().track(event) }).to raise_error(StandardError)
91
229
  end
92
230
 
231
+
93
232
  it "Score call must specify a user_id" do
94
- lambda { Sift::Client.new("foo").score(nil) }.should raise_error
95
- lambda { Sift::Client.new("foo").score("") }.should raise_error
233
+ expect(lambda { Sift::Client.new().score(nil) }).to raise_error(StandardError)
234
+ expect(lambda { Sift::Client.new().score("") }).to raise_error(StandardError)
235
+ end
236
+
237
+
238
+ it "Handles parse exceptions for 500 status" do
239
+ api_key = "foobar"
240
+ event = "$transaction"
241
+ properties = valid_transaction_properties
242
+ res = nil
243
+
244
+ stub_request(:any, /.*/).to_return(:body => "{123", :status => 500)
245
+
246
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.not_to raise_error
247
+ expect(res.http_status_code).to eq(500)
248
+ end
249
+
250
+
251
+ it "Handles parse exceptions for 200 status" do
252
+ api_key = "foobar"
253
+ event = "$transaction"
254
+ properties = valid_transaction_properties
255
+ res = nil
256
+
257
+ stub_request(:any, /.*/).to_return(:body => "{123", :status => 200)
258
+
259
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.to raise_error(TypeError)
96
260
  end
97
261
 
98
- it "Doesn't raise an exception on Net/HTTP errors" do
99
262
 
263
+ it "Preserves response on HTTP errors but does not raise an exception" do
100
264
  api_key = "foobar"
101
265
  event = "$transaction"
102
266
  properties = valid_transaction_properties
267
+ res = nil
103
268
 
104
269
  stub_request(:any, /.*/).to_return(:status => 401)
105
270
 
106
- # This method should just return nil -- the track call failed because
107
- # of an HTTP error
108
- Sift::Client.new(api_key).track(event, properties).should eq(nil)
271
+ expect { res = Sift::Client.new(:api_key => api_key).track(event, properties) }.not_to raise_error
272
+ expect(res.http_status_code).to eq(401)
109
273
  end
110
274
 
111
- it "Returns nil when a StandardError occurs within the request" do
112
275
 
276
+ it "Propagates exception when a StandardError occurs within the request" do
113
277
  api_key = "foobar"
114
278
  event = "$transaction"
115
279
  properties = valid_transaction_properties
116
280
 
117
281
  stub_request(:any, /.*/).to_raise(StandardError)
118
282
 
119
- # This method should just return nil -- the track call failed because
120
- # a StandardError exception was thrown
121
- Sift::Client.new(api_key).track(event, properties).should eq(nil)
283
+ expect { Sift::Client.new(:api_key => api_key).track(event, properties) }.to raise_error(StandardError)
122
284
  end
123
285
 
124
- it "Successfuly handles an event and returns OK" do
125
286
 
287
+ it "Successfuly handles an event and returns OK" do
126
288
  response_json = { :status => 0, :error_message => "OK" }
127
289
 
128
- stub_request(:post, "https://api.siftscience.com/v203/events").
290
+ stub_request(:post, "https://api.siftscience.com/v205/events").
129
291
  with { |request|
130
292
  parsed_body = JSON.parse(request.body)
131
- parsed_body.should include("$buyer_user_id" => "123456")
132
- }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
293
+ expect(parsed_body).to include("$buyer_user_id" => "123456")
294
+ }.to_return(:status => 200, :body => MultiJson.dump(response_json),
295
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
296
+ "content-length"=> "74"})
133
297
 
134
298
  api_key = "foobar"
135
299
  event = "$transaction"
136
300
  properties = valid_transaction_properties
137
301
 
138
- response = Sift::Client.new(api_key).track(event, properties)
139
- response.ok?.should eq(true)
140
- response.api_status.should eq(0)
141
- response.api_error_message.should eq("OK")
302
+ response = Sift::Client.new(:api_key => api_key).track(event, properties)
303
+ expect(response.ok?).to eq(true)
304
+ expect(response.api_status).to eq(0)
305
+ expect(response.api_error_message).to eq("OK")
142
306
  end
143
307
 
144
- it "Successfully submits event with overriden key" do
308
+
309
+ it "Successfully submits event with overridden key" do
145
310
  response_json = { :status => 0, :error_message => "OK"}
146
- stub_request(:post, "https://api.siftscience.com/v203/events").
311
+ stub_request(:post, "https://api.siftscience.com/v205/events").
147
312
  with { | request|
148
313
  parsed_body = JSON.parse(request.body)
149
- parsed_body.should include("$buyer_user_id" => "123456")
150
- parsed_body.should include("$api_key" => "overridden")
314
+ expect(parsed_body).to include("$buyer_user_id" => "123456")
315
+ expect(parsed_body).to include("$api_key" => "overridden")
151
316
  }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
152
317
 
153
318
  api_key = "foobar"
154
319
  event = "$transaction"
155
320
  properties = valid_transaction_properties
156
321
 
157
- response = Sift::Client.new(api_key).track(event, properties, nil, nil, false, "overridden")
158
- response.ok?.should eq(true)
159
- response.api_status.should eq(0)
160
- response.api_error_message.should eq("OK")
322
+ response = Sift::Client.new(:api_key => api_key)
323
+ .track(event, properties, :api_key => "overridden")
324
+ expect(response.ok?).to eq(true)
325
+ expect(response.api_status).to eq(0)
326
+ expect(response.api_error_message).to eq("OK")
161
327
  end
162
328
 
163
- it "Successfuly scrubs nils" do
164
329
 
330
+ it "Successfully scrubs nils" do
165
331
  response_json = { :status => 0, :error_message => "OK" }
166
332
 
167
- stub_request(:post, "https://api.siftscience.com/v203/events").
168
- with { |request|
333
+ stub_request(:post, "https://api.siftscience.com/v205/events")
334
+ .with { |request|
169
335
  parsed_body = JSON.parse(request.body)
170
- parsed_body.should_not include("fake_property")
171
- parsed_body.should include("sub_object" => {"one" => "two"})
172
- }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
336
+ expect(parsed_body).not_to include("fake_property")
337
+ expect(parsed_body).to include("sub_object" => {"one" => "two"})
338
+ }.to_return(:status => 200, :body => MultiJson.dump(response_json),
339
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
340
+ "content-length"=> "74"})
173
341
 
174
342
  api_key = "foobar"
175
343
  event = "$transaction"
@@ -180,47 +348,131 @@ describe Sift::Client do
180
348
  "three" => nil
181
349
  }
182
350
  )
183
- response = Sift::Client.new(api_key).track(event, properties)
184
- response.ok?.should eq(true)
185
- response.api_status.should eq(0)
186
- response.api_error_message.should eq("OK")
351
+ response = Sift::Client.new(:api_key => api_key).track(event, properties)
352
+ expect(response.ok?).to eq(true)
353
+ expect(response.api_status).to eq(0)
354
+ expect(response.api_error_message).to eq("OK")
187
355
  end
188
356
 
189
- it "Successfully fetches a score" do
190
357
 
358
+ it "Successfully fetches a score" do
191
359
  api_key = "foobar"
192
360
  response_json = score_response_json
193
361
 
194
- stub_request(:get, "https://api.siftscience.com/v203/score/247019/?api_key=foobar").
195
- to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
362
+ stub_request(:get, "https://api.siftscience.com/v205/score/247019/?api_key=foobar")
363
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
364
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
365
+ "content-length"=> "74"})
196
366
 
197
- response = Sift::Client.new(api_key).score(score_response_json[:user_id])
198
- response.ok?.should eq(true)
199
- response.api_status.should eq(0)
200
- response.api_error_message.should eq("OK")
367
+ response = Sift::Client.new(:api_key => api_key).score(score_response_json[:user_id])
368
+ expect(response.ok?).to eq(true)
369
+ expect(response.api_status).to eq(0)
370
+ expect(response.api_error_message).to eq("OK")
201
371
 
202
- response.body["score"].should eq(0.93)
372
+ expect(response.body["score"]).to eq(0.93)
203
373
  end
204
374
 
205
- it "Successfully fetches a score with an overridden key" do
206
375
 
376
+ it "Successfully fetches a score with an overridden key" do
207
377
  api_key = "foobar"
208
378
  response_json = score_response_json
209
379
 
210
- stub_request(:get, "https://api.siftscience.com/v203/score/247019/?api_key=overridden").
211
- to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
380
+ stub_request(:get, "https://api.siftscience.com/v205/score/247019/?api_key=overridden")
381
+ .to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
382
+
383
+ response = Sift::Client.new(:api_key => api_key)
384
+ .score(score_response_json[:user_id], :api_key => "overridden")
385
+ expect(response.ok?).to eq(true)
386
+ expect(response.api_status).to eq(0)
387
+ expect(response.api_error_message).to eq("OK")
388
+
389
+ expect(response.body["score"]).to eq(0.93)
390
+ end
391
+
392
+
393
+ it "Successfully executes client.get_user_score()" do
394
+ api_key = "foobar"
395
+ response_json = user_score_response_json
396
+
397
+ stub_request(:get, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
398
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
399
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
400
+ "content-length"=> "74"})
212
401
 
213
- response = Sift::Client.new(api_key).score(score_response_json[:user_id], nil, "overridden")
214
- response.ok?.should eq(true)
215
- response.api_status.should eq(0)
216
- response.api_error_message.should eq("OK")
402
+ response = Sift::Client.new(:api_key => api_key).get_user_score(user_score_response_json[:entity_id])
403
+ expect(response.ok?).to eq(true)
404
+ expect(response.api_status).to eq(0)
405
+ expect(response.api_error_message).to eq("OK")
217
406
 
218
- response.body["score"].should eq(0.93)
407
+ expect(response.body["entity_id"]).to eq("247019")
408
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
219
409
  end
220
410
 
221
411
 
222
- it "Successfuly make a sync score request" do
412
+ it "Successfully executes client.get_user_score() with abuse types specified" do
413
+ api_key = "foobar"
414
+ response_json = user_score_response_json
415
+
416
+ stub_request(:get, "https://api.siftscience.com/v205/users/247019/score" +
417
+ "?api_key=foobar&abuse_types=content_abuse,payment_abuse")
418
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
419
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
420
+ "content-length"=> "74"})
421
+
422
+ response = Sift::Client.new(:api_key => api_key).get_user_score(user_score_response_json[:entity_id],
423
+ :abuse_types => ["content_abuse",
424
+ "payment_abuse"])
425
+ expect(response.ok?).to eq(true)
426
+ expect(response.api_status).to eq(0)
427
+ expect(response.api_error_message).to eq("OK")
428
+
429
+ expect(response.body["entity_id"]).to eq("247019")
430
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
431
+ end
432
+
433
+
434
+ it "Successfully executes client.rescore_user()" do
435
+ api_key = "foobar"
436
+ response_json = user_score_response_json
437
+
438
+ stub_request(:post, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
439
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
440
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
441
+ "content-length"=> "74"})
442
+
443
+ response = Sift::Client.new(:api_key => api_key).rescore_user(user_score_response_json[:entity_id])
444
+ expect(response.ok?).to eq(true)
445
+ expect(response.api_status).to eq(0)
446
+ expect(response.api_error_message).to eq("OK")
447
+
448
+ expect(response.body["entity_id"]).to eq("247019")
449
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
450
+ end
451
+
452
+
453
+ it "Successfully executes client.rescore_user() with abuse types specified" do
454
+ api_key = "foobar"
455
+ response_json = user_score_response_json
456
+
457
+ stub_request(:post, "https://api.siftscience.com/v205/users/247019/score" +
458
+ "?api_key=foobar&abuse_types=content_abuse,payment_abuse")
459
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
460
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
461
+ "content-length"=> "74"})
462
+
463
+ response = Sift::Client.new(:api_key => api_key).rescore_user(user_score_response_json[:entity_id],
464
+ :abuse_types => ["content_abuse",
465
+ "payment_abuse"])
466
+ expect(response.ok?).to eq(true)
467
+ expect(response.api_status).to eq(0)
468
+ expect(response.api_error_message).to eq("OK")
469
+
470
+ expect(response.body["entity_id"]).to eq("247019")
471
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
472
+ end
473
+
223
474
 
475
+ it "Successfully make a sync score request" do
224
476
  api_key = "foobar"
225
477
  response_json = {
226
478
  :status => 0,
@@ -228,18 +480,243 @@ describe Sift::Client do
228
480
  :score_response => score_response_json
229
481
  }
230
482
 
231
- stub_request(:post, "https://api.siftscience.com/v203/events?return_score=true").
232
- to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
483
+ stub_request(:post, "https://api.siftscience.com/v205/events?return_score=true")
484
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
485
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
486
+ "content-length"=> "74"})
233
487
 
234
488
  event = "$transaction"
235
489
  properties = valid_transaction_properties
236
- response = Sift::Client.new(api_key).track(event, properties, nil, nil, true)
237
- response.ok?.should eq(true)
238
- response.api_status.should eq(0)
239
- response.api_error_message.should eq("OK")
240
- response.body["score_response"]["score"].should eq(0.93)
490
+ response = Sift::Client.new(:api_key => api_key)
491
+ .track(event, properties, :return_score => true)
492
+ expect(response.ok?).to eq(true)
493
+ expect(response.api_status).to eq(0)
494
+ expect(response.api_error_message).to eq("OK")
495
+ expect(response.body["score_response"]["score"]).to eq(0.93)
241
496
  end
242
497
 
243
498
 
499
+ it "Successfully make a sync action request" do
500
+ api_key = "foobar"
501
+ response_json = {
502
+ :status => 0,
503
+ :error_message => "OK",
504
+ :score_response => action_response_json
505
+ }
506
+
507
+ stub_request(:post, "https://api.siftscience.com/v205/events?return_action=true")
508
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
509
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
510
+ "content-length"=> "74"})
244
511
 
512
+ event = "$transaction"
513
+ properties = valid_transaction_properties
514
+ response = Sift::Client.new(:api_key => api_key)
515
+ .track(event, properties, :return_action => true)
516
+ expect(response.ok?).to eq(true)
517
+ expect(response.api_status).to eq(0)
518
+ expect(response.api_error_message).to eq("OK")
519
+ expect(response.body["score_response"]["actions"].first["entity"]["type"]).to eq("USER")
520
+ end
521
+
522
+
523
+ it "Successfully make a sync workflow request" do
524
+ api_key = "foobar"
525
+ response_json = {
526
+ :status => 0,
527
+ :error_message => "OK",
528
+ :score_response => {
529
+ :status => -1,
530
+ :error_message => "Internal server error."
531
+ }
532
+ }
533
+
534
+ stub_request(:post,
535
+ "https://api.siftscience.com/v205/events?return_workflow_status=true&return_route_info=true&abuse_types=legacy,payment_abuse")
536
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
537
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
538
+ "content-length"=> "74"})
539
+
540
+ event = "$transaction"
541
+ properties = valid_transaction_properties
542
+ response = Sift::Client.new(:api_key => api_key)
543
+ .track(event, properties,
544
+ :return_workflow_status => true, :return_route_info => true,
545
+ :abuse_types => ['legacy', 'payment_abuse'])
546
+ expect(response.ok?).to eq(true)
547
+ expect(response.api_status).to eq(0)
548
+ expect(response.api_error_message).to eq("OK")
549
+ end
550
+
551
+
552
+ it "Successfully make a workflow status request" do
553
+ response_text = '{"id":"skdjfnkse","config":{"id":"5rrbr4iaaa","version":"1468367620871"},"config_display_name":"workflow config","abuse_types":["payment_abuse"],"state":"running","entity":{"id":"example_user","type":"user"},"history":[{"app":"user","name":"Entity","state":"finished","config":{}}]}'
554
+
555
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/workflows/runs/skdjfnkse")
556
+ .to_return(:status => 200, :body => response_text, :headers => {})
557
+
558
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
559
+ response = client.get_workflow_status("skdjfnkse")
560
+
561
+ expect(response.ok?).to eq(true)
562
+ expect(response.body["id"]).to eq("skdjfnkse")
563
+ expect(response.body["state"]).to eq("running")
564
+ end
565
+
566
+
567
+ it "Successfully make a user decisions request" do
568
+ response_text = '{"decisions":{"content_abuse":{"decision":{"id":"user_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
569
+
570
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/decisions")
571
+ .to_return(:status => 200, :body => response_text, :headers => {})
572
+
573
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
574
+ response = client.get_user_decisions("example_user")
575
+
576
+ expect(response.ok?).to eq(true)
577
+ expect(response.body["decisions"]["content_abuse"]["decision"]["id"]).to eq("user_decision")
578
+ end
579
+
580
+
581
+ it "Successfully make an order decisions request" do
582
+ response_text = '{"decisions":{"payment_abuse":{"decision":{"id":"decision7"},"time":1468599638005,"webhook_succeeded":false},"promotion_abuse":{"decision":{"id":"good_order"},"time":1468517407135,"webhook_succeeded":true}}}'
583
+
584
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/orders/example_order/decisions")
585
+ .to_return(:status => 200, :body => response_text, :headers => {})
586
+
587
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
588
+ response = client.get_order_decisions("example_order", :timeout => 3)
589
+
590
+ expect(response.ok?).to eq(true)
591
+ expect(response.body["decisions"]["payment_abuse"]["decision"]["id"]).to eq("decision7")
592
+ end
593
+
594
+ it "Successfully make a session decisions request" do
595
+ response_text = '{"decisions":{"account_takeover":{"decision":{"id":"session_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
596
+
597
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/sessions/example_session/decisions")
598
+ .to_return(:status => 200, :body => response_text, :headers => {})
599
+
600
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
601
+ response = client.get_session_decisions("example_user", "example_session")
602
+
603
+ expect(response.ok?).to eq(true)
604
+ expect(response.body["decisions"]["account_takeover"]["decision"]["id"]).to eq("session_decision")
605
+ end
606
+
607
+ it "Successfully make an content decisions request" do
608
+ response_text = '{"decisions":{"content_abuse":{"decision":{"id":"decision7"},"time":1468599638005,"webhook_succeeded":false}}}'
609
+
610
+ stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/USER/content/example_content/decisions")
611
+ .to_return(:status => 200, :body => response_text, :headers => {})
612
+
613
+ client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
614
+ response = client.get_content_decisions("USER", "example_content", :timeout => 3)
615
+
616
+ expect(response.ok?).to eq(true)
617
+ expect(response.body["decisions"]["content_abuse"]["decision"]["id"]).to eq("decision7")
618
+ end
619
+
620
+ it "Successfully submits a v205 event with SCORE_PERCENTILES" do
621
+ response_json =
622
+ { :status => 0, :error_message => "OK", :score_response => percentile_response_json}
623
+ stub_request(:post, "https://api.siftscience.com/v205/events?fields=SCORE_PERCENTILES&return_score=true").
624
+ with { | request|
625
+ parsed_body = JSON.parse(request.body)
626
+ expect(parsed_body).to include("$api_key" => "overridden")
627
+ }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
628
+
629
+ api_key = "foobar"
630
+ event = "$transaction"
631
+ properties = valid_transaction_properties
632
+
633
+ response = Sift::Client.new(:api_key => api_key, :version => "205")
634
+ .track(event, properties, :api_key => "overridden", :include_score_percentiles => "true", :return_score => "true")
635
+ expect(response.ok?).to eq(true)
636
+ expect(response.api_status).to eq(0)
637
+ expect(response.api_error_message).to eq("OK")
638
+ expect(response.body["score_response"]["scores"]["account_abuse"]["percentiles"]["last_7_days"]).to eq(-1.0)
639
+ end
640
+
641
+ it "Successfully submits a v205 event with SCORE_PERCENTILES" do
642
+ response_json =
643
+ { :status => 0, :error_message => "OK", :score_response => percentile_response_json}
644
+ stub_request(:post, "https://api.siftscience.com/v205/events?fields=SCORE_PERCENTILES&return_score=true").
645
+ with { | request|
646
+ parsed_body = JSON.parse(request.body)
647
+ expect(parsed_body).to include("$api_key" => "overridden")
648
+ }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
649
+
650
+ api_key = "foobar"
651
+ event = "$transaction"
652
+ properties = valid_transaction_properties
653
+
654
+ response = Sift::Client.new(:api_key => api_key, :version => "205")
655
+ .track(event, properties, :api_key => "overridden", :include_score_percentiles => "true", :return_score => "true")
656
+ expect(response.ok?).to eq(true)
657
+ expect(response.api_status).to eq(0)
658
+ expect(response.api_error_message).to eq("OK")
659
+ expect(response.body["score_response"]["scores"]["account_abuse"]["percentiles"]["last_7_days"]).to eq(-1.0)
660
+ end
661
+
662
+ it "Successfully fetches a v205 score with SCORE_PERCENTILES" do
663
+
664
+ api_key = "foobar"
665
+ response_json = score_response_json
666
+
667
+ stub_request(:get, "https://api.siftscience.com/v205/score/247019/?api_key=foobar&fields=SCORE_PERCENTILES")
668
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
669
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
670
+ "content-length"=> "74"})
671
+
672
+ response = Sift::Client.new(:api_key => api_key)
673
+ .score(score_response_json[:user_id], :version => 205, :include_score_percentiles => "true")
674
+ expect(response.ok?).to eq(true)
675
+ expect(response.api_status).to eq(0)
676
+ expect(response.api_error_message).to eq("OK")
677
+
678
+ expect(response.body["score"]).to eq(0.93)
679
+ end
680
+
681
+ it "Successfully executes client.get_user_score() with SCORE_PERCENTILES" do
682
+
683
+ api_key = "foobar"
684
+ response_json = user_score_response_json
685
+
686
+ stub_request(:get, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar&fields=SCORE_PERCENTILES")
687
+ .to_return(:status => 200, :body => MultiJson.dump(response_json),
688
+ :headers => {"content-type"=>"application/json; charset=UTF-8",
689
+ "content-length"=> "74"})
690
+
691
+ response = Sift::Client.new(:api_key => api_key)
692
+ .get_user_score(user_score_response_json[:entity_id], :include_score_percentiles => "true")
693
+ expect(response.ok?).to eq(true)
694
+ expect(response.api_status).to eq(0)
695
+ expect(response.api_error_message).to eq("OK")
696
+
697
+ expect(response.body["entity_id"]).to eq("247019")
698
+ expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
699
+ end
700
+
701
+ it "Successfully submits a v205 event with WARNINGS" do
702
+ response_json =
703
+ { :status => 0, :error_message => "OK", :warnings => warnings}
704
+ stub_request(:post, "https://api.siftscience.com/v205/events?fields=WARNINGS").
705
+ with { | request|
706
+ parsed_body = JSON.parse(request.body)
707
+ expect(parsed_body).to include("$api_key" => "overridden")
708
+ }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
709
+
710
+ api_key = "foobar"
711
+ event = "$transaction"
712
+ properties = valid_transaction_properties
713
+
714
+ response = Sift::Client.new(:api_key => api_key, :version => "205")
715
+ .track(event, properties, :api_key => "overridden",:warnings => "true")
716
+ expect(response.ok?).to eq(true)
717
+ expect(response.api_status).to eq(0)
718
+ expect(response.api_error_message).to eq("OK")
719
+ expect(response.body["warnings"]["count"]).to eq(1)
720
+ expect(response.body["warnings"]["items"][0]["message"]).to eq("Invalid currency")
721
+ end
245
722
  end