sift 1.1.5 → 1.1.6.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 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