eloqua 1.1.0

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.
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,171 @@
1
+ require 'spec_helper'
2
+
3
+ class EloquaSpecReceving
4
+ class << self
5
+ def one_argument(from_delg, arg)
6
+
7
+ end
8
+
9
+ def one_argument2(from_delg, arg)
10
+
11
+ end
12
+
13
+ def three_argument(from_delg1, from_delg2, arg)
14
+
15
+ end
16
+
17
+ def three_argument2(from_delg1, from_delg2, arg)
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ describe Eloqua do
25
+
26
+ context '#self.configure' do
27
+ it 'should provide self' do
28
+ save = nil
29
+ Eloqua.configure do |config|
30
+ config.should == Eloqua
31
+ end
32
+ end
33
+ end
34
+
35
+ context '#self.authenticate' do
36
+ before do
37
+ Eloqua.authenticate('user', 'pass')
38
+ end
39
+
40
+ it 'should have set username to user' do
41
+ Eloqua.user.should == 'user'
42
+ end
43
+
44
+ it 'should have set password to pass' do
45
+ Eloqua.password.should == 'pass'
46
+ end
47
+
48
+ end
49
+
50
+ context "#self.format_results_for_array" do
51
+ context "level 1 depth ending in single" do
52
+ let(:input) do
53
+ {
54
+ :one => :hit
55
+ }
56
+ end
57
+
58
+ let(:expected) { [:hit] }
59
+
60
+ it 'should return expected' do
61
+ result = subject.format_results_for_array(input, :one)
62
+ result.should == expected
63
+ end
64
+
65
+ end
66
+
67
+ context "level 3 depth ending in multiple" do
68
+
69
+ let(:input) do
70
+ {
71
+ :one => {
72
+ :two => {
73
+ :three => [:hit, :hit]
74
+ }
75
+ }
76
+ }
77
+ end
78
+
79
+ let(:expected) { [:hit, :hit] }
80
+
81
+ it 'should return expected' do
82
+ result = subject.format_results_for_array(input, :one, :two, :three)
83
+ result.should == expected
84
+ end
85
+ end
86
+
87
+ context "level 3 depth ending in single" do
88
+
89
+ let(:input) do
90
+ {
91
+ :one => {
92
+ :two => {
93
+ :three => [:hit]
94
+ }
95
+ }
96
+ }
97
+ end
98
+
99
+ let(:expected) { [:hit] }
100
+
101
+ it 'should return expected' do
102
+ result = subject.format_results_for_array(input, :one, :two, :three)
103
+ result.should == expected
104
+ end
105
+ end
106
+ end
107
+
108
+ context "#self.delegate_with_args" do
109
+
110
+ let(:receiving) do
111
+ EloquaSpecReceving
112
+ end
113
+
114
+ let(:sending) do
115
+ Class.new do
116
+ class << self
117
+ def one
118
+ 1
119
+ end
120
+
121
+ def two
122
+ 2
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ context 'with multiple delegated methods and single argument method' do
129
+ before do
130
+ Eloqua.delegate_with_args(sending, receiving, [:one_argument, :one_argument1], [:two])
131
+ end
132
+
133
+ specify { sending.should respond_to(:one_argument) }
134
+ specify { sending.should respond_to(:one_argument1) }
135
+
136
+ it 'should delegate method arguments to receiving' do
137
+ flexmock(receiving).should_receive(:one_argument).with(2, 'arg').once
138
+ sending.one_argument('arg')
139
+ end
140
+
141
+ it 'should delegate method arguments to receiving on other methods' do
142
+ flexmock(receiving).should_receive(:one_argument1).with(2, 'arg').once
143
+ sending.one_argument1('arg')
144
+ end
145
+
146
+ end
147
+
148
+ context 'with multiple delegated methods and arguments' do
149
+ before do
150
+ Eloqua.delegate_with_args(sending, receiving, [:three_argument, :three_argument1], [:one, :two])
151
+ end
152
+
153
+ specify { sending.should respond_to(:three_argument) }
154
+ specify { sending.should respond_to(:three_argument1) }
155
+
156
+ it 'should delegate method arguments to receiving' do
157
+ flexmock(receiving).should_receive(:three_argument).with(1, 2, 'arg').once
158
+ sending.three_argument('arg')
159
+ end
160
+
161
+ it 'should delegate method arguments to receiving on other methods' do
162
+ flexmock(receiving).should_receive(:three_argument1).with(1, 2, 'arg').once
163
+ sending.three_argument1('arg')
164
+ end
165
+
166
+ end
167
+
168
+
169
+ end
170
+
171
+ end
@@ -0,0 +1,173 @@
1
+ shared_examples_for "uses attribute map" do
2
+
3
+
4
+ context "#self.map_attribute" do
5
+
6
+ before do
7
+ @class = Class.new(subject) do
8
+ map :name => 'C_Name', :id => 'ContactID'
9
+ end
10
+ end
11
+
12
+ it 'should return value in attribute_map when given a key exists' do
13
+ @class.map_attribute(:name).should == :C_Name
14
+ end
15
+
16
+ it 'should return given value when key does not exist within attribute_map' do
17
+ @class.map_attribute(:Cezar).should == 'Cezar'
18
+ end
19
+
20
+ end
21
+
22
+ context "#self.attribute_map" do
23
+ specify { subject.attribute_map.class == Hash }
24
+
25
+ context "when inherited entity attribute map is cloned by not the same object" do
26
+ before do
27
+ @super = Class.new(subject)
28
+ @super.attribute_map[:id] = 'ContactID'
29
+ @child = Class.new(@super)
30
+ end
31
+
32
+ it 'should have all the same keys' do
33
+ @child.attribute_map.keys.should == @super.attribute_map.keys
34
+ end
35
+
36
+ it 'should have all the same values' do
37
+ @child.attribute_map.values.should == @super.attribute_map.values
38
+ end
39
+
40
+ it 'should not be the same object as parent' do
41
+ @child.attribute_map.object_id.should_not === @super.attribute_map.object_id
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+
49
+ context "#self.map" do
50
+
51
+ before do
52
+ @class = Class.new(Eloqua::RemoteObject) do
53
+ self.remote_type = Eloqua::Api.remote_type('Contact')
54
+ end
55
+ end
56
+
57
+ it 'should be able to use map on the class level to map attributes' do
58
+ @class.map :id => 'C_Attribute'
59
+ @class.attribute_map[:id].should == :C_Attribute
60
+ end
61
+
62
+ it 'should be able to override existing maps' do
63
+ @class.map :id => 'not_me'
64
+ @class.map :id => 'me'
65
+ @class.attribute_map[:id].should == :me
66
+ end
67
+
68
+ context 'when reverse' do
69
+ it 'should also add the reverse to attribute_map_reverse' do
70
+ @class.map :Contact => 'name'
71
+ @class.map :IDC => 'id', :Real => 'email'
72
+
73
+ reverse = {
74
+ :name => :Contact,
75
+ :id => :IDC,
76
+ :email => :Real
77
+ }.with_indifferent_access
78
+ @class.attribute_map_reverse.should == reverse
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ context "#map_attributes" do
85
+ let(:input) do
86
+ {
87
+ :C_EmailAddress => 'email@address.com',
88
+ :ContactID => '1',
89
+ :normal_id => 'wow'
90
+ }.with_indifferent_access
91
+ end
92
+
93
+ let(:expected) do
94
+ {
95
+ :email_address => 'email@address.com',
96
+ :id => '1',
97
+ :normal_id => 'wow'
98
+ }.with_indifferent_access
99
+ end
100
+
101
+ let(:reverse) do
102
+ {
103
+ :email_address => 'C_EmailAddress',
104
+ :id => 'ContactID',
105
+ :normal_id => 'normal_id'
106
+ }.with_indifferent_access
107
+ end
108
+
109
+ before do
110
+ klass_object = Class.new(subject) do
111
+ map :ContactID => 'id'
112
+ end
113
+ @klass = klass_object.new
114
+ @result = @klass.send(:map_attributes, input)
115
+ end
116
+
117
+ it 'should map attributes from CamelCase format to snake_case format' do
118
+ @result.should == expected
119
+ end
120
+
121
+ it 'should store the original key names in attribute_keys_to_eloqua' do
122
+ @klass.instance_reverse_keys.should == reverse
123
+ end
124
+
125
+ context "#reverse_map_attributes" do
126
+
127
+ context "when given valid input" do
128
+ before do
129
+ @reversed = @klass.send(:reverse_map_attributes, @result)
130
+ end
131
+
132
+ it 'should be able to reverse map_attributes back into input' do
133
+ @reversed.should == input
134
+ end
135
+ end
136
+
137
+ context "when given invalid input" do
138
+ let(:input) do
139
+ {
140
+ :id => '1',
141
+ :phone_field => 'phoney'
142
+ }
143
+ end
144
+
145
+ let(:expected) do
146
+ {
147
+ :ContactID => '1',
148
+ :phone_field => 'phoney'
149
+ }.with_indifferent_access
150
+ end
151
+
152
+ let(:klass) do
153
+ Class.new(subject) do
154
+ map :ContactID => 'id'
155
+ end
156
+ end
157
+
158
+ before do
159
+ object = klass.new
160
+ object.send(:map_attributes, {})
161
+ @result = object.send(:reverse_map_attributes, input)
162
+ end
163
+
164
+ it "should use given key when it is not known" do
165
+ @result.should == expected
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,50 @@
1
+ # This is for testing the xml syntax given to the remote objects
2
+ # Actual response tests (with fixtures) should be done in the
3
+ # Entity or Asset spec's
4
+
5
+ shared_examples_for "class level delegation of remote operations for" do |remote_group|
6
+
7
+ before do
8
+ @remote_group = remote_group
9
+ end
10
+
11
+ let(:email) { 'test@email.com' }
12
+
13
+ let(:dynamic_key) { ("dynamic_#{remote_group}".to_sym) }
14
+ let(:field_key) { "#{remote_group}_fields".to_sym }
15
+
16
+ group_delegation = Eloqua::Api::Service.group_methods
17
+ type_delegation = Eloqua::Api::Service.type_methods
18
+ group_type_delegation = Eloqua::Api::Service.group_type_methods
19
+
20
+ group_delegation.each do |method|
21
+ context "#self.#{method}" do
22
+ it "should delegate #{method} to Eloqua::Api::Service with group name" do
23
+ flexmock(Eloqua::Api::Service).should_receive(method).\
24
+ with(subject.remote_group).once
25
+ subject.send(method)
26
+ end
27
+ end
28
+ end
29
+
30
+ group_type_delegation.each do |method|
31
+ context "#self.#{method}" do
32
+ it "should delegate #{method} to Eloqua::Api::Service with group name" do
33
+ flexmock(Eloqua::Api::Service).should_receive(method).\
34
+ with(subject.remote_group, subject.remote_type).once
35
+ subject.send(method)
36
+ end
37
+ end
38
+ end
39
+
40
+ type_delegation.each do |method|
41
+ context "#self.#{method}" do
42
+ it "should delegate #{method} to Eloqua::Api::Service with group name" do
43
+ flexmock(Eloqua::Api::Service).should_receive(method).\
44
+ with(subject.remote_type).once
45
+ subject.send(method)
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require "bundler/setup"
3
+ require 'flexmock'
4
+
5
+ require 'net/http'
6
+ require 'net/https'
7
+
8
+ Bundler.require :default, :test
9
+
10
+ require 'rspec'
11
+ require 'timecop'
12
+ require 'eloqua'
13
+ require 'savon_spec'
14
+
15
+ unless defined?(ELOQUA_LIB)
16
+ ELOQUA_LIB = File.dirname(__FILE__) + '/../lib'
17
+ $: << ELOQUA_LIB
18
+ end
19
+
20
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|support| require support}
21
+ Dir[File.dirname(__FILE__) + '/shared/**/*.rb'].each {|support| require support}
22
+
23
+ Savon.configure do |config|
24
+ config.log = false
25
+ config.raise_errors = false
26
+ end
27
+
28
+ RSpec.configure do |config|
29
+
30
+ include Eloqua::RSpec::Helper
31
+ Savon::Spec::Fixture.path = File.dirname(__FILE__) + '/fixtures/'
32
+
33
+ config.mock_with :flexmock
34
+
35
+ config.before do
36
+ # This is for adding actual authentication details.
37
+ # The core tests do not actually need to login as they
38
+ # operate on fixtures but we need to get the fixture data
39
+ # in the first place and for that valid authenticate is needed
40
+ initializer = File.dirname(__FILE__) + '/../eloqua_initializer.rb'
41
+ if(File.exist?(initializer))
42
+ load initializer
43
+ else
44
+ Eloqua.authenticate('company\\user', 'pass')
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,73 @@
1
+ module Eloqua
2
+ module RSpec
3
+ module Helper
4
+
5
+ def soap_fixture(type, name, code = 200, headers = {})
6
+ body = Savon::Spec::Fixture.load(type, name)
7
+ httpi = HTTPI::Response.new(code, headers, body)
8
+ Savon::SOAP::Response.new(httpi)
9
+ end
10
+
11
+ def mock_eloqua_request(type, name, code = 200, headers = {})
12
+ mock = soap_fixture(type, name, code, headers)
13
+ flexmock(Eloqua::Api).should_receive(:send_remote_request).and_return(mock)
14
+ end
15
+
16
+ def mock_response(type, name, code = 200, headers = {})
17
+ body = Savon::Spec::Fixture.load(type, name)
18
+ mock = HTTPI::Response.new(code, headers, body)
19
+ flexmock(HTTPI).should_receive(:post).and_return(mock)
20
+ end
21
+
22
+
23
+ def mock_api_request(method = nil, xml_body = nil, result = nil)
24
+ mocked_object = subject
25
+
26
+ if(mocked_object.respond_to?(:api))
27
+ mocked_object = mocked_object.api
28
+ end
29
+
30
+ if(!xml_body && !result)
31
+ # No with expectation
32
+ result = method
33
+ flexmock(mocked_object).should_receive(:request).\
34
+ and_return(result).once
35
+ else
36
+ # with expectation
37
+ flexmock(mocked_object).should_receive(:request).\
38
+ with(method, xml_body).\
39
+ and_return(result).once
40
+ end
41
+ end
42
+
43
+ def xml!(&block)
44
+ Eloqua::Api.builder(&block)
45
+ end
46
+
47
+ def group_name
48
+ (respond_to?(:remote_group))? remote_group : group
49
+ end
50
+
51
+ def remote_type
52
+ "#{group_name}Type"
53
+ end
54
+
55
+ def dynamic_type
56
+ "Dynamic#{group_name.to_s.camelize}"
57
+ end
58
+
59
+ def tag_with_type(tag)
60
+ "#{tag}_#{group_name}".to_sym
61
+ end
62
+
63
+ def remote_method(method)
64
+ if(group_name == :entity)
65
+ method.to_sym
66
+ else
67
+ ("#{method}_#{group_name}").to_sym
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end