endicia_ruby 0.0.1

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