signifyd 0.1.5
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.
- data/README.md +172 -0
- data/Rakefile +11 -0
- data/lib/data/ca-certificates.crt +3918 -0
- data/lib/signifyd.rb +387 -0
- data/lib/signifyd/api/create.rb +16 -0
- data/lib/signifyd/api/list.rb +20 -0
- data/lib/signifyd/api/update.rb +16 -0
- data/lib/signifyd/case.rb +7 -0
- data/lib/signifyd/errors/api_connection_error.rb +4 -0
- data/lib/signifyd/errors/api_error.rb +4 -0
- data/lib/signifyd/errors/authentication_error.rb +4 -0
- data/lib/signifyd/errors/invalid_request_error.rb +4 -0
- data/lib/signifyd/errors/not_implemented_error.rb +4 -0
- data/lib/signifyd/errors/signifyd_error.rb +20 -0
- data/lib/signifyd/resource.rb +19 -0
- data/lib/signifyd/signifyd_object.rb +151 -0
- data/lib/signifyd/util.rb +94 -0
- data/lib/signifyd/version.rb +3 -0
- data/spec/fixtures/signifyd_requests.rb +123 -0
- data/spec/lib/signifyd/case_spec.rb +126 -0
- data/spec/lib/signifyd_base_spec.rb +503 -0
- data/spec/spec_helper.rb +19 -0
- metadata +292 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Signifyd
|
2
|
+
class SignifydError < StandardError
|
3
|
+
attr_reader :message
|
4
|
+
attr_reader :http_status
|
5
|
+
attr_reader :http_body
|
6
|
+
attr_reader :json_body
|
7
|
+
|
8
|
+
def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
|
9
|
+
@message = message
|
10
|
+
@http_status = http_status
|
11
|
+
@http_body = http_body
|
12
|
+
@json_body = json_body
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
|
17
|
+
"#{status_string}#{@message}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Signifyd
|
2
|
+
class Resource < SignifydObject
|
3
|
+
def self.class_name
|
4
|
+
self.name.split('::')[-1]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.url
|
8
|
+
raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Case).') if self == Resource
|
9
|
+
"#{Signifyd.api_version}/#{CGI.escape(class_name.downcase)}s"
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
unless id = self['id']
|
14
|
+
raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
|
15
|
+
end
|
16
|
+
"#{self.class.url}/#{CGI.escape(id)}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Signifyd
|
2
|
+
class SignifydObject
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_accessor :api_key
|
6
|
+
@@permanent_attributes = Set.new([:api_key, :id])
|
7
|
+
|
8
|
+
if method_defined?(:id)
|
9
|
+
undef :id
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(id=nil, api_key=nil)
|
13
|
+
if id.kind_of?(Hash)
|
14
|
+
@retrieve_options = id.dup
|
15
|
+
@retrieve_options.delete(:id)
|
16
|
+
id = id[:id]
|
17
|
+
else
|
18
|
+
@retrieve_options = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
@api_key = api_key
|
22
|
+
@values = {}
|
23
|
+
@unsaved_values = Set.new
|
24
|
+
@transient_values = Set.new
|
25
|
+
self.id = id if id
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.construct_from(values, api_key=nil)
|
29
|
+
obj = self.new(values[:id], api_key)
|
30
|
+
obj.refresh_from(values, api_key)
|
31
|
+
obj
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s(*args)
|
35
|
+
Signifyd::JSON.dump(@values, :pretty => true)
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
|
40
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + Signifyd::JSON.dump(@values, :pretty => true)
|
41
|
+
end
|
42
|
+
|
43
|
+
def refresh_from(values, api_key, partial=false)
|
44
|
+
@api_key = api_key
|
45
|
+
|
46
|
+
removed = partial ? Set.new : Set.new(@values.keys - values.keys)
|
47
|
+
added = Set.new(values.keys - @values.keys)
|
48
|
+
|
49
|
+
instance_eval do
|
50
|
+
remove_accessors(removed)
|
51
|
+
add_accessors(added)
|
52
|
+
end
|
53
|
+
removed.each do |k|
|
54
|
+
@values.delete(k)
|
55
|
+
@transient_values.add(k)
|
56
|
+
@unsaved_values.delete(k)
|
57
|
+
end
|
58
|
+
values.each do |k, v|
|
59
|
+
@values[k] = Util.convert_to_signifyd_object(v, api_key)
|
60
|
+
@transient_values.delete(k)
|
61
|
+
@unsaved_values.delete(k)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def [](k)
|
66
|
+
k = k.to_sym if k.kind_of?(String)
|
67
|
+
@values[k]
|
68
|
+
end
|
69
|
+
|
70
|
+
def []=(k, v)
|
71
|
+
send(:"#{k}=", v)
|
72
|
+
end
|
73
|
+
|
74
|
+
def keys
|
75
|
+
@values.keys
|
76
|
+
end
|
77
|
+
|
78
|
+
def values
|
79
|
+
@values.values
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_json(*a)
|
83
|
+
JSON.dump(@values)
|
84
|
+
end
|
85
|
+
|
86
|
+
def as_json(*a)
|
87
|
+
@values.as_json(*a)
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_hash
|
91
|
+
@values
|
92
|
+
end
|
93
|
+
|
94
|
+
def each(&blk)
|
95
|
+
@values.each(&blk)
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def metaclass
|
101
|
+
class << self; self; end
|
102
|
+
end
|
103
|
+
|
104
|
+
def remove_accessors(keys)
|
105
|
+
metaclass.instance_eval do
|
106
|
+
keys.each do |k|
|
107
|
+
next if @@permanent_attributes.include?(k)
|
108
|
+
k_eq = :"#{k}="
|
109
|
+
remove_method(k) if method_defined?(k)
|
110
|
+
remove_method(k_eq) if method_defined?(k_eq)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_accessors(keys)
|
116
|
+
metaclass.instance_eval do
|
117
|
+
keys.each do |k|
|
118
|
+
next if @@permanent_attributes.include?(k)
|
119
|
+
k_eq = :"#{k}="
|
120
|
+
define_method(k) { @values[k] }
|
121
|
+
define_method(k_eq) do |v|
|
122
|
+
@values[k] = v
|
123
|
+
@unsaved_values.add(k)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def method_missing(name, *args)
|
130
|
+
if name.to_s.end_with?('=')
|
131
|
+
attr = name.to_s[0...-1].to_sym
|
132
|
+
@values[attr] = args[0]
|
133
|
+
@unsaved_values.add(attr)
|
134
|
+
add_accessors([attr])
|
135
|
+
return
|
136
|
+
else
|
137
|
+
return @values[name] if @values.has_key?(name)
|
138
|
+
end
|
139
|
+
|
140
|
+
begin
|
141
|
+
super
|
142
|
+
rescue NoMethodError => e
|
143
|
+
if @transient_values.include?(name)
|
144
|
+
raise NoMethodError.new(e.message + ". The '#{name}' attribute was set in the past, however. The attributes currently available on this object are: #{@values.keys.join(', ')}")
|
145
|
+
else
|
146
|
+
raise
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Signifyd
|
2
|
+
class Util
|
3
|
+
def self.objects_to_ids(h)
|
4
|
+
case h
|
5
|
+
when Resource
|
6
|
+
h.id
|
7
|
+
when Hash
|
8
|
+
res = {}
|
9
|
+
h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
|
10
|
+
res
|
11
|
+
when Array
|
12
|
+
h.map { |v| objects_to_ids(v) }
|
13
|
+
else
|
14
|
+
h
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.file_readable(file)
|
19
|
+
begin
|
20
|
+
File.open(file) { |f| }
|
21
|
+
rescue
|
22
|
+
false
|
23
|
+
else
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.symbolize_names(object)
|
29
|
+
case object
|
30
|
+
when Hash
|
31
|
+
new = {}
|
32
|
+
object.each do |key, value|
|
33
|
+
key = (key.to_sym rescue key) || key
|
34
|
+
new[key] = symbolize_names(value)
|
35
|
+
end
|
36
|
+
new
|
37
|
+
when Array
|
38
|
+
object.map { |value| symbolize_names(value) }
|
39
|
+
else
|
40
|
+
object
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.url_encode(key)
|
45
|
+
URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.flatten_params(params, parent_key=nil)
|
49
|
+
result = []
|
50
|
+
params.each do |key, value|
|
51
|
+
calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
|
52
|
+
if value.is_a?(Hash)
|
53
|
+
result += flatten_params(value, calculated_key)
|
54
|
+
elsif value.is_a?(Array)
|
55
|
+
result += flatten_params_array(value, calculated_key)
|
56
|
+
else
|
57
|
+
result << [calculated_key, value]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.flatten_params_array(value, calculated_key)
|
64
|
+
result = []
|
65
|
+
value.each do |elem|
|
66
|
+
if elem.is_a?(Hash)
|
67
|
+
result += flatten_params(elem, calculated_key)
|
68
|
+
elsif elem.is_a?(Array)
|
69
|
+
result += flatten_params_array(elem, calculated_key)
|
70
|
+
else
|
71
|
+
result << ["#{calculated_key}[]", elem]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.encode(value, key = nil)
|
78
|
+
case value
|
79
|
+
when Hash then value.map { |k,v| encode(v, append_key(key,k)) }.join('&')
|
80
|
+
when Array then value.map { |v| encode(v, "#{key}[]") }.join('&')
|
81
|
+
when nil then ''
|
82
|
+
else
|
83
|
+
"#{key}=#{CGI.escape(value.to_s)}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def self.append_key(root_key, key)
|
90
|
+
root_key.nil? ? key : "#{root_key}[#{key.to_s}]"
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
class SignifydRequests
|
2
|
+
class << self
|
3
|
+
def valid_case
|
4
|
+
email = Faker::Internet.email
|
5
|
+
full_name = "#{Faker::Name.first_name} #{Faker::Name.last_name}"
|
6
|
+
street_address = Faker::Address.street_address
|
7
|
+
username = Faker::Internet.user_name
|
8
|
+
phone_number = Faker::PhoneNumber.phone_number
|
9
|
+
|
10
|
+
{
|
11
|
+
"attackMethod" => "STOLEN_CC",
|
12
|
+
"purchase" => {
|
13
|
+
"browserIpAddress" => "50.141.59.109",
|
14
|
+
"createdAt" => "2013-02-21T18:37:35-05:00",
|
15
|
+
"currency" => "CAD",
|
16
|
+
"totalPrice" => "495.00",
|
17
|
+
"shippingPrice" => "20.00",
|
18
|
+
"products" => [
|
19
|
+
{
|
20
|
+
"itemId" => (rand * 1000000).ceil,
|
21
|
+
"itemName" => Faker::Company.catch_phrase,
|
22
|
+
"itemQuantity" => (rand * 10).ceil,
|
23
|
+
"itemPrice" => "#{(rand * 60).round(2)}",
|
24
|
+
"itemWeight" => (rand * 10).ceil
|
25
|
+
}
|
26
|
+
]
|
27
|
+
},
|
28
|
+
"recipient"=> {
|
29
|
+
"fullName"=>full_name,
|
30
|
+
"confirmationEmail"=>nil,
|
31
|
+
"deliveryAddress"=> {
|
32
|
+
"streetAddress"=>street_address,
|
33
|
+
"unit"=>nil,
|
34
|
+
"city"=>"Palo Alto",
|
35
|
+
"provinceCode"=>"CA",
|
36
|
+
"postalCode"=>"94306",
|
37
|
+
"countryCode"=>"US",
|
38
|
+
"latitude"=>"37.4248",
|
39
|
+
"longitude"=>"-122.148"
|
40
|
+
}
|
41
|
+
},
|
42
|
+
"card"=> {
|
43
|
+
"cardHolderName"=>full_name,
|
44
|
+
"bin"=>nil,
|
45
|
+
"billingAddress"=> {
|
46
|
+
"streetAddress"=>street_address,
|
47
|
+
"unit"=>nil,
|
48
|
+
"city"=>"Palo Alto",
|
49
|
+
"provinceCode"=>"CA",
|
50
|
+
"postalCode"=>"94306",
|
51
|
+
"countryCode"=>"US",
|
52
|
+
"latitude"=>"37.4248",
|
53
|
+
"longitude"=>"-122.148"
|
54
|
+
}
|
55
|
+
},
|
56
|
+
"userAccount"=>{
|
57
|
+
"email"=>email,
|
58
|
+
"username"=>username,
|
59
|
+
"phone"=>phone_number,
|
60
|
+
"createdDate"=>nil,
|
61
|
+
"accountNumber"=>nil,
|
62
|
+
"lastOrderId"=>nil,
|
63
|
+
"aggregateOrderCount"=>nil,
|
64
|
+
"aggregateOrderDollars"=>nil,
|
65
|
+
"lastUpdateDate"=>nil
|
66
|
+
},
|
67
|
+
"seller"=> {
|
68
|
+
"name"=> "Amazon",
|
69
|
+
"domain"=> "amazon.com",
|
70
|
+
"shipFromAddress"=> {
|
71
|
+
"streetAddress"=> "1850 Mercer Rd",
|
72
|
+
"unit"=> nil,
|
73
|
+
"city"=> "Lexington",
|
74
|
+
"provinceCode"=> "KY",
|
75
|
+
"postalCode"=> "40511",
|
76
|
+
"countryCode"=> "US",
|
77
|
+
"latitude"=> nil,
|
78
|
+
"longitude"=> nil
|
79
|
+
},
|
80
|
+
"corporateAddress"=> {
|
81
|
+
"streetAddress"=> "410 Terry Ave",
|
82
|
+
"unit"=> "3L",
|
83
|
+
"city"=> "Seattle",
|
84
|
+
"provinceCode"=> "WA",
|
85
|
+
"postalCode"=> "98109",
|
86
|
+
"countryCode"=> "US",
|
87
|
+
"latitude"=> nil,
|
88
|
+
"longitude"=> nil
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
# systematic: example of correctly formatted json
|
95
|
+
def correct_case_json
|
96
|
+
"{\"attackMethod\":\"STOLEN_CC\",\"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-21T18:37:35-05:00\",\"currency\":\"CAD\",\"totalPrice\":\"495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
97
|
+
end
|
98
|
+
|
99
|
+
# synopsis: Removed ":" between attackMethod and STOLEN_CC - should be attackMethod:STOLEN_CC
|
100
|
+
def invalid_case_bad_json_01
|
101
|
+
"{\"attackMethod\" \"STOLEN_CC\",\"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-21T18:37:35-05:00\",\"currency\":\"CAD\",\"totalPrice\":\"495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# synopsis: Removed "," between STOLEN_CC and purchase - should be STOLEN_CC, purchase
|
105
|
+
def invalid_case_bad_json_02
|
106
|
+
"{\"attackMethod\":\"STOLEN_CC\" \"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-21T18:37:35-05:00\",\"currency\":\"CAD\",\"totalPrice\":\"495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
107
|
+
end
|
108
|
+
|
109
|
+
# synopsis: All json here will be of standard, the date will just be in wrong format for the SignifydPlatform API
|
110
|
+
def invalid_case_bad_formatted_date_01
|
111
|
+
"{\"attackMethod\":\"STOLEN_CC\",\"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-afdadafasdf:77:35-05:00\",\"currency\":\"CAD\",\"totalPrice\":\"495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
112
|
+
end
|
113
|
+
|
114
|
+
# synopsis: All json here will be of standard, the date will just be in wrong format for the SignifydPlatform API
|
115
|
+
def invalid_case_bad_formatted_date_02
|
116
|
+
"{\"attackMethod\":\"STOLEN_CC\",\"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-:z\",\"currency\":\"CAD\",\"totalPrice\":495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def invalid_case_non_existent_key
|
120
|
+
"{\"attackMethod\":\"STOLEN_CC\",\"purchase\":{\"browserIpAddress\":\"50.141.59.109\",\"createdAt\":\"2013-02-21T18:37:35-05:00\",\"hotDogs\":\"with ketchup\",\"currency\":\"CAD\",\"totalPrice\":\"495.00\",\"shippingPrice\":\"20.00\",\"products\":[{\"itemId\":272111,\"itemName\":\"Intuitive 4th generation Graphic Interface\",\"itemQuantity\":5,\"itemPrice\":\"59.53\",\"itemWeight\":7}]},\"recipient\":{\"fullName\":\"Courtney Cruickshank\",\"confirmationEmail\":null,\"deliveryAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"card\":{\"cardHolderName\":\"Courtney Cruickshank\",\"bin\":null,\"billingAddress\":{\"streetAddress\":\"22660 Greenholt Camp\",\"unit\":null,\"city\":\"Palo Alto\",\"provinceCode\":\"CA\",\"postalCode\":\"94306\",\"countryCode\":\"US\",\"latitude\":\"37.4248\",\"longitude\":\"-122.148\"}},\"userAccount\":{\"email\":\"[email protected]\",\"username\":\"tamia\",\"phone\":\"(715)038-1226\",\"createdDate\":null,\"accountNumber\":null,\"lastOrderId\":null,\"aggregateOrderCount\":null,\"aggregateOrderDollars\":null,\"lastUpdateDate\":null},\"seller\":{\"name\":\"Amazon\",\"domain\":\"amazon.com\",\"shipFromAddress\":{\"streetAddress\":\"1850 Mercer Rd\",\"unit\":null,\"city\":\"Lexington\",\"provinceCode\":\"KY\",\"postalCode\":\"40511\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null},\"corporateAddress\":{\"streetAddress\":\"410 Terry Ave\",\"unit\":\"3L\",\"city\":\"Seattle\",\"provinceCode\":\"WA\",\"postalCode\":\"98109\",\"countryCode\":\"US\",\"latitude\":null,\"longitude\":null}}}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Signifyd::Case do
|
4
|
+
let(:hash) { SignifydRequests.valid_case }
|
5
|
+
let(:json) { JSON.dump(hash) }
|
6
|
+
let(:case_id) { (rand * 1000).ceil }
|
7
|
+
let(:order_id) { 'unique_id'}
|
8
|
+
let(:investigation) { "{\"investigationId\":#{case_id}}" }
|
9
|
+
let(:retrieved_case) {"{\"orderId\":#{case_id}}" }
|
10
|
+
|
11
|
+
context '.create' do
|
12
|
+
context 'when creating a case with a valid API key' do
|
13
|
+
context 'and passing the correct parameters' do
|
14
|
+
before {
|
15
|
+
Signifyd.api_key = SIGNIFYD_API_KEY
|
16
|
+
|
17
|
+
stub_request(:post, "https://#{Signifyd.api_key}@api.signifyd.com/v2/cases").
|
18
|
+
with(:body => json, :headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>json.size, 'Content-Type'=>'application/json', 'User-Agent'=>'Signifyd Ruby v2'}).
|
19
|
+
to_return(:status => 201, :body => investigation, :headers => {})
|
20
|
+
}
|
21
|
+
|
22
|
+
after {
|
23
|
+
Signifyd.api_key = nil
|
24
|
+
}
|
25
|
+
|
26
|
+
subject {
|
27
|
+
Signifyd::Case.create(hash)
|
28
|
+
}
|
29
|
+
|
30
|
+
it { should be_true }
|
31
|
+
it { should_not be_nil }
|
32
|
+
it { expect(subject[:code]).to eq(201) }
|
33
|
+
it { expect(subject[:body][:investigationId]).to eq(JSON.parse(investigation)[:investigationId]) }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'and passing incorrect or nil parameters' do
|
37
|
+
before {
|
38
|
+
Signifyd.api_key = SIGNIFYD_API_KEY
|
39
|
+
}
|
40
|
+
|
41
|
+
after {
|
42
|
+
Signifyd.api_key = nil
|
43
|
+
}
|
44
|
+
|
45
|
+
it { lambda { Signifyd::Case.create() }.should raise_error }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when creating a case with an invalid API key' do
|
50
|
+
it { lambda { Signifyd::Case.create(hash) }.should raise_error }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context '.update' do
|
55
|
+
context 'when updating a case' do
|
56
|
+
context 'and the proper case_id and parameters have been sent' do
|
57
|
+
before {
|
58
|
+
Signifyd.api_key = SIGNIFYD_API_KEY
|
59
|
+
|
60
|
+
stub_request(:put, "https://#{Signifyd.api_key}@api.signifyd.com/v2/cases/#{case_id}").
|
61
|
+
with(:body => json, :headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>json.size, 'Content-Type'=>'application/json', 'User-Agent'=>'Signifyd Ruby v2'}).
|
62
|
+
to_return(:status => 200, :body => investigation, :headers => {})
|
63
|
+
}
|
64
|
+
|
65
|
+
after {
|
66
|
+
Signifyd.api_key = nil
|
67
|
+
}
|
68
|
+
|
69
|
+
subject {
|
70
|
+
Signifyd::Case.update(case_id, hash)
|
71
|
+
}
|
72
|
+
|
73
|
+
it { should be_true }
|
74
|
+
it { should_not be_nil }
|
75
|
+
it { expect(subject[:code]).to eq(200) }
|
76
|
+
it { expect(subject[:body][:investigationId]).to eq(JSON.parse(investigation)[:investigationId]) }
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'and incorrect parameters have been passed' do
|
80
|
+
before {
|
81
|
+
Signifyd.api_key = SIGNIFYD_API_KEY
|
82
|
+
}
|
83
|
+
|
84
|
+
after {
|
85
|
+
Signifyd.api_key = nil
|
86
|
+
}
|
87
|
+
|
88
|
+
it { lambda { Signifyd::Case.update(case_id, {}) }.should raise_error }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#find' do
|
94
|
+
before :each do
|
95
|
+
Signifyd.api_key = SIGNIFYD_API_KEY
|
96
|
+
end
|
97
|
+
after :each do
|
98
|
+
Signifyd.api_key = nil
|
99
|
+
end
|
100
|
+
context 'when case with orderId exists' do
|
101
|
+
it 'should return case in hash format' do
|
102
|
+
stub_request(:get, "https://#{Signifyd.api_key}@api.signifyd.com/v2/orders/#{order_id}/case").
|
103
|
+
with(:body => {}, :headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>2, 'Content-Type'=>'application/json', 'User-Agent'=>'Signifyd Ruby v2'}).
|
104
|
+
to_return(:status => 200, :body => retrieved_case, :headers => {})
|
105
|
+
|
106
|
+
subject = Signifyd::Case.find({order_id: order_id})
|
107
|
+
expect(subject).to be_true
|
108
|
+
expect(subject[:code]).to eq(200)
|
109
|
+
expect(subject[:body][:orderId]).to eq(JSON.parse(retrieved_case)[:orderId])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
context 'when case with orderID does not exist' do
|
113
|
+
it 'should raise exception' do
|
114
|
+
exception = RestClient::ExceptionWithResponse.new('test')
|
115
|
+
exception.should_receive(:http_code).and_return(400)
|
116
|
+
exception.should_receive(:http_body).and_return('as')
|
117
|
+
stub_request(:get, "https://#{Signifyd.api_key}@api.signifyd.com/v2/orders/#{order_id}/case").
|
118
|
+
with(:body => {}, :headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>2, 'Content-Type'=>'application/json', 'User-Agent'=>'Signifyd Ruby v2'}).
|
119
|
+
to_raise(exception)
|
120
|
+
|
121
|
+
expect {Signifyd::Case.find({order_id: order_id})}.to raise_error(Signifyd::InvalidRequestError)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|