endicia_label_server-ruby 0.2.2

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.hound.yml +2 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +1065 -0
  6. data/.travis.yml +10 -0
  7. data/Gemfile +11 -0
  8. data/Gemfile.lock +62 -0
  9. data/LICENSE.txt +14 -0
  10. data/README.md +66 -0
  11. data/Rakefile +19 -0
  12. data/endicia.wsdl +4272 -0
  13. data/endicia_label_server.gemspec +23 -0
  14. data/lib/endicia_label_server-ruby.rb +2 -0
  15. data/lib/endicia_label_server.rb +33 -0
  16. data/lib/endicia_label_server/builders/builder_base.rb +91 -0
  17. data/lib/endicia_label_server/builders/change_pass_phrase_builder.rb +18 -0
  18. data/lib/endicia_label_server/builders/postage_label_builder.rb +24 -0
  19. data/lib/endicia_label_server/builders/postage_rate_builder.rb +23 -0
  20. data/lib/endicia_label_server/builders/postage_rates_builder.rb +23 -0
  21. data/lib/endicia_label_server/builders/user_sign_up_builder.rb +24 -0
  22. data/lib/endicia_label_server/connection.rb +96 -0
  23. data/lib/endicia_label_server/exceptions.rb +5 -0
  24. data/lib/endicia_label_server/parsers/change_pass_phrase_parser.rb +20 -0
  25. data/lib/endicia_label_server/parsers/parser_base.rb +42 -0
  26. data/lib/endicia_label_server/parsers/postage_label_parser.rb +52 -0
  27. data/lib/endicia_label_server/parsers/postage_rate_parser.rb +43 -0
  28. data/lib/endicia_label_server/parsers/postage_rates_parser.rb +16 -0
  29. data/lib/endicia_label_server/parsers/user_sign_up_parser.rb +24 -0
  30. data/lib/endicia_label_server/service/mailpiece_shape.rb +92 -0
  31. data/lib/endicia_label_server/services.rb +37 -0
  32. data/lib/endicia_label_server/util.rb +29 -0
  33. data/lib/endicia_label_server/version.rb +10 -0
  34. data/spec/endicia_label_server/builders/builder_base_spec.rb +47 -0
  35. data/spec/endicia_label_server/connection/change_pass_phrase_spec.rb +45 -0
  36. data/spec/endicia_label_server/connection/multiple_rate_spec.rb +96 -0
  37. data/spec/endicia_label_server/connection/postage_label_spec.rb +67 -0
  38. data/spec/endicia_label_server/connection/single_rate_spec.rb +57 -0
  39. data/spec/endicia_label_server/connection/user_sign_up_spec.rb +93 -0
  40. data/spec/endicia_label_server/connection_spec.rb +21 -0
  41. data/spec/endicia_label_server/service/mailpiece_shape_spec.rb +159 -0
  42. data/spec/spec_helper.rb +18 -0
  43. data/spec/stubs/change_pass_phrase_success.xml +8 -0
  44. data/spec/stubs/postage_label_success.xml +17 -0
  45. data/spec/stubs/postage_rate_success.xml +147 -0
  46. data/spec/stubs/postage_rates_success.xml +184 -0
  47. data/spec/stubs/user_sign_up_success.xml +7 -0
  48. metadata +150 -0
@@ -0,0 +1,52 @@
1
+ require 'base64'
2
+
3
+ module EndiciaLabelServer
4
+ module Parsers
5
+ class PostageLabelParser < ParserBase
6
+ attr_accessor :pic,
7
+ :tracking_number,
8
+ :final_postage,
9
+ :transaction_id,
10
+ :transaction_date_time,
11
+ :postmark_date,
12
+ :postage_balance,
13
+ :cost_center,
14
+ :requester_id,
15
+ :label
16
+
17
+ def start_element(name)
18
+ super
19
+ @current_element = name.to_s
20
+ end
21
+
22
+ def value(value)
23
+ super
24
+
25
+ element = underscore(@current_element)
26
+ string_value = value.as_s
27
+ if switch_active? :Base64LabelImage
28
+ parse_label(string_value)
29
+ else
30
+ function_name = "#{element}="
31
+ send(function_name, string_value) if respond_to?(function_name)
32
+ end
33
+ end
34
+
35
+ def underscore(value)
36
+ value.gsub(/::/, '/')
37
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
38
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
39
+ .tr('-', '_')
40
+ .downcase
41
+ end
42
+
43
+ def parse_label(encoded_label)
44
+ label_file = Tempfile.new(['endicia', '.png'])
45
+ label_file.binmode
46
+ label_file.write(Base64.decode64(encoded_label))
47
+ label_file.rewind
48
+ self.label = label_file
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,43 @@
1
+ module EndiciaLabelServer
2
+ module Parsers
3
+ class PostageRateParser < ParserBase
4
+ attr_accessor :rated_shipments
5
+
6
+ def initialize
7
+ super
8
+ self.rated_shipments = []
9
+ @current_rate = {}
10
+ end
11
+
12
+ def start_element(name)
13
+ super
14
+ end
15
+
16
+ def end_element(name)
17
+ super
18
+ return unless name == :Postage
19
+ rated_shipments << @current_rate
20
+ @current_rate = {}
21
+ end
22
+
23
+ def value(value)
24
+ super
25
+ if switch_active?(:Postage, :MailService)
26
+ parse_mail_service value
27
+ elsif switch_active?(:Postage, :Rate)
28
+ parse_total_charges value
29
+ end
30
+ end
31
+
32
+ def parse_mail_service(value)
33
+ service_code = EndiciaLabelServer::SERVICES.invert[value.as_s]
34
+ @current_rate[:service_code] = service_code
35
+ @current_rate[:service_name] = value.as_s
36
+ end
37
+
38
+ def parse_total_charges(value)
39
+ @current_rate[:total] = value.as_s
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module EndiciaLabelServer
2
+ module Parsers
3
+ class PostageRatesParser < PostageRateParser
4
+ def attr_value(name, value)
5
+ @current_rate[:total] = value.as_s if name == :TotalAmount
6
+ end
7
+
8
+ def value(value)
9
+ super
10
+ if switch_active?(:MailClass)
11
+ @current_rate[:mail_class] = value.as_s
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module EndiciaLabelServer
2
+ module Parsers
3
+ class UserSignUpParser < ParserBase
4
+ attr_accessor :confirmation_number, :account_id, :token
5
+
6
+ def value(value)
7
+ super
8
+
9
+ string_value = value.as_s
10
+ if switch_active? :ConfirmationNumber
11
+ self.confirmation_number = string_value
12
+ elsif switch_active? :AccountID
13
+ self.account_id = string_value
14
+ elsif switch_active? :Token
15
+ self.token = string_value
16
+ end
17
+ end
18
+
19
+ def success?
20
+ !['0', 0].include? confirmation_number
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,92 @@
1
+ module EndiciaLabelServer
2
+ module Service
3
+
4
+ class MailpieceShape
5
+ attr_accessor :service_name
6
+
7
+ MAILPIECESHAPES.each do |mailpiece|
8
+ define_method "#{Util.underscore(mailpiece).gsub(' ', '_')}_mailpiece" do
9
+ mailpiece
10
+ end
11
+ end
12
+
13
+ def initialize(service_name)
14
+ @service_name = service_name
15
+ end
16
+
17
+ def all
18
+ send("#{Util.underscore(service_name).gsub(' ', '_').to_sym}_mailpiece_shapes")
19
+ end
20
+
21
+ private
22
+
23
+ def express_mail_mailpiece_shapes
24
+ [flat_mailpiece, parcel_mailpiece, large_parcel_mailpiece, flat_rate_legal_envelope_mailpiece, flat_rate_padded_envelope_mailpiece, flat_rate_gift_card_envelope_mailpiece, small_flat_rate_envelope_mailpiece]
25
+ end
26
+
27
+ def express_mail_international_mailpiece_shapes
28
+ [flat_mailpiece, flat_rate_padded_envelope_mailpiece, parcel_mailpiece, flat_rate_envelope_mailpiece]
29
+ end
30
+
31
+ def priority_mail_express_mailpiece_shapes
32
+ [flat_mailpiece, parcel_mailpiece, large_parcel_mailpiece, flat_rate_envelope_mailpiece]
33
+ end
34
+
35
+ def priority_mail_express_international_mailpiece_shapes
36
+ [flat_mailpiece, flat_rate_padded_envelope_mailpiece, parcel_mailpiece, flat_rate_envelope_mailpiece]
37
+ end
38
+
39
+ def first_class_mail_mailpiece_shapes
40
+ [letter_mailpiece, flat_mailpiece, parcel_mailpiece]
41
+ end
42
+
43
+ def library_mail_mailpiece_shapes
44
+ [parcel_mailpiece]
45
+ end
46
+
47
+ def media_mail_mailpiece_shapes
48
+ [parcel_mailpiece]
49
+ end
50
+
51
+ def standard_post_mailpiece_shapes
52
+ [parcel_mailpiece, large_parcel_mailpiece]
53
+ end
54
+
55
+ def parcel_select_mailpiece_shapes
56
+ [parcel_mailpiece, large_parcel_mailpiece]
57
+ end
58
+
59
+ def parcel_select_barcoded_nonpresorted_mailpiece_shapes
60
+ [parcel_mailpiece, large_parcel_mailpiece]
61
+ end
62
+
63
+ def priority_mail_mailpiece_shapes
64
+ [flat_mailpiece, parcel_mailpiece, large_parcel_mailpiece, flat_rate_legal_envelope_mailpiece, flat_rate_padded_envelope_mailpiece, flat_rate_gift_card_envelope_mailpiece, small_flat_rate_envelope_mailpiece]
65
+ end
66
+
67
+ def critical_mail_mailpiece_shapes
68
+ []
69
+ end
70
+
71
+ def first_class_mail_international_mailpiece_shapes
72
+ [letter_mailpiece, flat_mailpiece, parcel_mailpiece]
73
+ end
74
+
75
+ def first_class_package_international_service_mailpiece_shapes
76
+ [letter_mailpiece, flat_mailpiece, parcel_mailpiece]
77
+ end
78
+
79
+ def priority_mail_international_mailpiece_shapes
80
+ [flat_mailpiece, parcel_mailpiece, flat_rate_envelope_mailpiece, flat_rate_legal_envelope_mailpiece, flat_rate_padded_envelope_mailpiece, small_flat_rate_box_mailpiece, medium_flat_rate_box_mailpiece, large_flat_rate_box_mailpiece]
81
+ end
82
+
83
+ def priority_mail_express_flat_rate_envelope_mailpiece_shapes
84
+ [flat_rate_envelope_mailpiece]
85
+ end
86
+
87
+ def priority_mail_flat_rate_envelope_mailpiece_shapes
88
+ [flat_rate_envelope_mailpiece]
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ module EndiciaLabelServer
2
+ SERVICES = {
3
+ 'Express' => 'Express Mail',
4
+ 'PriorityExpress' => 'Priority Mail Express',
5
+ 'First' => 'First-Class Mail',
6
+ 'LibraryMail' => 'Library Mail',
7
+ 'MediaMail' => 'Media Mail',
8
+ 'StandardPost' => 'Standard Post',
9
+ 'ParcelSelect' => 'Parcel Select Barcoded Nonpresorted',
10
+ 'Priority' => 'Priority Mail',
11
+ 'CriticalMail' => 'Critical Mail',
12
+ 'ExpressMailInternational' => 'Express Mail International',
13
+ 'PriorityMailExpressInternational' => 'Priority Mail Express International',
14
+ 'FirstClassMailInternational' => 'First Class Mail International',
15
+ 'FirstClassPackageInternationalService' => 'First Class Package International Service',
16
+ 'PriorityMailInternational' => 'Priority Mail International',
17
+ 'PriorityMailFlatRateEnvelope' => 'Priority Mail Flat Rate Envelope',
18
+ 'PriorityMailExpressFlatRateEnvelope' => 'Priority Mail Express Flat Rate Envelope'
19
+ }
20
+
21
+ MAILPIECESHAPES = [
22
+ 'Flat',
23
+ 'Letter',
24
+ 'FlatRateGiftCardEnvelope',
25
+ 'FlatRatePaddedEnvelope',
26
+ 'LargeParcel',
27
+ 'Parcel',
28
+ 'SmallFlatRateEnvelope',
29
+ 'FlatRateLegalEnvelope',
30
+ 'SmallFlatRateEnvelope',
31
+ 'Parcel',
32
+ 'FlatRateEnvelope',
33
+ 'SmallFlatRateBox',
34
+ 'MediumFlatRateBox',
35
+ 'LargeFlatRateBox'
36
+ ]
37
+ end
@@ -0,0 +1,29 @@
1
+ module EndiciaLabelServer
2
+ module Util
3
+ class << self
4
+ def camelize(value)
5
+ value.to_s.split('_').map { |v| capitalize_with_id_exception(v) }.join
6
+ end
7
+
8
+ def capitalize_with_id_exception(value)
9
+ (value == 'id') ? 'ID' : value.capitalize
10
+ end
11
+
12
+ def singularize(word)
13
+ if word =~ /([a-zA-Z]+?)(s\b|\b)/i
14
+ return $1
15
+ else
16
+ return word
17
+ end
18
+ end
19
+
20
+ def underscore(word)
21
+ word.gsub(/::/, '/').
22
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
23
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
24
+ tr("-", "_").
25
+ downcase
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ module EndiciaLabelServer
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ PATCH = 2
6
+ BUILD = nil
7
+
8
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe EndiciaLabelServer::Builders::BuilderBase do
4
+ describe '.add_array' do
5
+ let(:builder) { EndiciaLabelServer::Builders::BuilderBase.new 'MyBuilder' }
6
+
7
+ subject { builder.to_xml.gsub(/\s+/, "") }
8
+
9
+ before do
10
+ builder.add_array :test, ['1', '2']
11
+ end
12
+
13
+ it "should return valid XML" do
14
+ should eql '<MyBuilder><Test><TestID>1</TestID><TestID>2</TestID></Test></MyBuilder>'
15
+ end
16
+ end
17
+
18
+ describe '.add_hash' do
19
+ context "When passed symbolized keys" do
20
+ let(:builder) { EndiciaLabelServer::Builders::BuilderBase.new 'MyBuilder' }
21
+
22
+ subject { builder.to_xml.gsub(/\s+/, "") }
23
+
24
+ before do
25
+ builder.add_hash :test, { id: '1', source: 'test' }
26
+ end
27
+
28
+ it "should return valid XML" do
29
+ should eql '<MyBuilder><Test><ID>1</ID><Source>test</Source></Test></MyBuilder>'
30
+ end
31
+ end
32
+
33
+ context "When passed string keys" do
34
+ let(:builder) { EndiciaLabelServer::Builders::BuilderBase.new 'MyBuilder' }
35
+
36
+ subject { builder.to_xml.gsub(/\s+/, "") }
37
+
38
+ before do
39
+ builder.add_hash 'TestTest', { 'ID' => '1', 'SourceSource' => 'test' }
40
+ end
41
+
42
+ it "should return valid XML" do
43
+ should eql '<MyBuilder><TestTest><ID>1</ID><SourceSource>test</SourceSource></TestTest></MyBuilder>'
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+ require 'faker'
4
+
5
+ describe EndiciaLabelServer::Connection, '.change_pass_phrase' do
6
+ before do
7
+ Excon.defaults[:mock] = true
8
+ end
9
+
10
+ after do
11
+ Excon.stubs.clear
12
+ end
13
+
14
+ let(:stub_path) { File.expand_path("../../../stubs", __FILE__) }
15
+ let(:server) { EndiciaLabelServer::Connection.new(test_mode: true) }
16
+
17
+ context "when changing a users pass phrase" do
18
+ before do
19
+ Excon.stub({:method => :post}) do |params|
20
+ case params[:path]
21
+ when "#{EndiciaLabelServer::Connection::ROOT_PATH}#{EndiciaLabelServer::Connection::CHANGE_PASS_PHRASE_ENDPOINT}"
22
+ {body: File.read("#{stub_path}/change_pass_phrase_success.xml"), status: 200}
23
+ end
24
+ end
25
+ end
26
+
27
+ subject do
28
+ server.change_pass_phrase do |connection|
29
+ connection.add :requester_id, ENV['ENDICIA_REQUESTER_ID']
30
+ connection.add :request_id, 'ABC'
31
+ connection.add :certified_intermediary, {
32
+ account_id: '1234567',
33
+ pass_phrase: 'SUPER SECRET AND SECURE PASSWORD',
34
+ token: 'AGAIN ANOTHER AWESOME SECRET TOKEN'
35
+ },
36
+ new_pass_phrase: 'iAmAPassword1_c'
37
+ end
38
+ end
39
+
40
+ it "should return a single rate" do
41
+ expect { subject }.not_to raise_error
42
+ expect(subject.success?).to be_truthy
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe EndiciaLabelServer::Connection, '.rates' do
4
+ before do
5
+ Excon.defaults[:mock] = true
6
+ end
7
+
8
+ after do
9
+ Excon.stubs.clear
10
+ end
11
+
12
+ let(:expected_rates) {
13
+ [
14
+ {:mail_class => 'First', :total=>"1.93", :service_code=>"First", :service_name=>"First-Class Mail"},
15
+ {:mail_class => 'Priority', :total=>"6.51", :service_code=>"Priority", :service_name=>"Priority Mail"},
16
+ {:mail_class => 'PriorityExpress', :total=>"27.68", :service_code=>"PriorityExpress", :service_name=>"Priority Mail Express"},
17
+ {:mail_class => 'LibraryMail', :total=>"2.56", :service_code=>"LibraryMail", :service_name=>"Library Mail"},
18
+ {:mail_class => 'MediaMail', :total=>"2.69", :service_code=>"MediaMail", :service_name=>"Media Mail"},
19
+ {:mail_class => 'ParcelSelect', :total=>"6.68", :service_code=>"ParcelSelect", :service_name=>"Parcel Select Barcoded Nonpresorted"}
20
+ ]
21
+ }
22
+
23
+ def apply_config_to_rate_builder(rate_builder)
24
+ rate_builder.add :certified_intermediary, {
25
+ account_id: ENV['ENDICIA_ACCOUNT_ID'],
26
+ pass_phrase: ENV['ENDICIA_PASS_PHRASE'],
27
+ token: ENV['ENDICIA_TOKEN']
28
+ }
29
+ rate_builder.add :requester_id, ENV['ENDICIA_REQUESTER_ID']
30
+ rate_builder.add :mail_class, "Domestic"
31
+ rate_builder.add :mailpiece_dimensions, {
32
+ length: '10',
33
+ width: '10',
34
+ height: '10'
35
+ }
36
+ rate_builder.add :weight_oz, "2"
37
+ rate_builder.add :from_postal_code, '90210'
38
+ rate_builder.add :to_postal_code, '02215'
39
+ rate_builder.add :to_country_code, 'US'
40
+ end
41
+
42
+ let(:stub_path) { File.expand_path("../../../stubs", __FILE__) }
43
+ let(:server) { EndiciaLabelServer::Connection.new(test_mode: true) }
44
+
45
+ context "if requesting multiple rates with a block" do
46
+ before do
47
+ Excon.stub({:method => :post}) do |params|
48
+ case params[:path]
49
+ when "#{EndiciaLabelServer::Connection::ROOT_PATH}#{EndiciaLabelServer::Connection::REQUEST_RATES_ENDPOINT}"
50
+ {body: File.read("#{stub_path}/postage_rates_success.xml"), status: 200}
51
+ end
52
+ end
53
+ end
54
+
55
+ subject do
56
+ server.rates do |rate_builder|
57
+ apply_config_to_rate_builder(rate_builder)
58
+ end
59
+ end
60
+
61
+ it "should return a single rate" do
62
+ expect { subject }.not_to raise_error
63
+ expect(subject.rated_shipments).not_to be_empty
64
+ expect(subject.rated_shipments.count).to eql 6
65
+ expect(subject.rated_shipments).to eql expected_rates
66
+ end
67
+ end
68
+
69
+ context "if requesting multiple rates with a parameter" do
70
+ before do
71
+ Excon.stub({:method => :post}) do |params|
72
+ case params[:path]
73
+ when "#{EndiciaLabelServer::Connection::ROOT_PATH}#{EndiciaLabelServer::Connection::REQUEST_RATES_ENDPOINT}"
74
+ {body: File.read("#{stub_path}/postage_rates_success.xml"), status: 200}
75
+ end
76
+ end
77
+ end
78
+
79
+ let(:rate_builder) {
80
+ EndiciaLabelServer::Builders::PostageRatesBuilder.new.tap do |rate_builder|
81
+ apply_config_to_rate_builder(rate_builder)
82
+ end
83
+ }
84
+
85
+ subject do
86
+ server.rates(rate_builder)
87
+ end
88
+
89
+ it "should return a single rate" do
90
+ expect { subject }.not_to raise_error
91
+ expect(subject.rated_shipments).not_to be_empty
92
+ expect(subject.rated_shipments.count).to eql 6
93
+ expect(subject.rated_shipments).to eql expected_rates
94
+ end
95
+ end
96
+ end