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.
- 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
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Eloqua::Entity do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Class.new(Eloqua::Entity) do
|
7
|
+
self.remote_type = Eloqua::Api.remote_type('Contact')
|
8
|
+
def self.name
|
9
|
+
'ContactEntity'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:asset) do
|
15
|
+
Class.new(Eloqua::Asset) do
|
16
|
+
self.remote_type = api.remote_type('0', 'ContactGroup', '0')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:object) do
|
21
|
+
subject.new({:id => 1}, :remote)
|
22
|
+
end
|
23
|
+
|
24
|
+
it_behaves_like 'class level delegation of remote operations for', :entity
|
25
|
+
|
26
|
+
context "membership methods" do
|
27
|
+
|
28
|
+
context "#add_membership" do
|
29
|
+
it 'should call remove_member on given asset' do
|
30
|
+
flexmock(asset).should_receive(:add_member).with(object).once
|
31
|
+
object.add_membership(asset)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "#remove_membership" do
|
36
|
+
it 'should call remove_member on given asset' do
|
37
|
+
flexmock(asset).should_receive(:remove_member).with(object).once
|
38
|
+
object.remove_membership(asset)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context "#list_memberships" do
|
46
|
+
|
47
|
+
it 'should delegate call to class level with current id' do
|
48
|
+
flexmock(subject).should_receive(:list_memberships).with(1).once
|
49
|
+
object.list_memberships
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
context "#self.list_memberships" do
|
55
|
+
|
56
|
+
let(:mock_memberships) do
|
57
|
+
list = []
|
58
|
+
|
59
|
+
3.times do |n|
|
60
|
+
list << {:id => n, :type => 'ContactGroup', :name => "ContactGroup#{n}"}
|
61
|
+
end
|
62
|
+
|
63
|
+
list
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
it 'should delegate call to api' do
|
68
|
+
flexmock(subject.api).should_receive(:list_memberships).with(subject.remote_type, 1).once
|
69
|
+
subject.list_memberships(1)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should index memberships by id" do
|
73
|
+
flexmock(subject.api).should_receive(:list_memberships).with(subject.remote_type, 1).once.\
|
74
|
+
and_return(mock_memberships)
|
75
|
+
|
76
|
+
memberships = subject.list_memberships(1)
|
77
|
+
memberships.should be_an(Hash)
|
78
|
+
memberships.each do |key, membership|
|
79
|
+
key.should == membership[:id]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when low level api returns nil" do
|
84
|
+
|
85
|
+
before do
|
86
|
+
flexmock(subject.api).should_receive(:list_memberships).\
|
87
|
+
with(subject.remote_type, 1).\
|
88
|
+
once.and_return(nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return an empty hash" do
|
92
|
+
subject.list_memberships(1).should == {}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
context "#self.remote_group" do
|
99
|
+
specify { subject.remote_group.should == :entity }
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
context '#self.where' do
|
104
|
+
let(:expected_query) { "C_EmailAddress='james@lightsofapollo.com'" }
|
105
|
+
let(:klass) do
|
106
|
+
Class.new(subject) do
|
107
|
+
map :C_EmailAddress => :email
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when given no arguments" do
|
112
|
+
it "should return an Eloqua::Query object" do
|
113
|
+
query = subject.where
|
114
|
+
query.should be_an(Eloqua::Query)
|
115
|
+
query.remote_object.should == subject
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when successfuly finding single result with all fields" do
|
120
|
+
|
121
|
+
let(:input) { {:email => 'james@lightsofapollo.com'} }
|
122
|
+
let(:xml_body) do
|
123
|
+
api = subject.api
|
124
|
+
xml! do |xml|
|
125
|
+
xml.eloquaType do
|
126
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
127
|
+
end
|
128
|
+
xml.searchQuery(expected_query)
|
129
|
+
xml.pageNumber(1)
|
130
|
+
xml.pageSize(200)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
before do
|
135
|
+
mock_eloqua_request(:query, :contact_email_one).\
|
136
|
+
with(:service, :query, xml_body)
|
137
|
+
|
138
|
+
@results = klass.where(input)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should return an array' do
|
142
|
+
@results.class.should == Array
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should return an array of objects' do
|
146
|
+
@results.first.class.should == klass
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should have attributes acording to XML file (query/contact_email_one.xml)' do
|
150
|
+
record = @results.first
|
151
|
+
expected = {
|
152
|
+
:id => '1',
|
153
|
+
:email => 'james@lightsofapollo.com',
|
154
|
+
:first_name => 'James'
|
155
|
+
}
|
156
|
+
record.attributes.length.should == 3
|
157
|
+
expected.each do |attr, value|
|
158
|
+
record.attributes[attr].should == value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when successfuly finding results with limited number of fields" do
|
165
|
+
let(:input) { [{:email => 'james@lightsofapollo.com'}, [:email]] }
|
166
|
+
let(:xml_body) do
|
167
|
+
api = subject.api
|
168
|
+
xml! do |xml|
|
169
|
+
xml.eloquaType do
|
170
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
171
|
+
end
|
172
|
+
xml.searchQuery(expected_query)
|
173
|
+
xml.fieldNames do
|
174
|
+
xml.template!(:array, ['C_EmailAddress'])
|
175
|
+
end
|
176
|
+
xml.pageNumber(1)
|
177
|
+
xml.pageSize(200)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
before do
|
182
|
+
mock_eloqua_request(:query, :contact_email_one).\
|
183
|
+
with(:service, :query, xml_body)
|
184
|
+
|
185
|
+
@results = klass.where(*input)
|
186
|
+
end
|
187
|
+
|
188
|
+
# HINT- This is actually asserted above in the mock_eloqua_request
|
189
|
+
it "should request that the results only return the C_EmailAddress field" do
|
190
|
+
@results.should be_true
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
context "when rows are not found" do
|
196
|
+
let(:input) { {:email => 'james@lightsofapollo.com'} }
|
197
|
+
let(:xml_body) do
|
198
|
+
api = subject.api
|
199
|
+
|
200
|
+
xml! do |xml|
|
201
|
+
xml.eloquaType do
|
202
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
203
|
+
end
|
204
|
+
xml.searchQuery("C_EmailAddress='james@lightsofapollo.com'")
|
205
|
+
xml.pageNumber(1)
|
206
|
+
xml.pageSize(200)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
before do
|
211
|
+
mock_eloqua_request(:query, :contact_missing).\
|
212
|
+
with(:service, :query, xml_body)
|
213
|
+
|
214
|
+
@results = klass.where(input)
|
215
|
+
end
|
216
|
+
|
217
|
+
specify { @results.should be_false }
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
end
|
@@ -0,0 +1,596 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eloqua/query'
|
3
|
+
|
4
|
+
|
5
|
+
shared_examples_for "chainable query attribute that resets has_requested?" do |method, given|
|
6
|
+
context "##{method}" do
|
7
|
+
|
8
|
+
it "should act like a getter when given no value" do
|
9
|
+
subject.send(method).should == subject.instance_variable_get("@#{method}".to_sym)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should act like a setter and return self when given a value" do
|
13
|
+
subject.send(method, given).should === subject
|
14
|
+
subject.send(method).should == given
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when modifying #{method} after a request" do
|
18
|
+
|
19
|
+
before do
|
20
|
+
simple_request!
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should reset has_requested to false" do
|
24
|
+
subject.should have_requested
|
25
|
+
subject.send(method, given)
|
26
|
+
subject.should_not have_requested
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe Eloqua::Query do
|
35
|
+
subject { klass.new(entity) }
|
36
|
+
let(:klass) { Eloqua::Query }
|
37
|
+
let(:expected_query) { "C_EmailAddress='*' AND Date>'2011-04-20'" }
|
38
|
+
|
39
|
+
let(:entity) do
|
40
|
+
Class.new(Eloqua::Entity) do
|
41
|
+
self.remote_type = api.remote_type('Contact')
|
42
|
+
map :C_EmailAddress => :email
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
before do
|
47
|
+
subject.request_delay = false
|
48
|
+
end
|
49
|
+
|
50
|
+
after { Timecop.return }
|
51
|
+
|
52
|
+
def mock_query_hash(records, pages)
|
53
|
+
entities = []
|
54
|
+
records.to_i.times do |i|
|
55
|
+
entities << {:field_value_collection=>
|
56
|
+
{:entity_fields=>
|
57
|
+
[{:value=>"james@lightsofapollo.com",
|
58
|
+
:internal_name=>"C_EmailAddress"},
|
59
|
+
{:value=>"James", :internal_name=>"C_FirstName"}]},
|
60
|
+
:id=>"#{i + 1}",
|
61
|
+
:entity_type=>{:type=>"Base", :name=>"Contact", :id=>"0"}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
{
|
65
|
+
:total_pages=>"#{pages}",
|
66
|
+
:total_records=>"#{records * pages}",
|
67
|
+
:entities=>{ :dynamic_entity => entities },
|
68
|
+
:i=>"http://www.w3.org/2001/XMLSchema-instance"
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def conditions!(query = nil)
|
73
|
+
query = (query.nil?)? subject : query
|
74
|
+
query.\
|
75
|
+
on(:email, '=', '*').\
|
76
|
+
on('Date', '>', '2011-04-20')
|
77
|
+
end
|
78
|
+
|
79
|
+
def limit!(query = nil, page = 1)
|
80
|
+
query = (query.nil?)? subject : query
|
81
|
+
query.page(page).limit(200)
|
82
|
+
end
|
83
|
+
|
84
|
+
def expect_simple_request(page = 1)
|
85
|
+
xml_body = xml! do |xml|
|
86
|
+
api = subject.remote_object.api
|
87
|
+
xml.eloquaType do
|
88
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
89
|
+
end
|
90
|
+
xml.searchQuery(expected_query)
|
91
|
+
xml.pageNumber(page)
|
92
|
+
xml.pageSize(200)
|
93
|
+
end
|
94
|
+
mock_eloqua_request(:query, :contact_email_one).\
|
95
|
+
with(:service, :query, xml_body).globally.ordered.once
|
96
|
+
end
|
97
|
+
|
98
|
+
def round_1(number)
|
99
|
+
sprintf('%.1f', number).to_f
|
100
|
+
end
|
101
|
+
|
102
|
+
def expect_request_pages(records, pages, current_page = nil, limit = 200)
|
103
|
+
remote_results = mock_query_hash(records, pages)
|
104
|
+
mock = mock_api_request(remote_results)
|
105
|
+
if(current_page)
|
106
|
+
xml_body = xml! do |xml|
|
107
|
+
api = subject.remote_object.api
|
108
|
+
xml.eloquaType do
|
109
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
110
|
+
end
|
111
|
+
xml.searchQuery(expected_query)
|
112
|
+
xml.pageNumber(current_page)
|
113
|
+
xml.pageSize(limit)
|
114
|
+
end
|
115
|
+
mock.with(:query, xml_body)
|
116
|
+
end
|
117
|
+
mock
|
118
|
+
end
|
119
|
+
|
120
|
+
def simple_request!(page = 1)
|
121
|
+
expect_simple_request(page)
|
122
|
+
subject.clear_conditions!
|
123
|
+
conditions!
|
124
|
+
limit!(nil, page)
|
125
|
+
subject.request!
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
context "#test.mock_query_hash" do
|
130
|
+
it "should have proper number of pages" do
|
131
|
+
mock = mock_query_hash(20, 20)
|
132
|
+
mock[:total_pages].should == "20"
|
133
|
+
mock[:total_records].should == (20 * 20).to_s
|
134
|
+
mock[:entities][:dynamic_entity].length.should == 20
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
context "#api" do
|
140
|
+
it "should delegate #api to remote object" do
|
141
|
+
flexmock(subject.remote_object).should_receive(:api).once
|
142
|
+
subject.api
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "#new" do
|
147
|
+
|
148
|
+
it 'should raise ArgumentError when given anything but an Eloqua::RemoteObject' do
|
149
|
+
lambda { klass.new({}) }.should raise_exception(ArgumentError, /must provide an Eloqua::RemoteObject /)
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when initializing with Eloqua::RemoteObject" do
|
153
|
+
it "should have saved remote subject to #remote_object" do
|
154
|
+
subject.remote_object.should == entity
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should preset the #page to 1" do
|
158
|
+
subject.page.should == 1
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should preset #limit to 200" do
|
162
|
+
subject.limit.should == 200
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should have an empty collection" do
|
166
|
+
subject.collection.should be_empty
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should have no conditions" do
|
170
|
+
subject.conditions.should be_empty
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should #fields should be nil" do
|
174
|
+
subject.fields.should be_nil
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should not have requested yet" do
|
178
|
+
subject.should_not have_requested
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
it_behaves_like "chainable query attribute that resets has_requested?", :page, 5
|
186
|
+
it_behaves_like "chainable query attribute that resets has_requested?", :limit, 5
|
187
|
+
it_behaves_like "chainable query attribute that resets has_requested?", :fields, [:email, 'Date']
|
188
|
+
|
189
|
+
context "#on" do
|
190
|
+
|
191
|
+
context "adding a single condition" do
|
192
|
+
before do
|
193
|
+
@result = subject.on(:email, '=', '*')
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should return self" do
|
197
|
+
@result.should === subject
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should have added condition" do
|
201
|
+
subject.conditions.length.should == 1
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should have added condition field, type and value" do
|
205
|
+
subject.conditions.first.should == {
|
206
|
+
:field => :email,
|
207
|
+
:type => '=',
|
208
|
+
:value => '*'
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
context "adding additional condition after request" do
|
215
|
+
it "should have not made request" do
|
216
|
+
subject.should_not have_requested
|
217
|
+
end
|
218
|
+
|
219
|
+
context "after request" do
|
220
|
+
|
221
|
+
before do
|
222
|
+
simple_request!
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should have requested" do
|
226
|
+
subject.should have_requested
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should reset have requested when adding new condition" do
|
230
|
+
subject.on(:email, '=', 'ouch')
|
231
|
+
subject.should_not have_requested
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
context "#clear_conditions!" do
|
241
|
+
it "should clear conditions added by on" do
|
242
|
+
subject.on(:email, '=', '1')
|
243
|
+
subject.conditions.length.should == 1
|
244
|
+
subject.clear_conditions!
|
245
|
+
subject.conditions.length.should == 0
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should reset #has_requested?" do
|
249
|
+
simple_request!
|
250
|
+
subject.should have_requested
|
251
|
+
subject.clear_conditions!
|
252
|
+
subject.should_not have_requested
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
context "#build_query" do
|
260
|
+
let(:entity) do
|
261
|
+
Class.new(Eloqua::Entity) do
|
262
|
+
map :C_EmailAddress => :email
|
263
|
+
remote_type = api.remote_type('Contact')
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
before do
|
268
|
+
query = klass.new(entity)
|
269
|
+
conditions!(query)
|
270
|
+
@result = query.send(:build_query)
|
271
|
+
end
|
272
|
+
|
273
|
+
specify { @result.should == expected_query }
|
274
|
+
end
|
275
|
+
|
276
|
+
context "#request" do
|
277
|
+
context "when requesting without limiting the fields and remote returns one result" do
|
278
|
+
before do
|
279
|
+
@result = simple_request!
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should return an array of objects" do
|
283
|
+
@result.should be_an(Array)
|
284
|
+
@result.first.should be_an(Eloqua::Entity)
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should now mark query as #has_requested?" do
|
288
|
+
subject.should have_requested
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should have added results to collection" do
|
292
|
+
subject.collection.length.should == 1
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should have set total pages to 1" do
|
296
|
+
subject.total_pages.should == 1
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should have attributes acording to XML file (query/contact_email_one.xml)' do
|
300
|
+
record = subject.collection.first
|
301
|
+
record.should be_an(Eloqua::Entity)
|
302
|
+
expected = {
|
303
|
+
:id => '1',
|
304
|
+
:email => 'james@lightsofapollo.com',
|
305
|
+
:first_name => 'James'
|
306
|
+
}
|
307
|
+
record.attributes.length.should == 3
|
308
|
+
expected.each do |attr, value|
|
309
|
+
record.attributes[attr].should == value
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context "when has_requested? is true" do
|
314
|
+
it "should send a remote request again" do
|
315
|
+
flexmock(subject.api).should_receive(:send_remote_request).never
|
316
|
+
subject.should have_requested
|
317
|
+
subject.request!
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
context "when successfuly finding results with limited number of fields" do
|
324
|
+
let(:xml_body) do
|
325
|
+
api = subject.api
|
326
|
+
xml! do |xml|
|
327
|
+
xml.eloquaType do
|
328
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
329
|
+
end
|
330
|
+
xml.searchQuery(expected_query)
|
331
|
+
xml.fieldNames do
|
332
|
+
xml.template!(:array, ['C_EmailAddress'])
|
333
|
+
end
|
334
|
+
xml.pageNumber(1)
|
335
|
+
xml.pageSize(200)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
before do
|
340
|
+
mock_eloqua_request(:query, :contact_email_one).\
|
341
|
+
with(:service, :query, xml_body)
|
342
|
+
|
343
|
+
conditions!
|
344
|
+
|
345
|
+
@result = subject.fields([:email]).request!
|
346
|
+
end
|
347
|
+
|
348
|
+
# HINT- This is actually asserted above in the mock_eloqua_request
|
349
|
+
it "should request that the results only return the C_EmailAddress field" do
|
350
|
+
subject.should have_requested
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
context "when rows are not found" do
|
356
|
+
let(:xml_body) do
|
357
|
+
api = subject.api
|
358
|
+
|
359
|
+
xml! do |xml|
|
360
|
+
xml.eloquaType do
|
361
|
+
xml.template!(:object_type, api.remote_type('Contact'))
|
362
|
+
end
|
363
|
+
xml.searchQuery(expected_query)
|
364
|
+
xml.pageNumber(1)
|
365
|
+
xml.pageSize(200)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
before do
|
370
|
+
mock_eloqua_request(:query, :contact_missing).\
|
371
|
+
with(:service, :query, xml_body)
|
372
|
+
|
373
|
+
conditions!
|
374
|
+
subject.request!
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should an empty collection" do
|
378
|
+
subject.collection.should be_empty
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should have requested" do
|
382
|
+
subject.should have_requested
|
383
|
+
end
|
384
|
+
|
385
|
+
it "should have total pages as 0" do
|
386
|
+
subject.total_pages.should == 0
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
|
391
|
+
# Eloqua has a request limit of 1 per second on queries.
|
392
|
+
# The goal of this spec is to specify that no requests run
|
393
|
+
# for at least a second after the first request.
|
394
|
+
context "when making a request within the time of the request delay" do
|
395
|
+
|
396
|
+
it "should save the initial request time to query_started" do
|
397
|
+
Timecop.freeze(Time.now) do
|
398
|
+
simple_request!
|
399
|
+
subject.send(:query_started).should == Time.now
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
context "with #request_delay of 1" do
|
404
|
+
before { subject.request_delay = 1 }
|
405
|
+
let(:now) { Time.now }
|
406
|
+
|
407
|
+
it "should call sleep when requesting again within the delay" do
|
408
|
+
Timecop.freeze(now) do
|
409
|
+
simple_request! # Setup initial request
|
410
|
+
|
411
|
+
flexmock(subject).should_receive(:sleep).with(FlexMock.on {|arg| round_1(arg) == 0.9 }).once
|
412
|
+
Timecop.travel(now + 0.1)
|
413
|
+
simple_request!(2)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should not call sleep when request has been made after delay" do
|
418
|
+
Timecop.freeze(now) do
|
419
|
+
simple_request!
|
420
|
+
flexmock(subject).should_receive(:sleep).never
|
421
|
+
Timecop.travel(now + 1.1)
|
422
|
+
simple_request!(2)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
context "#wait_for_request_delay" do
|
433
|
+
|
434
|
+
it "should return false when request_delay is false" do
|
435
|
+
subject.request_delay = false
|
436
|
+
simple_request! # set query start time
|
437
|
+
|
438
|
+
subject.wait_for_request_delay.should == false
|
439
|
+
end
|
440
|
+
|
441
|
+
it "should retun false when query_started is false" do
|
442
|
+
subject.request_delay = 1
|
443
|
+
subject.wait_for_request_delay.should == false
|
444
|
+
end
|
445
|
+
|
446
|
+
it "should the amount of time to wait as a fraction when a delay is required" do
|
447
|
+
subject.request_delay = 1
|
448
|
+
now = Time.now
|
449
|
+
Timecop.freeze(now) do
|
450
|
+
simple_request!
|
451
|
+
Timecop.travel(now + 0.1)
|
452
|
+
delay = round_1(subject.wait_for_request_delay)
|
453
|
+
delay.should == 0.9
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
end
|
458
|
+
|
459
|
+
context "#all" do
|
460
|
+
|
461
|
+
context "when request has not yet been made" do
|
462
|
+
|
463
|
+
before do
|
464
|
+
expect_simple_request
|
465
|
+
conditions!
|
466
|
+
limit!
|
467
|
+
|
468
|
+
@result = subject.all
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should have made request" do
|
472
|
+
subject.should have_requested
|
473
|
+
end
|
474
|
+
|
475
|
+
it "should return an array of objects" do
|
476
|
+
@result.should be_an(Array)
|
477
|
+
@result.first.should be_an(Eloqua::Entity)
|
478
|
+
end
|
479
|
+
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
|
484
|
+
context "#each" do
|
485
|
+
before do
|
486
|
+
expect_request_pages(20, 1)
|
487
|
+
conditions!
|
488
|
+
limit!
|
489
|
+
end
|
490
|
+
|
491
|
+
it "should iterator through each result and mark query as requested" do
|
492
|
+
subject.should_not have_requested
|
493
|
+
ids = []
|
494
|
+
subject.each do |record|
|
495
|
+
record.should be_an(Eloqua::Entity)
|
496
|
+
ids << record.id
|
497
|
+
end
|
498
|
+
ids.length.should == 20
|
499
|
+
ids.should == ('1'..'20').to_a
|
500
|
+
subject.should have_requested
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
|
506
|
+
context "#each_page" do
|
507
|
+
|
508
|
+
let(:total) { 10 }
|
509
|
+
let(:pages) { 5 }
|
510
|
+
let(:limit) { 2 }
|
511
|
+
let(:expected_range) { (1..5).to_a }
|
512
|
+
let(:expected_ids) { (['1', '2'] * 5) }
|
513
|
+
|
514
|
+
context "when iterating through 5 pages of results" do
|
515
|
+
|
516
|
+
before do
|
517
|
+
|
518
|
+
# - Clarity over brief
|
519
|
+
expect_request_pages(limit, pages, 1, limit)
|
520
|
+
expect_request_pages(limit, pages, 2, limit)
|
521
|
+
expect_request_pages(limit, pages, 3, limit)
|
522
|
+
expect_request_pages(limit, pages, 4, limit)
|
523
|
+
expect_request_pages(limit, pages, 5, limit)
|
524
|
+
|
525
|
+
@ids = []
|
526
|
+
@pages = []
|
527
|
+
|
528
|
+
conditions!
|
529
|
+
subject.limit(limit)
|
530
|
+
|
531
|
+
last_page = 0
|
532
|
+
subject.each_page do |record|
|
533
|
+
@ids << record.id
|
534
|
+
|
535
|
+
if(last_page != subject.page)
|
536
|
+
@pages << subject.page
|
537
|
+
last_page = subject.page
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
it "should have iterated through 5 pages" do
|
543
|
+
@pages.should == expected_range
|
544
|
+
end
|
545
|
+
|
546
|
+
it "should have iterated through all records in each page" do
|
547
|
+
@ids.length.should == total
|
548
|
+
@ids.should == expected_ids
|
549
|
+
end
|
550
|
+
|
551
|
+
end
|
552
|
+
|
553
|
+
context "when iterating through 5 out of 10 pages" do
|
554
|
+
|
555
|
+
let(:pages) { 10 }
|
556
|
+
|
557
|
+
before do
|
558
|
+
# - Clarity over brief
|
559
|
+
expect_request_pages(limit, pages, 1, limit)
|
560
|
+
expect_request_pages(limit, pages, 2, limit)
|
561
|
+
expect_request_pages(limit, pages, 3, limit)
|
562
|
+
expect_request_pages(limit, pages, 4, limit)
|
563
|
+
expect_request_pages(limit, pages, 5, limit)
|
564
|
+
|
565
|
+
@ids = []
|
566
|
+
@pages = []
|
567
|
+
|
568
|
+
conditions!
|
569
|
+
subject.limit(limit)
|
570
|
+
|
571
|
+
last_page = 0
|
572
|
+
# Max of 5 pages
|
573
|
+
subject.each_page(5) do |record|
|
574
|
+
@ids << record.id
|
575
|
+
|
576
|
+
if(last_page != subject.page)
|
577
|
+
@pages << subject.page
|
578
|
+
last_page = subject.page
|
579
|
+
end
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
it "should have iterated through 5 pages" do
|
584
|
+
@pages.should == expected_range
|
585
|
+
end
|
586
|
+
|
587
|
+
it "should have iterated through all records in each page" do
|
588
|
+
@ids.length.should == total
|
589
|
+
@ids.should == expected_ids
|
590
|
+
end
|
591
|
+
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|