fedex-web-services 1.0.42

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