fex 0.0.1
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/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE.txt +22 -0
- data/README.md +161 -0
- data/Rakefile +1 -0
- data/fex.gemspec +23 -0
- data/lib/fex.rb +10 -0
- data/lib/fex/client.rb +26 -0
- data/lib/fex/response.rb +28 -0
- data/lib/fex/service.rb +57 -0
- data/lib/fex/service_factory.rb +69 -0
- data/lib/fex/services.yml +23 -0
- data/lib/fex/ship_response.rb +11 -0
- data/lib/fex/version.rb +3 -0
- data/lib/fex/wsdl.rb +14 -0
- data/spec/integration/address_validation_spec.rb +30 -0
- data/spec/integration/rate_spec.rb +67 -0
- data/spec/integration/ship_spec.rb +88 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/credentials.rb +27 -0
- data/spec/support/logger_helper.rb +15 -0
- data/spec/unit/service_spec.rb +22 -0
- data/spec/unit/wsdl_spec.rb +19 -0
- data/wsdl/address_validation.wsdl +603 -0
- data/wsdl/close.wsdl +833 -0
- data/wsdl/global_ship_address.wsdl +748 -0
- data/wsdl/open_ship.wsdl +5651 -0
- data/wsdl/package_movement_information.wsdl +545 -0
- data/wsdl/pickup.wsdl +2761 -0
- data/wsdl/rate.wsdl +4730 -0
- data/wsdl/return_tag.wsdl +421 -0
- data/wsdl/ship.wsdl +6097 -0
- data/wsdl/track.wsdl +1516 -0
- data/wsdl/upload_document.wsdl +537 -0
- metadata +136 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 iain
|
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,161 @@
|
|
1
|
+
# Fex
|
2
|
+
|
3
|
+
Small wrapper around Savon for using FedEx Web Services. It will expose you to
|
4
|
+
all the nitty gritty details from the FedEx API.
|
5
|
+
|
6
|
+
Disadvantages:
|
7
|
+
|
8
|
+
* The FedEx API is big and complex
|
9
|
+
* It's not easy to get started
|
10
|
+
|
11
|
+
Advantages:
|
12
|
+
|
13
|
+
* You can do everything that is allowed by the API.
|
14
|
+
* The code is really close to the documentation by FedEx.
|
15
|
+
|
16
|
+
It's up to you to decide which approach to take. If you don't do many special
|
17
|
+
things, a gem like `fedex`, might be a better alternative, because it hides
|
18
|
+
most of the complexity. If you find yourself needing a bit more control, you
|
19
|
+
can take this gem.
|
20
|
+
|
21
|
+
Note: This gem is not complete. It contains the ship service, address
|
22
|
+
validation and rate service. The other services FedEx supplies are a somewhat
|
23
|
+
different. It shouldn't be too difficult to support them, but I haven't gotten
|
24
|
+
around to it.
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
gem 'fex'
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install fex
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
Create a client with your credentials:
|
43
|
+
|
44
|
+
``` ruby
|
45
|
+
client = Fex.client(
|
46
|
+
|
47
|
+
# required:
|
48
|
+
|
49
|
+
credentials: {
|
50
|
+
key: "xxx",
|
51
|
+
password: "xxx",
|
52
|
+
account_number: "xxx",
|
53
|
+
meter_number: "xxx",
|
54
|
+
},
|
55
|
+
|
56
|
+
# optional:
|
57
|
+
mode: "test", # or "production"
|
58
|
+
|
59
|
+
# see Savon for more information about these
|
60
|
+
client: {
|
61
|
+
logger: Rails.logger,
|
62
|
+
log_level: "info",
|
63
|
+
log: true,
|
64
|
+
raise_errors: true
|
65
|
+
}
|
66
|
+
)
|
67
|
+
```
|
68
|
+
|
69
|
+
Create a service:
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
service = client.service(:ship)
|
73
|
+
|
74
|
+
# see what operations are available:
|
75
|
+
service.operations # => [ :process_shipment, .... ]
|
76
|
+
```
|
77
|
+
|
78
|
+
Perform a request:
|
79
|
+
|
80
|
+
``` ruby
|
81
|
+
response = service.call(
|
82
|
+
|
83
|
+
# the operation as mentioned earlier
|
84
|
+
:process_shipment,
|
85
|
+
|
86
|
+
# the attributes (after the Version tag)
|
87
|
+
requested_shipment: {
|
88
|
+
ship_timestamp: Time.now.utc.iso8601(2),
|
89
|
+
dropoff_type: "REGULAR_PICKUP",
|
90
|
+
service_type: "PRIORITY_OVERNIGHT",
|
91
|
+
packaging_type: "YOUR_PACKAGING",
|
92
|
+
# etc ...
|
93
|
+
}
|
94
|
+
)
|
95
|
+
```
|
96
|
+
|
97
|
+
The response has everything Savon offers, plus some methods specific to FedEx.
|
98
|
+
|
99
|
+
``` ruby
|
100
|
+
# From Savon:
|
101
|
+
response.success?
|
102
|
+
response.soap_fault?
|
103
|
+
all_the_things = response.body
|
104
|
+
severity = response.xpath("//Notifications/Severity").inner_text
|
105
|
+
severity = response.css("Severity").inner_text
|
106
|
+
|
107
|
+
# From Fex:
|
108
|
+
response.severity
|
109
|
+
response.code
|
110
|
+
response.message
|
111
|
+
response.image # only for shipments with labels
|
112
|
+
```
|
113
|
+
|
114
|
+
For examples on how to use this gem, visit the `spec/integration` directory.
|
115
|
+
|
116
|
+
## Running gem specs
|
117
|
+
|
118
|
+
To run the specs on this project, you can create a file called
|
119
|
+
`spec/support/credentials.yml`, and fill it with your own test keys.
|
120
|
+
|
121
|
+
``` yaml
|
122
|
+
production:
|
123
|
+
:key: "xxx"
|
124
|
+
:password: "xxx"
|
125
|
+
:account_number: "xxx"
|
126
|
+
:meter_number: "xxx"
|
127
|
+
|
128
|
+
test:
|
129
|
+
:key: "xxx"
|
130
|
+
:password: "xxx"
|
131
|
+
:account_number: "xxx"
|
132
|
+
:meter_number: "xxx"
|
133
|
+
```
|
134
|
+
|
135
|
+
You can specify production and/or test. The production keys are used for the
|
136
|
+
address validation service, because that doesn't work for me on test.
|
137
|
+
|
138
|
+
To use the keys in an integration spec, add `:production_environment` or
|
139
|
+
`:test_environment` as group metadata keys, and the methods `credentials` and
|
140
|
+
`mode` are made available for you:
|
141
|
+
|
142
|
+
``` ruby
|
143
|
+
describe "FedEx rate service", :test_environment do
|
144
|
+
|
145
|
+
it "should do something" do
|
146
|
+
client = Fex.client(credentials: credentials, mode: mode)
|
147
|
+
# etc ...
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
If you don't specify keys, these specs will be skipped.
|
154
|
+
|
155
|
+
## Contributing
|
156
|
+
|
157
|
+
1. Fork it
|
158
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
159
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
160
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
161
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/fex.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fex/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "fex"
|
8
|
+
gem.version = Fex::VERSION
|
9
|
+
gem.authors = ["iain"]
|
10
|
+
gem.email = ["iain@iain.nl"]
|
11
|
+
gem.description = %q{Small wrapper around Savon for using FedEx Web Services}
|
12
|
+
gem.summary = %q{Small wrapper around Savon for using FedEx Web Services}
|
13
|
+
gem.homepage = "https://github.com/yourkarma/fex"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'savon', '~> 2.0'
|
21
|
+
gem.add_development_dependency "rspec"
|
22
|
+
gem.add_development_dependency "pry-nav"
|
23
|
+
end
|
data/lib/fex.rb
ADDED
data/lib/fex/client.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "fex/service_factory"
|
3
|
+
|
4
|
+
module Fex
|
5
|
+
class Client
|
6
|
+
|
7
|
+
attr_reader :globals
|
8
|
+
|
9
|
+
def initialize(globals)
|
10
|
+
@globals = globals
|
11
|
+
end
|
12
|
+
|
13
|
+
def service(name, locals = {})
|
14
|
+
config = service_configuration[name]
|
15
|
+
opts = globals.deep_merge(config).deep_merge(locals)
|
16
|
+
ServiceFactory.new(name, opts).service
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def service_configuration
|
22
|
+
@service_configuration ||= YAML.load_file(File.expand_path("../services.yml", __FILE__))
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/fex/response.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "delegate"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Fex
|
5
|
+
class Response < SimpleDelegator
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :doc, :css
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
doc.remove_namespaces!
|
13
|
+
end
|
14
|
+
|
15
|
+
def severity
|
16
|
+
xpath("//Notifications/Severity").inner_text
|
17
|
+
end
|
18
|
+
|
19
|
+
def code
|
20
|
+
xpath("//Notifications/Code").inner_text
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
xpath("//Notifications/Message").inner_text
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/fex/service.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "savon"
|
2
|
+
require "forwardable"
|
3
|
+
require "fex/wsdl"
|
4
|
+
|
5
|
+
module Fex
|
6
|
+
|
7
|
+
# This is a wrapper around Savon, with more or less intelligent defaults.
|
8
|
+
class Service
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :client, :operations
|
12
|
+
|
13
|
+
# The name determines the name of the WSDL file located in the wsdl directory in the root of the gem.
|
14
|
+
# The full path will be calculated by the +WSDL+ class.
|
15
|
+
attr_reader :name
|
16
|
+
|
17
|
+
# Defaults are any values that always exist on this service. Examples are
|
18
|
+
# the credentials and the version.
|
19
|
+
attr_reader :defaults
|
20
|
+
|
21
|
+
# The response is the class that will handle building a response. The
|
22
|
+
# default is the generic +Response+ class.
|
23
|
+
attr_reader :response
|
24
|
+
|
25
|
+
def initialize(options)
|
26
|
+
@name = options.fetch(:name)
|
27
|
+
@client_options = options[:client] || {}
|
28
|
+
@wsdl = options[:wsdl]
|
29
|
+
@defaults = options[:defaults] || {}
|
30
|
+
@response = options.fetch(:response)
|
31
|
+
end
|
32
|
+
|
33
|
+
def wsdl
|
34
|
+
@wsdl || WSDL.new.path_for(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def client
|
38
|
+
Savon.client(client_options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(operation, message, options = {})
|
42
|
+
opts = {message: defaults.deep_merge(message)}.deep_merge(options)
|
43
|
+
savon_response = client.call(operation, opts)
|
44
|
+
response.new(savon_response)
|
45
|
+
end
|
46
|
+
|
47
|
+
def client_options
|
48
|
+
default_options = {
|
49
|
+
wsdl: wsdl,
|
50
|
+
convert_request_keys_to: :camelcase,
|
51
|
+
pretty_print_xml: true
|
52
|
+
}
|
53
|
+
default_options.deep_merge(@client_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "fex/service"
|
2
|
+
require "fex/response"
|
3
|
+
require "fex/ship_response"
|
4
|
+
|
5
|
+
module Fex
|
6
|
+
class ServiceFactory
|
7
|
+
|
8
|
+
PRODUCTION_ENDPOINT = "https://ws.fedex.com:443/web-services/rate"
|
9
|
+
|
10
|
+
attr_reader :name, :mode, :client_options, :version, :defaults, :wsdl, :response
|
11
|
+
|
12
|
+
# credentials
|
13
|
+
attr_reader :key, :password, :account_number, :meter_number
|
14
|
+
|
15
|
+
def initialize(name, options)
|
16
|
+
@name = name
|
17
|
+
@mode = options.fetch(:mode) { "test" }
|
18
|
+
@version = options.fetch(:version) {{}}
|
19
|
+
@client_options = options.fetch(:client) {{}}
|
20
|
+
@defaults = options.fetch(:defaults) {{}}
|
21
|
+
|
22
|
+
@credentials = options.fetch(:credentials)
|
23
|
+
@key = @credentials.fetch(:key)
|
24
|
+
@password = @credentials.fetch(:password)
|
25
|
+
@account_number = @credentials.fetch(:account_number)
|
26
|
+
@meter_number = @credentials.fetch(:meter_number)
|
27
|
+
|
28
|
+
@wsdl = options[:wsdl]
|
29
|
+
|
30
|
+
@response = options[:response] || "Response"
|
31
|
+
end
|
32
|
+
|
33
|
+
def service
|
34
|
+
Service.new(
|
35
|
+
name: name,
|
36
|
+
client: merged_client_options,
|
37
|
+
wsdl: wsdl,
|
38
|
+
defaults: merged_defaults,
|
39
|
+
response: Fex.const_get(response)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def merged_defaults
|
44
|
+
authentication.deep_merge(version: version).deep_merge(defaults)
|
45
|
+
end
|
46
|
+
|
47
|
+
def authentication
|
48
|
+
{
|
49
|
+
web_authentication_detail: {
|
50
|
+
user_credential: { key: key, password: password }
|
51
|
+
},
|
52
|
+
client_detail: { account_number: account_number, meter_number: meter_number }
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def merged_client_options
|
57
|
+
endpoint_options.deep_merge(client_options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def endpoint_options
|
61
|
+
if mode.to_s == "production"
|
62
|
+
{ endpoint: PRODUCTION_ENDPOINT }
|
63
|
+
else
|
64
|
+
{}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
---
|
2
|
+
:address_validation:
|
3
|
+
:version:
|
4
|
+
:service_id: aval
|
5
|
+
:major: 2
|
6
|
+
:intermediate: 0
|
7
|
+
:minor: 0
|
8
|
+
:rate:
|
9
|
+
:client:
|
10
|
+
:env_namespace: soapenv
|
11
|
+
:namespace_identifier: v13
|
12
|
+
:version:
|
13
|
+
:service_id: crs
|
14
|
+
:major: 13
|
15
|
+
:intermediate: 0
|
16
|
+
:minor: 0
|
17
|
+
:ship:
|
18
|
+
:response: "ShipResponse"
|
19
|
+
:version:
|
20
|
+
:service_id: ship
|
21
|
+
:major: 12
|
22
|
+
:intermediate: 1
|
23
|
+
:minor: 0
|