sift 2.2.1 → 4.1.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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +43 -0
- data/HISTORY +31 -0
- data/README.md +27 -8
- data/lib/sift/client/decision/apply_to.rb +7 -0
- data/lib/sift/client.rb +217 -21
- data/lib/sift/validate/decision.rb +6 -0
- data/lib/sift/version.rb +2 -2
- data/lib/sift.rb +30 -8
- data/sift.gemspec +1 -0
- data/spec/unit/client_label_spec.rb +2 -2
- data/spec/unit/client_spec.rb +182 -18
- metadata +21 -8
- data/.travis.yml +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4b20b0230176dc6340e5ea4939e51b6e1ed2642e788c25fa2f7f4e829b5c61ff
|
4
|
+
data.tar.gz: 28932a0fddd658fe868c6c6b6b408ab1d8529060cfbe0766ffeef76e2398e43e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b905f6002c28777a4cf82cc6b0e59b2c79c300fdf1f4b4b3bf8b0bff8691946325d83794da812d5abc4b344b5e35d24f72b720aae1ddc0783b7e935117f35588
|
7
|
+
data.tar.gz: e161d32f1c210f3fc4562b985537ceb6e2f304330a3fb14ddbce142cd105b0a0c567f6e177dd3c29c99e6c50133e3a7eb40c5ba922071914d9cf56b540c8f427
|
@@ -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,3 +1,34 @@
|
|
1
|
+
=== 4.1.0 2022-06-22
|
2
|
+
- Add return_route_info query param
|
3
|
+
|
4
|
+
=== 4.0.0 2019-05-15
|
5
|
+
- 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.
|
6
|
+
- Fix URL encoding
|
7
|
+
- Add Circle build
|
8
|
+
|
9
|
+
=== 3.3.0 2018-07-31
|
10
|
+
- Add support for rescore_user and get_user_score APIs
|
11
|
+
|
12
|
+
=== 3.2.0 2018-07-05
|
13
|
+
- Add new query parameter force_workflow_run
|
14
|
+
|
15
|
+
=== 3.1.0 2018-06-04
|
16
|
+
- Adds support for get session decisions to [Decisions API](https://siftscience.com/developers/docs/curl/decisions-api)
|
17
|
+
|
18
|
+
=== 3.0.1 2018-04-06
|
19
|
+
- Improved documentation on HISTORY and README.MD
|
20
|
+
|
21
|
+
=== 3.0.0 2018-03-05
|
22
|
+
- 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
|
23
|
+
- V205 APIs are now called -- **this is an incompatible change**
|
24
|
+
- (use `:version => 204` to call the previous API version)
|
25
|
+
- Adds support for content decisions to [Decisions API](https://siftscience.com/developers/docs/curl/decisions-api)
|
26
|
+
|
27
|
+
INCOMPATIBLE CHANGES INTRODUCED IN API V205:
|
28
|
+
- `$create_content` and `$update_content` have significantly changed, and the old format will be rejected
|
29
|
+
- `$send_message` and `$submit_review` events are no longer valid
|
30
|
+
- 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.**
|
31
|
+
|
1
32
|
=== 2.2.1.0 2018-02-12
|
2
33
|
* Add session level decisions in Apply Decisions APIs.
|
3
34
|
|
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# sift-ruby
|
2
|
+
[](https://circleci.com/gh/SiftScience/sift-ruby)
|
3
|
+
|
4
|
+
The official Ruby bindings for the latest version (v205) of the [Sift API](https://sift.com/developers/docs/java/apis-overview).
|
2
5
|
|
3
6
|
## Requirements
|
4
7
|
|
5
|
-
* Ruby
|
8
|
+
* Ruby 2.0.0 or above.
|
6
9
|
|
7
10
|
|
8
11
|
## Installation
|
@@ -32,7 +35,7 @@ client = Sift::Client.new()
|
|
32
35
|
|
33
36
|
##### OR
|
34
37
|
|
35
|
-
client = Sift::Client.new(api_key: '<your_api_key_here>', account_id: '<your_account_id_here>'
|
38
|
+
client = Sift::Client.new(api_key: '<your_api_key_here>', account_id: '<your_account_id_here>')
|
36
39
|
|
37
40
|
```
|
38
41
|
|
@@ -73,14 +76,14 @@ response = client.score(user_id)
|
|
73
76
|
|
74
77
|
## Decisions
|
75
78
|
|
76
|
-
To learn more about the decisions endpoint visit our [developer docs](https://
|
79
|
+
To learn more about the decisions endpoint visit our [developer docs](https://sift.com/developers/docs/ruby/decisions-api/get-decisions).
|
77
80
|
|
78
81
|
### List of Configured Decisions
|
79
82
|
|
80
83
|
Get a list of your decisions.
|
81
84
|
|
82
85
|
**Optional Params**
|
83
|
-
- `entity_type`: `:user` or `:order` or `:session`
|
86
|
+
- `entity_type`: `:user` or `:order` or `:session` or `:content`
|
84
87
|
- `abuse_types`: `["payment_abuse", "content_abuse", "content_abuse",
|
85
88
|
"account_abuse", "legacy", "account_takeover"]`
|
86
89
|
|
@@ -117,7 +120,7 @@ end
|
|
117
120
|
|
118
121
|
### Apply a decision
|
119
122
|
|
120
|
-
Applies a decision to an entity. Visit our [developer docs](http://
|
123
|
+
Applies a decision to an entity. Visit our [developer docs](http://sift.com/developers/docs/ruby/decisions-api/apply-decision) for more information.
|
121
124
|
|
122
125
|
**Required Params:**
|
123
126
|
- `decision_id`, `source`, `user_id`
|
@@ -140,7 +143,7 @@ response = client.apply_decision({
|
|
140
143
|
user_id: "john@example.com"
|
141
144
|
})
|
142
145
|
|
143
|
-
# apply decision to "
|
146
|
+
# apply decision to "john@example.com"'s order
|
144
147
|
response = client.apply_decision({
|
145
148
|
decision_id: "block_bad_order",
|
146
149
|
source: "manual_review",
|
@@ -149,7 +152,7 @@ response = client.apply_decision({
|
|
149
152
|
order_id: "ORDER_1234"
|
150
153
|
})
|
151
154
|
|
152
|
-
# apply decision to "
|
155
|
+
# apply decision to "john@example.com"'s session
|
153
156
|
response = client.apply_decision({
|
154
157
|
decision_id: "block_bad_session",
|
155
158
|
source: "manual_review",
|
@@ -158,6 +161,16 @@ response = client.apply_decision({
|
|
158
161
|
session_id: "SESSION_ID_1234"
|
159
162
|
})
|
160
163
|
|
164
|
+
# apply decision to "john@example.com"'s content
|
165
|
+
response = client.apply_decision({
|
166
|
+
decision_id: "block_bad_session",
|
167
|
+
source: "manual_review",
|
168
|
+
analyst: "bob@your_company.com",
|
169
|
+
user_id: "john@example.com",
|
170
|
+
content_id: "CONTENT_ID_1234"
|
171
|
+
})
|
172
|
+
|
173
|
+
|
161
174
|
# Make sure you handle the response after applying a decision:
|
162
175
|
|
163
176
|
if response.ok?
|
@@ -196,6 +209,12 @@ response = client.get_user_decisions('example_user_id')
|
|
196
209
|
|
197
210
|
# Get the latest decisions for an order
|
198
211
|
response = client.get_order_decisions('example_order_id')
|
212
|
+
|
213
|
+
# Get the latest decisions for a session
|
214
|
+
response = client.get_session_decisions('example_user_id', 'example_session_id')
|
215
|
+
|
216
|
+
# Get the latest decisions for an content
|
217
|
+
response = client.get_content_decisions('example_user_id', 'example_order_id')
|
199
218
|
```
|
200
219
|
|
201
220
|
## Response Object
|
@@ -15,6 +15,7 @@ module Sift
|
|
15
15
|
description
|
16
16
|
order_id
|
17
17
|
session_id
|
18
|
+
content_id
|
18
19
|
user_id
|
19
20
|
account_id
|
20
21
|
time
|
@@ -93,11 +94,17 @@ module Sift
|
|
93
94
|
configs.has_key?("session_id") || configs.has_key?(:session_id)
|
94
95
|
end
|
95
96
|
|
97
|
+
def applying_to_content?
|
98
|
+
configs.has_key?("content_id") || configs.has_key?(:content_id)
|
99
|
+
end
|
100
|
+
|
96
101
|
def path
|
97
102
|
if applying_to_order?
|
98
103
|
"#{user_path}/orders/#{CGI.escape(order_id)}/decisions"
|
99
104
|
elsif applying_to_session?
|
100
105
|
"#{user_path}/sessions/#{CGI.escape(session_id)}/decisions"
|
106
|
+
elsif applying_to_content?
|
107
|
+
"#{user_path}/content/#{CGI.escape(content_id)}/decisions"
|
101
108
|
else
|
102
109
|
"#{user_path}/decisions"
|
103
110
|
end
|
data/lib/sift/client.rb
CHANGED
@@ -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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
@@ -118,7 +128,7 @@ module Sift
|
|
118
128
|
#
|
119
129
|
# :version::
|
120
130
|
# The version of the Events API, Score API, and Labels API to call.
|
121
|
-
# By default, version
|
131
|
+
# By default, version 205.
|
122
132
|
#
|
123
133
|
# :path::
|
124
134
|
# The URL path to use for Events API path. By default, the
|
@@ -132,7 +142,7 @@ module Sift
|
|
132
142
|
@timeout = opts[:timeout] || 2 # 2-second timeout by default
|
133
143
|
@path = opts[:path] || Sift.rest_api_path(@version)
|
134
144
|
|
135
|
-
raise("api_key
|
145
|
+
raise("api_key") if !@api_key.is_a?(String) || @api_key.empty?
|
136
146
|
raise("path must be a non-empty string") if !@path.is_a?(String) || @path.empty?
|
137
147
|
end
|
138
148
|
|
@@ -194,8 +204,8 @@ module Sift
|
|
194
204
|
#
|
195
205
|
# ==== Returns:
|
196
206
|
#
|
197
|
-
# In the case of a
|
198
|
-
#
|
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,8 @@ 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
|
+
return_route_info = opts[:return_route_info]
|
220
|
+
force_workflow_run = opts[:force_workflow_run]
|
209
221
|
abuse_types = opts[:abuse_types]
|
210
222
|
|
211
223
|
raise("event must be a non-empty string") if (!event.is_a? String) || event.empty?
|
@@ -216,6 +228,8 @@ module Sift
|
|
216
228
|
query["return_score"] = "true" if return_score
|
217
229
|
query["return_action"] = "true" if return_action
|
218
230
|
query["return_workflow_status"] = "true" if return_workflow_status
|
231
|
+
query["return_route_info"] = "true" if return_route_info
|
232
|
+
query["force_workflow_run"] = "true" if force_workflow_run
|
219
233
|
query["abuse_types"] = abuse_types.join(",") if abuse_types
|
220
234
|
|
221
235
|
options = {
|
@@ -226,14 +240,8 @@ module Sift
|
|
226
240
|
}
|
227
241
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
228
242
|
|
229
|
-
|
230
|
-
|
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
|
243
|
+
response = self.class.post(path, options)
|
244
|
+
Response.new(response.body, response.code, response.response)
|
237
245
|
end
|
238
246
|
|
239
247
|
|
@@ -293,6 +301,114 @@ module Sift
|
|
293
301
|
end
|
294
302
|
|
295
303
|
|
304
|
+
# Fetches the latest score(s) computed for the specified user and abuse types.
|
305
|
+
#
|
306
|
+
# As opposed to client.score() and client.rescore_user(), this *does not* compute
|
307
|
+
# a new score for the user; it simply fetches the latest score(s) which have computed.
|
308
|
+
# These scores may be arbitrarily old.
|
309
|
+
#
|
310
|
+
# See https://siftscience.com/developers/docs/ruby/score-api/get-score for more details.
|
311
|
+
#
|
312
|
+
# ==== Parameters:
|
313
|
+
#
|
314
|
+
# user_id::
|
315
|
+
# A user's id. This id should be the same as the user_id used in
|
316
|
+
# event calls.
|
317
|
+
#
|
318
|
+
# opts (optional)::
|
319
|
+
# A Hash of optional parameters for the request --
|
320
|
+
#
|
321
|
+
# :abuse_types::
|
322
|
+
# List of abuse types, specifying for which abuse types a
|
323
|
+
# score should be returned. By default, a score is returned
|
324
|
+
# for every abuse type to which you are subscribed.
|
325
|
+
#
|
326
|
+
# :api_key::
|
327
|
+
# Overrides the API key for this call.
|
328
|
+
#
|
329
|
+
# :timeout::
|
330
|
+
# Overrides the timeout (in seconds) for this call.
|
331
|
+
#
|
332
|
+
# ==== Returns:
|
333
|
+
#
|
334
|
+
# A Response object containing a status code, status message, and,
|
335
|
+
# if successful, the user's score(s).
|
336
|
+
#
|
337
|
+
def get_user_score(user_id, opts = {})
|
338
|
+
abuse_types = opts[:abuse_types]
|
339
|
+
api_key = opts[:api_key] || @api_key
|
340
|
+
timeout = opts[:timeout] || @timeout
|
341
|
+
|
342
|
+
raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
|
343
|
+
raise("Bad api_key parameter") if api_key.empty?
|
344
|
+
|
345
|
+
query = {}
|
346
|
+
query["api_key"] = api_key
|
347
|
+
query["abuse_types"] = abuse_types.join(",") if abuse_types
|
348
|
+
|
349
|
+
options = {
|
350
|
+
:headers => {"User-Agent" => user_agent},
|
351
|
+
:query => query
|
352
|
+
}
|
353
|
+
options.merge!(:timeout => timeout) unless timeout.nil?
|
354
|
+
|
355
|
+
response = self.class.get(Sift.user_score_api_path(user_id, @version), options)
|
356
|
+
Response.new(response.body, response.code, response.response)
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
# Rescores the specified user for the specified abuse types and returns the resulting score(s).
|
361
|
+
#
|
362
|
+
# See https://siftscience.com/developers/docs/ruby/score-api/rescore for more details.
|
363
|
+
#
|
364
|
+
# ==== Parameters:
|
365
|
+
#
|
366
|
+
# user_id::
|
367
|
+
# A user's id. This id should be the same as the user_id used in
|
368
|
+
# event calls.
|
369
|
+
#
|
370
|
+
# opts (optional)::
|
371
|
+
# A Hash of optional parameters for the request --
|
372
|
+
#
|
373
|
+
# :abuse_types::
|
374
|
+
# List of abuse types, specifying for which abuse types a
|
375
|
+
# score should be returned. By default, a score is returned
|
376
|
+
# for every abuse type to which you are subscribed.
|
377
|
+
#
|
378
|
+
# :api_key::
|
379
|
+
# Overrides the API key for this call.
|
380
|
+
#
|
381
|
+
# :timeout::
|
382
|
+
# Overrides the timeout (in seconds) for this call.
|
383
|
+
#
|
384
|
+
# ==== Returns:
|
385
|
+
#
|
386
|
+
# A Response object containing a status code, status message, and,
|
387
|
+
# if successful, the user's score(s).
|
388
|
+
#
|
389
|
+
def rescore_user(user_id, opts = {})
|
390
|
+
abuse_types = opts[:abuse_types]
|
391
|
+
api_key = opts[:api_key] || @api_key
|
392
|
+
timeout = opts[:timeout] || @timeout
|
393
|
+
|
394
|
+
raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
|
395
|
+
raise("Bad api_key parameter") if api_key.empty?
|
396
|
+
|
397
|
+
query = {}
|
398
|
+
query["api_key"] = api_key
|
399
|
+
query["abuse_types"] = abuse_types.join(",") if abuse_types
|
400
|
+
|
401
|
+
options = {
|
402
|
+
:headers => {"User-Agent" => user_agent},
|
403
|
+
:query => query
|
404
|
+
}
|
405
|
+
options.merge!(:timeout => timeout) unless timeout.nil?
|
406
|
+
|
407
|
+
response = self.class.post(Sift.user_score_api_path(user_id, @version), options)
|
408
|
+
Response.new(response.body, response.code, response.response)
|
409
|
+
end
|
410
|
+
|
411
|
+
|
296
412
|
# Labels a user.
|
297
413
|
#
|
298
414
|
# See https://siftscience.com/developers/docs/ruby/labels-api/label-user .
|
@@ -504,6 +620,86 @@ module Sift
|
|
504
620
|
Response.new(response.body, response.code, response.response)
|
505
621
|
end
|
506
622
|
|
623
|
+
# Gets the decision status of a session.
|
624
|
+
#
|
625
|
+
# See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
|
626
|
+
#
|
627
|
+
# ==== Parameters
|
628
|
+
#
|
629
|
+
# user_id::
|
630
|
+
# The ID of the user in the session.
|
631
|
+
#
|
632
|
+
# session_id::
|
633
|
+
# The ID of a session.
|
634
|
+
#
|
635
|
+
# opts (optional)::
|
636
|
+
# A Hash of optional parameters for this request --
|
637
|
+
#
|
638
|
+
# :account_id::
|
639
|
+
# Overrides the account id for this call.
|
640
|
+
#
|
641
|
+
# :api_key::
|
642
|
+
# Overrides the API key for this call.
|
643
|
+
#
|
644
|
+
# :timeout::
|
645
|
+
# Overrides the timeout (in seconds) for this call.
|
646
|
+
#
|
647
|
+
def get_session_decisions(user_id, session_id, opts = {})
|
648
|
+
account_id = opts[:account_id] || @account_id
|
649
|
+
api_key = opts[:api_key] || @api_key
|
650
|
+
timeout = opts[:timeout] || @timeout
|
651
|
+
|
652
|
+
options = {
|
653
|
+
:headers => { "User-Agent" => user_agent },
|
654
|
+
:basic_auth => { :username => api_key, :password => "" }
|
655
|
+
}
|
656
|
+
options.merge!(:timeout => timeout) unless timeout.nil?
|
657
|
+
|
658
|
+
uri = API3_ENDPOINT + Sift.session_decisions_api_path(account_id, user_id, session_id)
|
659
|
+
response = self.class.get(uri, options)
|
660
|
+
Response.new(response.body, response.code, response.response)
|
661
|
+
end
|
662
|
+
|
663
|
+
# Gets the decision status of a piece of content.
|
664
|
+
#
|
665
|
+
# See https://siftscience.com/developers/docs/ruby/decisions-api/decision-status .
|
666
|
+
#
|
667
|
+
# ==== Parameters
|
668
|
+
#
|
669
|
+
# user_id::
|
670
|
+
# The ID of the owner of the content.
|
671
|
+
#
|
672
|
+
# content_id::
|
673
|
+
# The ID of a piece of content.
|
674
|
+
#
|
675
|
+
# opts (optional)::
|
676
|
+
# A Hash of optional parameters for this request --
|
677
|
+
#
|
678
|
+
# :account_id::
|
679
|
+
# Overrides the API key for this call.
|
680
|
+
#
|
681
|
+
# :api_key::
|
682
|
+
# Overrides the API key for this call.
|
683
|
+
#
|
684
|
+
# :timeout::
|
685
|
+
# Overrides the timeout (in seconds) for this call.
|
686
|
+
#
|
687
|
+
def get_content_decisions(user_id, content_id, opts = {})
|
688
|
+
account_id = opts[:account_id] || @account_id
|
689
|
+
api_key = opts[:api_key] || @api_key
|
690
|
+
timeout = opts[:timeout] || @timeout
|
691
|
+
|
692
|
+
options = {
|
693
|
+
:headers => { "User-Agent" => user_agent },
|
694
|
+
:basic_auth => { :username => api_key, :password => "" }
|
695
|
+
}
|
696
|
+
options.merge!(:timeout => timeout) unless timeout.nil?
|
697
|
+
|
698
|
+
uri = API3_ENDPOINT + Sift.content_decisions_api_path(account_id, user_id, content_id)
|
699
|
+
response = self.class.get(uri, options)
|
700
|
+
Response.new(response.body, response.code, response.response)
|
701
|
+
end
|
702
|
+
|
507
703
|
def decisions(opts = {})
|
508
704
|
decision_instance.list(opts)
|
509
705
|
end
|
data/lib/sift/version.rb
CHANGED
data/lib/sift.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative './sift/version'
|
2
|
+
require_relative './sift/client'
|
3
|
+
require 'erb'
|
3
4
|
|
4
5
|
module Sift
|
5
6
|
|
@@ -10,27 +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/#{
|
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/#{
|
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}
|
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}
|
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}
|
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"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the path for Content Decisions API
|
53
|
+
def self.content_decisions_api_path(account_id, user_id, content_id)
|
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"
|
34
57
|
end
|
35
58
|
|
36
59
|
# Module-scoped public API key
|
@@ -64,5 +87,4 @@ module Sift
|
|
64
87
|
def self.fatal(msg)
|
65
88
|
@logger.fatal(msg) if @logger
|
66
89
|
end
|
67
|
-
|
68
90
|
end
|
data/sift.gemspec
CHANGED
@@ -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
|
|
@@ -25,7 +25,7 @@ describe Sift::Client do
|
|
25
25
|
response_json = { :status => 0, :error_message => "OK" }
|
26
26
|
user_id = "frodo_baggins"
|
27
27
|
|
28
|
-
stub_request(:post, "https://api.siftscience.com/
|
28
|
+
stub_request(:post, "https://api.siftscience.com/v205/users/frodo_baggins/labels")
|
29
29
|
.with(:body => ('{"$abuse_type":"content_abuse","$is_bad":true,"$description":"Listed a fake item","$type":"$label","$api_key":"foobar"}'))
|
30
30
|
.to_return(:body => MultiJson.dump(response_json), :status => 200,
|
31
31
|
:headers => {"content-type"=>"application/json; charset=UTF-8",
|
@@ -45,7 +45,7 @@ describe Sift::Client do
|
|
45
45
|
user_id = "frodo_baggins"
|
46
46
|
|
47
47
|
stub_request(:delete,
|
48
|
-
"https://api.siftscience.com/
|
48
|
+
"https://api.siftscience.com/v205/users/frodo_baggins/labels?api_key=foobar&abuse_type=payment_abuse")
|
49
49
|
.to_return(:status => 204)
|
50
50
|
|
51
51
|
api_key = "foobar"
|
data/spec/unit/client_spec.rb
CHANGED
@@ -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,36 +172,59 @@ describe Sift::Client do
|
|
140
172
|
end
|
141
173
|
|
142
174
|
|
143
|
-
it "
|
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
|
-
|
151
|
-
|
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 "
|
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
|
-
|
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
|
|
169
224
|
it "Successfuly handles an event and returns OK" do
|
170
225
|
response_json = { :status => 0, :error_message => "OK" }
|
171
226
|
|
172
|
-
stub_request(:post, "https://api.siftscience.com/
|
227
|
+
stub_request(:post, "https://api.siftscience.com/v205/events").
|
173
228
|
with { |request|
|
174
229
|
parsed_body = JSON.parse(request.body)
|
175
230
|
expect(parsed_body).to include("$buyer_user_id" => "123456")
|
@@ -190,7 +245,7 @@ describe Sift::Client do
|
|
190
245
|
|
191
246
|
it "Successfully submits event with overridden key" do
|
192
247
|
response_json = { :status => 0, :error_message => "OK"}
|
193
|
-
stub_request(:post, "https://api.siftscience.com/
|
248
|
+
stub_request(:post, "https://api.siftscience.com/v205/events").
|
194
249
|
with { | request|
|
195
250
|
parsed_body = JSON.parse(request.body)
|
196
251
|
expect(parsed_body).to include("$buyer_user_id" => "123456")
|
@@ -212,7 +267,7 @@ describe Sift::Client do
|
|
212
267
|
it "Successfully scrubs nils" do
|
213
268
|
response_json = { :status => 0, :error_message => "OK" }
|
214
269
|
|
215
|
-
stub_request(:post, "https://api.siftscience.com/
|
270
|
+
stub_request(:post, "https://api.siftscience.com/v205/events")
|
216
271
|
.with { |request|
|
217
272
|
parsed_body = JSON.parse(request.body)
|
218
273
|
expect(parsed_body).not_to include("fake_property")
|
@@ -241,7 +296,7 @@ describe Sift::Client do
|
|
241
296
|
api_key = "foobar"
|
242
297
|
response_json = score_response_json
|
243
298
|
|
244
|
-
stub_request(:get, "https://api.siftscience.com/
|
299
|
+
stub_request(:get, "https://api.siftscience.com/v205/score/247019/?api_key=foobar")
|
245
300
|
.to_return(:status => 200, :body => MultiJson.dump(response_json),
|
246
301
|
:headers => {"content-type"=>"application/json; charset=UTF-8",
|
247
302
|
"content-length"=> "74"})
|
@@ -259,7 +314,7 @@ describe Sift::Client do
|
|
259
314
|
api_key = "foobar"
|
260
315
|
response_json = score_response_json
|
261
316
|
|
262
|
-
stub_request(:get, "https://api.siftscience.com/
|
317
|
+
stub_request(:get, "https://api.siftscience.com/v205/score/247019/?api_key=overridden")
|
263
318
|
.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
|
264
319
|
|
265
320
|
response = Sift::Client.new(:api_key => api_key)
|
@@ -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 = {
|
@@ -280,7 +417,7 @@ describe Sift::Client do
|
|
280
417
|
:score_response => score_response_json
|
281
418
|
}
|
282
419
|
|
283
|
-
stub_request(:post, "https://api.siftscience.com/
|
420
|
+
stub_request(:post, "https://api.siftscience.com/v205/events?return_score=true")
|
284
421
|
.to_return(:status => 200, :body => MultiJson.dump(response_json),
|
285
422
|
:headers => {"content-type"=>"application/json; charset=UTF-8",
|
286
423
|
"content-length"=> "74"})
|
@@ -304,7 +441,7 @@ describe Sift::Client do
|
|
304
441
|
:score_response => action_response_json
|
305
442
|
}
|
306
443
|
|
307
|
-
stub_request(:post, "https://api.siftscience.com/
|
444
|
+
stub_request(:post, "https://api.siftscience.com/v205/events?return_action=true")
|
308
445
|
.to_return(:status => 200, :body => MultiJson.dump(response_json),
|
309
446
|
:headers => {"content-type"=>"application/json; charset=UTF-8",
|
310
447
|
"content-length"=> "74"})
|
@@ -332,7 +469,7 @@ describe Sift::Client do
|
|
332
469
|
}
|
333
470
|
|
334
471
|
stub_request(:post,
|
335
|
-
"https://api.siftscience.com/
|
472
|
+
"https://api.siftscience.com/v205/events?return_workflow_status=true&return_route_info=true&abuse_types=legacy,payment_abuse")
|
336
473
|
.to_return(:status => 200, :body => MultiJson.dump(response_json),
|
337
474
|
:headers => {"content-type"=>"application/json; charset=UTF-8",
|
338
475
|
"content-length"=> "74"})
|
@@ -341,7 +478,8 @@ describe Sift::Client do
|
|
341
478
|
properties = valid_transaction_properties
|
342
479
|
response = Sift::Client.new(:api_key => api_key)
|
343
480
|
.track(event, properties,
|
344
|
-
:return_workflow_status => true, :
|
481
|
+
:return_workflow_status => true, :return_route_info => true,
|
482
|
+
:abuse_types => ['legacy', 'payment_abuse'])
|
345
483
|
expect(response.ok?).to eq(true)
|
346
484
|
expect(response.api_status).to eq(0)
|
347
485
|
expect(response.api_error_message).to eq("OK")
|
@@ -363,7 +501,7 @@ describe Sift::Client do
|
|
363
501
|
end
|
364
502
|
|
365
503
|
|
366
|
-
|
504
|
+
it "Successfully make a user decisions request" do
|
367
505
|
response_text = '{"decisions":{"content_abuse":{"decision":{"id":"user_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
|
368
506
|
|
369
507
|
stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/decisions")
|
@@ -390,4 +528,30 @@ describe Sift::Client do
|
|
390
528
|
expect(response.body["decisions"]["payment_abuse"]["decision"]["id"]).to eq("decision7")
|
391
529
|
end
|
392
530
|
|
531
|
+
it "Successfully make a session decisions request" do
|
532
|
+
response_text = '{"decisions":{"account_takeover":{"decision":{"id":"session_decision"},"time":1468707128659,"webhook_succeeded":false}}}'
|
533
|
+
|
534
|
+
stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/example_user/sessions/example_session/decisions")
|
535
|
+
.to_return(:status => 200, :body => response_text, :headers => {})
|
536
|
+
|
537
|
+
client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
|
538
|
+
response = client.get_session_decisions("example_user", "example_session")
|
539
|
+
|
540
|
+
expect(response.ok?).to eq(true)
|
541
|
+
expect(response.body["decisions"]["account_takeover"]["decision"]["id"]).to eq("session_decision")
|
542
|
+
end
|
543
|
+
|
544
|
+
it "Successfully make an content decisions request" do
|
545
|
+
response_text = '{"decisions":{"content_abuse":{"decision":{"id":"decision7"},"time":1468599638005,"webhook_succeeded":false}}}'
|
546
|
+
|
547
|
+
stub_request(:get, "https://foobar:@api3.siftscience.com/v3/accounts/ACCT/users/USER/content/example_content/decisions")
|
548
|
+
.to_return(:status => 200, :body => response_text, :headers => {})
|
549
|
+
|
550
|
+
client = Sift::Client.new(:api_key => "foobar", :account_id => "ACCT")
|
551
|
+
response = client.get_content_decisions("USER", "example_content", :timeout => 3)
|
552
|
+
|
553
|
+
expect(response.ok?).to eq(true)
|
554
|
+
expect(response.body["decisions"]["content_abuse"]["decision"]["id"]).to eq("decision7")
|
555
|
+
end
|
556
|
+
|
393
557
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sift
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Sadaghiani
|
8
8
|
- Yoav Schatzberg
|
9
9
|
- Jacob Burnim
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2022-07-11 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
|
@@ -142,7 +156,7 @@ files:
|
|
142
156
|
homepage: http://siftscience.com
|
143
157
|
licenses: []
|
144
158
|
metadata: {}
|
145
|
-
post_install_message:
|
159
|
+
post_install_message:
|
146
160
|
rdoc_options: []
|
147
161
|
require_paths:
|
148
162
|
- lib
|
@@ -157,9 +171,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
171
|
- !ruby/object:Gem::Version
|
158
172
|
version: '0'
|
159
173
|
requirements: []
|
160
|
-
|
161
|
-
|
162
|
-
signing_key:
|
174
|
+
rubygems_version: 3.1.4
|
175
|
+
signing_key:
|
163
176
|
specification_version: 4
|
164
177
|
summary: Sift Science Ruby API Gem
|
165
178
|
test_files:
|
data/.travis.yml
DELETED
@@ -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
|