after_ship 0.0.2
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 +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
|