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