after_ship 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rubocop-my.yml +2 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +18 -0
- data/.yardopts +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +18 -0
- data/after_ship.gemspec +39 -0
- data/lib/after_ship/checkpoint.rb +140 -0
- data/lib/after_ship/tracking.rb +218 -0
- data/lib/after_ship/version.rb +3 -0
- data/lib/after_ship.rb +323 -0
- data/lib/attributes.rb +12 -0
- data/lib/date_utils.rb +61 -0
- data/spec/lib/after_ship_spec.rb +137 -0
- data/spec/lib/checkpoint_spec.rb +139 -0
- data/spec/lib/date_utils_spec.rb +59 -0
- data/spec/lib/tracking_spec.rb +89 -0
- data/spec/request_stubs.rb +133 -0
- data/spec/requests/tracking/delivered_ok.json +263 -0
- data/spec/requests/tracking/delivered_wild.json +653 -0
- data/spec/requests/tracking/in_transit.json +443 -0
- data/spec/spec_helper.rb +13 -0
- metadata +220 -0
data/lib/after_ship.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
require 'attributes'
|
5
|
+
require 'date_utils'
|
6
|
+
require 'after_ship/version'
|
7
|
+
require 'after_ship/tracking'
|
8
|
+
require 'after_ship/checkpoint'
|
9
|
+
|
10
|
+
# Init the client:
|
11
|
+
#
|
12
|
+
# client = AfterShip.new(api_key: 'your-aftership-api-key')
|
13
|
+
#
|
14
|
+
# Get a list of trackings
|
15
|
+
# https://www.aftership.com/docs/api/3.0/tracking/get-trackings
|
16
|
+
#
|
17
|
+
# client.trackings
|
18
|
+
#
|
19
|
+
# # Will return list of Tracking objects:
|
20
|
+
#
|
21
|
+
# [
|
22
|
+
# #<AfterShip::Tracking ...>,
|
23
|
+
# #<AfterShip::Tracking ...>,
|
24
|
+
# ...
|
25
|
+
# ]
|
26
|
+
#
|
27
|
+
# Get a tracking
|
28
|
+
# https://www.aftership.com/docs/api/3.0/tracking/get-trackings-slug-tracking_number
|
29
|
+
#
|
30
|
+
# client.tracking('tracking-number', 'ups')
|
31
|
+
#
|
32
|
+
# # Will return Tracking object or raise AfterShip::ResourceNotFoundError
|
33
|
+
# # if not exists:
|
34
|
+
#
|
35
|
+
# #<AfterShip::Tracking:0x007fe555bd9560
|
36
|
+
# @active=false,
|
37
|
+
# @courier="UPS",
|
38
|
+
# @created_at=#<DateTime: 2014-05-08T15:25:01+00:00 ...>,
|
39
|
+
# @updated_at=#<DateTime: 2014-07-18T09:00:47+00:00 ...>>
|
40
|
+
# @custom_fields={},
|
41
|
+
# @customer_name=nil,
|
42
|
+
# @destination_country_iso3="USA",
|
43
|
+
# @emails=[],
|
44
|
+
# @expected_delivery=nil,
|
45
|
+
# @order_id="PL-12480166",
|
46
|
+
# @order_id_path=nil,
|
47
|
+
# @origin_country_iso3="IND",
|
48
|
+
# @shipment_package_count=0,
|
49
|
+
# @shipment_type="EXPEDITED",
|
50
|
+
# @signed_by="FRONT DOOR",
|
51
|
+
# @slug="ups",
|
52
|
+
# @smses=[],
|
53
|
+
# @source="api",
|
54
|
+
# @status="Delivered",
|
55
|
+
# @tag="Delivered",
|
56
|
+
# @title="1ZA2207X6790326683",
|
57
|
+
# @tracked_count=47,
|
58
|
+
# @tracking_number="1ZA2207X6790326683",
|
59
|
+
# @unique_token="ly9ueXUJC",
|
60
|
+
# @checkpoints=[
|
61
|
+
# #<AfterShip::Checkpoint:0x007fe555bb0340
|
62
|
+
# @checkpoint_time=#<DateTime: 2014-05-12T14:07:00+00:00 ...>,
|
63
|
+
# @city="NEW YORK",
|
64
|
+
# @country_iso3=nil,
|
65
|
+
# @country_name="US",
|
66
|
+
# @courier="UPS",
|
67
|
+
# @created_at=#<DateTime: 2014-05-12T18:34:32+00:00 ...>,
|
68
|
+
# @message="DELIVERED",
|
69
|
+
# @slug="ups",
|
70
|
+
# @state="NY",
|
71
|
+
# @status="Delivered",
|
72
|
+
# @tag="Delivered",
|
73
|
+
# @zip="10075">
|
74
|
+
# #<AfterShip::Checkpoint ...>,
|
75
|
+
# ...
|
76
|
+
# ]>
|
77
|
+
#
|
78
|
+
# Create a new tracking
|
79
|
+
# https://www.aftership.com/docs/api/3.0/tracking/post-trackings
|
80
|
+
#
|
81
|
+
# client.create_tracking('tracking-number', 'ups', order_id: 'external-id')
|
82
|
+
#
|
83
|
+
# # Will return Tracking object or raise AfterShip::InvalidArgumentError
|
84
|
+
# # if it exists:
|
85
|
+
#
|
86
|
+
# #<AfterShip::Tracking ...>
|
87
|
+
#
|
88
|
+
# Update a tracking
|
89
|
+
# https://www.aftership.com/docs/api/3.0/tracking/put-trackings-slug-tracking_number
|
90
|
+
#
|
91
|
+
# client.update_tracking('tracking-number', 'ups', order_id: 'external-id')
|
92
|
+
#
|
93
|
+
# To debug:
|
94
|
+
#
|
95
|
+
# AfterShip.debug = true
|
96
|
+
#
|
97
|
+
# client.tracking('9405903699300211343566', 'usps') # In transit
|
98
|
+
# client.tracking('1ZA2207X6794165804', 'ups') # Delivered, wild
|
99
|
+
# client.tracking('1ZA2207X6791425225', 'ups') # Delivered, ok
|
100
|
+
# client.tracking('1ZA2207X6790326683', 'ups') # Delivered, ok
|
101
|
+
class AfterShip
|
102
|
+
class Error < StandardError; end
|
103
|
+
class InvalidJSONDataError < Error; end # 400
|
104
|
+
class InvalidCredentialsError < Error; end # 401
|
105
|
+
class RequestFailedError < Error; end # 402
|
106
|
+
class ResourceNotFoundError < Error; end # 404
|
107
|
+
class InvalidArgumentError < Error; end # 409
|
108
|
+
class TooManyRequestsError < Error; end # 429
|
109
|
+
class ServerError < Error; end # 500, 502, 503, 504
|
110
|
+
class UnknownError < Error; end
|
111
|
+
|
112
|
+
DEFAULT_API_ADDRESS = 'https://api.aftership.com/v3'
|
113
|
+
TRACKINGS_ENDPOINT = "#{ DEFAULT_API_ADDRESS }/trackings"
|
114
|
+
|
115
|
+
JSON_OPTIONS = {
|
116
|
+
symbolize_keys: true # Symbol keys to string keys
|
117
|
+
}
|
118
|
+
|
119
|
+
# Tag to human-friendly status conversion
|
120
|
+
TAG_STATUS = {
|
121
|
+
'Pending' => 'Pending',
|
122
|
+
'InfoReceived' => 'Info Received',
|
123
|
+
'InTransit' => 'In Transit',
|
124
|
+
'OutForDelivery' => 'Out for Delivery',
|
125
|
+
'AttemptFail' => 'Attempt Failed',
|
126
|
+
'Delivered' => 'Delivered',
|
127
|
+
'Exception' => 'Exception',
|
128
|
+
'Expired' => 'Expired'
|
129
|
+
}
|
130
|
+
|
131
|
+
class << self
|
132
|
+
# If debugging is turned on, it is passed to Typhoeus as "verbose" options,
|
133
|
+
# which is passed down to Ethon and displays request/response in STDERR.
|
134
|
+
#
|
135
|
+
# @return [Bool]
|
136
|
+
attr_accessor :debug
|
137
|
+
end
|
138
|
+
|
139
|
+
attr_reader :api_key
|
140
|
+
|
141
|
+
# @param options [Hash]
|
142
|
+
# api_key [String]
|
143
|
+
def initialize(options)
|
144
|
+
require_arguments(
|
145
|
+
api_key: options[:api_key]
|
146
|
+
)
|
147
|
+
|
148
|
+
@api_key = options.delete(:api_key)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Get a list of trackings.
|
152
|
+
# https://www.aftership.com/docs/api/3.0/tracking/get-trackings
|
153
|
+
#
|
154
|
+
# @return [Hash]
|
155
|
+
def trackings
|
156
|
+
response = request_response(TRACKINGS_ENDPOINT, {}, :get)
|
157
|
+
data = response.fetch(:data).fetch(:trackings)
|
158
|
+
|
159
|
+
data.map { |datum| Tracking.new(datum) }
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get a single tracking. Raises an error if not found.
|
163
|
+
# https://www.aftership.com/docs/api/3.0/tracking/get-trackings-slug-tracking_number
|
164
|
+
#
|
165
|
+
# @param tracking_number [String]
|
166
|
+
# @param courier [String]
|
167
|
+
#
|
168
|
+
# @return [Hash]
|
169
|
+
def tracking(tracking_number, courier)
|
170
|
+
require_arguments(tracking_number: tracking_number, courier: courier)
|
171
|
+
|
172
|
+
url = "#{ TRACKINGS_ENDPOINT }/#{ courier }/#{ tracking_number }"
|
173
|
+
|
174
|
+
response = request_response(url, {}, :get)
|
175
|
+
data = response.fetch(:data).fetch(:tracking)
|
176
|
+
|
177
|
+
Tracking.new(data)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Create a new tracking.
|
181
|
+
# https://www.aftership.com/docs/api/3.0/tracking/post-trackings
|
182
|
+
#
|
183
|
+
# @param tracking_number [String]
|
184
|
+
# @param courier [String]
|
185
|
+
# @param options [Hash]
|
186
|
+
#
|
187
|
+
# @return [Hash]
|
188
|
+
def create_tracking(tracking_number, courier, options = {})
|
189
|
+
require_arguments(tracking_number: tracking_number, courier: courier)
|
190
|
+
|
191
|
+
params = {
|
192
|
+
tracking: {
|
193
|
+
tracking_number: tracking_number,
|
194
|
+
slug: courier
|
195
|
+
}.merge(options)
|
196
|
+
}
|
197
|
+
|
198
|
+
response = request_response(TRACKINGS_ENDPOINT, params, :post)
|
199
|
+
data = response.fetch(:data).fetch(:tracking)
|
200
|
+
|
201
|
+
Tracking.new(data)
|
202
|
+
end
|
203
|
+
|
204
|
+
# https://www.aftership.com/docs/api/3.0/tracking/put-trackings-slug-tracking_number
|
205
|
+
#
|
206
|
+
# @param tracking_number [String]
|
207
|
+
# @param courier [String]
|
208
|
+
# @param options [Hash]
|
209
|
+
#
|
210
|
+
# @return [Hash]
|
211
|
+
def update_tracking(tracking_number, courier, options = {})
|
212
|
+
require_arguments(tracking_number: tracking_number, courier: courier)
|
213
|
+
|
214
|
+
url = "#{ TRACKINGS_ENDPOINT }/#{ courier }/#{ tracking_number }"
|
215
|
+
params = {
|
216
|
+
tracking: options
|
217
|
+
}
|
218
|
+
|
219
|
+
response = request_response(url, params, :put)
|
220
|
+
data = response.fetch(:data).fetch(:tracking)
|
221
|
+
|
222
|
+
Tracking.new(data)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Raises an ArgumentError if any of the args is empty or nil.
|
226
|
+
#
|
227
|
+
# @param hash [Hash] arguments needed in options
|
228
|
+
def require_arguments(hash)
|
229
|
+
hash.each do |name, value|
|
230
|
+
if value.respond_to?(:empty?)
|
231
|
+
invalid_argument!(name) if value.empty?
|
232
|
+
else
|
233
|
+
invalid_argument!(name)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
protected
|
239
|
+
|
240
|
+
# @param name [Symbol]
|
241
|
+
def invalid_argument!(name)
|
242
|
+
fail ArgumentError, "Argument #{ name } cannot be empty"
|
243
|
+
end
|
244
|
+
|
245
|
+
# Prepare a `Typhoeus::Request`, send it over the net and deal
|
246
|
+
# with te response by either returning a Hash or raising an error.
|
247
|
+
#
|
248
|
+
# @param url [String]
|
249
|
+
# @param body_hash [Hash]
|
250
|
+
# @param method [Symbol]
|
251
|
+
#
|
252
|
+
# @return [Hash]
|
253
|
+
def request_response(url, body_hash, method = :get)
|
254
|
+
body_json = MultiJson.dump(body_hash)
|
255
|
+
|
256
|
+
request = Typhoeus::Request.new(
|
257
|
+
url,
|
258
|
+
method: method,
|
259
|
+
verbose: self.class.debug,
|
260
|
+
body: body_json,
|
261
|
+
headers: {
|
262
|
+
'aftership-api-key' => @api_key,
|
263
|
+
'Content-Type' => 'application/json'
|
264
|
+
}
|
265
|
+
)
|
266
|
+
|
267
|
+
if self.class.debug
|
268
|
+
request.on_complete do |response|
|
269
|
+
puts
|
270
|
+
puts 'Request body:'
|
271
|
+
puts request.options[:body]
|
272
|
+
puts
|
273
|
+
puts 'Response body:'
|
274
|
+
puts response.body
|
275
|
+
puts
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
response = request.run
|
280
|
+
response_to_json(response)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Deal with API response, either return a Hash or raise an error.
|
284
|
+
#
|
285
|
+
# @param response [Typhoeus::Response]
|
286
|
+
#
|
287
|
+
# @return [Hash]
|
288
|
+
#
|
289
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
290
|
+
def response_to_json(response)
|
291
|
+
json_response = parse_response(response)
|
292
|
+
|
293
|
+
case json_response[:meta][:code]
|
294
|
+
when 200, 201
|
295
|
+
return json_response
|
296
|
+
when 400
|
297
|
+
fail InvalidJSONDataError, json_response[:meta][:error_message]
|
298
|
+
when 401
|
299
|
+
fail InvalidCredentialsError, json_response[:meta][:error_message]
|
300
|
+
when 402
|
301
|
+
fail RequestFailedError, json_response[:meta][:error_message]
|
302
|
+
when 404
|
303
|
+
fail ResourceNotFoundError, json_response[:meta][:error_message]
|
304
|
+
when 409
|
305
|
+
fail InvalidArgumentError, json_response[:meta][:error_message]
|
306
|
+
when 429
|
307
|
+
fail TooManyRequestsError, json_response[:meta][:error_message]
|
308
|
+
when 500, 502, 503, 504
|
309
|
+
fail ServerError, json_response[:meta][:error_message]
|
310
|
+
else
|
311
|
+
fail UnknownError, json_response[:meta][:error_message]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Parse response body into a Hash.
|
316
|
+
#
|
317
|
+
# @param response [Typhoeus::Response]
|
318
|
+
#
|
319
|
+
# @return [Hash]
|
320
|
+
def parse_response(response)
|
321
|
+
MultiJson.load(response.body, JSON_OPTIONS)
|
322
|
+
end
|
323
|
+
end
|
data/lib/attributes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Extracted attributes loading.
|
2
|
+
module Attributes
|
3
|
+
# Loop through the data hash and for each key call a setter with the value.
|
4
|
+
#
|
5
|
+
# @param data [Hash]
|
6
|
+
def load_attributes(data)
|
7
|
+
data.each do |attribute, value|
|
8
|
+
setter = "#{ attribute }="
|
9
|
+
send(setter, value) if respond_to?(setter)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/date_utils.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Simple utility class for parsing dates and datetimes.
|
2
|
+
class DateUtils
|
3
|
+
# Date:
|
4
|
+
#
|
5
|
+
# +YYYY-MM-DD+
|
6
|
+
DATE_REGEX = /
|
7
|
+
\A
|
8
|
+
\d{4}-\d{2}-\d{2}
|
9
|
+
\Z
|
10
|
+
/x
|
11
|
+
|
12
|
+
# Datetime without zone:
|
13
|
+
#
|
14
|
+
# +YYYY-MM-DDTHH:MM:SS+
|
15
|
+
DATETIME_REGEX = /
|
16
|
+
\A
|
17
|
+
\d{4}-\d{2}-\d{2}
|
18
|
+
T
|
19
|
+
\d{2}:\d{2}:\d{2}
|
20
|
+
\Z
|
21
|
+
/x
|
22
|
+
|
23
|
+
# Datetime with zone:
|
24
|
+
#
|
25
|
+
# +YYYY-MM-DDTHH:MM:SSZ+
|
26
|
+
# +YYYY-MM-DDTHH:MM:SS+HH:MM+
|
27
|
+
# +YYYY-MM-DDTHH:MM:SS-HH:MM+
|
28
|
+
DATETIME_WITH_ZONE_REGEX = /
|
29
|
+
\A
|
30
|
+
\d{4}-\d{2}-\d{2}
|
31
|
+
T
|
32
|
+
\d{2}:\d{2}:\d{2}
|
33
|
+
(Z|[+-]\d{2}:\d{2})
|
34
|
+
\Z
|
35
|
+
/x
|
36
|
+
|
37
|
+
# Try to parse a date or datetime from a string.
|
38
|
+
#
|
39
|
+
# @param value [String]
|
40
|
+
# Empty String,
|
41
|
+
# YYYY-MM-DD,
|
42
|
+
# YYYY-MM-DDTHH:MM:SS,
|
43
|
+
# YYYY-MM-DDTHH:MM:SSZ,
|
44
|
+
# YYYY-MM-DDTHH:MM:SS+HH:MM or
|
45
|
+
# YYYY-MM-DDTHH:MM:SS-HH:MM.
|
46
|
+
#
|
47
|
+
def self.parse(value)
|
48
|
+
case value
|
49
|
+
when ''
|
50
|
+
nil
|
51
|
+
when nil
|
52
|
+
nil
|
53
|
+
when DATE_REGEX
|
54
|
+
Date.parse(value)
|
55
|
+
when DATETIME_REGEX, DATETIME_WITH_ZONE_REGEX
|
56
|
+
DateTime.parse(value)
|
57
|
+
else
|
58
|
+
fail ArgumentError, "Invalid expected_delivery date #{ value.inspect }"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe AfterShip do
|
4
|
+
it 'fails to make a client' do
|
5
|
+
expect { AfterShip.new }.to raise_error
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'With api_key' do
|
9
|
+
before do
|
10
|
+
@client = AfterShip.new(api_key: 'key')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'api_key' do
|
14
|
+
expect(@client.api_key).to eq('key')
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'require_arguments' do
|
18
|
+
it 'tracking_number: nil raises error' do
|
19
|
+
expect { @client.require_arguments tracking_number: nil }
|
20
|
+
.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "tracking_number: 1, courier: '' raises error" do
|
24
|
+
expect { @client.require_arguments tracking_number: 1, courier: '' }
|
25
|
+
.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "tracking_number: 'abc', courier: 'def' does not raise error" do
|
29
|
+
options = { tracking_number: 'abc', courier: 'def' }
|
30
|
+
expect { @client.require_arguments(options) }
|
31
|
+
.to_not raise_error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'trackings' do
|
36
|
+
it 'response 200' do
|
37
|
+
expect { @client.trackings }
|
38
|
+
.to_not raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'response 200 with debug' do
|
42
|
+
AfterShip.debug = true
|
43
|
+
|
44
|
+
expect { @client.trackings }
|
45
|
+
.to_not raise_error
|
46
|
+
|
47
|
+
AfterShip.debug = nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'tracking' do
|
52
|
+
it 'response 200' do
|
53
|
+
expect { @client.tracking('ABC123', 'ups') }
|
54
|
+
.to_not raise_error
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'response 201' do
|
58
|
+
expect { @client.tracking('201', 'ups') }
|
59
|
+
.to_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'response 400' do
|
63
|
+
expect { @client.tracking('400', 'ups') }
|
64
|
+
.to raise_error(AfterShip::InvalidJSONDataError)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'response 401' do
|
68
|
+
expect { @client.tracking('401', 'ups') }
|
69
|
+
.to raise_error(AfterShip::InvalidCredentialsError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'response 402' do
|
73
|
+
expect { @client.tracking('402', 'ups') }
|
74
|
+
.to raise_error(AfterShip::RequestFailedError)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'response 404' do
|
78
|
+
expect { @client.tracking('404', 'ups') }
|
79
|
+
.to raise_error(AfterShip::ResourceNotFoundError)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'response 409' do
|
83
|
+
expect { @client.tracking('409', 'ups') }
|
84
|
+
.to raise_error(AfterShip::InvalidArgumentError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'response 429' do
|
88
|
+
expect { @client.tracking('429', 'ups') }
|
89
|
+
.to raise_error(AfterShip::TooManyRequestsError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'response 500' do
|
93
|
+
expect { @client.tracking('500', 'ups') }
|
94
|
+
.to raise_error(AfterShip::ServerError)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'response 502' do
|
98
|
+
expect { @client.tracking('502', 'ups') }
|
99
|
+
.to raise_error(AfterShip::ServerError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'response 503' do
|
103
|
+
expect { @client.tracking('503', 'ups') }
|
104
|
+
.to raise_error(AfterShip::ServerError)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'response 504' do
|
108
|
+
expect { @client.tracking('504', 'ups') }
|
109
|
+
.to raise_error(AfterShip::ServerError)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'response 666' do
|
113
|
+
expect { @client.tracking('666', 'ups') }
|
114
|
+
.to raise_error(AfterShip::UnknownError)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'create_tracking' do
|
119
|
+
it 'response 200' do
|
120
|
+
expect { @client.create_tracking('ABC123', 'ups') }
|
121
|
+
.to_not raise_error
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'with options response 200' do
|
125
|
+
expect { @client.create_tracking('ABC123', 'ups', order_id: '1234') }
|
126
|
+
.to_not raise_error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'update_tracking' do
|
131
|
+
it 'response 200' do
|
132
|
+
expect { @client.update_tracking('ABC123', 'ups', order_id: '1234') }
|
133
|
+
.to_not raise_error
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe AfterShip::Tracking do
|
4
|
+
before do
|
5
|
+
@client = AfterShip.new(api_key: 'key')
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'Attributes' do
|
9
|
+
before do
|
10
|
+
data = {
|
11
|
+
checkpoints: [
|
12
|
+
{
|
13
|
+
slug: 'ups',
|
14
|
+
city: 'Mumbai',
|
15
|
+
created_at: '2014-05-06T08:03:52+00:00',
|
16
|
+
country_name: 'IN',
|
17
|
+
message: 'BILLING INFORMATION RECEIVED',
|
18
|
+
country_iso3: 'IND',
|
19
|
+
tag: 'InfoReceived',
|
20
|
+
checkpoint_time: '2014-05-01T10:33:38',
|
21
|
+
coordinates: [],
|
22
|
+
state: nil,
|
23
|
+
zip: nil
|
24
|
+
}
|
25
|
+
]
|
26
|
+
}
|
27
|
+
|
28
|
+
tracking = AfterShip::Tracking.new(data)
|
29
|
+
@checkpoint = tracking.checkpoints.first
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'slug' do
|
33
|
+
expect(@checkpoint.slug).to eq('ups')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'city' do
|
37
|
+
expect(@checkpoint.city).to eq('Mumbai')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'courier' do
|
41
|
+
expect(@checkpoint.courier).to eq('UPS')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'created_at is a DateTime' do
|
45
|
+
expect(@checkpoint.created_at).to be_a(DateTime)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'created_at matches pattern' do
|
49
|
+
expect(@checkpoint.created_at.to_s).to eq('2014-05-06T08:03:52+00:00')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'country_name' do
|
53
|
+
expect(@checkpoint.country_name).to eq('IN')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'country_iso3' do
|
57
|
+
expect(@checkpoint.country_iso3).to eq('IND')
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'message' do
|
61
|
+
expect(@checkpoint.message).to eq('BILLING INFORMATION RECEIVED')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'tag' do
|
65
|
+
expect(@checkpoint.tag).to eq('InfoReceived')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'checkpoint_time is a DateTime' do
|
69
|
+
expect(@checkpoint.checkpoint_time).to be_a(DateTime)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'checkpoint_time matches pattern' do
|
73
|
+
expect(@checkpoint.checkpoint_time.to_s).to eq('2014-05-01T10:33:38+00:00')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'state' do
|
77
|
+
expect(@checkpoint.state).to be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'zip' do
|
81
|
+
expect(@checkpoint.zip).to be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'status' do
|
86
|
+
it 'Pending' do
|
87
|
+
data = { tag: 'Pending' }
|
88
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
89
|
+
expect(checkpoint.status).to eq('Pending')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'InfoReceived' do
|
93
|
+
data = { tag: 'InfoReceived' }
|
94
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
95
|
+
expect(checkpoint.status).to eq('Info Received')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'InTransit' do
|
99
|
+
data = { tag: 'InTransit' }
|
100
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
101
|
+
expect(checkpoint.status).to eq('In Transit')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'OutForDelivery' do
|
105
|
+
data = { tag: 'OutForDelivery' }
|
106
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
107
|
+
expect(checkpoint.status).to eq('Out for Delivery')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'AttemptFail' do
|
111
|
+
data = { tag: 'AttemptFail' }
|
112
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
113
|
+
expect(checkpoint.status).to eq('Attempt Failed')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'Delivered' do
|
117
|
+
data = { tag: 'Delivered' }
|
118
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
119
|
+
expect(checkpoint.status).to eq('Delivered')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'Exception' do
|
123
|
+
data = { tag: 'Exception' }
|
124
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
125
|
+
expect(checkpoint.status).to eq('Exception')
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'Expired' do
|
129
|
+
data = { tag: 'Expired' }
|
130
|
+
checkpoint = AfterShip::Checkpoint.new(data)
|
131
|
+
expect(checkpoint.status).to eq('Expired')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'error' do
|
135
|
+
data = { tag: 'error' }
|
136
|
+
expect { AfterShip::Checkpoint.new(data) }.to raise_error(KeyError)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|