sift 1.1.5 → 1.1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OWIwOGNhOTBlNjNlZmZlOThiNTg2ODMwNjAzYjc2ZDJlZTUxMTc2Yw==
4
+ YzY5Mjg2YTQ4ZjRmYWRlOTQ4NTk4NzFmNjNiZjFmYmE3NTg3MDM2MQ==
5
5
  data.tar.gz: !binary |-
6
- OWI1ZTAyZmFmNTUxYWRmM2NiZjYyZGQ0YjVmOWUzMTNiMDU5MzU0Nw==
7
- !binary "U0hBNTEy":
6
+ ZGIzOTAwOWE4OTExMWQ0MzlkYTRiYjk4OTQzNmU0NzQ5YWVkMzg3OQ==
7
+ SHA512:
8
8
  metadata.gz: !binary |-
9
- ZDE2ZjMxY2Q2MWY5MzgzYTE0MjVhZjY3OWFhMTQxZWUwMTE0NDY5ZjI3Njlm
10
- Yzk1MjA4NmRhNWZmNWVkNDJhOWM1NDVhYWM2NzVlMWY3YTRhZWY0MDYwNjRj
11
- N2Y3NzJlODgxZjc3ODNjZmU0Yzk3NmQxODQ3YmY3ZTQ2MWNkZjU=
9
+ NTM1ZTM3ZGU3ODI4ZmE5YzdiMTQ4ZWMxODUxZDhjZTAzZmZiYTAzMGZjMzZk
10
+ YzYxZjI5MDk1MjQ4ZWI1NDFhNWI1ODY2Y2MyYjk1MTI3YTA4ZjdmZjhjOTNl
11
+ NTc0ZGYzYzM5NmI0N2ZiM2JmMzgzMDg5M2ZhZWIwYjQwNWY1MGQ=
12
12
  data.tar.gz: !binary |-
13
- Yzc4YmFhYzJjMWVjYTcyZmM2NGEzYzQ2NjUyMTY5YWEzZTY3NTU0YjZhYTNh
14
- Y2UwZDU4MWNiYzRiNTk5NzExMDdiOTU0NDAwYjk5NmQ1MmZkZDg4MGFhN2U4
15
- OWE1NmRiNDNhYjFlMTM5YjdjMjdlNGE1NjQzZTQ0ZjU1YzY4Yzg=
13
+ NzlhNGRiODU4M2UzNWQzZGRkM2U2OGM4MTk5MWVkZmE4ZjQ1YmJkYzcxZjI2
14
+ OWY1OGI5OWRjMzA4ODM1YWVkZGE4MGVhMzBhMjRkNDg3M2ZlMzNhY2M0YTQ4
15
+ OWJkNjkwN2EzNzIyOTRjMGNhOGNjNzIyMWYzNDdmN2Y3OWQ4ZTE=
data/README.rdoc CHANGED
@@ -1,5 +1,17 @@
1
1
  = Sift Science Ruby bindings {<img src="https://travis-ci.org/SiftScience/sift-ruby.png?branch=master" alt="Build Status" />}[https://travis-ci.org/SiftScience/sift-ruby]
2
2
 
3
+ == Requirements
4
+
5
+ * Ruby 1.8.7 or above. (Ruby 1.8.6 might work if you load ActiveSupport.)
6
+ * HTTParty, 0.11.0 or greater
7
+ * Multi Json, 1.0 or greater
8
+
9
+ For development only:
10
+ * bundler
11
+ * rspec, 2.14.1 or greater
12
+ * webmock, 1.16 or greater
13
+ * rake, any version
14
+
3
15
  == Installation
4
16
 
5
17
  If you want to build the gem from source:
@@ -9,16 +21,49 @@ If you want to build the gem from source:
9
21
  Alternatively, you can install the gem from Rubyforge:
10
22
 
11
23
  $ gem install sift
24
+
25
+ == Usage
26
+ require "sift"
12
27
 
13
- == Requirements
28
+ Sift.api_key = '<your_api_key_here>'
29
+ client = Sift::Client.new()
30
+
31
+ # send a transaction event -- note this is blocking
32
+ event = "$transaction"
33
+
34
+ user_id = "23056" # User ID's may only contain a-z, A-Z, 0-9, =, ., -, _, +, @, :, &, ^, %, !, $
14
35
 
15
- * Ruby 1.8.7 or above. (Ruby 1.8.6 might work if you load ActiveSupport.)
16
- * HTTParty, 0.8.3 or greater
36
+ properties = {
37
+ "$user_id" => user_id,
38
+ "$user_email" => "buyer@gmail.com",
39
+ "$seller_user_id" => "2371",
40
+ "seller_user_email" => "seller@gmail.com",
41
+ "$transaction_id" => "573050",
42
+ "$payment_method" => {
43
+ "$payment_type" => "$credit_card",
44
+ "$payment_gateway" => "$braintree",
45
+ "$card_bin" => "542486",
46
+ "$card_last4" => "4444"
47
+ },
48
+ "$currency_code" => "USD",
49
+ "$amount" => 15230000,
50
+ }
51
+
52
+ response = client.track(event, properties)
53
+
54
+ response.ok? # returns true or false
55
+
56
+ response.http_status_code # HTTP response code, 200 is ok.
17
57
 
18
- For development only:
19
- * bundler
20
- * rspec, 2.9 or greater
21
- * fakeweb, 1.3 or greater
58
+ response.api_status # status field in the return body, Link to Error Codes
59
+
60
+ response.api_error_message # Error message associated with status Error Code
61
+
62
+ # Request a score forthe user with user_id 23056
63
+ response = client.score(user_id)
64
+
65
+ # Label the user with user_id 23056 as Bad with all optional fields
66
+ response = client.label(user_id,{ "$is_bad" => true, "$reasons" => ["$chargeback", ], "$description" => "Chargeback issued", "$source" => "Manual Review", "$analyst" => "analyst.name@your_domain.com"})
22
67
 
23
68
  == Building
24
69
 
data/lib/sift.rb CHANGED
@@ -5,12 +5,17 @@ module Sift
5
5
 
6
6
  # Returns the path for the current API version
7
7
  def self.current_rest_api_path
8
- "/v203/events"
8
+ "/v#{API_VERSION}/events"
9
9
  end
10
10
 
11
11
  def self.current_users_label_api_path(user_id)
12
12
  # This API version is a minor version ahead of the /events API
13
- "/v203/users/#{URI.encode(user_id)}/labels"
13
+ "/v#{API_VERSION}/users/#{URI.encode(user_id)}/labels"
14
+ end
15
+
16
+ # Adding module scoped public API key
17
+ class << self
18
+ attr_accessor :api_key
14
19
  end
15
20
 
16
21
  # Sets the Output logger to use within the client. This can be left uninitializaed
data/lib/sift/client.rb CHANGED
@@ -6,11 +6,11 @@ module Sift
6
6
  # Represents the payload returned from a call through the track API
7
7
  #
8
8
  class Response
9
- attr_reader :json
9
+ attr_reader :body
10
10
  attr_reader :http_status_code
11
11
  attr_reader :api_status
12
12
  attr_reader :api_error_message
13
- attr_reader :original_request
13
+ attr_reader :request
14
14
 
15
15
  # Constructor
16
16
  #
@@ -21,11 +21,11 @@ module Sift
21
21
  # sections.
22
22
  #
23
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"]
24
+ @body = MultiJson.load(http_response)
25
+ @request = MultiJson.load(@body["request"].to_s) if @body["request"]
26
26
  @http_status_code = http_response_code
27
- @api_status = @json["status"].to_i
28
- @api_error_message = @json["error_message"].to_s
27
+ @api_status = @body["status"].to_i
28
+ @api_error_message = @body["error_message"].to_s
29
29
  end
30
30
 
31
31
  # Helper method returns true if and only if the response from the API call was
@@ -36,6 +36,19 @@ module Sift
36
36
  def ok?
37
37
  0 == @api_status.to_i
38
38
  end
39
+
40
+
41
+ # DEPRECIATED
42
+ # Getter method for depreciated 'json' member variable.
43
+ def json
44
+ @body
45
+ end
46
+
47
+ # DEPRECIATED
48
+ # Getter method for depreciated 'original_request' member variable.
49
+ def original_request
50
+ @request
51
+ end
39
52
  end
40
53
 
41
54
  # This class wraps accesses through the API
@@ -46,7 +59,6 @@ module Sift
46
59
 
47
60
  include HTTParty
48
61
  base_uri API_ENDPOINT
49
- default_timeout API_TIMEOUT
50
62
 
51
63
  # Constructor
52
64
  #
@@ -57,10 +69,22 @@ module Sift
57
69
  # path
58
70
  # The path to the event API, e.g., "/v201/events"
59
71
  #
60
- def initialize(api_key, path = Sift.current_rest_api_path)
61
- @api_key = api_key.to_s
72
+ def initialize(api_key = Sift.api_key, path = Sift.current_rest_api_path, timeout = API_TIMEOUT)
73
+ raise(RuntimeError, "api_key must be a non-empty string") if (!api_key.is_a? String) || api_key.empty?
74
+ raise(RuntimeError, "path must be a non-empty string") if (!path.is_a? String) || path.empty?
75
+ @api_key = api_key
62
76
  @path = path
63
- raise(RuntimeError, "api_key is required") if @api_key.nil? || @api_key.empty?
77
+ @timeout = timeout
78
+
79
+
80
+ end
81
+
82
+ def api_key
83
+ @api_key
84
+ end
85
+
86
+ def user_agent
87
+ "SiftScience/v#{API_VERSION} sift-ruby/#{VERSION}"
64
88
  end
65
89
 
66
90
  # Tracks an event and associated properties through the Sift Science API. This call
@@ -94,13 +118,17 @@ module Sift
94
118
  # result, though.
95
119
  #
96
120
  def track(event, properties = {}, timeout = nil, path = nil, return_score = false)
97
- raise(RuntimeError, "event must be a string") if event.nil? || event.to_s.empty?
121
+ raise(RuntimeError, "event must be a non-empty string") if (!event.is_a? String) || event.empty?
98
122
  raise(RuntimeError, "properties cannot be empty") if properties.empty?
99
123
  path ||= @path
100
- path = path + "?return_score=#{return_score ? "true" : "false"}"
124
+ timeout ||= @timeout
125
+ if return_score
126
+ path = path + "?return_score=true"
127
+ end
101
128
  options = {
102
129
  :body => MultiJson.dump(delete_nils(properties).merge({"$type" => event,
103
- "$api_key" => @api_key}))
130
+ "$api_key" => @api_key})),
131
+ :headers => {"User-Agent" => user_agent}
104
132
  }
105
133
  options.merge!(:timeout => timeout) unless timeout.nil?
106
134
  begin
@@ -125,11 +153,15 @@ module Sift
125
153
  # A Response object is returned and captures the status message and
126
154
  # status code. In general, you can ignore the returned result, though.
127
155
  #
128
- def score(user_id)
156
+ def score(user_id, timeout = nil)
157
+
158
+ raise(RuntimeError, "user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
159
+ timetout ||= @timeout
129
160
 
130
- raise(RuntimeError, "user_id must be a string") if user_id.nil? || user_id.to_s.empty?
161
+ options = { :headers => {"User-Agent" => user_agent} }
162
+ options.merge!(:timeout => timeout) unless timeout.nil?
131
163
 
132
- response = self.class.get("/v203/score/#{user_id}/?api_key=#{@api_key}")
164
+ response = self.class.get("/v#{API_VERSION}/score/#{user_id}/?api_key=#{@api_key}", options)
133
165
  Response.new(response.body, response.code)
134
166
 
135
167
  end
@@ -155,7 +187,7 @@ module Sift
155
187
  #
156
188
  def label(user_id, properties = {}, timeout = nil)
157
189
 
158
- raise(RuntimeError, "user_id must be a string") if user_id.nil? || user_id.to_s.empty?
190
+ raise(RuntimeError, "user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
159
191
 
160
192
  path = Sift.current_users_label_api_path(user_id)
161
193
  track("$label", delete_nils(properties), timeout, path)
data/lib/sift/version.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  module Sift
2
- VERSION = "1.1.5"
2
+ VERSION = "1.1.6.0"
3
+ API_VERSION = "203"
3
4
  end
data/sift.gemspec CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = "sift"
7
7
  s.version = Sift::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Fred Sadaghiani"]
10
- s.email = ["freds@siftscience.com"]
9
+ s.authors = ["Fred Sadaghiani", "Yoav Schatzberg"]
10
+ s.email = ["support@siftscience.com"]
11
11
  s.homepage = "http://siftscience.com"
12
12
  s.summary = %q{Sift Science Ruby API Gem}
13
13
  s.description = %q{Sift Science Ruby API. Please see http://siftscience.com for more details.}
@@ -20,13 +20,12 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ["lib"]
21
21
 
22
22
  # Gems that must be intalled for sift to compile and build
23
- s.add_development_dependency "rspec", "~> 2.14.1"
24
- s.add_development_dependency "webmock", "~> 1.16.0"
23
+ s.add_development_dependency "rspec", ">=2.14.1"
24
+ s.add_development_dependency "webmock", ">= 1.16.0"
25
25
 
26
26
  # Gems that must be intalled for sift to work
27
27
  s.add_dependency "httparty", ">= 0.11.0"
28
- s.add_dependency "multi_json", "~> 1.0"
28
+ s.add_dependency "multi_json", ">= 1.0"
29
29
 
30
- s.add_development_dependency("rspec", ">= 2.0")
31
30
  s.add_development_dependency("rake")
32
31
  end
@@ -2,6 +2,10 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
2
 
3
3
  describe Sift::Client do
4
4
 
5
+ before :each do
6
+ Sift.api_key = nil
7
+ end
8
+
5
9
  def valid_transaction_properties
6
10
  {
7
11
  :$buyer_user_id => "123456",
@@ -22,13 +26,53 @@ describe Sift::Client do
22
26
  }
23
27
  end
24
28
 
29
+ def score_response_json
30
+ {
31
+ :user_id => "247019",
32
+ :score => 0.93,
33
+ :reasons => [{
34
+ :name => "UsersPerDevice",
35
+ :value => 4,
36
+ :details => {
37
+ :users => "a, b, c, d"
38
+ }
39
+ }],
40
+ :status => 0,
41
+ :error_message => "OK"
42
+ }
43
+ end
44
+
25
45
  def fully_qualified_api_endpoint
26
46
  Sift::Client::API_ENDPOINT + Sift.current_rest_api_path
27
47
  end
28
48
 
29
- it "Cannot instantiate client with nil or blank api key" do
49
+ it "Can instantiate client with blank api key if Sift.api_key set" do
50
+ Sift.api_key = "test_global_api_key"
51
+ Sift::Client.new().api_key.should eq(Sift.api_key)
52
+ end
53
+
54
+ it "Parameter passed api key takes precedence over Sift.api_key" do
55
+ Sift.api_key = "test_global_api_key"
56
+ api_key = "test_local_api_key"
57
+ Sift::Client.new(api_key).api_key.should eq(api_key)
58
+ end
59
+
60
+ it "Cannot instantiate client with nil, empty, non-string, or blank api key" do
30
61
  lambda { Sift::Client.new(nil) }.should raise_error
31
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
65
+ end
66
+
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
72
+ end
73
+
74
+ 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
32
76
  end
33
77
 
34
78
  it "Track call must specify an event name" do
@@ -126,31 +170,38 @@ describe Sift::Client do
126
170
  it "Successfully fetches a score" do
127
171
 
128
172
  api_key = "foobar"
129
- user_id = "247019"
173
+ response_json = score_response_json
130
174
 
175
+ stub_request(:get, "https://api.siftscience.com/v203/score/247019/?api_key=foobar").
176
+ to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
177
+
178
+ response = Sift::Client.new(api_key).score(score_response_json[:user_id])
179
+ response.ok?.should eq(true)
180
+ response.api_status.should eq(0)
181
+ response.api_error_message.should eq("OK")
182
+
183
+ response.body["score"].should eq(0.93)
184
+ end
185
+
186
+ it "Successfuly make a sync score request" do
187
+
188
+ api_key = "foobar"
131
189
  response_json = {
132
- :user_id => user_id,
133
- :score => 0.93,
134
- :reasons => [{
135
- :name => "UsersPerDevice",
136
- :value => 4,
137
- :details => {
138
- :users => "a, b, c, d"
139
- }
140
- }],
141
190
  :status => 0,
142
- :error_message => "OK"
191
+ :error_message => "OK",
192
+ :score_response => score_response_json
143
193
  }
144
194
 
145
- stub_request(:get, "https://api.siftscience.com/v203/score/247019/?api_key=foobar").
195
+ stub_request(:post, "https://api.siftscience.com/v203/events?return_score=true").
146
196
  to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
147
197
 
148
- response = Sift::Client.new(api_key).score(user_id)
198
+ event = "$transaction"
199
+ properties = valid_transaction_properties
200
+ response = Sift::Client.new(api_key).track(event, properties, nil, nil, true)
149
201
  response.ok?.should eq(true)
150
202
  response.api_status.should eq(0)
151
203
  response.api_error_message.should eq("OK")
152
-
153
- response.json["score"].should eq(0.93)
204
+ response.body["score_response"]["score"].should eq(0.93)
154
205
  end
155
206
 
156
207
  end
metadata CHANGED
@@ -1,41 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sift
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Sadaghiani
8
+ - Yoav Schatzberg
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-05-31 00:00:00.000000000 Z
12
+ date: 2014-09-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rspec
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - ~>
18
+ - - ! '>='
18
19
  - !ruby/object:Gem::Version
19
20
  version: 2.14.1
20
21
  type: :development
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - ~>
25
+ - - ! '>='
25
26
  - !ruby/object:Gem::Version
26
27
  version: 2.14.1
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: webmock
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - ~>
32
+ - - ! '>='
32
33
  - !ruby/object:Gem::Version
33
34
  version: 1.16.0
34
35
  type: :development
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - ~>
39
+ - - ! '>='
39
40
  - !ruby/object:Gem::Version
40
41
  version: 1.16.0
41
42
  - !ruby/object:Gem::Dependency
@@ -56,30 +57,16 @@ dependencies:
56
57
  name: multi_json
57
58
  requirement: !ruby/object:Gem::Requirement
58
59
  requirements:
59
- - - ~>
60
+ - - ! '>='
60
61
  - !ruby/object:Gem::Version
61
62
  version: '1.0'
62
63
  type: :runtime
63
64
  prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ~>
67
- - !ruby/object:Gem::Version
68
- version: '1.0'
69
- - !ruby/object:Gem::Dependency
70
- name: rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ! '>='
74
- - !ruby/object:Gem::Version
75
- version: '2.0'
76
- type: :development
77
- prerelease: false
78
65
  version_requirements: !ruby/object:Gem::Requirement
79
66
  requirements:
80
67
  - - ! '>='
81
68
  - !ruby/object:Gem::Version
82
- version: '2.0'
69
+ version: '1.0'
83
70
  - !ruby/object:Gem::Dependency
84
71
  name: rake
85
72
  requirement: !ruby/object:Gem::Requirement
@@ -96,7 +83,7 @@ dependencies:
96
83
  version: '0'
97
84
  description: Sift Science Ruby API. Please see http://siftscience.com for more details.
98
85
  email:
99
- - freds@siftscience.com
86
+ - support@siftscience.com
100
87
  executables: []
101
88
  extensions: []
102
89
  extra_rdoc_files: []
@@ -137,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
124
  version: '0'
138
125
  requirements: []
139
126
  rubyforge_project: sift
140
- rubygems_version: 2.0.7
127
+ rubygems_version: 2.2.2
141
128
  signing_key:
142
129
  specification_version: 4
143
130
  summary: Sift Science Ruby API Gem