endicia_label_server-ruby 0.2.2

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