fedex-web-services 1.0.42

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.
data/lib/fedex.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "fedex/version"
2
+ require "fedex/web_services"
3
+
4
+ module Fedex
5
+ require 'fedex/railtie' if defined?(Rails)
6
+ end
@@ -0,0 +1,9 @@
1
+ module Fedex
2
+ class Railtie < Rails::Railtie
3
+ railtie_name :fedex
4
+
5
+ rake_tasks do
6
+ load "fedex/tasks/fedex.rake"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ namespace :fedex do
2
+ desc "Generate the ruby definition files from the FedEx wsdl files"
3
+ task :generate_definitions do
4
+ wsdl_dir = ENV['WSDL_DIR']
5
+ if (!Dir.exist?(wsdl_dir || ""))
6
+ puts "Set the WSDL_DIR environment variable to location of the Fedex wsdl files"
7
+ else
8
+ Fedex::WebServices::Definitions.generate_definitions(
9
+ File.join(Rails.root, 'lib'),
10
+ *Dir.glob(File.join(ENV['WSDL_DIR'], '*.wsdl'))
11
+ )
12
+
13
+ File.open(File.join(Rails.root, 'config', 'initializers', 'fedex.rb'), "w") do |file|
14
+ file.puts "Fedex::WebServices::Definitions.load_definitions('lib')"
15
+ end
16
+
17
+ puts "Added lib/fedex/web_services/definitions/"
18
+ puts "Added config/initializers/fedex.rb"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Fedex
2
+ VERSION = "1.0.42"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'fedex/web_services/definitions'
2
+
3
+ require 'fedex/web_services/request/base'
4
+ require 'fedex/web_services/request/process_shipment'
5
+ require 'fedex/web_services/request/delete_shipment'
6
+ require 'fedex/web_services/request/get_rates'
7
+
8
+ require 'fedex/web_services/service/base'
9
+ require 'fedex/web_services/service/ship'
10
+ require 'fedex/web_services/service/rate'
@@ -0,0 +1,46 @@
1
+ module Fedex
2
+ module WebServices
3
+ module Definitions
4
+ extend(self)
5
+
6
+ MODULE_PATH = File.join(%w(fedex web_services definitions))
7
+
8
+ def load_definitions(lib_dir)
9
+ $VERBOSE = $VERBOSE.tap do
10
+ $VERBOSE = nil
11
+
12
+ $:.unshift(lib_dir)
13
+ $:.unshift(File.join(lib_dir, MODULE_PATH))
14
+ Dir.glob(File.join(lib_dir, MODULE_PATH, "*DefinitionsDriver.rb")).each do |definition_file|
15
+ begin
16
+ require File.join(MODULE_PATH, File.basename(definition_file))
17
+ rescue Exception
18
+ raise "Error loading Fedex definition file #{definition_file}: #{$!.message}"
19
+ end
20
+ end
21
+ $:.shift
22
+ $:.shift
23
+ end
24
+ end
25
+
26
+ def generate_definitions(lib_dir, *wsdl_files)
27
+ require 'wsdl/soap/wsdl2ruby'
28
+ require 'fileutils'
29
+
30
+ wsdl_files.each do |wsdl_file|
31
+ worker = WSDL::SOAP::WSDL2Ruby.new
32
+ worker.basedir = FileUtils.mkdir_p(File.join(Dir.new(lib_dir), MODULE_PATH)).first
33
+ worker.location = File.new(wsdl_file).path
34
+ worker.logger.level = Logger::WARN
35
+ worker.opt.update(
36
+ "module_path" => "Fedex::WebServices::Definitions",
37
+ "mapping_registry" => nil,
38
+ "driver" => nil,
39
+ "classdef" => nil
40
+ )
41
+ worker.run
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,69 @@
1
+ require 'time'
2
+
3
+ module Fedex::WebServices
4
+ module Request
5
+ class Base
6
+ include Fedex::WebServices::Definitions
7
+
8
+ def initialize(service)
9
+ @service = service
10
+ end
11
+
12
+ def remote_method
13
+ raise "remote_method must be implemented by subclasses of Service"
14
+ end
15
+
16
+ private
17
+ def web_authentication_detail
18
+ WebAuthenticationDetail.new.tap do |o|
19
+ o.userCredential = WebAuthenticationCredential.new.tap do |o|
20
+ o.key = @service.credentials.key
21
+ o.password = @service.credentials.password
22
+ end
23
+ end
24
+ end
25
+
26
+ def client_detail
27
+ ClientDetail.new.tap do |o|
28
+ o.accountNumber = @service.credentials.account_number
29
+ o.meterNumber = @service.credentials.meter_number
30
+ end
31
+ end
32
+
33
+ def version
34
+ VersionId.new.tap do |o|
35
+ o.serviceId = @service.service_id
36
+ o.major = 10
37
+ o.intermediate = 0
38
+ o.minor = 0
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ # return
45
+ #
46
+ #
47
+ # ship_service = Fedex::WebServices::Service::Ship.new(creds)
48
+ # a = nil
49
+ # ship_service.delete_shipment('1234', 'GROUND') { |contents| a = contents }
50
+ #
51
+ # # req = FedExRequest.new(credentials).delete_shipment_request('794797411470', TrackingIdType::GROUND)
52
+ # req = FedExRequest.new(credentials).process_shipment
53
+ # ship_port = ShipPortType.new('https://wsbeta.fedex.com:443/web-services')
54
+ # ship_port.wiredump_dev = STDOUT
55
+ #
56
+ # begin
57
+ # # response = ship_port.deleteShipment(req)
58
+ # response = ship_port.processShipment(req)
59
+ # check_response(response)
60
+ # rescue Exception => root_exception
61
+ # begin
62
+ # err = RuntimeError.new(root_exception.detail.fault.details.validationFailureDetail.message * ",")
63
+ # err.set_backtrace([ "#{__FILE__}:#{__LINE__}", *root_exception.backtrace ])
64
+ # rescue
65
+ # raise root_exception.message
66
+ # end
67
+ #
68
+ # raise err
69
+ # end
@@ -0,0 +1,27 @@
1
+ module Fedex::WebServices
2
+ module Request
3
+ class DeleteShipment < Base
4
+ attr_accessor :tracking_id
5
+
6
+ def initialize(service, tracking_id)
7
+ super(service)
8
+ @tracking_id = tracking_id
9
+ end
10
+
11
+ def remote_method
12
+ :deleteShipment
13
+ end
14
+
15
+ def contents
16
+ DeleteShipmentRequest.new.tap do |o|
17
+ o.webAuthenticationDetail = web_authentication_detail
18
+ o.version = version
19
+ o.clientDetail = client_detail
20
+ o.deletionControl = DeletionControlType::DELETE_ALL_PACKAGES
21
+
22
+ o.trackingId = @tracking_id
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ module Fedex::WebServices
2
+ module Request
3
+ class GetRates < Base
4
+
5
+ def initialize(service, service_type, rate_request_type, shipper, recipient, weight)
6
+
7
+ super(service)
8
+
9
+ @service_type = service_type
10
+ @shipper = shipper
11
+ @recipient = recipient
12
+ @rate_request_type = rate_request_type
13
+ @requested_package_line_items = [
14
+ RequestedPackageLineItem.new.tap do |o|
15
+ o.groupPackageCount = 1
16
+ o.sequenceNumber = 1
17
+ o.weight = weight
18
+ end
19
+ ]
20
+ end
21
+
22
+ def remote_method
23
+ :getRates
24
+ end
25
+
26
+ def contents
27
+ RateRequest.new.tap do |o|
28
+ o.webAuthenticationDetail = web_authentication_detail
29
+ o.version = version
30
+ o.clientDetail = client_detail
31
+
32
+ o.requestedShipment = RequestedShipment.new.tap do |o|
33
+ o.shipTimestamp = Time.now.iso8601
34
+ o.serviceType = @service_type
35
+ o.packagingType = PackagingType::YOUR_PACKAGING
36
+
37
+ o.shipper = @shipper
38
+ o.recipient = @recipient
39
+
40
+ o.shippingChargesPayment = Payment.new.tap do |o|
41
+ o.paymentType = PaymentType::SENDER
42
+ o.payor = Payor.new.tap do |o|
43
+ o.accountNumber = @service.credentials.account_number
44
+ end
45
+ end
46
+
47
+ o.rateRequestTypes = [ @rate_request_type ]
48
+
49
+ o.packageCount = @requested_package_line_items.size
50
+ o.requestedPackageLineItems = @requested_package_line_items
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,76 @@
1
+ module Fedex::WebServices
2
+ module Request
3
+ class ProcessShipment < Base
4
+
5
+ def self.mps_requests(service, service_type, shipper, recipient,
6
+ label_specification, package_weights)
7
+
8
+ package_weights.map.with_index do |weight, index|
9
+ requested_package_line_item = RequestedPackageLineItem.new.tap do |o|
10
+ o.sequenceNumber = index + 1
11
+ o.weight = weight
12
+ end
13
+
14
+ ProcessShipment.new(
15
+ service,
16
+ service_type,
17
+ shipper,
18
+ recipient,
19
+ label_specification,
20
+ package_weights.size,
21
+ [ requested_package_line_item ]
22
+ )
23
+ end
24
+ end
25
+
26
+ def initialize(service, service_type, shipper, recipient,
27
+ label_specification, package_count, requested_package_line_items)
28
+
29
+ super(service)
30
+
31
+ @service_type = service_type
32
+ @shipper = shipper
33
+ @recipient = recipient
34
+ @label_specification = label_specification
35
+ @package_count = package_count
36
+ @requested_package_line_items = requested_package_line_items
37
+ end
38
+
39
+ def remote_method
40
+ :processShipment
41
+ end
42
+
43
+ def contents
44
+ ProcessShipmentRequest.new.tap do |o|
45
+ o.webAuthenticationDetail = web_authentication_detail
46
+ o.version = version
47
+ o.clientDetail = client_detail
48
+
49
+ o.requestedShipment = RequestedShipment.new.tap do |o|
50
+ o.shipTimestamp = Time.now.iso8601
51
+ o.dropoffType = DropoffType::REGULAR_PICKUP
52
+ o.serviceType = @service_type
53
+ o.packagingType = PackagingType::YOUR_PACKAGING
54
+
55
+ o.shipper = @shipper
56
+ o.recipient = @recipient
57
+
58
+ o.shippingChargesPayment = Payment.new.tap do |o|
59
+ o.paymentType = PaymentType::SENDER
60
+ o.payor = Payor.new.tap do |o|
61
+ o.accountNumber = @service.credentials.account_number
62
+ end
63
+ end
64
+
65
+ o.labelSpecification = @label_specification
66
+
67
+ o.rateRequestTypes = [ RateRequestType::LIST ]
68
+
69
+ o.requestedPackageLineItems = @requested_package_line_items
70
+ o.packageCount = @package_count
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,66 @@
1
+ require 'stringio'
2
+
3
+ module Fedex::WebServices
4
+ module Service
5
+ class ServiceException < RuntimeError
6
+ attr_accessor :details
7
+ end
8
+
9
+ class Base
10
+ include Fedex::WebServices::Definitions
11
+ include Fedex::WebServices::Request
12
+
13
+ Credentials = Struct.new("Credentials",
14
+ :account_number, :meter_number, :key, :password, :environment
15
+ )
16
+
17
+ attr_accessor :credentials
18
+ attr_reader :wiredump
19
+
20
+ def initialize(credentials)
21
+ @credentials = credentials
22
+ end
23
+
24
+ def service_id
25
+ raise "service_id must be implemented by subclasses of Service"
26
+ end
27
+
28
+ protected
29
+ def port
30
+ raise "port must be implemented by subclasses of Service"
31
+ end
32
+
33
+ private
34
+ def issue_request(request)
35
+ port = self.port
36
+ port.wiredump_dev = StringIO.new(@wiredump = "")
37
+
38
+ request_contents = request.contents
39
+ yield(request_contents) if (block_given?)
40
+
41
+ port.send(request.remote_method, request_contents).tap do |response|
42
+ check_response(response)
43
+ end
44
+ rescue Exception => root_exception
45
+ err = ServiceException.new(root_exception.message)
46
+ err.details = root_exception.detail.fault.details.validationFailureDetail.message rescue nil
47
+ err.set_backtrace([ "#{__FILE__}:#{__LINE__ + 1}", *root_exception.backtrace ])
48
+ raise err
49
+ end
50
+
51
+ def check_response(response)
52
+ error_notifications = response.notifications.reject do |notification|
53
+ [
54
+ NotificationSeverityType::SUCCESS,
55
+ NotificationSeverityType::NOTE,
56
+ NotificationSeverityType::WARNING
57
+ ].include?(notification.severity)
58
+ end
59
+
60
+ if (error_notifications.any?)
61
+ raise error_notifications.map(&:message) * ". "
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ require 'bigdecimal'
2
+
3
+ module Fedex::WebServices
4
+ module Service
5
+ class Rate < Base
6
+
7
+ def get_rates(service_type, rate_request_type, shipper, recipient, weight, &process_contents)
8
+ request = GetRates.new(self,
9
+ service_type,
10
+ rate_request_type,
11
+ shipper,
12
+ recipient,
13
+ weight
14
+ )
15
+
16
+ response = issue_request(request, &process_contents)
17
+ return [ Rate.rate_for(response, rate_request_type), response ]
18
+ end
19
+
20
+ def service_id
21
+ :crs
22
+ end
23
+
24
+ def self.rate_for(response, rate_request_type)
25
+ details = response.rateReplyDetails.first.ratedShipmentDetails.select do |detail|
26
+ detail.shipmentRateDetail.rateType == "PAYOR_#{rate_request_type}_PACKAGE"
27
+ end
28
+
29
+ details.inject(0) do |acc, detail|
30
+ acc + BigDecimal.new(detail.shipmentRateDetail.totalNetCharge.amount)
31
+ end
32
+ end
33
+
34
+ protected
35
+ def port
36
+ RatePortType.new('https://wsbeta.fedex.com:443/web-services')
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,83 @@
1
+ require 'bigdecimal'
2
+ require 'base64'
3
+
4
+ module Fedex::WebServices
5
+ module Service
6
+ class Ship < Base
7
+
8
+ def service_id
9
+ :ship
10
+ end
11
+
12
+ def process_shipment(service_type, shipper, recipient,
13
+ label_specification, package_weights, &process_contents)
14
+
15
+ curry_process_contents_callback = ->(request_number) do
16
+ return ->(request_contents) do
17
+ process_contents.call(request_contents, request_number) if (process_contents)
18
+ end
19
+ end
20
+
21
+ requests = ProcessShipment.mps_requests(self,
22
+ service_type,
23
+ shipper,
24
+ recipient,
25
+ label_specification,
26
+ package_weights
27
+ )
28
+
29
+ first, rest = requests.first, requests[1..-1]
30
+
31
+ first_response = issue_request(first, &curry_process_contents_callback.call(1))
32
+
33
+ rest_responses = rest.map.with_index do |request, index|
34
+ issue_request(request) do |request_contents|
35
+ request_contents.requestedShipment.masterTrackingId = TrackingId.new.tap do |o|
36
+ o.trackingNumber = Ship::tracking_number_for(first_response)
37
+ end
38
+
39
+ curry_process_contents_callback.call(index + 2).call(request_contents)
40
+ end
41
+ end
42
+
43
+ [ first_response, *rest_responses ].map do |response|
44
+ [
45
+ self.class.tracking_number_for(response),
46
+ self.class.label_for(response),
47
+ self.class.package_rate_for(response),
48
+ response
49
+ ]
50
+ end
51
+ end
52
+
53
+ def delete_shipment(*args, &process_contents)
54
+ issue_request(DeleteShipment.new(self, *args), &process_contents)
55
+ end
56
+
57
+ def self.label_for(response)
58
+ label = response.completedShipmentDetail.completedPackageDetails.first.label
59
+ Base64.decode64(label.parts.map { |p| Base64.decode64(p.image) } * "")
60
+ end
61
+
62
+ def self.tracking_number_for(response)
63
+ response.completedShipmentDetail.completedPackageDetails[0].trackingIds[0].trackingNumber
64
+ rescue
65
+ raise "Unable to extract tracking number from response"
66
+ end
67
+
68
+ def self.package_rate_for(response)
69
+ response.completedShipmentDetail.completedPackageDetails.first.packageRating.
70
+ packageRateDetails.inject(0) do |acc, rate|
71
+ rate.rateType == ReturnedRateType::PAYOR_ACCOUNT_PACKAGE ?
72
+ acc + BigDecimal.new(rate.netCharge.amount) :
73
+ acc
74
+ end
75
+ end
76
+
77
+ protected
78
+ def port
79
+ ShipPortType.new('https://wsbeta.fedex.com:443/web-services')
80
+ end
81
+ end
82
+ end
83
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fedex-web-services
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.42
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Abreu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-18 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Interfaces with the FedEx web services API to look up shipping rates,
15
+ generate labels, and cancel shipments
16
+ email:
17
+ - brian@nut.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/fedex/railtie.rb
23
+ - lib/fedex/tasks/fedex.rake
24
+ - lib/fedex/version.rb
25
+ - lib/fedex/web_services/definitions.rb
26
+ - lib/fedex/web_services/request/base.rb
27
+ - lib/fedex/web_services/request/delete_shipment.rb
28
+ - lib/fedex/web_services/request/get_rates.rb
29
+ - lib/fedex/web_services/request/process_shipment.rb
30
+ - lib/fedex/web_services/service/base.rb
31
+ - lib/fedex/web_services/service/rate.rb
32
+ - lib/fedex/web_services/service/ship.rb
33
+ - lib/fedex/web_services.rb
34
+ - lib/fedex.rb
35
+ homepage: https://github.com/brewski/fedex-web-services
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project: fedex-web-services
55
+ rubygems_version: 1.8.10
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Provies an interface to the FedEx web services API (version 10)
59
+ test_files: []