eloqua 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +23 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +81 -0
- data/LICENSE +21 -0
- data/README.md +245 -0
- data/Rakefile +13 -0
- data/TODO.md +8 -0
- data/eloqua.gemspec +32 -0
- data/eloqua_initializer.tpl.rb +3 -0
- data/lib/eloqua.rb +51 -0
- data/lib/eloqua/api.rb +119 -0
- data/lib/eloqua/api/action.rb +41 -0
- data/lib/eloqua/api/service.rb +240 -0
- data/lib/eloqua/asset.rb +31 -0
- data/lib/eloqua/builder/templates.rb +31 -0
- data/lib/eloqua/builder/xml.rb +129 -0
- data/lib/eloqua/entity.rb +72 -0
- data/lib/eloqua/exceptions.rb +5 -0
- data/lib/eloqua/helper/attribute_map.rb +78 -0
- data/lib/eloqua/query.rb +291 -0
- data/lib/eloqua/remote_object.rb +274 -0
- data/lib/eloqua/version.rb +3 -0
- data/lib/eloqua/wsdl/action.wsdl +1 -0
- data/lib/eloqua/wsdl/data.wsdl +1 -0
- data/lib/eloqua/wsdl/email.wsdl +1 -0
- data/lib/eloqua/wsdl/service.wsdl +1 -0
- data/lib/tasks/test.rake +24 -0
- data/rspec.watchr +74 -0
- data/spec/fixtures/add_group_member/success.xml +18 -0
- data/spec/fixtures/create/contact_duplicate.xml +30 -0
- data/spec/fixtures/create/contact_success.xml +25 -0
- data/spec/fixtures/create_asset/failure.xml +30 -0
- data/spec/fixtures/create_asset/group_success.xml +25 -0
- data/spec/fixtures/delete_asset/access_deny.xml +31 -0
- data/spec/fixtures/describe_asset/success.xml +72 -0
- data/spec/fixtures/describe_asset_type/success.xml +23 -0
- data/spec/fixtures/describe_entity/success.xml +54 -0
- data/spec/fixtures/describe_entity_type/success.xml +45 -0
- data/spec/fixtures/get_member_count_in_step_by_status/success.xml +15 -0
- data/spec/fixtures/list_asset_types/success.xml +28 -0
- data/spec/fixtures/list_entity_types/success.xml +21 -0
- data/spec/fixtures/list_group_membership/success.xml +25 -0
- data/spec/fixtures/list_members_in_step_by_status/success.xml +15 -0
- data/spec/fixtures/query/contact_email_one.xml +38 -0
- data/spec/fixtures/query/contact_email_two.xml +56 -0
- data/spec/fixtures/query/contact_missing.xml +19 -0
- data/spec/fixtures/query/fault.xml +43 -0
- data/spec/fixtures/remove_group_member/success.xml +18 -0
- data/spec/fixtures/retrieve/contact_missing.xml +17 -0
- data/spec/fixtures/retrieve/contact_multiple.xml +3460 -0
- data/spec/fixtures/retrieve/contact_single.xml +38 -0
- data/spec/fixtures/retrieve_asset/failure.xml +17 -0
- data/spec/fixtures/retrieve_asset/success.xml +50 -0
- data/spec/fixtures/update/contact_success.xml +26 -0
- data/spec/lib/eloqua/api/action_spec.rb +36 -0
- data/spec/lib/eloqua/api/service_spec.rb +498 -0
- data/spec/lib/eloqua/api_spec.rb +133 -0
- data/spec/lib/eloqua/asset_spec.rb +63 -0
- data/spec/lib/eloqua/builder/templates_spec.rb +68 -0
- data/spec/lib/eloqua/builder/xml_spec.rb +254 -0
- data/spec/lib/eloqua/entity_spec.rb +224 -0
- data/spec/lib/eloqua/helper/attribute_map_spec.rb +14 -0
- data/spec/lib/eloqua/query_spec.rb +596 -0
- data/spec/lib/eloqua/remote_object_spec.rb +742 -0
- data/spec/lib/eloqua_spec.rb +171 -0
- data/spec/shared/attribute_map.rb +173 -0
- data/spec/shared/class_to_api_delegation.rb +50 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/helper.rb +73 -0
- metadata +366 -0
data/lib/eloqua.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/core_ext/class'
|
2
|
+
require 'active_support/core_ext/module'
|
3
|
+
|
4
|
+
module Eloqua
|
5
|
+
|
6
|
+
autoload :Api, 'eloqua/api'
|
7
|
+
autoload :Entity, 'eloqua/entity'
|
8
|
+
autoload :Asset, 'eloqua/asset'
|
9
|
+
autoload :RemoteObject, 'eloqua/remote_object'
|
10
|
+
autoload :Query, 'eloqua/query'
|
11
|
+
|
12
|
+
mattr_accessor :user, :password
|
13
|
+
|
14
|
+
def self.configure(&block)
|
15
|
+
yield self
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.authenticate(user, password)
|
19
|
+
self.user = user
|
20
|
+
self.password = password
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.format_results_for_array(results, *keys)
|
24
|
+
max_depth = keys.length
|
25
|
+
depth = 0
|
26
|
+
keys.each do |key|
|
27
|
+
if(results.has_key?(key))
|
28
|
+
depth += 1
|
29
|
+
results = results[key]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
if(depth == max_depth && !results.is_a?(Array))
|
33
|
+
results = [results]
|
34
|
+
end
|
35
|
+
results
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.delegate_with_args(from_klass, to_klass, methods, methods_to_argument)
|
39
|
+
argument_string = methods_to_argument.join(', ')
|
40
|
+
methods.each do |__method_name|
|
41
|
+
from_klass.module_eval(<<-RUBY)
|
42
|
+
def self.#{__method_name}(*args, &block)
|
43
|
+
#{to_klass}.__send__(#{__method_name.inspect}, #{argument_string}, *args, &block)
|
44
|
+
end
|
45
|
+
RUBY
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
data/lib/eloqua/api.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'savon'
|
2
|
+
|
3
|
+
require 'active_support/core_ext/class'
|
4
|
+
require 'active_support/concern'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
|
7
|
+
require 'eloqua/builder/xml'
|
8
|
+
require 'eloqua/exceptions'
|
9
|
+
|
10
|
+
module Eloqua
|
11
|
+
|
12
|
+
class Api
|
13
|
+
|
14
|
+
autoload :Service, 'eloqua/api/service'
|
15
|
+
autoload :Action, 'eloqua/api/action'
|
16
|
+
|
17
|
+
# The namespace for Eloqua Array objects
|
18
|
+
XML_NS_ARRAY = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
|
19
|
+
|
20
|
+
# WSDLs are from 3-30-2011
|
21
|
+
WSDL = {
|
22
|
+
:service => File.dirname(__FILE__) + '/wsdl/service.wsdl',
|
23
|
+
:data => File.dirname(__FILE__) + '/wsdl/data.wsdl',
|
24
|
+
:email => File.dirname(__FILE__) + '/wsdl/email.wsdl',
|
25
|
+
:action => File.dirname(__FILE__) + '/wsdl/action.wsdl'
|
26
|
+
}
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
delegate :define_builder_template, :to => Eloqua::Builder::Xml
|
31
|
+
delegate :builder_template, :to => Eloqua::Builder::Xml
|
32
|
+
delegate :builder_templates, :to => Eloqua::Builder::Xml
|
33
|
+
|
34
|
+
attr_accessor :last_request, :last_response, :soap_error, :http_error
|
35
|
+
|
36
|
+
@@clients = {}
|
37
|
+
|
38
|
+
def reset_clients
|
39
|
+
@@clients = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def clients
|
43
|
+
@@clients
|
44
|
+
end
|
45
|
+
|
46
|
+
# There are four currently supported wsdl types for eloqua
|
47
|
+
# 1. Service
|
48
|
+
# 2. Data
|
49
|
+
# 3. Email
|
50
|
+
# 4. External Action
|
51
|
+
def client(type)
|
52
|
+
if(!Eloqua.user || !Eloqua.password)
|
53
|
+
raise('Eloqua.user or Eloqua.password is not set see Eloqua.authenticate')
|
54
|
+
end
|
55
|
+
clients[type] ||= Savon::Client.new do
|
56
|
+
wsdl.document = WSDL[type]
|
57
|
+
wsse.credentials Eloqua.user, Eloqua.password
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def builder(&block)
|
62
|
+
Eloqua::Builder::Xml.create(:namespace => :wsdl, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def remote_type(name, type = 'Base', id = 0)
|
66
|
+
{
|
67
|
+
:name => name,
|
68
|
+
:type => type,
|
69
|
+
:id => id
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def request(type, name, soap_body = nil, &block)
|
74
|
+
result = send_remote_request(type, name, soap_body, &block)
|
75
|
+
|
76
|
+
self.last_request = client(type).soap.to_xml if client(type).soap
|
77
|
+
self.last_response = result.to_xml if result.respond_to?(:to_xml)
|
78
|
+
|
79
|
+
if(result)
|
80
|
+
result = result.to_hash
|
81
|
+
response_key = "#{name}_response".to_sym
|
82
|
+
result_key = "#{name}_result".to_sym
|
83
|
+
if(result.has_key?(response_key))
|
84
|
+
result = result[response_key]
|
85
|
+
end
|
86
|
+
if(result.has_key?(result_key))
|
87
|
+
result = result[result_key]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
result
|
91
|
+
end
|
92
|
+
|
93
|
+
# Sends remote request and returns a response object
|
94
|
+
def send_remote_request(type, name, soap_body = nil, &block)
|
95
|
+
@soap_error = nil
|
96
|
+
@http_error = nil
|
97
|
+
|
98
|
+
request = client(type).request(:wsdl, name) do
|
99
|
+
soap.namespaces["xmlns:arr"] = XML_NS_ARRAY
|
100
|
+
soap.element_form_default = :qualified
|
101
|
+
soap.body = soap_body if soap_body
|
102
|
+
instance_eval(&block) if block_given?
|
103
|
+
end
|
104
|
+
response_errors(request)
|
105
|
+
request
|
106
|
+
end
|
107
|
+
|
108
|
+
def response_errors(response)
|
109
|
+
@soap_error = Eloqua::SoapError.new(response.http)
|
110
|
+
@http_error = Eloqua::HTTPError.new(response.http)
|
111
|
+
|
112
|
+
raise @soap_error if @soap_error.present?
|
113
|
+
raise @http_error if @http_error.present?
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'eloqua/api'
|
2
|
+
|
3
|
+
module Eloqua
|
4
|
+
|
5
|
+
class Api
|
6
|
+
class Action
|
7
|
+
|
8
|
+
cattr_reader :type_methods
|
9
|
+
@@type_methods = [:list_external_action_steps_by_type, :get_member_count_in_step_by_status, :list_members_in_step_by_status, :set_member_status]
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
delegate :builder, :remote_type, :to => Eloqua::Api
|
14
|
+
|
15
|
+
def get_member_count_in_step_by_status(stepId, status)
|
16
|
+
xml_query = builder do |xml|
|
17
|
+
xml.tag!(:stepId, stepId)
|
18
|
+
xml.tag!(:status, status)
|
19
|
+
end
|
20
|
+
results = request(:get_member_count_in_step_by_status, xml_query)
|
21
|
+
results = results.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
#still seems finicky, returns a funky hash table if there are no members in that step
|
25
|
+
def list_members_in_step_by_status(stepId, status, pageNumber, pageSize)
|
26
|
+
xml_query = builder do |xml|
|
27
|
+
xml.tag!(:stepId, stepId)
|
28
|
+
xml.tag!(:status, status)
|
29
|
+
xml.tag!(:pageNumber, pageNumber)
|
30
|
+
xml.tag!(:pageSize, pageSize)
|
31
|
+
end
|
32
|
+
results = request(:list_members_in_step_by_status, xml_query)
|
33
|
+
end
|
34
|
+
|
35
|
+
def request(*args)
|
36
|
+
Eloqua::Api.request(:action, *args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'eloqua/api'
|
2
|
+
|
3
|
+
module Eloqua
|
4
|
+
|
5
|
+
class Api
|
6
|
+
class Service
|
7
|
+
|
8
|
+
cattr_reader :group_methods
|
9
|
+
cattr_reader :group_type_methods
|
10
|
+
cattr_reader :type_methods
|
11
|
+
|
12
|
+
@@group_methods = [:key_with_object, :object_method, :list_types, :describe_type]
|
13
|
+
@@type_methods = []
|
14
|
+
@@group_type_methods = [:create_object, :update_object, :delete_object, :find_object, :describe]
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
delegate :builder, :remote_type, :to => Eloqua::Api
|
19
|
+
|
20
|
+
def entity_association_xml(asset_type, asset_id, entity, entity_id)
|
21
|
+
xml_query = builder do |xml|
|
22
|
+
xml.template!(:object, :entity, entity, entity_id)
|
23
|
+
xml.template!(:object, :asset, asset_type, asset_id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delegate Group
|
28
|
+
def key_with_object(group, name)
|
29
|
+
if (group == :entity)
|
30
|
+
name.to_sym
|
31
|
+
else
|
32
|
+
parts = name.to_s.split('_')
|
33
|
+
"#{parts[0]}_#{group}_#{parts[1]}".to_sym
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Delegate Group
|
38
|
+
def object_method(group, method)
|
39
|
+
if (group == :entity)
|
40
|
+
method.to_sym
|
41
|
+
else
|
42
|
+
"#{method}_#{group}".to_sym
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def request(*args)
|
47
|
+
Eloqua::Api.request(:service, *args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Delegate, Group, Type
|
51
|
+
def create_object(group, type, attributes)
|
52
|
+
xml_query = builder do |xml|
|
53
|
+
xml.object_collection!(group) do
|
54
|
+
xml.dynamic_object!(group) do
|
55
|
+
xml.template!(:dynamic, group, type, nil, attributes)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
result = request(object_method(group, :create), xml_query)
|
61
|
+
result = result[key_with_object(group, :create_result)]
|
62
|
+
|
63
|
+
if (result[:errors].nil? && result[:id])
|
64
|
+
{:id => result[:id].to_i}
|
65
|
+
else
|
66
|
+
handle_exception(result)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Delegate Group, Type
|
71
|
+
def update_object(group, type, entity_id, attributes)
|
72
|
+
xml_query = builder do |xml|
|
73
|
+
xml.object_collection!(group) do
|
74
|
+
xml.dynamic_object!(group) do
|
75
|
+
xml.template!(:dynamic, group, type, entity_id, attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
result = request(object_method(group, :update), xml_query)
|
81
|
+
result = result[key_with_object(group, :update_result)]
|
82
|
+
|
83
|
+
if (result[:success] && result[:id].to_s == entity_id.to_s)
|
84
|
+
true
|
85
|
+
else
|
86
|
+
handle_exception(result)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Delegate Group, Type
|
91
|
+
def delete_object(group, type, id)
|
92
|
+
xml_query = builder do |xml|
|
93
|
+
xml.object_type_lower!(group) do
|
94
|
+
xml.template!(:object_type, type)
|
95
|
+
end
|
96
|
+
xml.ids do
|
97
|
+
xml.template!(:int_array, [id])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
result = request(object_method(group, :delete), xml_query)
|
102
|
+
result = result[key_with_object(group, :delete_result)]
|
103
|
+
|
104
|
+
if (result[:success] && result[:id].to_s == id.to_s)
|
105
|
+
[result[:id]]
|
106
|
+
else
|
107
|
+
handle_exception(result)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Delegate Group, Type
|
112
|
+
def find_object(group, type, id)
|
113
|
+
xml_query = builder do |xml|
|
114
|
+
xml.object_type_lower!(group) do
|
115
|
+
xml.template!(:object_type, type)
|
116
|
+
end
|
117
|
+
xml.ids do
|
118
|
+
xml.template!(:int_array, [id])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
result = request(object_method(group, :retrieve), xml_query)
|
124
|
+
|
125
|
+
field_key = "#{group}_fields".to_sym
|
126
|
+
dynamic_key = "dynamic_#{group}".to_sym
|
127
|
+
|
128
|
+
if (result[dynamic_key] && result[dynamic_key][:field_value_collection])
|
129
|
+
attribute_list = result[dynamic_key][:field_value_collection][field_key]
|
130
|
+
attributes = {:id => result[dynamic_key][:id].to_i}
|
131
|
+
attribute_list.each do |map|
|
132
|
+
attributes[map[:internal_name].to_sym] = map[:value]
|
133
|
+
end
|
134
|
+
attributes
|
135
|
+
else
|
136
|
+
false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Delegate Type
|
141
|
+
def list_memberships(entity_type, entity_id)
|
142
|
+
xml_query = builder do |xml|
|
143
|
+
xml.template!(:object, :entity, entity_type, entity_id)
|
144
|
+
end
|
145
|
+
results = request(:list_group_membership, xml_query)
|
146
|
+
if(results.has_key?(:dynamic_asset))
|
147
|
+
results = Eloqua.format_results_for_array(results, :dynamic_asset)
|
148
|
+
results.inject([]) do |map, object|
|
149
|
+
map << object[:asset_type]
|
150
|
+
map
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def entity_asset_operation(request_method, asset_type, asset_id, entity, entity_id)
|
156
|
+
xml_query = entity_association_xml(asset_type, asset_id, entity, entity_id)
|
157
|
+
result = request(request_method.to_sym, xml_query)
|
158
|
+
if (result[:success])
|
159
|
+
true
|
160
|
+
elsif (result[:errors])
|
161
|
+
handle_exception(result)
|
162
|
+
else
|
163
|
+
false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def add_group_member(asset_type, asset_id, entity_type, entity_id)
|
168
|
+
entity_asset_operation(:add_group_member, asset_type, asset_id, entity_type, entity_id)
|
169
|
+
end
|
170
|
+
|
171
|
+
def remove_group_member(asset_type, asset_id, entity_type, entity_id)
|
172
|
+
entity_asset_operation(:remove_group_member, asset_type, asset_id, entity_type, entity_id)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Delegate Group
|
176
|
+
def list_types(group)
|
177
|
+
types = "#{group}_types".to_sym
|
178
|
+
result = request("list_#{types}".to_sym)
|
179
|
+
if (result && result[types])
|
180
|
+
result[types][:string]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Delegate Group, Type
|
185
|
+
def describe(group, type)
|
186
|
+
xml_query = builder do |xml|
|
187
|
+
xml.object_type_lower!(group) do
|
188
|
+
xml.template!(:object_type, type)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
remote_method = "describe_#{group}".to_sym
|
192
|
+
result = request(remote_method, xml_query)
|
193
|
+
if (result)
|
194
|
+
field_describe_key = "dynamic_#{group}_field_definition".to_sym
|
195
|
+
fields = result[:fields]
|
196
|
+
if (fields.is_a?(Hash) && fields.has_key?(field_describe_key))
|
197
|
+
result[:fields] = fields[field_describe_key]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
result
|
201
|
+
end
|
202
|
+
|
203
|
+
# Delegate Group, Type
|
204
|
+
def describe_type(group, type_name)
|
205
|
+
key_type = "#{group}_type".to_sym
|
206
|
+
key_types = "#{key_type}s".to_sym
|
207
|
+
|
208
|
+
if(group == :entity)
|
209
|
+
type_name_key = :global_entity_type
|
210
|
+
else
|
211
|
+
type_name_key = key_type
|
212
|
+
end
|
213
|
+
result = request("describe_#{group}_type".to_sym, type_name_key => type_name)
|
214
|
+
Eloqua.format_results_for_array(result, key_types, key_type)
|
215
|
+
end
|
216
|
+
|
217
|
+
def handle_exception(response)
|
218
|
+
exception = response[:errors][:error]
|
219
|
+
|
220
|
+
error_code = exception[:error_code]
|
221
|
+
message = exception[:message]
|
222
|
+
|
223
|
+
error_message = sprintf("Eloqua Error: Code (%s) | Message: %s", error_code, message)
|
224
|
+
|
225
|
+
if (error_code =~ /Duplicate/)
|
226
|
+
raise(Eloqua::DuplicateRecordError, error_message)
|
227
|
+
else
|
228
|
+
raise(Eloqua::RemoteError, error_message)
|
229
|
+
end
|
230
|
+
false
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|