endicia_ruby 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f2de8392ca2c214fdb8b8a14f2b05bc4521349c8
4
+ data.tar.gz: 5becd6d57d1652bb94d369618d51703305597d4c
5
+ SHA512:
6
+ metadata.gz: 0d2f26229ecb9af9e764dda8f9fe3d63b7cf99f7047f655d56b5ee4af8b65121d003d5945a40901c7c539bfbcd6312b453eedb094a63901bc4541bfdff1ce938
7
+ data.tar.gz: 1a4d7e2fc91b8afc9662f042fb07afdbd581692b836a13556e7eea45dbc4eb9b8c5410b5132212e07694ccd991e331f5ab8f88bcf84555731e7b0e1cacf60a4f
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sw?
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ endicia_ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in endicia_ruby.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dave Copeland
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # endicia_ruby
2
+
3
+ **Do not introduce Stitch Fix secret info to this repository** We would like this to be open-sourced, but are keeping it private until that can be done. So please avoid adding any Stitch Fix repo keys, API credentials, or anything like that here.
4
+
5
+ Simple wrapper around Endicia's label generation and refunding API. This currently supports only those features that Stitch Fix requires.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'endicia_ruby'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install endicia_ruby
20
+
21
+ ## Usage
22
+
23
+ TBD
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,10 @@
1
+ development: &development
2
+ credentials:
3
+ AccountID: <%= ENV['ENDICIA_ACCOUNT_ID'] || '2500334' %>
4
+ RequesterID: <%= ENV['ENDICIA_REQUESTER_ID'] || 'lxxx' %>
5
+ PassPhrase: <%= ENV['ENDICIA_PASSPHRASE'] || 'endicia.com' %>
6
+ environment: <%= ENV['ENDICIA_ENV'] || 'test' %>
7
+
8
+ test:
9
+ <<: *development
10
+
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'endicia_ruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "endicia_ruby"
8
+ spec.version = Endicia::VERSION
9
+ spec.authors = ["Jon Dean", "Dave Copeland"]
10
+ spec.email = ["jon@stitchfix.com","dave@stitchfix.com"]
11
+ spec.summary = %q{Wrapper around Endicia's APIs}
12
+ spec.description = %q{Wrapper around Endicia's APIs}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri"
22
+ spec.add_dependency "activesupport"
23
+ spec.add_dependency "httparty"
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "webmock"
28
+ spec.add_development_dependency "vcr"
29
+ spec.add_development_dependency "faker"
30
+ end
@@ -0,0 +1,53 @@
1
+ require 'nokogiri'
2
+
3
+ require_relative 'label_response'
4
+ require_relative 'request'
5
+
6
+ module Endicia
7
+ class Label < Request
8
+
9
+ # values is a Hash of the API values you wish to send to Endicia, nested as the documentation describes
10
+ # options is a Hash of other options (see default_options() for available options)
11
+ def request_label(values = {}, options = {})
12
+ # Build the options for this method with passed in values overriding defaults
13
+ root_node_attrs = root_attributes.merge!(values[:attrs] || {})
14
+ # Include API credentials in the values we use to build the XML
15
+ node_values = @options[:credentials].merge!(values[:nodes] || {})
16
+
17
+ builder = Nokogiri::XML::Builder.new do |xml|
18
+ xml.LabelRequest(root_node_attrs) do
19
+ recursive_build_xml_nodes!(xml, node_values)
20
+ end
21
+ end
22
+ xml_body = builder.to_xml
23
+
24
+ # Log the XML of the request if desired
25
+ log("ENDICIA LABEL REQUEST: #{format_xml_for_logging(xml_body)}") if options[:log_requests]
26
+
27
+ # Post the XML to the appropriate API URL
28
+ url = "#{api_url_base}/GetPostageLabelXML"
29
+
30
+ # Endicia's test server has an invalid certificate o_O
31
+ raw_response = self.class.post(url, body: "labelRequestXML=#{xml_body}", verify: environment != :test)
32
+
33
+ # Log the XML of the response if desired
34
+ log("ENDICIA LABEL RESPONSE: #{format_xml_for_logging(raw_response.body)}") if options[:log_responses]
35
+
36
+ # Build a nice response object and return it
37
+ Endicia::LabelResponse.new(raw_response).tap do |the_label|
38
+ the_label.request_body = xml_body.to_s
39
+ the_label.request_url = url
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def root_attributes
46
+ {
47
+ Test: api_test_mode_value,
48
+ LabelType: 'Default',
49
+ }
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+
3
+ module Endicia
4
+ class LabelResponse
5
+ attr_accessor :image,
6
+ :status,
7
+ :tracking_number,
8
+ :final_postage,
9
+ :transaction_date_time,
10
+ :transaction_id,
11
+ :postmark_date,
12
+ :postage_balance,
13
+ :pic,
14
+ :error_message,
15
+ :reference_id,
16
+ :reference_id2,
17
+ :reference_id3,
18
+ :reference_id4,
19
+ :requester_id,
20
+ :cost_center,
21
+ :request_body,
22
+ :request_url,
23
+ :response_body
24
+ def initialize(result)
25
+ self.response_body = filter_response_body(result.body.dup)
26
+ data = result["LabelRequestResponse"] || {}
27
+ data.each do |k, v|
28
+ if k == 'Base64LabelImage'
29
+ k = "image"
30
+ elsif k == 'Label'
31
+ # when customs form is included in xml, Endicia returns the image in a different format
32
+ k = "image"
33
+ v = v["Image"]["__content__"]
34
+ end
35
+ setter = :"#{k.tableize.singularize}="
36
+ send(setter, v) if !k['xmlns'] && respond_to?(setter)
37
+ end
38
+ end
39
+
40
+ private
41
+ def filter_response_body(string)
42
+ # Strip image data for readability:
43
+ string.sub(/<.*Image>.+<\/.*Image>/, "<Image>[data]</Image>")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ require 'uri'
2
+ require 'nokogiri'
3
+ require_relative 'request'
4
+ require_relative 'refund_response'
5
+
6
+ module Endicia
7
+ class Refund < Request
8
+
9
+ # Request a refund for the given tracking number(s)
10
+ #
11
+ # tracking_numbers can be an array of strings or a single string
12
+ #
13
+ # Returns an Endicia::RefundResponse
14
+ #
15
+ def request_refund(tracking_numbers, options = {})
16
+ # Build the options for this method with passed in values overriding defaults
17
+ options.reverse_merge!(default_options)
18
+
19
+ # If we didn't get an array of tracking numbers make it one for simplicity
20
+ tracking_numbers = tracking_numbers.is_a?(Array) ? tracking_numbers : [tracking_numbers]
21
+
22
+ # Build the XML document
23
+ builder = Nokogiri::XML::Builder.new do |xml|
24
+ xml.RefundRequest do
25
+ # Add the credentials
26
+ recursive_build_xml_nodes!(xml, @options[:credentials])
27
+ # Add the value for Test since this API is different and wants it as a node and not attribute, apparently
28
+ xml.Test(api_test_mode_value)
29
+ # Add all tracking numbers
30
+ xml.RefundList do |xml|
31
+ tracking_numbers.collect do |tracking_number|
32
+ xml.PICNumber(tracking_number)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ xml_body = builder.to_xml
38
+
39
+ # Log the XML of the request if desired
40
+ log("ENDICIA REFUND REQUEST: #{format_xml_for_logging(xml_body)}") if options[:log_requests]
41
+
42
+ params = { method: 'RefundRequest', XMLInput: URI.encode(xml_body) }
43
+ raw_response = self.class.get(els_service_url(params))
44
+
45
+ # Log the XML of the response if desired
46
+ log("ENDICIA REFUND RESPONSE: #{format_xml_for_logging(raw_response.body)}") if options[:log_responses]
47
+
48
+ Endicia::RefundResponse.new(raw_response)
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ require 'ostruct'
2
+ require 'nokogiri'
3
+
4
+ module Endicia
5
+ class RefundResponse
6
+
7
+ attr_accessor :success, # boolean: true if all tracking numbers are approved or false otherwise
8
+ # (Probably better to use tracking_numbers to check each individually)
9
+ :form_number, # Form Number for refunded label
10
+ :raw_response, # The XML response as a string
11
+ :parsed_response, # A Nokogiri document of the XML response
12
+ :tracking_numbers # An array with information about all requested tracking numbers
13
+ # Each entry is an OpenStruct with:
14
+ # pic_number: '123456789', # the tracking number you requested
15
+ # approved: true, # or false
16
+ # message: "the message" # message describing success or failure
17
+
18
+ def initialize(response)
19
+ @success = true
20
+ @tracking_numbers = []
21
+ @raw_response = response.body
22
+
23
+ @parsed_response = Nokogiri::XML(response.body)
24
+ refund_response = @parsed_response.xpath('/RefundResponse')
25
+ @form_number = refund_response.at('FormNumber').content
26
+
27
+ refund_list = refund_response.at('RefundList')
28
+
29
+ refund_list.xpath('//PICNumber').each do |refund_node|
30
+ approved = refund_node.at('IsApproved').content.try(:strip).try(:upcase) == "YES"
31
+ @tracking_numbers << OpenStruct.new(pic_number: refund_node.children.first.text.try(:strip),
32
+ approved: approved,
33
+ message: refund_node.at('ErrorMsg').content.try(:strip))
34
+ @success = @success && approved
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,168 @@
1
+ require 'httparty'
2
+ require 'active_support/core_ext/hash/keys'
3
+ require 'active_support/core_ext/hash/reverse_merge'
4
+ require 'active_support/core_ext/object/try'
5
+ require 'active_support/core_ext/object/blank'
6
+
7
+ # Hack fix because Endicia sends response back without protocol in xmlns uri
8
+ module HTTParty
9
+ class Request
10
+ alias_method :parse_response_without_hack, :parse_response
11
+ def parse_response(body)
12
+ parse_response_without_hack(
13
+ body.sub(/xmlns=("|')(.*envmgr.com|.*endicia.com)/i, 'xmlns=\1https://\2'))
14
+ end
15
+ end
16
+ end
17
+
18
+ module Endicia
19
+ class Request
20
+
21
+ include HTTParty
22
+
23
+ ENDICIA_API_HOSTS = {
24
+ test: 'https://www.envmgr.com',
25
+ sandbox: 'https://elstestserver.endicia.com',
26
+ production: 'https://LabelServer.Endicia.com',
27
+ }
28
+
29
+ # Pass a hash of options with:
30
+ # {
31
+ # credentials: {
32
+ # AccountID: '2500334',
33
+ # RequesterID: 'lxxx',
34
+ # PassPhrase: 'endicia.com',
35
+ # },
36
+ # environment: 'test' # or 'production' or 'sandbox'
37
+ # }
38
+ #
39
+ # Or if using Rails, create an endicia.yml in {Rails.root}/config/ in this format:
40
+ # development: &development
41
+ # credentials:
42
+ # AccountID: youraccountid
43
+ # RequesterID: lxxx
44
+ # PassPhrase: endicia.com
45
+ # environment: sandbox
46
+ #
47
+ # test:
48
+ # <<: *development
49
+ #
50
+ # staging:
51
+ # <<: *development
52
+ #
53
+ # production:
54
+ # credentials:
55
+ # AccountID: youraccountid
56
+ # RequesterID: lsfx
57
+ # PassPhrase: yourpassword
58
+ # environment: production
59
+ #
60
+ # If both an endicia.yml file is present and a hash of options is passed, any values in the hash take precedence
61
+ def initialize(options = {})
62
+ @options = load_options_from_config_file.deep_merge(options)
63
+ end
64
+
65
+ protected
66
+
67
+ # Separated so you can easily stub in tests with something like the following (in rspec):
68
+ # Endicia::Request.any_instance.stub(:environment).and_return(:sandbox)
69
+ # Endicia::Label.request_label(values)
70
+ # Note: since api_url_base is memoized you want to do this before you make the first API call on that object
71
+ def environment
72
+ @options[:environment].try(:to_sym)
73
+ end
74
+
75
+ # Most requests use this url
76
+ def api_host
77
+ if ENDICIA_API_HOSTS.key?(environment)
78
+ ENDICIA_API_HOSTS[environment]
79
+ else
80
+ raise "Invalid environment value: '#{environment}'"
81
+ end
82
+ end
83
+
84
+ def api_url_base
85
+ @api_url_base ||= "#{api_host}/LabelService/EwsLabelService.asmx"
86
+ end
87
+
88
+ # Some requests use the ELS service url. This URL is used for requests that
89
+ # can accept GET, and have params passed via URL instead of a POST body.
90
+ # Pass a hash of params to have them converted to a &key=value string and
91
+ # appended to the URL.
92
+ def els_service_url(params = {})
93
+ params = params.to_a.map { |i| "#{i[0]}=#{i[1]}"}.join('&')
94
+ "http://www.endicia.com/ELS/ELSServices.cfc?wsdl&#{params}"
95
+ end
96
+
97
+ def default_options
98
+ {
99
+ log_requests: false,
100
+ log_responses: false,
101
+ }
102
+ end
103
+
104
+ # Build nodes for the given xml builder recursively from a Hash
105
+ def recursive_build_xml_nodes!(xml, nodes)
106
+ nodes.each do |key, value|
107
+ node_name = key.to_s.sub(/^./,&:upcase) # convert "fooBar" to "FooBar"
108
+ case value
109
+ when Hash
110
+ xml.send(node_name) do
111
+ recursive_build_xml_nodes!(xml, value)
112
+ end
113
+ when Array
114
+ xml.send(node_name) do
115
+ value.each do |v|
116
+ recursive_build_xml_nodes!(xml, v)
117
+ end
118
+ end
119
+ else
120
+ xml.send(node_name, value)
121
+ end
122
+ end
123
+ end
124
+
125
+ # The Test attribute for the root node of the XML request
126
+ # Looks like:
127
+ # <LabelRequest Test="YES" ...>
128
+ # or:
129
+ # <LabelRequest Test="NO" ...>
130
+ def api_test_mode_value
131
+ @options[:environment] == 'production' ? 'NO' : 'YES'
132
+ end
133
+
134
+ # If using Rails, load default config options from {Rails.root}/config/endicia.yml
135
+ def load_options_from_config_file
136
+ configuration_values = {}
137
+ if defined?(Rails)
138
+ config_file = File.join(Rails.root, 'config', 'endicia.yml')
139
+ if File.exist?(config_file)
140
+ configuration_values = YAML.load(ERB.new(File.read(config_file)).result)[Rails.env]
141
+ recursive_symbolize_keys!(configuration_values)
142
+ end
143
+ end
144
+ configuration_values
145
+ end
146
+
147
+ # Used by load_options_from_config_file
148
+ def recursive_symbolize_keys! hash
149
+ hash.symbolize_keys!
150
+ hash.values.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)}
151
+ end
152
+
153
+ # Make a one line string with image data stripped for use in logging
154
+ def format_xml_for_logging(xml_string)
155
+ xml_string.to_s.gsub("\r\n", '').gsub("\n", '').gsub("\t", '').gsub("\s{2,}", ' ').sub(/<.*Image>.*<\/.*Image>/, '<Image>[data]</Image>')
156
+ end
157
+
158
+ # Helper for logging messages via Rails.logger if available or just puts if not using Rails
159
+ def log(message, level: :info)
160
+ if defined?(Rails)
161
+ Rails.logger.send(level, message)
162
+ else
163
+ puts message
164
+ end
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,3 @@
1
+ module Endicia
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "endicia_ruby/version"
2
+ require "endicia_ruby/label"
3
+ require "endicia_ruby/label_response"
4
+ require "endicia_ruby/refund"
5
+ require "endicia_ruby/refund_response"
6
+ require "endicia_ruby/request"
7
+
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Endicia::LabelResponse do
4
+ describe "initialize" do
5
+ let(:result) {
6
+ OpenStruct.new(
7
+ "LabelRequestResponse" => {
8
+ image_key => image_value,
9
+ "Status" => Faker::Lorem.sentence,
10
+ "TrackingNumber" => Faker::Lorem.sentence,
11
+ "FinalPostage" => Faker::Lorem.sentence,
12
+ "TransactionDateTime" => Faker::Lorem.sentence,
13
+ "TransactionId" => Faker::Lorem.sentence,
14
+ "PostmarkDate" => Faker::Lorem.sentence,
15
+ "PostageBalance" => Faker::Lorem.sentence,
16
+ "Pic" => Faker::Lorem.sentence,
17
+ "ErrorMessage" => Faker::Lorem.sentence,
18
+ "ReferenceId" => Faker::Lorem.sentence,
19
+ "ReferenceId2" => Faker::Lorem.sentence,
20
+ "ReferenceId3" => Faker::Lorem.sentence,
21
+ "ReferenceId4" => Faker::Lorem.sentence,
22
+ "RequesterId" => Faker::Lorem.sentence,
23
+ "CostCenter" => Faker::Lorem.sentence,
24
+ "RequestBody" => Faker::Lorem.sentence,
25
+ "RequestUrl" => Faker::Lorem.sentence,
26
+ "Ignored" => Faker::Lorem.sentence,
27
+ },
28
+ body: "<Image>ksadjhfalskjfhalsjfhaslfjhasdljkf</Image>"
29
+ )
30
+ }
31
+ let(:expected_image_value) { Faker::Lorem.sentence }
32
+ let(:image_key) { "Base64LabelImage" }
33
+ let(:image_value) { expected_image_value }
34
+
35
+ subject(:response) { described_class.new(result) }
36
+
37
+ it "turns the awful Enterprise keys into snake-case and sets 'em up appropriate" do
38
+ expect(response.status).to eq(result["LabelRequestResponse"]["Status"])
39
+ expect(response.tracking_number).to eq(result["LabelRequestResponse"]["TrackingNumber"])
40
+ expect(response.final_postage).to eq(result["LabelRequestResponse"]["FinalPostage"])
41
+ expect(response.transaction_date_time).to eq(result["LabelRequestResponse"]["TransactionDateTime"])
42
+ expect(response.transaction_id).to eq(result["LabelRequestResponse"]["TransactionId"])
43
+ expect(response.postmark_date).to eq(result["LabelRequestResponse"]["PostmarkDate"])
44
+ expect(response.postage_balance).to eq(result["LabelRequestResponse"]["PostageBalance"])
45
+ expect(response.pic).to eq(result["LabelRequestResponse"]["Pic"])
46
+ expect(response.error_message).to eq(result["LabelRequestResponse"]["ErrorMessage"])
47
+ expect(response.reference_id).to eq(result["LabelRequestResponse"]["ReferenceId"])
48
+ expect(response.reference_id2).to eq(result["LabelRequestResponse"]["ReferenceId2"])
49
+ expect(response.reference_id3).to eq(result["LabelRequestResponse"]["ReferenceId3"])
50
+ expect(response.reference_id4).to eq(result["LabelRequestResponse"]["ReferenceId4"])
51
+ expect(response.requester_id).to eq(result["LabelRequestResponse"]["RequesterId"])
52
+ expect(response.cost_center).to eq(result["LabelRequestResponse"]["CostCenter"])
53
+ expect(response.request_body).to eq(result["LabelRequestResponse"]["RequestBody"])
54
+ expect(response.request_url).to eq(result["LabelRequestResponse"]["RequestUrl"])
55
+ end
56
+
57
+ it "masks data in the body" do
58
+ expect(response.response_body).to eq("<Image>[data]</Image>")
59
+ end
60
+
61
+ context "for typical case" do
62
+ let(:image_key) { "Base64LabelImage" }
63
+ let(:image_value) { expected_image_value }
64
+
65
+ it "translates Base64LabelImage to 'image'" do
66
+ expect(response.image).to eq(expected_image_value)
67
+ end
68
+ end
69
+
70
+ context "for customs form case" do
71
+ let(:image_key) { "Label" }
72
+ let(:image_value) {
73
+ {
74
+ "Image" => {
75
+ "__content__" => expected_image_value
76
+ }
77
+ }
78
+ }
79
+ it "translates Base64LabelImage to 'image'" do
80
+ expect(response.image).to eq(expected_image_value)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Endicia::Label do
4
+ subject(:label) { described_class.new }
5
+
6
+ describe "#request_label", vcr: { record: :new_episodes } do
7
+ let(:values) {
8
+ {
9
+ ToName: Faker::Name.name,
10
+ ToCompany: Faker::Company.name,
11
+ ToPostalCode: "90210",
12
+ ToAddress1: Faker::Address.street_address,
13
+ ToAddress2: Faker::Address.secondary_address,
14
+ ToCity: Faker::Address.city,
15
+ ToState: "CA",
16
+ PartnerTransactionID: 12345,
17
+ PartnerCustomerID: 45678,
18
+ MailClass: "Priority",
19
+ MailpieceShape: "Parcel",
20
+ ToZIP4: '1234',
21
+ WeightOz: "32", # 2 pounds
22
+ MailpieceDimensions: {
23
+ Length: "12",
24
+ Width: "16",
25
+ Height: "18",
26
+ },
27
+ FromCompany: Faker::Company.name,
28
+ ReturnAddress1: Faker::Address.street_address,
29
+ ReturnAddress2: Faker::Address.secondary_address,
30
+ FromCity: Faker::Address.city,
31
+ FromState: "DC",
32
+ FromPostalCode: "20005",
33
+ FromZIP4: "1234",
34
+ FromPhone: "2025551212",
35
+ }
36
+ }
37
+
38
+ it "makes a request and properly parses the result" do
39
+ response = label.request_label({ nodes: values, attrs: {} }, { log_requests: true, log_responses: true })
40
+ expect(response.status).to eq("0")
41
+ expect(response.tracking_number).to_not be_blank
42
+ expect(response.final_postage).to match(/^\d+\.\d+/)
43
+ expect(response.transaction_date_time).to match(/^\d{14}$/)
44
+ expect(response.transaction_id).to_not be_blank
45
+ expect(response.postmark_date).to match(/^\d{8}$/)
46
+ expect(response.postage_balance).to match(/^\d+\.\d+/)
47
+ expect(response.pic).to_not be_blank
48
+ expect(response.error_message).to be_blank
49
+ expect(response.reference_id).to be_blank
50
+ expect(response.reference_id2).to be_blank
51
+ expect(response.reference_id3).to be_blank
52
+ expect(response.reference_id4).to be_blank
53
+ expect(response.requester_id).to_not be_blank
54
+ expect(response.cost_center).to_not be_blank
55
+ expect(response.request_url).to_not be_blank
56
+ expect(response.request_body).to_not be_blank
57
+ end
58
+
59
+ it "handles a nonsense request" do
60
+ response = label.request_label({ nodes: {}, attrs: {} }, { log_requests: true, log_responses: true })
61
+ expect(response.status).to_not eq("0")
62
+ end
63
+ end
64
+ end