ecircle_soap_client 0.0.5
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/.document +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +20 -0
- data/README.md +89 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/ecircle_soap_client.gemspec +100 -0
- data/lib/ecircle/base.rb +40 -0
- data/lib/ecircle/client.rb +145 -0
- data/lib/ecircle/configuration.rb +14 -0
- data/lib/ecircle/group.rb +73 -0
- data/lib/ecircle/member.rb +31 -0
- data/lib/ecircle/message.rb +93 -0
- data/lib/ecircle/user.rb +120 -0
- data/lib/ecircle_soap_client.rb +48 -0
- data/start_pry_with_ecircle +31 -0
- data/test/.login.yml.sample +4 -0
- data/test/helper.rb +116 -0
- data/test/test_ecircle_soap_client.rb +317 -0
- data/test/test_ecircle_soap_client_group.rb +61 -0
- data/test/test_ecircle_soap_client_member.rb +48 -0
- data/test/test_ecircle_soap_client_message.rb +174 -0
- data/test/test_ecircle_soap_client_user.rb +220 -0
- metadata +288 -0
data/.document
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create ree-1.8.7-2011.03@ecirclegemset
|
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
source "http://rubygems.org"
|
3
|
+
|
4
|
+
gem 'rake', "= 0.8.7"
|
5
|
+
gem 'savon'
|
6
|
+
gem 'nokogiri'
|
7
|
+
gem 'multipart-post', :git => 'git://github.com/gorenje/multipart-post.git'
|
8
|
+
gem 'uuidtools'
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem 'pry'
|
12
|
+
gem 'pry-doc'
|
13
|
+
gem 'gist'
|
14
|
+
gem "thoughtbot-shoulda", ">= 0"
|
15
|
+
gem "bundler", "~> 1.0.0"
|
16
|
+
gem "jeweler", "~> 1.6.4"
|
17
|
+
gem "rcov", ">= 0"
|
18
|
+
gem 'rr'
|
19
|
+
gem "cheat"
|
20
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
eCircle SOAP Client
|
2
|
+
===================
|
3
|
+
|
4
|
+
Warning
|
5
|
+
-------
|
6
|
+
Experimental code and not yet complete. Lots of testing are missing and functionality is limited.
|
7
|
+
Simple things like creating and deleting users and groups is possible, but message creation and
|
8
|
+
sending are not yet supported.
|
9
|
+
|
10
|
+
Description
|
11
|
+
-----------
|
12
|
+
|
13
|
+
Based on [Savon](http://savonrb.com/) and [Nokogiri](http://nokogiri.org/) and provides a simple interface for accessing the eCircle [WSDL synchronous API](http://www.soapclient.com/soapclient?template=%2Fclientform.html&fn=soapform&SoapTemplate=%2FSoapResult.html&SoapWSDL=http%3A%2F%2Fwebservices.ecircle-ag.com%2Fsoap%2Fecm.wsdl&_ArraySize=2).
|
14
|
+
|
15
|
+
Intention is to provide a ActiveRecord/ActiveModel-like interface to the individual entities provided by the SOAP API. It's far from complete and you can only do a few selected things with this gem.
|
16
|
+
|
17
|
+
Installation
|
18
|
+
------------
|
19
|
+
|
20
|
+
Currently only via bundle with:
|
21
|
+
|
22
|
+
```
|
23
|
+
gem 'ecircle_soap_client', :git => 'git://github.com/gorenje/ecircle_soap_client.git'
|
24
|
+
```
|
25
|
+
|
26
|
+
Usage
|
27
|
+
-----
|
28
|
+
|
29
|
+
Configration:
|
30
|
+
|
31
|
+
```
|
32
|
+
Ecircle.configure do |config|
|
33
|
+
config.user = "your@user.email.com"
|
34
|
+
config.realm = "http://example.com"
|
35
|
+
config.password = "supersecret"
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
the WSDL settings are assumed to be
|
40
|
+
|
41
|
+
```
|
42
|
+
wsdl.document = "http://webservices.ecircle-ag.com/soap/ecm.wsdl"
|
43
|
+
wsdl.endpoint = "http://webservices.ecircle-ag.com/rpc"
|
44
|
+
wsdl.namespace = "http://webservices.ecircleag.com/rpcns"
|
45
|
+
```
|
46
|
+
|
47
|
+
but can be overridden in the configuration
|
48
|
+
|
49
|
+
```
|
50
|
+
Ecircle.configure do |config|
|
51
|
+
config.wsdl.document = "http://example.com/?wsdl"
|
52
|
+
config.wsdl.endpoint = "http://blah.com"
|
53
|
+
config.wsdl.namespace = "some new namespace"
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Having configured the client, User retrieval is done via email or id:
|
58
|
+
|
59
|
+
```
|
60
|
+
user = Ecircle::User.find_by_email("some@email.com")
|
61
|
+
user = Ecircle::User.find_by_id("1234131")
|
62
|
+
```
|
63
|
+
|
64
|
+
Logon is handled automagically by the Ecircle::Client, however the caller is responsible for
|
65
|
+
retrying requests if a Ecircle::Client::NotLoggedIn exception is raised. There no need to
|
66
|
+
explicitly call Ecircle::Client#logon, the exception notifies the client that it's not longer
|
67
|
+
logged in and needs to login before the next request.
|
68
|
+
|
69
|
+
Groups and Members
|
70
|
+
------------------
|
71
|
+
|
72
|
+
Having gotten a user, it's possible to find their memeberships of groups
|
73
|
+
|
74
|
+
```
|
75
|
+
groups = user.membership
|
76
|
+
```
|
77
|
+
|
78
|
+
Now a new user can be added to a group
|
79
|
+
|
80
|
+
```
|
81
|
+
member = groups.first.add_member Ecircle::User.create_by_email( "another@email.com" )
|
82
|
+
```
|
83
|
+
|
84
|
+
But if the member should be deleted from the group again
|
85
|
+
|
86
|
+
```
|
87
|
+
member.delete
|
88
|
+
```
|
89
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "ecircle_soap_client"
|
18
|
+
gem.homepage = "http://google.com/search?q=ecircle"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Ecircle SOAP client for synchron API}
|
21
|
+
gem.description = %Q{See summary.}
|
22
|
+
gem.email = "gerrit.riessen@gmail.com"
|
23
|
+
gem.authors = ["Gerrit Riessen"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "ecircle_soap_client #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.5
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ecircle_soap_client"
|
8
|
+
s.version = "0.0.5"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Gerrit Riessen"]
|
12
|
+
s.date = "2011-09-01"
|
13
|
+
s.description = "See summary."
|
14
|
+
s.email = "gerrit.riessen@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".rvmrc",
|
21
|
+
"Gemfile",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"ecircle_soap_client.gemspec",
|
26
|
+
"lib/ecircle/base.rb",
|
27
|
+
"lib/ecircle/client.rb",
|
28
|
+
"lib/ecircle/configuration.rb",
|
29
|
+
"lib/ecircle/group.rb",
|
30
|
+
"lib/ecircle/member.rb",
|
31
|
+
"lib/ecircle/message.rb",
|
32
|
+
"lib/ecircle/user.rb",
|
33
|
+
"lib/ecircle_soap_client.rb",
|
34
|
+
"start_pry_with_ecircle",
|
35
|
+
"test/.login.yml.sample",
|
36
|
+
"test/helper.rb",
|
37
|
+
"test/test_ecircle_soap_client.rb",
|
38
|
+
"test/test_ecircle_soap_client_group.rb",
|
39
|
+
"test/test_ecircle_soap_client_member.rb",
|
40
|
+
"test/test_ecircle_soap_client_message.rb",
|
41
|
+
"test/test_ecircle_soap_client_user.rb"
|
42
|
+
]
|
43
|
+
s.homepage = "http://google.com/search?q=ecircle"
|
44
|
+
s.licenses = ["MIT"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = "1.8.10"
|
47
|
+
s.summary = "Ecircle SOAP client for synchron API"
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<rake>, ["= 0.8.7"])
|
54
|
+
s.add_runtime_dependency(%q<savon>, [">= 0"])
|
55
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
56
|
+
s.add_runtime_dependency(%q<multipart-post>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<uuidtools>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<pry>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<pry-doc>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<gist>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
64
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<cheat>, [">= 0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
69
|
+
s.add_dependency(%q<savon>, [">= 0"])
|
70
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
71
|
+
s.add_dependency(%q<multipart-post>, [">= 0"])
|
72
|
+
s.add_dependency(%q<uuidtools>, [">= 0"])
|
73
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
74
|
+
s.add_dependency(%q<pry-doc>, [">= 0"])
|
75
|
+
s.add_dependency(%q<gist>, [">= 0"])
|
76
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
77
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
79
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
80
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
81
|
+
s.add_dependency(%q<cheat>, [">= 0"])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
85
|
+
s.add_dependency(%q<savon>, [">= 0"])
|
86
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
87
|
+
s.add_dependency(%q<multipart-post>, [">= 0"])
|
88
|
+
s.add_dependency(%q<uuidtools>, [">= 0"])
|
89
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
90
|
+
s.add_dependency(%q<pry-doc>, [">= 0"])
|
91
|
+
s.add_dependency(%q<gist>, [">= 0"])
|
92
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
93
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
94
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
95
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
96
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
97
|
+
s.add_dependency(%q<cheat>, [">= 0"])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
data/lib/ecircle/base.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class Base
|
3
|
+
attr_reader :id, :all_fields, :named_attrs
|
4
|
+
|
5
|
+
def id=(value)
|
6
|
+
@id = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](name)
|
10
|
+
@all_fields[name.to_sym]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@id, @all_fields, @named_attrs = "", {}, {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def init_with_xml(element_name, xml_string)
|
18
|
+
n = Nokogiri.parse(xml_string)
|
19
|
+
@id = n.xpath("#{element_name}/@id" ).to_s
|
20
|
+
@all_fields = Hash[ n.xpath("//#{element_name}/*").collect do |a|
|
21
|
+
[a.name.to_sym, a.children.first.to_s]
|
22
|
+
end ]
|
23
|
+
@named_attrs = Hash[ n.xpath("#{element_name}/namedattr").collect do |a|
|
24
|
+
[a.attributes["name"].value,
|
25
|
+
a.children.empty? ? "" : a.children.first.to_s]
|
26
|
+
end ]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Handle all assignments, everything else is propagated to super.
|
30
|
+
def method_missing(method, *args, &block)
|
31
|
+
case method.to_s
|
32
|
+
when /\[\]=/ then super
|
33
|
+
when /(.+)=/
|
34
|
+
@all_fields[$1.to_sym] = args.first
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class Client
|
3
|
+
attr_reader :session_token
|
4
|
+
|
5
|
+
LoginFailed = Class.new(RuntimeError)
|
6
|
+
NotLoggedIn = Class.new(RuntimeError)
|
7
|
+
PermissionDenied = Class.new(RuntimeError)
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@session_token = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# helper method for allowing clients to automatically retry requests to
|
15
|
+
# ecircle.
|
16
|
+
def attempt(retries = 2, &block)
|
17
|
+
begin
|
18
|
+
yield
|
19
|
+
rescue Ecircle::Client::NotLoggedIn => e
|
20
|
+
raise e unless retries > 0
|
21
|
+
attempt(retries - 1, &block)
|
22
|
+
|
23
|
+
rescue Ecircle::Client::PermissionDenied => e
|
24
|
+
raise e unless retries > 0
|
25
|
+
Ecircle.client.logon
|
26
|
+
attempt(retries - 1, &block)
|
27
|
+
|
28
|
+
rescue NoMethodError => e
|
29
|
+
raise e unless retries > 0
|
30
|
+
## FIXME horrible hack because ecircle sometimes forgets that they
|
31
|
+
## FIXME have a FindMembershipsByEmail method - old software with bad memory?
|
32
|
+
if e.message =~ /No such operation 'FindMembershipsByEmail'/
|
33
|
+
attempt(retries - 1, &block)
|
34
|
+
else
|
35
|
+
raise e
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This is the magic. All we do here is take the method name, camelcase it and
|
42
|
+
# then pass it to eCircle. It is assumed that the args array contains a hash
|
43
|
+
# as the first element. This then contains the arguments for the call. E.g.:
|
44
|
+
# Ecircle.client.lookup_user_by_email :email => "willy@wonka.net"
|
45
|
+
# This will do a number of things:
|
46
|
+
# 1. login if session token is undefined
|
47
|
+
# 2. set the session parameter along with the email argument
|
48
|
+
# 3. check for specific eCircle exceptions and attempt to handle them.
|
49
|
+
def method_missing(method, *args, &block)
|
50
|
+
logon if @session_token.nil?
|
51
|
+
|
52
|
+
method_name, body = method.to_s.camelcase, args.first || {}
|
53
|
+
body[:session] = @session_token
|
54
|
+
|
55
|
+
response = begin
|
56
|
+
client.request(method_name) { soap.body = body }
|
57
|
+
rescue Savon::SOAP::Fault => e
|
58
|
+
handle_savon_fault(e, :for_method => method)
|
59
|
+
end
|
60
|
+
|
61
|
+
data = response.
|
62
|
+
body["#{method}_response".to_sym]["#{method}_return".to_sym]
|
63
|
+
|
64
|
+
if block_given?
|
65
|
+
yield(data)
|
66
|
+
else
|
67
|
+
case data
|
68
|
+
when /^[<]user/ then Ecircle::User.new(data)
|
69
|
+
when /^[<]member/ then Ecircle::Member.new(data)
|
70
|
+
when Hash
|
71
|
+
# If the response is only an empty element, the data will look like this:
|
72
|
+
# {:"@xmlns:ns1"=>"http://webservices.ecircleag.com/rpcns"}
|
73
|
+
# Return nil if we only have attributes.
|
74
|
+
data.all? { |key, value| key.to_s =~ /^@/ } ? nil : data
|
75
|
+
else
|
76
|
+
data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Generate a Savon::Client to use for making requests. The configuration for the
|
82
|
+
# client is taken from the configure object.
|
83
|
+
def client
|
84
|
+
@client ||= Savon::Client.new do
|
85
|
+
wsdl.document = Ecircle.configure.wsdl.document
|
86
|
+
wsdl.endpoint = Ecircle.configure.wsdl.endpoint
|
87
|
+
wsdl.namespace = Ecircle.configure.wsdl.namespace
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Send logon request and store the session token if successful.
|
92
|
+
# The session token is then passed into each subsequent request to
|
93
|
+
# eCircle.
|
94
|
+
def logon
|
95
|
+
@session_token = (client.request :logon do
|
96
|
+
soap.body = {
|
97
|
+
:user => Ecircle.configure.user,
|
98
|
+
:realm => Ecircle.configure.realm,
|
99
|
+
:passwd => Ecircle.configure.password
|
100
|
+
}
|
101
|
+
end).body[:logon_response][:logon_return].to_s
|
102
|
+
rescue Savon::SOAP::Fault => e
|
103
|
+
@session_token = nil
|
104
|
+
raise LoginFailed, "Msg: #{e.message}"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Invalid the session token and set it to nil.
|
108
|
+
# The session token automagically expires after 10 minutes of inactivity, so
|
109
|
+
# you might have to logout if methods are with NotLoggedIn
|
110
|
+
def logout
|
111
|
+
client.request :logout do
|
112
|
+
soap.body = {
|
113
|
+
:session => @session_token
|
114
|
+
}
|
115
|
+
end
|
116
|
+
ensure
|
117
|
+
@session_token = nil
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def handle_savon_fault(exception, opts = {})
|
123
|
+
method = opts[:for_method] || "UNKNOWN"
|
124
|
+
|
125
|
+
case exception.message
|
126
|
+
|
127
|
+
when /No such operation/ then
|
128
|
+
raise NoMethodError, "#{method} (by way of #{exception.message})"
|
129
|
+
|
130
|
+
when /Not authenticated/, /LoginException/ then
|
131
|
+
@session_token = nil # automagically login with the next call.
|
132
|
+
raise NotLoggedIn, exception.message
|
133
|
+
|
134
|
+
when /Authorisation failure/, /Permission Problem/ then
|
135
|
+
# This is not a login issue per-se, this is an authorisation
|
136
|
+
# issue, so the client must decide whether to logon again to
|
137
|
+
# attempt to resolve the issue.
|
138
|
+
raise PermissionDenied, exception.message
|
139
|
+
|
140
|
+
else
|
141
|
+
raise exception
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|