usps-ruby-client 0.1.0

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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +41 -0
  6. data/.rubocop_todo.yml +0 -0
  7. data/.travis.yml +6 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +77 -0
  12. data/Rakefile +9 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/data/api/address-information-api.htm +8229 -0
  16. data/lib/data/api/domestic-mail-service-standards-api.htm +11016 -0
  17. data/lib/data/api/evs-international-label-api.htm +33291 -0
  18. data/lib/data/api/evs-label-api.htm +18895 -0
  19. data/lib/data/api/hold-for-pickup-facilities-lookup-api.htm +5530 -0
  20. data/lib/data/api/package-pickup-api.htm +19157 -0
  21. data/lib/data/api/rate-calculator-api.htm +16355 -0
  22. data/lib/data/api/scan-api.htm +5934 -0
  23. data/lib/data/api/service-delivery-calculator-get-locations-api.htm +12548 -0
  24. data/lib/data/api/track-and-confirm-api.htm +21288 -0
  25. data/lib/data/api/uspsreturnslabel-api.htm +9474 -0
  26. data/lib/helpers/erubis_helper.rb +10 -0
  27. data/lib/tasks/api.rake +246 -0
  28. data/lib/usps-ruby-client.rb +30 -0
  29. data/lib/usps/api/endpoints.rb +70 -0
  30. data/lib/usps/api/endpoints/carrier_pickup_schedule.rb +117 -0
  31. data/lib/usps/api/endpoints/city_state_lookup.rb +50 -0
  32. data/lib/usps/api/endpoints/e_vs_express_mail_intl.rb +359 -0
  33. data/lib/usps/api/endpoints/e_vs_first_class_mail_intl.rb +323 -0
  34. data/lib/usps/api/endpoints/e_vs_priority_mail_intl.rb +366 -0
  35. data/lib/usps/api/endpoints/e_vsgxg_get_label.rb +366 -0
  36. data/lib/usps/api/endpoints/e_vsi_cancel.rb +55 -0
  37. data/lib/usps/api/endpoints/hfp_facility_info.rb +71 -0
  38. data/lib/usps/api/endpoints/intl_rate_v2.rb +135 -0
  39. data/lib/usps/api/endpoints/pts_email.rb +73 -0
  40. data/lib/usps/api/endpoints/pts_pod.rb +86 -0
  41. data/lib/usps/api/endpoints/pts_rre.rb +72 -0
  42. data/lib/usps/api/endpoints/ptst_pod.rb +75 -0
  43. data/lib/usps/api/endpoints/rate_v4.rb +140 -0
  44. data/lib/usps/api/endpoints/scan.rb +121 -0
  45. data/lib/usps/api/endpoints/track_v2.rb +62 -0
  46. data/lib/usps/api/endpoints/usps_returns_label.rb +149 -0
  47. data/lib/usps/api/endpoints/verify.rb +75 -0
  48. data/lib/usps/api/endpoints/zip_code_lookup.rb +69 -0
  49. data/lib/usps/api/error.rb +6 -0
  50. data/lib/usps/api/errors/too_many_requests_error.rb +22 -0
  51. data/lib/usps/api/errors/usps_error.rb +30 -0
  52. data/lib/usps/api/templates/_build_xml.erb +7 -0
  53. data/lib/usps/api/templates/_options.erb +6 -0
  54. data/lib/usps/api/templates/_throw_argument_error.erb +3 -0
  55. data/lib/usps/api/templates/endpoints.erb +22 -0
  56. data/lib/usps/api/templates/method.erb +49 -0
  57. data/lib/usps/api/templates/method_spec.erb +22 -0
  58. data/lib/usps/api/xml.rb +8 -0
  59. data/lib/usps/client.rb +37 -0
  60. data/lib/usps/config.rb +50 -0
  61. data/lib/usps/faraday/connection.rb +34 -0
  62. data/lib/usps/faraday/request.rb +39 -0
  63. data/lib/usps/faraday/response/raise_error.rb +16 -0
  64. data/lib/usps/logger.rb +14 -0
  65. data/lib/usps/version.rb +4 -0
  66. data/usps-ruby-client.gemspec +46 -0
  67. metadata +307 -0
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was auto-generated by lib/tasks/api.rake
4
+
5
+ module Usps
6
+ module Api
7
+ module Endpoints
8
+ module Verify
9
+ #
10
+ # Address Validation API
11
+ #
12
+ #
13
+ # @option option [(Alias)] :AddressValidateRequest (Required)
14
+ # - API = AddressValidateRequest
15
+ # @option option [String] :Revision (Required)
16
+ # - Integer value used to return of all available response fields. Set this value to 1 to return all currently documented response fields. Example: Revision>1</Revision>
17
+ # @option option [(group)] :Address (Required)
18
+ # - Up to 5 address verifications can be included per transaction.
19
+ # @option option [String] :FirmName (Optional)
20
+ # - Firm Name Example:<FirmName>XYZ Corp.</FirmName>
21
+ # @option option [String] :Address1 (Optional)
22
+ # - Delivery Address in the destination address. May contain secondary unit designator, such as APT or SUITE, for Accountable mail.)
23
+ # @option option [String] :Address2 (Required)
24
+ # - Delivery Address in the destination address. Required for all mail and packages, however 11-digit Destination Delivery Point ZIP+4 Code can be provided as an alternative in the Detail 1 Record.
25
+ # @option option [String] :City (Optional)
26
+ # - City name of the destination address.
27
+ # @option option [String] :State (Optional)
28
+ # - Two-character state code of the destination address.
29
+ # @option option [String] :Urbanization (Optional)
30
+ # - Urbanization. For Puerto Rico addresses only.
31
+ # @option option [String] :Zip5 (Optional)
32
+ # - Destination 5-digit ZIP Code. Numeric values (0-9) only. If International, all zeroes.
33
+ # @option option [String] :Zip4 (Optional)
34
+ # - Destination ZIP+4 Numeric values (0-9) only. If International, all zeroes. Default to spaces if not available.
35
+
36
+ #
37
+ # @see
38
+ def verify(options = {})
39
+ throw ArgumentError.new('Required arguments :address_validate_request missing') if options[:address_validate_request].nil?
40
+ throw ArgumentError.new('Required arguments :address_validate_request, :revision missing') if options[:address_validate_request][:revision].nil?
41
+ throw ArgumentError.new('Required arguments :address_validate_request, :address missing') if options[:address_validate_request][:address].nil?
42
+ throw ArgumentError.new('Required arguments :address_validate_request, :address, :address2 missing') if options[:address_validate_request][:address][:address2].nil?
43
+
44
+ request = build_request(:verify, options)
45
+ get('https://secure.shippingapis.com/ShippingAPI.dll', {
46
+ API: 'Verify',
47
+ XML: request,
48
+ })
49
+ end
50
+
51
+ private
52
+
53
+ def tag_unless_blank(xml, tag_name, data)
54
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
55
+ end
56
+
57
+ def build_verify_request(xml, options = {})
58
+ xml.tag!('Revision', options[:address_validate_request][:revision])
59
+ xml.tag!('Address') do
60
+ tag_unless_blank(xml, 'FirmName', options[:address_validate_request][:address][:firm_name])
61
+ tag_unless_blank(xml, 'Address1', options[:address_validate_request][:address][:address1])
62
+ xml.tag!('Address2', options[:address_validate_request][:address][:address2])
63
+ tag_unless_blank(xml, 'City', options[:address_validate_request][:address][:city])
64
+ tag_unless_blank(xml, 'State', options[:address_validate_request][:address][:state])
65
+ tag_unless_blank(xml, 'Urbanization', options[:address_validate_request][:address][:urbanization])
66
+ tag_unless_blank(xml, 'Zip5', options[:address_validate_request][:address][:zip5])
67
+ tag_unless_blank(xml, 'Zip4', options[:address_validate_request][:address][:zip4])
68
+ end
69
+ xml.target!
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was auto-generated by lib/tasks/api.rake
4
+
5
+ module Usps
6
+ module Api
7
+ module Endpoints
8
+ module ZipCodeLookup
9
+ #
10
+ # ZIP Code Lookup API
11
+ #
12
+ # The ZipCodeLookup API, which returns the ZIP Code and ZIP
13
+ # Code + 4 corresponding to the given address, city, and state (use USPS state
14
+ # abbreviations). The ZipCodeLookup API processes up to
15
+ # five lookups per request.
16
+ #
17
+ # @option option [(Alias)] :ZipCodeLookupRequest (Required)
18
+ # - API = ZipCodeLookupRequest
19
+ # @option option [(Group)] :Address (Optional)
20
+ # @option option [String] :FirmName (Optional)
21
+ # - Up to 5 address verifications can be included per transaction.
22
+ # @option option [String] :Address1 (Optional)
23
+ # - Delivery Address in the destination address. May contain secondary unit designator, such as APT or SUITE, for Accountable mail.)
24
+ # @option option [String] :Address2 (Required)
25
+ # - Delivery Address in the destination address. Required for all mail and packages, however 11-digit Destination Delivery Point ZIP+4 Code can be provided as an alternative in the Detail 1 Record.
26
+ # @option option [String] :City (Optional)
27
+ # - City name of the destination address. Field is required, unless a verified 11-digit DPV is provided for the mail piece.
28
+ # @option option [String] :State (Optional)
29
+ # - Two-character state code of the destination address.
30
+ # @option option [String] :Zip5 (Optional)
31
+ # - Destination 5-digit ZIP Code. Must be 5-digits. Numeric values (0-9) only. If International, all zeroes.
32
+ # @option option [String] :Zip4 (Optional)
33
+ # - Destination ZIP+4. Numeric values (0-9) only. If International, all zeroes. Default to spaces if not available.
34
+
35
+ #
36
+ # @see
37
+ def zip_code_lookup(options = {})
38
+ throw ArgumentError.new('Required arguments :zip_code_lookup_request missing') if options[:zip_code_lookup_request].nil?
39
+
40
+ request = build_request(:zip_code_lookup, options)
41
+ get('https://secure.shippingapis.com/ShippingAPI.dll', {
42
+ API: 'ZipCodeLookup',
43
+ XML: request,
44
+ })
45
+ end
46
+
47
+ private
48
+
49
+ def tag_unless_blank(xml, tag_name, data)
50
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
51
+ end
52
+
53
+ def build_zip_code_lookup_request(xml, options = {})
54
+ xml.tag!('Address') do
55
+ tag_unless_blank(xml, 'FirmName', options[:zip_code_lookup_request][:address][:firm_name])
56
+ tag_unless_blank(xml, 'Address1', options[:zip_code_lookup_request][:address][:address1])
57
+ xml.tag!('Address2', options[:zip_code_lookup_request][:address][:address2])
58
+ tag_unless_blank(xml, 'City', options[:zip_code_lookup_request][:address][:city])
59
+ tag_unless_blank(xml, 'State', options[:zip_code_lookup_request][:address][:state])
60
+ tag_unless_blank(xml, 'Zip5', options[:zip_code_lookup_request][:address][:zip5])
61
+ tag_unless_blank(xml, 'Zip4', options[:zip_code_lookup_request][:address][:zip4])
62
+ end if options[:zip_code_lookup_request][:address].present?
63
+ xml.target!
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Api
4
+ Error = Errors::UspsError
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Api
4
+ module Errors
5
+ class TooManyRequestsError < ::Faraday::Error
6
+ attr_reader :response
7
+
8
+ def initialize(response)
9
+ @response = response
10
+ end
11
+
12
+ def message
13
+ "Retry after #{retry_after} seconds"
14
+ end
15
+
16
+ def retry_after
17
+ response.headers['retry-after'].to_i
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Api
4
+ module Errors
5
+ class UspsError < ::Faraday::Error
6
+ attr_reader :response
7
+
8
+ def initialize(error, response = nil)
9
+ super error['Description']
10
+ @error_number = error['Number']
11
+ @error_description = error['Description']
12
+ @error_source = error['Source']
13
+ @response = response
14
+ end
15
+
16
+ def error
17
+ response.body.error
18
+ end
19
+
20
+ def errors
21
+ response.body.errors
22
+ end
23
+
24
+ def response_metadata
25
+ response.body.response_metadata
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ <% if option[:children].blank? %>
2
+ <% if option[:required] %>
3
+ <% indentation.times do %><%= "\t" %><% end %>xml.tag!('<%= option[:name] %>', options<% parents.each do |parent| %>[:<%= parent %>]<% end %>[:<%= option[:name].underscore %>])<% else %><% indentation.times do %><%= "\t" %><% end %>tag_unless_blank(xml, '<%= option[:name] %>', options<% parents.each do |parent| %>[:<%= parent %>]<% end %>[:<%= option[:name].underscore %>])<% end %><% else %><% indentation.times do %><%= "\t" %><% end %>xml.tag!('<%= option[:name] %>') do
4
+ <% option[:children].each do |child_option_name, child_option| %>
5
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_build_xml.erb')).result(option: child_option, parents: parents + [option[:name].underscore], indentation: indentation + 1) %>
6
+ <% end %>
7
+ <% (indentation+1).times do %><%= "\t" %><% end %>end <% if !option[:required] %>if options<% parents.each do |parent| %>[:<%= parent %>]<% end %>[:<%= option[:name].underscore %>].present?<% end %><% end %>
@@ -0,0 +1,6 @@
1
+ <%= ErubisHelper.tabs(static_indentation) %>#<%= ErubisHelper.spaces(indentation*2) %>@option option [<%= option[:type] %>] :<%= option[:name] %> (<%= option[:required] ? 'Required' : 'Optional' %>)
2
+ <% if option[:description].present? %>
3
+ <%= ErubisHelper.tabs(static_indentation) %>#<%= ErubisHelper.spaces(indentation*2) %>- <%= option[:description] %>
4
+ <% end %>
5
+ <% option[:children].each do |child_option_name, child_option| %>
6
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_options.erb')).result(option: child_option, parents: [], indentation: indentation + 1, static_indentation: static_indentation) %><% end %>
@@ -0,0 +1,3 @@
1
+ <%= ErubisHelper.tabs(indentation) %>throw ArgumentError.new('Required arguments <% parents.each do |parent| %>:<%= parent %>, <% end %>:<%= option[:name].underscore %> missing') if options<% parents.each do |parent| %>[:<%= parent %>]<% end %>[:<%= option[:name].underscore %>].nil?
2
+ <% option[:children].select{|n,o| o[:required]}.each do |child_option_name, child_option| %>
3
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_throw_argument_error.erb')).result(option: child_option, parents: parents + [option[:name].underscore], indentation: indentation) %><% end %>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # This file was auto-generated by lib/tasks/web.rake
3
+
4
+ <% files.each do |f| %>
5
+ require_relative 'endpoints/<%= f[:file] %>'
6
+ <% end %>
7
+
8
+ module Usps
9
+ module Api
10
+ module Endpoints
11
+ <% files.each do |f| %>
12
+ include <%= f[:module] %>
13
+ <% end %>
14
+
15
+ ACTIONS = {
16
+ <% files.each do |f| %>
17
+ <%= f[:file] %>: '<%= f[:module] %>',
18
+ <% end %>
19
+ }.freeze
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was auto-generated by lib/tasks/api.rake
4
+
5
+ module Usps
6
+ module Api
7
+ module Endpoints
8
+ module <%= data[:group].camelize %>
9
+ #
10
+ # <%= data[:title] %>
11
+ #
12
+ <% data[:description].to_s.split("\r").each do |line| %>
13
+ # <%= line.strip %>
14
+ <% end %>
15
+ #
16
+ <% data[:request_descriptions].each do |option_name, option| %>
17
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_options.erb')).result(option: option, parents: [], indentation: 0, static_indentation: 3) %><% end %>
18
+ #
19
+ # @see <%= data[:link] %>
20
+ def <%= data[:group].underscore %>(options = {})
21
+ <% data[:request_descriptions].select{|n,o| o[:required]}.each do |option_name, option| %>
22
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_throw_argument_error.erb')).result(option: option, parents: [], indentation: 3) %>
23
+ <% end %>
24
+ request = build_request(:<%= data[:group].underscore %>, options)
25
+ get('<%= data[:url] %>', {
26
+ API: '<%= data[:group] %>',
27
+ XML: request,
28
+ })
29
+ end
30
+
31
+ private
32
+
33
+ def tag_unless_blank(xml, tag_name, data)
34
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
35
+ end
36
+
37
+ def build_<%= data[:group].underscore %>_request(xml, options = {})
38
+ <% data[:request_descriptions].each do |option_name, option| %>
39
+ <% option[:children].each do |child_option_name, child_option| %>
40
+ <%= Erubis::Eruby.new(File.read('lib/usps/api/templates/_build_xml.erb')).result(option: child_option, parents: [option[:name].underscore], indentation: 4) %>
41
+ <% end %>
42
+ <% end %>
43
+ xml.target!
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was auto-generated by lib/tasks/api.rake
4
+
5
+ require 'spec_helper'
6
+
7
+ RSpec.describe Usps::Api::Endpoints::<%= data[:group].camelize %> do
8
+ let(:client) { Usps::Client.new }
9
+ <% [data[:group]].sort.each_with_index do |(name), index| %>
10
+ <% next if data['mixin'] %>
11
+ <% required_params = data[:request_descriptions].select{|n,o| o[:required]} %>
12
+ <% next if required_params.none? %>
13
+ context '<%= data[:group].underscore %>' do
14
+ <% required_params.each do |arg_name, arg_v| %>
15
+ it 'requires <%= arg_name.underscore %>' do
16
+ <% params_except_required = required_params.reject{ |name, _| name == arg_name }.map{|var, opts| "#{var}: '#{opts['example']}'"}.join(', ') %>
17
+ expect { client.<%= group.underscore %><%= params_except_required.empty? ? '' : "(#{params_except_required})" %> }.to raise_error ArgumentError, /Required arguments :<%= arg_name.underscore %> missing/
18
+ end
19
+ <% end %>
20
+ end
21
+ <% end %>
22
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Api
4
+ def tag_unless_blank(xml, tag_name, data)
5
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ class Client
4
+ include Faraday::Connection
5
+ include Faraday::Request
6
+ include Api::Endpoints
7
+
8
+ attr_accessor(*Config::ATTRIBUTES)
9
+
10
+ def initialize(options = {})
11
+ Usps::Config::ATTRIBUTES.each do |key|
12
+ send("#{key}=", options.fetch(key, Usps.config.send(key)))
13
+ end
14
+ @logger ||= Usps::Config.logger || Usps::Logger.default
15
+ @token ||= Usps.config.token
16
+ @user_id ||= Usps.config.user_id
17
+ end
18
+
19
+ def build_request(action, options)
20
+ xml = Builder::XmlMarkup.new(indent: 2)
21
+ # xml.instruct!(:xml, version: '1.0', encoding: 'utf-8')
22
+ xml.tag!("#{Usps::Api::Endpoints::ACTIONS[action]}Request", USERID: user_id) do
23
+ send("build_#{action}_request", xml, options)
24
+ end
25
+ end
26
+
27
+ class << self
28
+ def configure
29
+ block_given? ? yield(Config) : Config
30
+ end
31
+
32
+ def config
33
+ Config
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Config
4
+ extend self
5
+
6
+ ATTRIBUTES = %i[
7
+ proxy
8
+ user_agent
9
+ ca_path
10
+ ca_file
11
+ logger
12
+ endpoint
13
+ user_id
14
+ token
15
+ timeout
16
+ open_timeout
17
+ default_page_size
18
+ default_max_retries
19
+ ].freeze
20
+
21
+ attr_accessor(*Config::ATTRIBUTES)
22
+
23
+ def reset
24
+ self.endpoint = 'https://secure.shippingapis.com/'
25
+ self.user_agent = "USPS Ruby Client/#{Usps::VERSION}"
26
+ self.ca_path = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_DIR : nil
27
+ self.ca_file = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_FILE : nil
28
+ self.user_id = nil
29
+ self.token = nil
30
+ self.proxy = nil
31
+ self.logger = nil
32
+ self.timeout = nil
33
+ self.open_timeout = nil
34
+ self.default_page_size = 100
35
+ self.default_max_retries = 100
36
+ end
37
+ end
38
+
39
+ class << self
40
+ def configure
41
+ block_given? ? yield(Config) : Config
42
+ end
43
+
44
+ def config
45
+ Config
46
+ end
47
+ end
48
+ end
49
+
50
+ Usps::Config.reset
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ module Usps
3
+ module Faraday
4
+ module Connection
5
+ private
6
+
7
+ def connection
8
+ options = {
9
+ headers: { 'Accept' => 'application/json; charset=utf-8' },
10
+ }
11
+
12
+ options[:headers]['User-Agent'] = user_agent if user_agent
13
+ options[:proxy] = proxy if proxy
14
+ options[:ssl] = { ca_path: ca_path, ca_file: ca_file } if ca_path || ca_file
15
+
16
+ request_options = {}
17
+ request_options[:timeout] = timeout if timeout
18
+ request_options[:open_timeout] = open_timeout if open_timeout
19
+ options[:request] = request_options if request_options.any?
20
+
21
+ ::Faraday::Connection.new(endpoint, options) do |connection|
22
+ connection.use ::Faraday::Request::Multipart
23
+ connection.use ::Faraday::Request::UrlEncoded
24
+ connection.use ::Faraday::Response::RaiseError
25
+ connection.use ::Usps::Faraday::Response::RaiseError
26
+ # connection.use ::FaradayMiddleware::Mashify, mash_class: Slack::Messages::Message
27
+ connection.use ::FaradayMiddleware::ParseXml
28
+ connection.response :logger, logger if logger
29
+ connection.adapter ::Faraday.default_adapter
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end