usps-ruby-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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