postmen 1.0.0.pre.alpha.1 → 1.0.0.pre.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef3279747f60ddf021d56412eed1d833adb77f1c
4
- data.tar.gz: e93b892733da84d2b7d3ae556c004baef30bc9f6
3
+ metadata.gz: 05652c1c08402bef17419ea6b02586580eae907e
4
+ data.tar.gz: 8f6676faa166b9ba2f59248593198cfc4d60e8c0
5
5
  SHA512:
6
- metadata.gz: 721df92fa7baf87693a78098a04cf5f1986f8829ff2f83a752899767fd2aa147ab252f16065b807408108e3872ae6e81c972c024c7231c06ea6c09ec4a02a625
7
- data.tar.gz: 9436a466ca9044c098e05e3df986861bbe5f64dd257f944f486341cddc5dfbd791437bc0dee7ffea3df7e234f352fd96f38893dd0380dbd7ced2ab4f7f5867db
6
+ metadata.gz: 47156b956f5511b485afbee474f381f78e080d7844d642e7d39654417039cfc5b7bea409adef20ffc65df77d8d5bc1bcc5f31f1b04ab8f0b7d9230e30abdf9a0
7
+ data.tar.gz: bf6f08c3f8ace491be96fe8d75b90a82663386008a64ecb6ddd633ea7a1dcb5f3aa4ad9061e4dab3e3a91162cf8acc9bb37c548e7bc84127283133c3ee778ec8
@@ -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
- Response.new(raw_get(path, options)).tap(&:parse_response!)
21
- rescue RateLimitExceeded
22
- @requests += 1
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
- Response.new(raw_post(path, options))
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
- Response.new(raw_put(path, options))
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
@@ -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
- def update_credentials(params = {})
51
- Connection.new.put(
50
+ # @raise [RequestError]
51
+ def update_credentials!(params = {})
52
+ response = Connection.new.put(
52
53
  "/shipper-accounts/#{@id}/credentials",
53
- ShipperAccountUpdateCredentialsQuery.new(params).to_hash
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
- Connection.new.put("/shipper-accounts/#{@id}/info", ShipperAccountUpdateQuery.new(params.merge(subject: self)).to_query)
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(Connection.new.post('/shipper-accounts', CreateShipperAccountQuery.new(params).to_query).parsed_response[:data])
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
@@ -1,4 +1,4 @@
1
1
  class Postmen
2
2
  # SDK Version
3
- VERSION = '1.0.0-alpha.1'.freeze
3
+ VERSION = '1.0.0-alpha.2'.freeze
4
4
  end
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 || "https://#{config.region}-api.postmen.com/v3"
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.1
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-03 00:00:00.000000000 Z
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