postmen 1.0.0.pre.alpha.1 → 1.0.0.pre.alpha.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 +4 -4
- data/lib/postmen/connection.rb +82 -8
- data/lib/postmen/errors.rb +52 -0
- data/lib/postmen/response.rb +12 -0
- data/lib/postmen/shipper_account.rb +42 -4
- data/lib/postmen/shipper_account_collection.rb +6 -1
- data/lib/postmen/version.rb +1 -1
- data/lib/postmen.rb +15 -12
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05652c1c08402bef17419ea6b02586580eae907e
|
4
|
+
data.tar.gz: 8f6676faa166b9ba2f59248593198cfc4d60e8c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47156b956f5511b485afbee474f381f78e080d7844d642e7d39654417039cfc5b7bea409adef20ffc65df77d8d5bc1bcc5f31f1b04ab8f0b7d9230e30abdf9a0
|
7
|
+
data.tar.gz: bf6f08c3f8ace491be96fe8d75b90a82663386008a64ecb6ddd633ea7a1dcb5f3aa4ad9061e4dab3e3a91162cf8acc9bb37c548e7bc84127283133c3ee778ec8
|
data/lib/postmen/connection.rb
CHANGED
@@ -5,6 +5,12 @@ class Postmen
|
|
5
5
|
# Maximum number of retries
|
6
6
|
MAX_REQUESTS = 5
|
7
7
|
|
8
|
+
# Main domain used during normal usage.
|
9
|
+
MAIN_DOMAIN = 'postmen.com'.freeze
|
10
|
+
|
11
|
+
# Failover domain used during DNS issues with main domain
|
12
|
+
FAILOVER_DOMAIN = 'postmen.net'.freeze
|
13
|
+
|
8
14
|
def initialize
|
9
15
|
@requests = 0
|
10
16
|
end
|
@@ -17,12 +23,9 @@ class Postmen
|
|
17
23
|
# .get('/labels')
|
18
24
|
# .get('/labels', { params: { limit: 5 } })
|
19
25
|
def get(path, options = {})
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
raise if @requests > MAX_REQUESTS
|
24
|
-
sleep(60)
|
25
|
-
retry
|
26
|
+
with_error_handling do
|
27
|
+
Response.new(raw_get(path, options)).tap(&:parse_response!)
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
31
|
# Performs a HTTP POST request.
|
@@ -33,7 +36,9 @@ class Postmen
|
|
33
36
|
# .post('/labels')
|
34
37
|
# .post('/labels', { json: { my: { sample: :data } } })
|
35
38
|
def post(path, options = {})
|
36
|
-
|
39
|
+
with_error_handling do
|
40
|
+
Response.new(raw_post(path, options))
|
41
|
+
end
|
37
42
|
end
|
38
43
|
|
39
44
|
# Performs a HTTP PUT request.
|
@@ -44,8 +49,11 @@ class Postmen
|
|
44
49
|
# .put('/shipper-accounts/123/info')
|
45
50
|
# ..put('/shipper-accounts/123/info', { json: { my: { sample: :data } } })
|
46
51
|
def put(path, options = {})
|
47
|
-
|
52
|
+
with_error_handling do
|
53
|
+
Response.new(raw_put(path, options)).tap(&:parse_response!)
|
54
|
+
end
|
48
55
|
end
|
56
|
+
|
49
57
|
# Performs a HTTP DELETE request
|
50
58
|
#
|
51
59
|
# @param path [String]
|
@@ -57,8 +65,74 @@ class Postmen
|
|
57
65
|
.delete(get_full_url(path))
|
58
66
|
end
|
59
67
|
|
68
|
+
# Returns the endpoint used in SDK, based on the region(subdomain) and
|
69
|
+
# a failover switch
|
70
|
+
#
|
71
|
+
# @param subdomain [String] The region/subdomain used.
|
72
|
+
# @param failover [Boolean]
|
73
|
+
def self.endpoint(subdomain, failover = false)
|
74
|
+
URI::HTTPS.build(scheme: 'https',
|
75
|
+
host: hostname(subdomain, failover),
|
76
|
+
path: '/v3').to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the hostname based on the region(subdomain) and a failover switch
|
80
|
+
#
|
81
|
+
# @param subdomain [String] The region/subdomain used.
|
82
|
+
# @param failover [Boolean]
|
83
|
+
def self.hostname(subdomain, failover)
|
84
|
+
base = failover ? FAILOVER_DOMAIN : MAIN_DOMAIN
|
85
|
+
[subdomain, base].join('.')
|
86
|
+
end
|
87
|
+
|
60
88
|
private
|
61
89
|
|
90
|
+
# rubocop:disable Metrics/MethodLength
|
91
|
+
# rubocop:disable Metrics/AbcSize
|
92
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
93
|
+
def with_error_handling
|
94
|
+
raise ArgumentError unless block_given?
|
95
|
+
|
96
|
+
begin
|
97
|
+
yield
|
98
|
+
# Rescue from any connection issues
|
99
|
+
rescue HTTP::ConnectionError
|
100
|
+
# Raise error if we already tried to use failover domain
|
101
|
+
raise if Postmen.failover?
|
102
|
+
# Switch to failover domain & retry the request
|
103
|
+
Postmen.failover!
|
104
|
+
retry
|
105
|
+
# Handle Rate limits.
|
106
|
+
# Rate limits are being reset every 60 seconds - we're retrying
|
107
|
+
# given request after that.
|
108
|
+
# @see https://docs.postmen.com/ratelimit.html Documentation
|
109
|
+
rescue RateLimitExceeded
|
110
|
+
@requests += 1
|
111
|
+
raise if @requests > MAX_REQUESTS
|
112
|
+
sleep(60)
|
113
|
+
retry
|
114
|
+
# If the resource was not found, simply re-raise the exception
|
115
|
+
rescue ResourceNotFound
|
116
|
+
raise
|
117
|
+
# Handle request errors.
|
118
|
+
# Our current error handling policy depends on the error type.
|
119
|
+
# If the API returns information, that the request is retriable,
|
120
|
+
# We're waiting 5 seconds, and trying again with exact same request.
|
121
|
+
# To prevent having infinite loops, we're trying maximum 5 times.
|
122
|
+
# In case that we were unable to make successfull request with that 5 tries,
|
123
|
+
# MaximumNumberOfRetriesReachedError is being raised.
|
124
|
+
#
|
125
|
+
# @raise RequestError if the request is not retriable
|
126
|
+
# @raise MaximumNumberOfRetriesReachedError if the API returned error after 5 retries
|
127
|
+
rescue RequestError => error
|
128
|
+
raise unless error.retriable?
|
129
|
+
raise MaximumNumberOfRetriesReachedError, self if @requests > MAX_REQUESTS
|
130
|
+
@requests += 1
|
131
|
+
sleep(5)
|
132
|
+
retry
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
62
136
|
def raw_get(path, options)
|
63
137
|
HTTP
|
64
138
|
.headers(headers)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Postmen
|
2
|
+
# Generic Exception class, all other exceptions should inherit from this class
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
# Generic exception raised if the API returns an error
|
6
|
+
class RequestError < Error
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@request, :meta, :data
|
10
|
+
|
11
|
+
def initialize(request)
|
12
|
+
@request = request
|
13
|
+
end
|
14
|
+
|
15
|
+
# Indicates whether request is retryable.
|
16
|
+
# @see https://docs.postmen.com/errors.html API Documentation
|
17
|
+
def retryable?
|
18
|
+
meta.fetch(:retryable, false)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns details for the request error
|
22
|
+
# @see https://docs.postmen.com/#meta API Documentation
|
23
|
+
def details
|
24
|
+
meta[:details]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Internal API error code
|
28
|
+
# @see https://docs.postmen.com/errors.html List of Error codes
|
29
|
+
def code
|
30
|
+
meta[:code]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns human-readable error message
|
34
|
+
# @see https://docs.postmen.com/#meta API Documentation
|
35
|
+
def message
|
36
|
+
meta[:message]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :request
|
42
|
+
end
|
43
|
+
|
44
|
+
# Exception raised if rate limit was exceeded
|
45
|
+
RateLimitExceeded = Class.new(Error)
|
46
|
+
# Exception raised in case of any connection error
|
47
|
+
ConnectionError = Class.new(Error)
|
48
|
+
# Exception raised if retriable request reached maximum number of attempts.
|
49
|
+
MaximumNumberOfRetriesReachedError = Class.new(Error)
|
50
|
+
# Expcetion raised if resource was not found.
|
51
|
+
ResourceNotFound = Class.new(RequestError)
|
52
|
+
end
|
data/lib/postmen/response.rb
CHANGED
@@ -16,12 +16,24 @@ class Postmen
|
|
16
16
|
@meta ||= parsed_response[:meta]
|
17
17
|
end
|
18
18
|
|
19
|
+
# Holds the data
|
20
|
+
# @see https://docs.postmen.com/#data API Documentation
|
21
|
+
# @return [Hash]
|
22
|
+
def data
|
23
|
+
@data ||= parsed_response[:data]
|
24
|
+
end
|
25
|
+
|
19
26
|
# Parses the json response
|
20
27
|
# @return [Hash]
|
21
28
|
def parsed_response
|
22
29
|
@parsed_response ||= JSON.parse(body, symbolize_names: true)
|
23
30
|
end
|
24
31
|
|
32
|
+
# Checks if response were successfull
|
33
|
+
def success?
|
34
|
+
meta[:code] == 200
|
35
|
+
end
|
36
|
+
|
25
37
|
# Checks if rate limit was exceeded
|
26
38
|
def rate_limit_exceeded?
|
27
39
|
code == 429
|
@@ -47,11 +47,46 @@ class Postmen
|
|
47
47
|
#
|
48
48
|
# @see https://docs.postmen.com/api.html#shipper-accounts-update-a-shipper-account-credentials
|
49
49
|
# @return [ShipperAccount] Updated ShipperAccount resource
|
50
|
-
|
51
|
-
|
50
|
+
# @raise [RequestError]
|
51
|
+
def update_credentials!(params = {})
|
52
|
+
response = Connection.new.put(
|
52
53
|
"/shipper-accounts/#{@id}/credentials",
|
53
|
-
ShipperAccountUpdateCredentialsQuery.new(params).
|
54
|
+
ShipperAccountUpdateCredentialsQuery.new(params).to_query
|
55
|
+
)
|
56
|
+
|
57
|
+
raise RequestError, response unless response.success?
|
58
|
+
|
59
|
+
ShipperAccount.new(response.data)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Update a ShipperAccount credentials
|
63
|
+
#
|
64
|
+
# @see https://docs.postmen.com/api.html#shipper-accounts-update-a-shipper-account-credentials
|
65
|
+
# @return [ShipperAccount] Updated ShipperAccount resource
|
66
|
+
# @return [Hash] a Hash with detailed information about what went wrong
|
67
|
+
def update_credentials(params = {})
|
68
|
+
update_credentials!(params)
|
69
|
+
rescue RequestError => error
|
70
|
+
error.meta
|
71
|
+
end
|
72
|
+
|
73
|
+
# Update a shipper account information
|
74
|
+
#
|
75
|
+
# @see https://docs.postmen.com/api.html#shipper-accounts-update-a-shipper-account-information API Documentation
|
76
|
+
# @example
|
77
|
+
# .update(description: "Your new description")
|
78
|
+
# .update(address: {})
|
79
|
+
# @return [ShipperAccount] Updated ShipperAccount resource
|
80
|
+
# @raise [RequestError]
|
81
|
+
def update!(params = {})
|
82
|
+
response = Connection.new.put(
|
83
|
+
"/shipper-accounts/#{@id}/info",
|
84
|
+
ShipperAccountUpdateQuery.new(params.merge(subject: self)).to_query
|
54
85
|
)
|
86
|
+
|
87
|
+
raise RequestError, response unless response.success?
|
88
|
+
|
89
|
+
ShipperAccount.new(response.data)
|
55
90
|
end
|
56
91
|
|
57
92
|
# Update a shipper account information
|
@@ -61,8 +96,11 @@ class Postmen
|
|
61
96
|
# .update(description: "Your new description")
|
62
97
|
# .update(address: {})
|
63
98
|
# @return [ShipperAccount] Updated ShipperAccount resource
|
99
|
+
# @return [Hash] a Hash with detailed information about what went wrong
|
64
100
|
def update(params = {})
|
65
|
-
|
101
|
+
update!(params)
|
102
|
+
rescue RequestError => error
|
103
|
+
error.meta
|
66
104
|
end
|
67
105
|
end
|
68
106
|
end
|
@@ -33,7 +33,12 @@ class Postmen
|
|
33
33
|
# @see https://docs.postmen.com/api.html#shipper-accounts-create-a-shipper-account API documentation
|
34
34
|
# @return [ShipperAccount]
|
35
35
|
def self.create(params)
|
36
|
-
ShipperAccount.new(
|
36
|
+
ShipperAccount.new(
|
37
|
+
Connection.new.post(
|
38
|
+
'/shipper-accounts',
|
39
|
+
CreateShipperAccountQuery.new(params).to_query
|
40
|
+
).parsed_response[:data]
|
41
|
+
)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
data/lib/postmen/version.rb
CHANGED
data/lib/postmen.rb
CHANGED
@@ -6,6 +6,7 @@ require 'pathname'
|
|
6
6
|
require 'forwardable'
|
7
7
|
|
8
8
|
require 'postmen/version'
|
9
|
+
require 'postmen/errors'
|
9
10
|
require 'postmen/types'
|
10
11
|
require 'postmen/connection'
|
11
12
|
require 'postmen/collection_proxy'
|
@@ -37,17 +38,6 @@ require 'postmen/manifest_collection'
|
|
37
38
|
class Postmen
|
38
39
|
extend Dry::Configurable
|
39
40
|
|
40
|
-
# Generic Exception class, all other exceptions should inherit from this class
|
41
|
-
Error = Class.new(StandardError)
|
42
|
-
# Exception raised if rate limit was exceeded
|
43
|
-
RateLimitExceeded = Class.new(Error)
|
44
|
-
# Exception raised in case of any connection error
|
45
|
-
ConnectionError = Class.new(Error)
|
46
|
-
# Generic exception raised if the API returns an error
|
47
|
-
RequestError = Class.new(Error)
|
48
|
-
# Expcetion raised if resource was not found.
|
49
|
-
ResourceNotFound = Class.new(RequestError)
|
50
|
-
|
51
41
|
# @#!attribute [rw] api_key [String] API key taken from the application.
|
52
42
|
setting :api_key
|
53
43
|
|
@@ -57,11 +47,14 @@ class Postmen
|
|
57
47
|
# @#!attribute endpoint [String] Endoint name - specify if you'd like to use custom endpoint
|
58
48
|
setting :endpoint
|
59
49
|
|
50
|
+
# @#!attribute failover [Bool] Indicates if the SDK is using failover domain.
|
51
|
+
setting :failover, false
|
52
|
+
|
60
53
|
# Returns the endpoint used in all queries
|
61
54
|
#
|
62
55
|
# @return [String] endpoint url
|
63
56
|
def self.endpoint
|
64
|
-
config.endpoint || "
|
57
|
+
config.endpoint || Connection.endpoint("#{config.region}-api", config.failover)
|
65
58
|
end
|
66
59
|
|
67
60
|
# Returns path where gem is installed
|
@@ -70,4 +63,14 @@ class Postmen
|
|
70
63
|
def self.root
|
71
64
|
Pathname.new(File.expand_path(File.join(File.dirname(__FILE__), '../')))
|
72
65
|
end
|
66
|
+
|
67
|
+
# Checks wheter we're in failover mode
|
68
|
+
def self.failover?
|
69
|
+
!!config.failover
|
70
|
+
end
|
71
|
+
|
72
|
+
# Switch to failover domain
|
73
|
+
def self.failover!
|
74
|
+
config.failover = true
|
75
|
+
end
|
73
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postmen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.alpha.
|
4
|
+
version: 1.0.0.pre.alpha.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- postmen.com
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- lib/postmen.rb
|
162
162
|
- lib/postmen/collection_proxy.rb
|
163
163
|
- lib/postmen/connection.rb
|
164
|
+
- lib/postmen/errors.rb
|
164
165
|
- lib/postmen/label.rb
|
165
166
|
- lib/postmen/label_collection.rb
|
166
167
|
- lib/postmen/manifest.rb
|