eloqua 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.gitignore +11 -0
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +81 -0
  6. data/LICENSE +21 -0
  7. data/README.md +245 -0
  8. data/Rakefile +13 -0
  9. data/TODO.md +8 -0
  10. data/eloqua.gemspec +32 -0
  11. data/eloqua_initializer.tpl.rb +3 -0
  12. data/lib/eloqua.rb +51 -0
  13. data/lib/eloqua/api.rb +119 -0
  14. data/lib/eloqua/api/action.rb +41 -0
  15. data/lib/eloqua/api/service.rb +240 -0
  16. data/lib/eloqua/asset.rb +31 -0
  17. data/lib/eloqua/builder/templates.rb +31 -0
  18. data/lib/eloqua/builder/xml.rb +129 -0
  19. data/lib/eloqua/entity.rb +72 -0
  20. data/lib/eloqua/exceptions.rb +5 -0
  21. data/lib/eloqua/helper/attribute_map.rb +78 -0
  22. data/lib/eloqua/query.rb +291 -0
  23. data/lib/eloqua/remote_object.rb +274 -0
  24. data/lib/eloqua/version.rb +3 -0
  25. data/lib/eloqua/wsdl/action.wsdl +1 -0
  26. data/lib/eloqua/wsdl/data.wsdl +1 -0
  27. data/lib/eloqua/wsdl/email.wsdl +1 -0
  28. data/lib/eloqua/wsdl/service.wsdl +1 -0
  29. data/lib/tasks/test.rake +24 -0
  30. data/rspec.watchr +74 -0
  31. data/spec/fixtures/add_group_member/success.xml +18 -0
  32. data/spec/fixtures/create/contact_duplicate.xml +30 -0
  33. data/spec/fixtures/create/contact_success.xml +25 -0
  34. data/spec/fixtures/create_asset/failure.xml +30 -0
  35. data/spec/fixtures/create_asset/group_success.xml +25 -0
  36. data/spec/fixtures/delete_asset/access_deny.xml +31 -0
  37. data/spec/fixtures/describe_asset/success.xml +72 -0
  38. data/spec/fixtures/describe_asset_type/success.xml +23 -0
  39. data/spec/fixtures/describe_entity/success.xml +54 -0
  40. data/spec/fixtures/describe_entity_type/success.xml +45 -0
  41. data/spec/fixtures/get_member_count_in_step_by_status/success.xml +15 -0
  42. data/spec/fixtures/list_asset_types/success.xml +28 -0
  43. data/spec/fixtures/list_entity_types/success.xml +21 -0
  44. data/spec/fixtures/list_group_membership/success.xml +25 -0
  45. data/spec/fixtures/list_members_in_step_by_status/success.xml +15 -0
  46. data/spec/fixtures/query/contact_email_one.xml +38 -0
  47. data/spec/fixtures/query/contact_email_two.xml +56 -0
  48. data/spec/fixtures/query/contact_missing.xml +19 -0
  49. data/spec/fixtures/query/fault.xml +43 -0
  50. data/spec/fixtures/remove_group_member/success.xml +18 -0
  51. data/spec/fixtures/retrieve/contact_missing.xml +17 -0
  52. data/spec/fixtures/retrieve/contact_multiple.xml +3460 -0
  53. data/spec/fixtures/retrieve/contact_single.xml +38 -0
  54. data/spec/fixtures/retrieve_asset/failure.xml +17 -0
  55. data/spec/fixtures/retrieve_asset/success.xml +50 -0
  56. data/spec/fixtures/update/contact_success.xml +26 -0
  57. data/spec/lib/eloqua/api/action_spec.rb +36 -0
  58. data/spec/lib/eloqua/api/service_spec.rb +498 -0
  59. data/spec/lib/eloqua/api_spec.rb +133 -0
  60. data/spec/lib/eloqua/asset_spec.rb +63 -0
  61. data/spec/lib/eloqua/builder/templates_spec.rb +68 -0
  62. data/spec/lib/eloqua/builder/xml_spec.rb +254 -0
  63. data/spec/lib/eloqua/entity_spec.rb +224 -0
  64. data/spec/lib/eloqua/helper/attribute_map_spec.rb +14 -0
  65. data/spec/lib/eloqua/query_spec.rb +596 -0
  66. data/spec/lib/eloqua/remote_object_spec.rb +742 -0
  67. data/spec/lib/eloqua_spec.rb +171 -0
  68. data/spec/shared/attribute_map.rb +173 -0
  69. data/spec/shared/class_to_api_delegation.rb +50 -0
  70. data/spec/spec_helper.rb +48 -0
  71. data/spec/support/helper.rb +73 -0
  72. metadata +366 -0
@@ -0,0 +1,3 @@
1
+ Eloqua.configure do |config|
2
+ config.authenticate('Company\\User', 'Password')
3
+ end
@@ -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
@@ -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