Skip to content

Commit

Permalink
Add support for get_user_score and rescore_user
Browse files Browse the repository at this point in the history
  • Loading branch information
atpaino committed Jul 31, 2018
1 parent f168f7b commit c1bf6c2
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 1 deletion.
3 changes: 3 additions & 0 deletions HISTORY
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
=== 3.3.0 2018-07-31
- Add support for rescore_user and get_user_score APIs

=== 3.2.0 2018-07-05
- Add new query parameter force_workflow_run

Expand Down
5 changes: 5 additions & 0 deletions lib/sift.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ def self.score_api_path(user_id, version=API_VERSION)
"/v#{version}/score/#{URI.encode(user_id)}/"
end

# Returns the User Score API path for the specified user ID and API version
def self.user_score_api_path(user_id, version=API_VERSION)
"/v#{version}/users/#{URI.encode(user_id)}/score"
end

# Returns the users API path for the specified user ID and API version
def self.users_label_api_path(user_id, version=API_VERSION)
"/v#{version}/users/#{URI.encode(user_id)}/labels"
Expand Down
108 changes: 108 additions & 0 deletions lib/sift/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,114 @@ def score(user_id, opts = {})
end


# Fetches the latest score(s) computed for the specified user and abuse types.
#
# As opposed to client.score() and client.rescore_user(), this *does not* compute
# a new score for the user; it simply fetches the latest score(s) which have computed.
# These scores may be arbitrarily old.
#
# See https://siftscience.com/developers/docs/ruby/score-api/get-score for more details.
#
# ==== Parameters:
#
# user_id::
# A user's id. This id should be the same as the user_id used in
# event calls.
#
# opts (optional)::
# A Hash of optional parameters for the request --
#
# :abuse_types::
# List of abuse types, specifying for which abuse types a
# score should be returned. By default, a score is returned
# for every abuse type to which you are subscribed.
#
# :api_key::
# Overrides the API key for this call.
#
# :timeout::
# Overrides the timeout (in seconds) for this call.
#
# ==== Returns:
#
# A Response object containing a status code, status message, and,
# if successful, the user's score(s).
#
def get_user_score(user_id, opts = {})
abuse_types = opts[:abuse_types]
api_key = opts[:api_key] || @api_key
timeout = opts[:timeout] || @timeout

raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
raise("Bad api_key parameter") if api_key.empty?

query = {}
query["api_key"] = api_key
query["abuse_types"] = abuse_types.join(",") if abuse_types

options = {
:headers => {"User-Agent" => user_agent},
:query => query
}
options.merge!(:timeout => timeout) unless timeout.nil?

response = self.class.get(Sift.user_score_api_path(user_id, @version), options)
Response.new(response.body, response.code, response.response)
end


# Rescores the specified user for the specified abuse types and returns the resulting score(s).
#
# See https://siftscience.com/developers/docs/ruby/score-api/rescore for more details.
#
# ==== Parameters:
#
# user_id::
# A user's id. This id should be the same as the user_id used in
# event calls.
#
# opts (optional)::
# A Hash of optional parameters for the request --
#
# :abuse_types::
# List of abuse types, specifying for which abuse types a
# score should be returned. By default, a score is returned
# for every abuse type to which you are subscribed.
#
# :api_key::
# Overrides the API key for this call.
#
# :timeout::
# Overrides the timeout (in seconds) for this call.
#
# ==== Returns:
#
# A Response object containing a status code, status message, and,
# if successful, the user's score(s).
#
def rescore_user(user_id, opts = {})
abuse_types = opts[:abuse_types]
api_key = opts[:api_key] || @api_key
timeout = opts[:timeout] || @timeout

raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
raise("Bad api_key parameter") if api_key.empty?

query = {}
query["api_key"] = api_key
query["abuse_types"] = abuse_types.join(",") if abuse_types

options = {
:headers => {"User-Agent" => user_agent},
:query => query
}
options.merge!(:timeout => timeout) unless timeout.nil?

response = self.class.post(Sift.user_score_api_path(user_id, @version), options)
Response.new(response.body, response.code, response.response)
end


# Labels a user.
#
# See https://siftscience.com/developers/docs/ruby/labels-api/label-user .
Expand Down
2 changes: 1 addition & 1 deletion lib/sift/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Sift
VERSION = "3.1.0"
VERSION = "3.3.0"
API_VERSION = "205"
end
70 changes: 70 additions & 0 deletions spec/unit/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ def score_response_json
}
end

def user_score_response_json
{
:entity_type => "user",
:entity_id => "247019",
:scores => {
:payment_abuse => {
:score => 0.78
},
:content_abuse => {
:score => 0.11
}
},
:latest_decisions => {
:payment_abuse => {
:id => "user_looks_bad_payment_abuse",
:category => "block",
:source => "AUTOMATED_RULE",
:time => 1352201880,
:description => "Bad Fraudster"
}
},
:latest_labels => {
:payment_abuse => {
:is_bad => true,
:time => 1352201880
}
},
:status => 0,
:error_message => "OK"
}
end

def action_response_json
{
:user_id => "247019",
Expand Down Expand Up @@ -272,6 +304,44 @@ def fully_qualified_api_endpoint
end


it "Successfully executes client.get_user_score()" do
api_key = "foobar"
response_json = user_score_response_json

stub_request(:get, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
.to_return(:status => 200, :body => MultiJson.dump(response_json),
:headers => {"content-type"=>"application/json; charset=UTF-8",
"content-length"=> "74"})

response = Sift::Client.new(:api_key => api_key).get_user_score(user_score_response_json[:entity_id])
expect(response.ok?).to eq(true)
expect(response.api_status).to eq(0)
expect(response.api_error_message).to eq("OK")

expect(response.body["entity_id"]).to eq("247019")
expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
end


it "Successfully executes client.rescore_user()" do
api_key = "foobar"
response_json = user_score_response_json

stub_request(:post, "https://api.siftscience.com/v205/users/247019/score?api_key=foobar")
.to_return(:status => 200, :body => MultiJson.dump(response_json),
:headers => {"content-type"=>"application/json; charset=UTF-8",
"content-length"=> "74"})

response = Sift::Client.new(:api_key => api_key).rescore_user(user_score_response_json[:entity_id])
expect(response.ok?).to eq(true)
expect(response.api_status).to eq(0)
expect(response.api_error_message).to eq("OK")

expect(response.body["entity_id"]).to eq("247019")
expect(response.body["scores"]["payment_abuse"]["score"]).to eq(0.78)
end


it "Successfully make a sync score request" do
api_key = "foobar"
response_json = {
Expand Down

0 comments on commit c1bf6c2

Please sign in to comment.