ruby_odata 0.1.0 → 0.1.6
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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +7 -2
- data/.simplecov +10 -0
- data/.travis.yml +10 -4
- data/.yardopts +6 -0
- data/CHANGELOG.md +157 -0
- data/Guardfile +14 -0
- data/LICENSE +20 -22
- data/README.md +289 -0
- data/Rakefile +19 -9
- data/features/basic_auth.feature +3 -2
- data/features/batch_request.feature +7 -6
- data/features/cassettes/basic_auth_protected_resource.yml +57 -0
- data/features/cassettes/batch_request_additions.yml +69 -0
- data/features/cassettes/batch_request_deletes.yml +69 -0
- data/features/cassettes/batch_request_updates.yml +69 -0
- data/features/cassettes/clean_database_for_testing.yml +46 -0
- data/features/cassettes/cucumber_tags/basic_auth.yml +297 -0
- data/features/cassettes/cucumber_tags/batch_request.yml +1459 -0
- data/features/cassettes/cucumber_tags/complex_types.yml +326 -0
- data/features/cassettes/cucumber_tags/error_handling.yml +64 -0
- data/features/cassettes/cucumber_tags/query_builder.yml +2025 -0
- data/features/cassettes/cucumber_tags/service.yml +234 -0
- data/features/cassettes/cucumber_tags/service_manage.yml +937 -0
- data/features/cassettes/cucumber_tags/service_methods.yml +647 -0
- data/features/cassettes/cucumber_tags/ssl.yml +203 -0
- data/features/cassettes/cucumber_tags/type_conversion.yml +337 -0
- data/features/cassettes/service_manage_additions.yml +65 -0
- data/features/cassettes/service_manage_deletions.yml +58 -0
- data/features/cassettes/service_manage_deletions_2.yml +58 -0
- data/features/cassettes/unsecured_metadata.yml +89 -0
- data/features/complex_types.feature +4 -3
- data/features/error_handling.feature +13 -0
- data/features/query_builder.feature +30 -9
- data/features/service.feature +4 -3
- data/features/service_manage.feature +6 -5
- data/features/service_methods.feature +3 -2
- data/features/ssl.feature +8 -8
- data/features/step_definitions/service_steps.rb +32 -74
- data/features/support/env.rb +6 -3
- data/features/support/hooks.rb +3 -2
- data/features/support/pickle.rb +29 -18
- data/features/support/vcr.rb +24 -0
- data/features/type_conversion.feature +16 -17
- data/gemfiles/Gemfile.ruby187 +6 -0
- data/lib/ruby_odata.rb +20 -18
- data/lib/ruby_odata/association.rb +7 -6
- data/lib/ruby_odata/class_builder.rb +31 -14
- data/lib/ruby_odata/exceptions.rb +11 -0
- data/lib/ruby_odata/helpers.rb +17 -0
- data/lib/ruby_odata/operation.rb +5 -6
- data/lib/ruby_odata/property_metadata.rb +9 -7
- data/lib/ruby_odata/query_builder.rb +127 -63
- data/lib/ruby_odata/service.rb +265 -147
- data/lib/ruby_odata/version.rb +3 -1
- data/ruby_odata.gemspec +22 -13
- data/spec/fixtures/error_without_message.xml +5 -0
- data/spec/fixtures/int64_ids/edmx_boat_service.xml +19 -0
- data/spec/fixtures/int64_ids/edmx_car_service.xml +21 -0
- data/spec/fixtures/int64_ids/result_boats.xml +26 -0
- data/spec/fixtures/int64_ids/result_cars.xml +28 -0
- data/spec/fixtures/ms_system_center/edmx_ms_system_center.xml +1645 -0
- data/spec/fixtures/ms_system_center/edmx_ms_system_center_v2.xml +2120 -0
- data/spec/fixtures/ms_system_center/hardware_profiles.xml +61 -0
- data/spec/fixtures/ms_system_center/virtual_machines.xml +175 -0
- data/spec/fixtures/ms_system_center/vm_templates.xml +1193 -0
- data/spec/fixtures/nested_expands/edmx_northwind.xml +557 -0
- data/spec/fixtures/nested_expands/northwind_products_category_expands.xml +774 -0
- data/spec/fixtures/sample_service/result_select_categories_expand.xml +268 -0
- data/spec/fixtures/sample_service/result_select_categories_no_property.xml +6 -0
- data/spec/fixtures/sample_service/result_select_categories_travsing_no_expand.xml +6 -0
- data/spec/fixtures/sample_service/result_select_products_name_price.xml +92 -0
- data/spec/property_metadata_spec.rb +10 -10
- data/spec/query_builder_spec.rb +153 -14
- data/spec/revised_service_spec.rb +111 -6
- data/spec/service_spec.rb +389 -85
- data/spec/spec_helper.rb +3 -0
- data/spec/support/sample_service_matcher.rb +15 -0
- data/test/RubyODataService/RubyODataService/App_Data/.gitkeep +0 -0
- data/test/blueprints.rb +15 -9
- data/test/usage_samples/querying.rb +5 -1
- data/test/usage_samples/sample_data.rb +1 -3
- metadata +276 -76
- data/CHANGELOG.rdoc +0 -88
- data/README.rdoc +0 -259
@@ -10,7 +10,7 @@ module OData
|
|
10
10
|
@cat_prod_service = OData::Service.new "http://test.com/test.svc"
|
11
11
|
end
|
12
12
|
subject { @cat_prod_service }
|
13
|
-
|
13
|
+
|
14
14
|
context "methods" do
|
15
15
|
it { should respond_to :update_object }
|
16
16
|
it { should respond_to :delete_object }
|
@@ -25,7 +25,7 @@ module OData
|
|
25
25
|
it { should respond_to :collections }
|
26
26
|
it { should respond_to :options }
|
27
27
|
it { should respond_to :function_imports }
|
28
|
-
|
28
|
+
|
29
29
|
context "after parsing metadata" do
|
30
30
|
it { should respond_to :Products }
|
31
31
|
it { should respond_to :Categories }
|
@@ -43,7 +43,7 @@ module OData
|
|
43
43
|
end
|
44
44
|
it "should expose the local model type" do
|
45
45
|
subject['Products'][:type].should eq Product
|
46
|
-
subject['Categories'][:type].should eq Category
|
46
|
+
subject['Categories'][:type].should eq Category
|
47
47
|
end
|
48
48
|
end
|
49
49
|
context "class metadata" do
|
@@ -51,6 +51,7 @@ module OData
|
|
51
51
|
it { should_not be_empty}
|
52
52
|
it { should have_key 'Product' }
|
53
53
|
it { should have_key 'Category' }
|
54
|
+
|
54
55
|
context "should have keys for each property" do
|
55
56
|
subject { @cat_prod_service.class_metadata['Category'] }
|
56
57
|
it { should have_key 'Id' }
|
@@ -69,6 +70,7 @@ module OData
|
|
69
70
|
meta.fc_target_path.should be_nil
|
70
71
|
meta.fc_keep_in_content.should be_nil
|
71
72
|
meta.nav_prop.should eq false
|
73
|
+
meta.is_key.should eq true
|
72
74
|
end
|
73
75
|
it "should have correct PropertyMetadata for Category.Name" do
|
74
76
|
meta = subject['Name']
|
@@ -78,6 +80,7 @@ module OData
|
|
78
80
|
meta.fc_target_path.should be_nil
|
79
81
|
meta.fc_keep_in_content.should be_nil
|
80
82
|
meta.nav_prop.should eq false
|
83
|
+
meta.is_key.should eq false
|
81
84
|
end
|
82
85
|
it "should have correct PropertyMetadata for Category.Products" do
|
83
86
|
meta = subject['Products']
|
@@ -88,6 +91,7 @@ module OData
|
|
88
91
|
meta.fc_keep_in_content.should be_nil
|
89
92
|
meta.nav_prop.should eq true
|
90
93
|
meta.association.should_not be_nil
|
94
|
+
meta.is_key.should eq false
|
91
95
|
end
|
92
96
|
end
|
93
97
|
end
|
@@ -115,7 +119,7 @@ module OData
|
|
115
119
|
subject['EntityCategoryWebGet'][:parameters].should be_nil
|
116
120
|
subject['EntitySingleCategoryWebGet'][:parameters].should be_a Hash
|
117
121
|
subject['EntitySingleCategoryWebGet'][:parameters]['id'].should eq 'Edm.Int32'
|
118
|
-
end
|
122
|
+
end
|
119
123
|
context "after parsing function imports" do
|
120
124
|
subject { @cat_prod_service }
|
121
125
|
it { should respond_to :CleanDatabaseForTesting }
|
@@ -152,10 +156,10 @@ module OData
|
|
152
156
|
subject { @cat_prod_service }
|
153
157
|
before(:each) do
|
154
158
|
stub_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").to_return(:status => 204)
|
155
|
-
|
159
|
+
|
156
160
|
stub_request(:get, "http://test.com/test.svc/EntityCategoryWebGet").
|
157
161
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_category_web_get.xml", __FILE__)), :headers => {})
|
158
|
-
|
162
|
+
|
159
163
|
stub_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1").
|
160
164
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_single_category_web_get.xml", __FILE__)), :headers => {})
|
161
165
|
|
@@ -194,4 +198,105 @@ module OData
|
|
194
198
|
end
|
195
199
|
end
|
196
200
|
end
|
201
|
+
|
202
|
+
describe "Keys" do
|
203
|
+
describe "Collection with an int64 key Named 'id'" do
|
204
|
+
|
205
|
+
before(:all) do
|
206
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
207
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
208
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {})
|
209
|
+
@svc = OData::Service.new "http://test.com/test.svc/"
|
210
|
+
end
|
211
|
+
|
212
|
+
context "has the correct metadata" do
|
213
|
+
before(:all) do
|
214
|
+
@id_meta = @svc.class_metadata['Car']['id']
|
215
|
+
end
|
216
|
+
|
217
|
+
subject { @id_meta }
|
218
|
+
|
219
|
+
its(:name) { should eq('id') }
|
220
|
+
its(:type) { should eq('Edm.Int64') }
|
221
|
+
its(:nullable) { should be_false }
|
222
|
+
its(:fc_target_path) { should be_nil }
|
223
|
+
its(:fc_keep_in_content) { should be_nil }
|
224
|
+
end
|
225
|
+
|
226
|
+
context "can parse Id correctly" do
|
227
|
+
before(:each) do
|
228
|
+
stub_request(:get, "http://test.com/test.svc/Cars(213L)").
|
229
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
230
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_cars.xml", __FILE__)), :headers => {})
|
231
|
+
|
232
|
+
@svc.Cars(213)
|
233
|
+
results = @svc.execute
|
234
|
+
@car = results.first
|
235
|
+
end
|
236
|
+
|
237
|
+
subject { @car }
|
238
|
+
|
239
|
+
its(:id) { should eq(213) }
|
240
|
+
its(:color) { should eq('peach') }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "Collection with an int64 key named 'KeyId'" do
|
245
|
+
|
246
|
+
before(:all) do
|
247
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
248
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
249
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_boat_service.xml", __FILE__)), :headers => {})
|
250
|
+
@svc = OData::Service.new "http://test.com/test.svc/"
|
251
|
+
end
|
252
|
+
|
253
|
+
context "has the correct metadata" do
|
254
|
+
before(:all) do
|
255
|
+
@id_meta = @svc.class_metadata['Boat']['KeyId']
|
256
|
+
end
|
257
|
+
|
258
|
+
subject { @id_meta }
|
259
|
+
|
260
|
+
its(:name) { should eq('KeyId') }
|
261
|
+
its(:type) { should eq('Edm.Int64') }
|
262
|
+
its(:nullable) { should be_false }
|
263
|
+
its(:fc_target_path) { should be_nil }
|
264
|
+
its(:fc_keep_in_content) { should be_nil }
|
265
|
+
end
|
266
|
+
|
267
|
+
context "can parse Id correctly" do
|
268
|
+
before(:each) do
|
269
|
+
stub_request(:get, "http://test.com/test.svc/Boats(213L)").
|
270
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
271
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_boats.xml", __FILE__)), :headers => {})
|
272
|
+
|
273
|
+
@svc.Boats(213)
|
274
|
+
results = @svc.execute
|
275
|
+
@boat = results.first
|
276
|
+
end
|
277
|
+
|
278
|
+
subject { @boat }
|
279
|
+
|
280
|
+
its(:id) { should eq(213) }
|
281
|
+
its(:color) { should eq('blue') }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "Dual Namespaces" do
|
287
|
+
before(:all) do
|
288
|
+
stub_request(:get, "http://xxxx%5Cyyyy:zzzz@test.com/test.svc/$metadata").
|
289
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
290
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {})
|
291
|
+
end
|
292
|
+
after(:all) do
|
293
|
+
VMM.constants.each do |constant|
|
294
|
+
VMM.send :remove_const, constant
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should parse the service without errors" do
|
299
|
+
lambda { OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error
|
300
|
+
end
|
301
|
+
end
|
197
302
|
end
|
data/spec/service_spec.rb
CHANGED
@@ -3,12 +3,12 @@ require 'spec_helper'
|
|
3
3
|
module OData
|
4
4
|
describe Service do
|
5
5
|
describe "#initialize" do
|
6
|
-
it "truncates passed in end slash from uri when making the request" do
|
6
|
+
it "truncates passed in end slash from uri when making the request" do
|
7
7
|
# Required for the build_classes method
|
8
8
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
9
9
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
10
10
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {})
|
11
|
-
|
11
|
+
|
12
12
|
svc = OData::Service.new "http://test.com/test.svc/"
|
13
13
|
end
|
14
14
|
it "doesn't error with lowercase entities" do
|
@@ -16,10 +16,10 @@ module OData
|
|
16
16
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
17
17
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
18
18
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {})
|
19
|
-
|
19
|
+
|
20
20
|
lambda { OData::Service.new "http://test.com/test.svc" }.should_not raise_error
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
describe "additional query string parameters" do
|
24
24
|
before(:each) do
|
25
25
|
# Required for the build_classes method
|
@@ -36,6 +36,23 @@ module OData
|
|
36
36
|
a_request(:get, "http://test.com/test.svc/$metadata?x=1&y=2").should have_been_made
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
describe "rest-client options" do
|
41
|
+
before(:each) do
|
42
|
+
# Required for the build_classes method
|
43
|
+
stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
|
44
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
45
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {})
|
46
|
+
end
|
47
|
+
it "should accept in options that will be passed to the rest-client lib" do
|
48
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } }
|
49
|
+
svc.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"]
|
50
|
+
end
|
51
|
+
it "should merge the rest options with the built in options" do
|
52
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } }
|
53
|
+
svc.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"]
|
54
|
+
end
|
55
|
+
end
|
39
56
|
end
|
40
57
|
describe "additional query string parameters" do
|
41
58
|
before(:each) do
|
@@ -52,7 +69,7 @@ module OData
|
|
52
69
|
end
|
53
70
|
it "should pass the parameters as part of a save" do
|
54
71
|
svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
|
55
|
-
new_flight = ZDemoFlight.new
|
72
|
+
new_flight = ZDemoFlight.new
|
56
73
|
svc.AddToflight_dataCollection(new_flight)
|
57
74
|
svc.save_changes
|
58
75
|
a_request(:post, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made
|
@@ -75,9 +92,9 @@ module OData
|
|
75
92
|
end
|
76
93
|
it "should pass the parameters as part of a batch save" do
|
77
94
|
svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
|
78
|
-
new_flight = ZDemoFlight.new
|
95
|
+
new_flight = ZDemoFlight.new
|
79
96
|
svc.AddToflight_dataCollection(new_flight)
|
80
|
-
new_flight2 = ZDemoFlight.new
|
97
|
+
new_flight2 = ZDemoFlight.new
|
81
98
|
svc.AddToflight_dataCollection(new_flight2)
|
82
99
|
svc.save_changes
|
83
100
|
a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made
|
@@ -103,7 +120,28 @@ module OData
|
|
103
120
|
a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made
|
104
121
|
end
|
105
122
|
end
|
106
|
-
|
123
|
+
|
124
|
+
describe "exception handling" do
|
125
|
+
before(:each) do
|
126
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
127
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
128
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
129
|
+
|
130
|
+
stub_request(:get, "http://test.com/test.svc/Categories?$select=Price").
|
131
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
132
|
+
to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/error_without_message.xml", __FILE__)), :headers => {})
|
133
|
+
end
|
134
|
+
|
135
|
+
it "includes a generic message if the error is not in the response" do
|
136
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
137
|
+
svc.Categories.select "Price"
|
138
|
+
expect { svc.execute }.to raise_error(OData::ServiceError) { |error|
|
139
|
+
error.http_code.should eq 400
|
140
|
+
error.message.should eq "Server returned error but no message."
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
107
145
|
describe "lowercase collections" do
|
108
146
|
before(:each) do
|
109
147
|
# Required for the build_classes method
|
@@ -111,12 +149,12 @@ module OData
|
|
111
149
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
112
150
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {})
|
113
151
|
end
|
114
|
-
|
152
|
+
|
115
153
|
it "should respond_to a lowercase collection" do
|
116
154
|
svc = OData::Service.new "http://test.com/test.svc"
|
117
155
|
svc.respond_to?('acronyms').should be_true
|
118
156
|
end
|
119
|
-
|
157
|
+
|
120
158
|
it "should allow a lowercase collections to be queried" do
|
121
159
|
svc = OData::Service.new "http://test.com/test.svc"
|
122
160
|
lambda { svc.send('acronyms') }.should_not raise_error
|
@@ -142,19 +180,19 @@ module OData
|
|
142
180
|
results.first.should be_a_kind_of(ZDemoFlight)
|
143
181
|
end
|
144
182
|
end
|
145
|
-
|
183
|
+
|
146
184
|
describe "collections, objects, metadata etc" do
|
147
185
|
before(:each) do
|
148
186
|
# Metadata
|
149
187
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
150
188
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
151
189
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/edmx_feed_customization.xml", __FILE__)), :headers => {})
|
152
|
-
|
190
|
+
|
153
191
|
# Content - Products
|
154
192
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products(?:.*)/).
|
155
193
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
156
194
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_products_expand.xml", __FILE__)), :headers => {})
|
157
|
-
|
195
|
+
|
158
196
|
# Content - Categories expanded Products
|
159
197
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Categories(?:.*)/).
|
160
198
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
@@ -208,9 +246,9 @@ module OData
|
|
208
246
|
meta.nullable.should eq true
|
209
247
|
meta.fc_target_path.should eq "SyndicationSummary"
|
210
248
|
meta.fc_keep_in_content.should eq false
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
214
252
|
describe "single class" do
|
215
253
|
it "should handle properties where a property is represented in the syndication title instead of the properties collection" do
|
216
254
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -224,46 +262,46 @@ module OData
|
|
224
262
|
results = svc.execute
|
225
263
|
results.first.Description.should eq "Whole grain bread"
|
226
264
|
end
|
227
|
-
end
|
228
|
-
|
265
|
+
end
|
266
|
+
|
229
267
|
describe "expanded inline class" do
|
230
268
|
it "should handle properties where a property is represented in the syndication title instead of the properties collection" do
|
231
269
|
svc = OData::Service.new "http://test.com/test.svc/"
|
232
270
|
svc.Categories
|
233
271
|
results = svc.execute
|
234
|
-
|
272
|
+
|
235
273
|
beverages = results[1]
|
236
|
-
|
274
|
+
|
237
275
|
milk = beverages.Products.first
|
238
276
|
milk.Name.should eq "Milk"
|
239
277
|
milk.Description.should eq "Low fat milk"
|
240
|
-
|
278
|
+
|
241
279
|
lemonade = beverages.Products.last
|
242
280
|
lemonade.Name.should eq "Pink Lemonade"
|
243
281
|
lemonade.Description.should eq "36 Ounce Cans (Pack of 3)"
|
244
282
|
end
|
245
283
|
end
|
246
284
|
end
|
247
|
-
|
285
|
+
|
248
286
|
describe "handling inline collections/properties" do
|
249
287
|
it "should make plural named properties arrays and not a single class" do
|
250
288
|
svc = OData::Service.new "http://test.com/test.svc/"
|
251
289
|
svc.Categories
|
252
290
|
results = svc.execute
|
253
291
|
food = results[0]
|
254
|
-
|
292
|
+
|
255
293
|
food.Products.should be_an Array
|
256
294
|
end
|
257
|
-
|
295
|
+
|
258
296
|
it "should not make an array if the navigation property name is singular" do
|
259
297
|
svc = OData::Service.new "http://test.com/test.svc/"
|
260
298
|
svc.Products
|
261
299
|
results = svc.execute
|
262
300
|
product = results.first
|
263
301
|
product.Category.should_not be_an Array
|
264
|
-
end
|
302
|
+
end
|
265
303
|
end
|
266
|
-
|
304
|
+
|
267
305
|
describe "navigation properties" do
|
268
306
|
it "should fill in PropertyMetadata for navigation properties" do
|
269
307
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -271,27 +309,27 @@ module OData
|
|
271
309
|
end
|
272
310
|
end
|
273
311
|
end
|
274
|
-
|
312
|
+
|
275
313
|
describe "single layer inheritance" do
|
276
314
|
before(:each) do
|
277
315
|
# Metadata
|
278
316
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
279
317
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
280
318
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {})
|
281
|
-
|
319
|
+
|
282
320
|
# Content - Courses
|
283
321
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/).
|
284
322
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
285
323
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {})
|
286
324
|
end
|
287
|
-
|
325
|
+
|
288
326
|
it "should build all inherited attributes" do
|
289
327
|
OData::Service.new "http://test.com/test.svc/"
|
290
328
|
methods = Course.instance_methods.reject {|m| Object.methods.index(m)}
|
291
|
-
|
329
|
+
|
292
330
|
# Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data
|
293
331
|
methods.map! {|m| m.to_sym}
|
294
|
-
|
332
|
+
|
295
333
|
methods.should include(:Title)
|
296
334
|
methods.should include(:Description)
|
297
335
|
methods.should include(:VideoLength)
|
@@ -319,7 +357,7 @@ module OData
|
|
319
357
|
course.Category.should_not be_nil
|
320
358
|
end
|
321
359
|
end
|
322
|
-
|
360
|
+
|
323
361
|
describe "handling partial collections" do
|
324
362
|
before(:each) do
|
325
363
|
# Metadata
|
@@ -360,15 +398,40 @@ module OData
|
|
360
398
|
end
|
361
399
|
results.count.should eq 3
|
362
400
|
end
|
401
|
+
|
402
|
+
context "with additional_params" do
|
403
|
+
before(:each) do
|
404
|
+
stub_request(:get, "http://test.com/test.svc/$metadata?extra_param=value").
|
405
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
406
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {})
|
407
|
+
|
408
|
+
stub_request(:get, "http://test.com/test.svc/Partials?extra_param=value").
|
409
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
410
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {})
|
411
|
+
|
412
|
+
stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value").
|
413
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
414
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {})
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should persist the additional parameters for the next call" do
|
418
|
+
svc = OData::Service.new("http://test.com/test.svc/", :eager_partial => false, :additional_params => { :extra_param => 'value' })
|
419
|
+
svc.Partials
|
420
|
+
svc.execute
|
421
|
+
svc.next
|
422
|
+
|
423
|
+
a_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value").should have_been_made
|
424
|
+
end
|
425
|
+
end
|
363
426
|
end
|
364
|
-
|
427
|
+
|
365
428
|
describe "link queries" do
|
366
429
|
before(:each) do
|
367
430
|
# Required for the build_classes method
|
368
431
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
|
369
432
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
370
433
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
371
|
-
|
434
|
+
|
372
435
|
stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products").
|
373
436
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
374
437
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {})
|
@@ -381,72 +444,72 @@ module OData
|
|
381
444
|
results.first.should be_a_kind_of(URI)
|
382
445
|
results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)"
|
383
446
|
results[1].path.should eq "/SampleService/RubyOData.svc/Products(2)"
|
384
|
-
results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)"
|
447
|
+
results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)"
|
385
448
|
end
|
386
449
|
end
|
387
|
-
|
450
|
+
|
388
451
|
describe "sample service" do
|
389
452
|
before(:each) do
|
390
453
|
# Required for the build_classes method
|
391
454
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
|
392
455
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
393
456
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
394
|
-
|
457
|
+
|
395
458
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/).
|
396
459
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
397
460
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product.xml", __FILE__)), :headers => {})
|
398
|
-
|
461
|
+
|
399
462
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/).
|
400
463
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
401
464
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product_not_found.xml", __FILE__)), :headers => {})
|
402
|
-
|
465
|
+
|
403
466
|
stub_request(:get, "http://test.com/test.svc/Products(1)/Category").
|
404
467
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
405
468
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
|
406
|
-
|
469
|
+
|
407
470
|
stub_request(:get, "http://test.com/test.svc/Categories(1)").
|
408
471
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
409
472
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
|
410
|
-
|
473
|
+
|
411
474
|
stub_request(:get, "http://test.com/test.svc/Categories(1)/Products").
|
412
475
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
413
476
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_multiple_category_products.xml", __FILE__)), :headers => {})
|
414
|
-
|
477
|
+
|
415
478
|
stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204)
|
416
479
|
stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202)
|
417
480
|
end
|
418
|
-
|
481
|
+
|
419
482
|
describe "lazy loading" do
|
420
483
|
after(:each) do
|
421
484
|
Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
|
422
485
|
Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
|
423
486
|
end
|
424
|
-
|
487
|
+
|
425
488
|
it "should have a load property method" do
|
426
489
|
svc = OData::Service.new "http://test.com/test.svc/"
|
427
490
|
svc.should respond_to(:load_property)
|
428
491
|
end
|
429
|
-
|
492
|
+
|
430
493
|
it "should throw an exception if the object isn't tracked" do
|
431
494
|
svc = OData::Service.new "http://test.com/test.svc/"
|
432
495
|
new_object = Product.new
|
433
|
-
lambda { svc.load_property(new_object, "Category") }.should raise_error(
|
496
|
+
lambda { svc.load_property(new_object, "Category") }.should raise_error(NotSupportedError, "You cannot load a property on an entity that isn't tracked")
|
434
497
|
end
|
435
|
-
|
498
|
+
|
436
499
|
it "should throw an exception if there isn't a method matching the navigation property passed in" do
|
437
500
|
svc = OData::Service.new "http://test.com/test.svc/"
|
438
501
|
svc.Products(1)
|
439
502
|
product = svc.execute.first
|
440
|
-
lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property")
|
503
|
+
lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property")
|
441
504
|
end
|
442
|
-
|
505
|
+
|
443
506
|
it "should throw an exception if the method passed in is a standard property (non-navigation)" do
|
444
507
|
svc = OData::Service.new "http://test.com/test.svc/"
|
445
508
|
svc.Products(1)
|
446
509
|
product = svc.execute.first
|
447
|
-
lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property")
|
510
|
+
lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property")
|
448
511
|
end
|
449
|
-
|
512
|
+
|
450
513
|
it "should fill a single navigation property" do
|
451
514
|
svc = OData::Service.new "http://test.com/test.svc/"
|
452
515
|
svc.Products(1)
|
@@ -456,7 +519,7 @@ module OData
|
|
456
519
|
product.Category.Id.should eq 1
|
457
520
|
product.Category.Name.should eq 'Category 1'
|
458
521
|
end
|
459
|
-
|
522
|
+
|
460
523
|
it "should fill a collection navigation property" do
|
461
524
|
svc = OData::Service.new "http://test.com/test.svc/"
|
462
525
|
svc.Categories(1)
|
@@ -466,59 +529,68 @@ module OData
|
|
466
529
|
category.Products[0].Id.should eq 1
|
467
530
|
category.Products[1].Id.should eq 2
|
468
531
|
end
|
532
|
+
|
533
|
+
it "should not mutate the object's metadata" do
|
534
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
535
|
+
svc.Products(1)
|
536
|
+
product = svc.execute.first
|
537
|
+
original_metadata = product.__metadata.to_json
|
538
|
+
svc.load_property(product, "Category")
|
539
|
+
product.__metadata.to_json.should == original_metadata
|
540
|
+
end
|
469
541
|
end
|
470
|
-
|
542
|
+
|
471
543
|
describe "find, create, add, update, and delete" do
|
472
544
|
after(:each) do
|
473
545
|
Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
|
474
546
|
Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
|
475
547
|
end
|
476
|
-
|
548
|
+
|
477
549
|
it "should implement an AddTo method for collection" do
|
478
550
|
svc = OData::Service.new "http://test.com/test.svc/"
|
479
551
|
svc.should respond_to :AddToCategories
|
480
552
|
svc.should respond_to :AddToProducts
|
481
553
|
end
|
482
|
-
|
554
|
+
|
483
555
|
it "should create objects with an initialize method that can build the object from a hash" do
|
484
556
|
svc = OData::Service.new "http://test.com/test.svc/"
|
485
557
|
product = Product.new 'Id' => 1000, 'Name' => 'New Product'
|
486
558
|
product.Id.should eq 1000
|
487
559
|
product.Name.should eq 'New Product'
|
488
560
|
end
|
489
|
-
|
561
|
+
|
490
562
|
it "should create objects that rejects keys that don't have corresponding methods" do
|
491
563
|
svc = OData::Service.new "http://test.com/test.svc/"
|
492
564
|
lambda { Product.new 'NotAProperty' => true }.should raise_error NoMethodError
|
493
565
|
end
|
494
|
-
|
566
|
+
|
495
567
|
it "should create objects that expose a properties class method that lists the properties for the object" do
|
496
568
|
svc = OData::Service.new "http://test.com/test.svc/"
|
497
569
|
Product.properties.should include 'Id'
|
498
570
|
Product.properties.should include 'Name'
|
499
|
-
Product.properties.should include 'Category'
|
571
|
+
Product.properties.should include 'Category'
|
500
572
|
end
|
501
|
-
|
573
|
+
|
502
574
|
it "should have full metadata for a property returned from the properties method" do
|
503
575
|
svc = OData::Service.new "http://test.com/test.svc/"
|
504
576
|
Product.properties['Category'].should be_a PropertyMetadata
|
505
577
|
Product.properties['Category'].nav_prop.should be_true
|
506
578
|
end
|
507
|
-
|
579
|
+
|
508
580
|
it "should create objects that expose an id property" do
|
509
581
|
svc = OData::Service.new "http://test.com/test.svc/"
|
510
582
|
svc.Products(1)
|
511
583
|
product = svc.execute.first
|
512
584
|
product.should respond_to :id
|
513
585
|
end
|
514
|
-
|
586
|
+
|
515
587
|
it "should extract the id from the metadata" do
|
516
588
|
svc = OData::Service.new "http://test.com/test.svc/"
|
517
589
|
svc.Products(1)
|
518
590
|
product = svc.execute.first
|
519
591
|
product.id.should eq 1
|
520
592
|
end
|
521
|
-
|
593
|
+
|
522
594
|
describe "Class.first method" do
|
523
595
|
it "should exist on the create server objects" do
|
524
596
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -528,7 +600,7 @@ module OData
|
|
528
600
|
svc = OData::Service.new "http://test.com/test.svc/"
|
529
601
|
product = Product.first(nil)
|
530
602
|
product.should be_nil
|
531
|
-
end
|
603
|
+
end
|
532
604
|
it "should return nil if an id isn't found" do
|
533
605
|
svc = OData::Service.new "http://test.com/test.svc/"
|
534
606
|
product = Product.first(1234567890)
|
@@ -541,29 +613,29 @@ module OData
|
|
541
613
|
end
|
542
614
|
end
|
543
615
|
end
|
544
|
-
|
616
|
+
|
545
617
|
describe "namespaces" do
|
546
618
|
after(:each) do
|
547
619
|
VisoftInc::Sample::Models.send(:remove_const, 'Product') if VisoftInc::Sample::Models.const_defined? 'Product'
|
548
620
|
VisoftInc::Sample::Models.send(:remove_const, 'Category') if VisoftInc::Sample::Models.const_defined? 'Category'
|
549
|
-
|
621
|
+
|
550
622
|
VisoftInc::Sample.send(:remove_const, 'Models') if VisoftInc::Sample.const_defined? 'Models'
|
551
|
-
VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample'
|
623
|
+
VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample'
|
552
624
|
Object.send(:remove_const, 'VisoftInc') if Object.const_defined? 'VisoftInc'
|
553
625
|
end
|
554
|
-
|
626
|
+
|
555
627
|
it "should create models in the specified namespace if the option is set (using a .NET style namespace with dots)" do
|
556
628
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc.Sample.Models' }
|
557
629
|
defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
|
558
630
|
defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
|
559
631
|
end
|
560
|
-
|
632
|
+
|
561
633
|
it "should create models in the specified namespace if the option is set (using Ruby style namespaces with double colons)" do
|
562
634
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
563
635
|
defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
|
564
636
|
defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
|
565
637
|
end
|
566
|
-
|
638
|
+
|
567
639
|
it "should fill object defined in a namespace" do
|
568
640
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
569
641
|
svc.Categories(1)
|
@@ -573,19 +645,19 @@ module OData
|
|
573
645
|
category.Id.should eq 1
|
574
646
|
category.Name.should eq 'Category 1'
|
575
647
|
end
|
576
|
-
|
648
|
+
|
577
649
|
it "should fill the class_metadata hash" do
|
578
650
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
579
651
|
svc.class_metadata.should_not be_empty
|
580
652
|
end
|
581
|
-
|
653
|
+
|
582
654
|
it "should add a key (based on the name) for each property class_metadata hash" do
|
583
655
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
584
656
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Id'
|
585
657
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Name'
|
586
658
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Description'
|
587
659
|
end
|
588
|
-
|
660
|
+
|
589
661
|
it "should lazy load objects defined in a namespace" do
|
590
662
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
591
663
|
svc.Categories(1)
|
@@ -593,7 +665,7 @@ module OData
|
|
593
665
|
svc.load_property category, 'Products'
|
594
666
|
category.Products.should_not be_nil
|
595
667
|
category.Products.first.Id.should eq 1
|
596
|
-
category.Products.first.Name.should eq 'Widget 1'
|
668
|
+
category.Products.first.Name.should eq 'Widget 1'
|
597
669
|
end
|
598
670
|
end
|
599
671
|
|
@@ -602,15 +674,15 @@ module OData
|
|
602
674
|
svc = OData::Service.new "http://test.com/test.svc/"
|
603
675
|
svc.should respond_to(:add_link)
|
604
676
|
end
|
605
|
-
|
677
|
+
|
606
678
|
it "shouldn't be allowed if a parent isn't tracked" do
|
607
679
|
svc = OData::Service.new "http://test.com/test.svc/"
|
608
680
|
category = Category.new :Name => 'New Category'
|
609
681
|
property = nil # Not needed for this test
|
610
682
|
product = nil # Not needed for this test
|
611
|
-
lambda { svc.add_link(category, property, product) }.should raise_error(
|
683
|
+
lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on an entity that isn't tracked (Category)")
|
612
684
|
end
|
613
|
-
|
685
|
+
|
614
686
|
it "shouldn't be allowed if a property isn't found on the parent" do
|
615
687
|
svc = OData::Service.new "http://test.com/test.svc/"
|
616
688
|
svc.Categories(1)
|
@@ -619,7 +691,7 @@ module OData
|
|
619
691
|
product = nil # Not needed for this test
|
620
692
|
lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'NotAProperty' is not a valid navigation property for Category")
|
621
693
|
end
|
622
|
-
|
694
|
+
|
623
695
|
it "shouldn't be allowed if a property isn't a navigation property on the parent" do
|
624
696
|
svc = OData::Service.new "http://test.com/test.svc/"
|
625
697
|
svc.Categories(1)
|
@@ -628,16 +700,16 @@ module OData
|
|
628
700
|
product = nil # Not needed for this test
|
629
701
|
lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'Name' is not a valid navigation property for Category")
|
630
702
|
end
|
631
|
-
|
703
|
+
|
632
704
|
it "shouldn't be allowed if a child isn't tracked" do
|
633
705
|
svc = OData::Service.new "http://test.com/test.svc/"
|
634
706
|
svc.Categories(1)
|
635
707
|
category = svc.execute.first
|
636
708
|
property = 'Products'
|
637
|
-
product = Product.new :Name => 'Widget 1'
|
638
|
-
lambda { svc.add_link(category, property, product) }.should raise_error(
|
709
|
+
product = Product.new :Name => 'Widget 1'
|
710
|
+
lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on a child entity that isn't tracked (Product)")
|
639
711
|
end
|
640
|
-
|
712
|
+
|
641
713
|
it "should perform a post against the correct URL with the correct body on a single_save" do
|
642
714
|
svc = OData::Service.new "http://test.com/test.svc/"
|
643
715
|
svc.Categories(1)
|
@@ -704,8 +776,240 @@ module OData
|
|
704
776
|
end
|
705
777
|
end
|
706
778
|
end
|
779
|
+
|
780
|
+
describe "JSON serialization of objects" do
|
781
|
+
before(:each) do
|
782
|
+
# Required for the build_classes method
|
783
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/$metadata").
|
784
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
785
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {})
|
786
|
+
|
787
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/VirtualMachines").
|
788
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
789
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {})
|
790
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
791
|
+
svc.VirtualMachines
|
792
|
+
results = svc.execute
|
793
|
+
@json = results.first.as_json
|
794
|
+
end
|
795
|
+
|
796
|
+
it "Should quote Edm.Int64 properties" do
|
797
|
+
@json["PerfDiskBytesWrite"].should be_a(String)
|
798
|
+
end
|
799
|
+
|
800
|
+
it "Should output collections with metadata" do
|
801
|
+
@json["VMNetworkAssignments"].should be_a(Hash)
|
802
|
+
@json["VMNetworkAssignments"].should have_key("__metadata")
|
803
|
+
@json["VMNetworkAssignments"]["__metadata"].should be_a(Hash)
|
804
|
+
@json["VMNetworkAssignments"]["__metadata"].should have_key("type")
|
805
|
+
@json["VMNetworkAssignments"]["__metadata"]["type"].should eq("Collection(VMM.VMNetworkAssignment)")
|
806
|
+
@json["VMNetworkAssignments"].should have_key("results")
|
807
|
+
@json["VMNetworkAssignments"]["results"].should be_a(Array)
|
808
|
+
@json["VMNetworkAssignments"]["results"].should eq([])
|
809
|
+
end
|
810
|
+
end
|
811
|
+
|
812
|
+
describe "handling of Microsoft System Center 2012" do
|
813
|
+
before(:each) do
|
814
|
+
# Required for the build_classes method
|
815
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/$metadata").
|
816
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
817
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {})
|
818
|
+
|
819
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/VirtualMachines").
|
820
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
821
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {})
|
822
|
+
|
823
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500").
|
824
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
825
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/hardware_profiles.xml", __FILE__)), :headers => {})
|
826
|
+
|
827
|
+
stub_request(:get, "http://blabla:@test.com/test.svc/VMTemplates").
|
828
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
829
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/vm_templates.xml", __FILE__)), :headers => {})
|
830
|
+
end
|
831
|
+
|
832
|
+
after(:all) do
|
833
|
+
VMM.constants.each do |constant|
|
834
|
+
VMM.send :remove_const, constant
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
it "should successfully parse null valued string properties" do
|
839
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
840
|
+
svc.VirtualMachines
|
841
|
+
results = svc.execute
|
842
|
+
results.first.should be_a_kind_of(VMM::VirtualMachine)
|
843
|
+
results.first.CostCenter.should be_nil
|
844
|
+
end
|
845
|
+
|
846
|
+
it "should successfully return a virtual machine" do
|
847
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
848
|
+
svc.VirtualMachines
|
849
|
+
results = svc.execute
|
850
|
+
results.first.should be_a_kind_of(VMM::VirtualMachine)
|
851
|
+
end
|
852
|
+
|
853
|
+
it "should successfully return a hardware profile for results that include a collection of complex types" do
|
854
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
855
|
+
svc.HardwareProfiles.filter("Memory eq 3500")
|
856
|
+
results = svc.execute
|
857
|
+
results.first.should be_a_kind_of(VMM::HardwareProfile)
|
858
|
+
end
|
859
|
+
|
860
|
+
it "should successfully return a collection of complex types" do
|
861
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
862
|
+
svc.HardwareProfiles.filter("Memory eq 3500")
|
863
|
+
results = svc.execute
|
864
|
+
granted_list = results.first.GrantedToList
|
865
|
+
granted_list.should be_a_kind_of(Array)
|
866
|
+
granted_list.first.should be_a_kind_of(VMM::UserAndRole)
|
867
|
+
granted_list.first.RoleName.should == "Important Tenant"
|
868
|
+
end
|
869
|
+
|
870
|
+
|
871
|
+
it "should successfully return results that include a collection of Edm types" do
|
872
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
873
|
+
svc.VMTemplates
|
874
|
+
results = svc.execute
|
875
|
+
results.first.should be_a_kind_of(VMM::VMTemplate)
|
876
|
+
end
|
877
|
+
|
878
|
+
it "should successfully return a collection of Edm types" do
|
879
|
+
svc = OData::Service.new "http://test.com/test.svc/", { :username => "blabla", :password=> "", :verify_ssl => false, :namespace => "VMM" }
|
880
|
+
svc.VMTemplates
|
881
|
+
results = svc.execute
|
882
|
+
boot_order = results.first.BootOrder
|
883
|
+
boot_order.should be_a_kind_of(Array)
|
884
|
+
boot_order.first.should be_a_kind_of(String)
|
885
|
+
boot_order.should eq ['CD', 'IdeHardDrive', 'PxeBoot', 'Floppy']
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
describe "handling of nested expands" do
|
890
|
+
before(:each) do
|
891
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
892
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
893
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/edmx_northwind.xml", __FILE__)), :headers => {})
|
894
|
+
|
895
|
+
stub_request(:get, "http://test.com/test.svc/Products?$expand=Category,Category/Products&$top=2").
|
896
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
897
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/northwind_products_category_expands.xml", __FILE__)), :headers => {})
|
898
|
+
end
|
899
|
+
|
900
|
+
it "should successfully parse the results" do
|
901
|
+
svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" }
|
902
|
+
svc.Products.expand('Category').expand('Category/Products').top(2)
|
903
|
+
lambda { svc.execute }.should_not raise_exception
|
904
|
+
end
|
905
|
+
|
906
|
+
it "should successfully parse a Category as a Category" do
|
907
|
+
svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" }
|
908
|
+
svc.Products.expand('Category').expand('Category/Products').top(2)
|
909
|
+
products = svc.execute
|
910
|
+
products.first.Category.should be_a_kind_of(NW::Category)
|
911
|
+
end
|
912
|
+
|
913
|
+
it "should successfully parse the Category properties" do
|
914
|
+
svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" }
|
915
|
+
svc.Products.expand('Category').expand('Category/Products').top(2)
|
916
|
+
products = svc.execute
|
917
|
+
products.first.Category.CategoryID.should eq 1
|
918
|
+
end
|
919
|
+
|
920
|
+
it "should successfully parse the Category children Products" do
|
921
|
+
svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" }
|
922
|
+
svc.Products.expand('Category').expand('Category/Products').top(2)
|
923
|
+
products = svc.execute
|
924
|
+
products.first.Category.Products.length.should eq 12
|
925
|
+
end
|
926
|
+
|
927
|
+
it "should successfully parse the Category's child Product properties" do
|
928
|
+
svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" }
|
929
|
+
svc.Products.expand('Category').expand('Category/Products').top(2)
|
930
|
+
products = svc.execute
|
931
|
+
products.first.Category.Products.first.ProductName.should eq "Chai"
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
describe "handling of custom select queries" do
|
936
|
+
|
937
|
+
context "when results are found" do
|
938
|
+
before(:each) do
|
939
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
940
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
941
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
942
|
+
|
943
|
+
stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price").
|
944
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
945
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_products_name_price.xml", __FILE__)), :headers => {})
|
946
|
+
end
|
947
|
+
|
948
|
+
subject do
|
949
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
950
|
+
svc.Products.select "Name", "Price"
|
951
|
+
svc.execute
|
952
|
+
end
|
953
|
+
|
954
|
+
it { should be_an Array }
|
955
|
+
it { should_not be_empty }
|
956
|
+
its(:first) { should be_a Product }
|
957
|
+
end
|
958
|
+
|
959
|
+
context "when there isn't a property by the name specified" do
|
960
|
+
before(:each) do
|
961
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
962
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
963
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
964
|
+
|
965
|
+
stub_request(:get, "http://test.com/test.svc/Categories?$select=Price").
|
966
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
967
|
+
to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_no_property.xml", __FILE__)), :headers => {})
|
968
|
+
end
|
969
|
+
|
970
|
+
it "raises an exception" do
|
971
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
972
|
+
svc.Categories.select "Price"
|
973
|
+
expect { svc.execute }.to raise_error(OData::ServiceError) { |error|
|
974
|
+
error.http_code.should eq 400
|
975
|
+
error.message.should eq "Type 'RubyODataService.Category' does not have a property named 'Price' or there is no type with 'Price' name."
|
976
|
+
}
|
977
|
+
end
|
978
|
+
end
|
979
|
+
|
980
|
+
context "when a property requires $expand to traverse" do
|
981
|
+
before(:each) do
|
982
|
+
stub_request(:get, "http://test.com/test.svc/$metadata").
|
983
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
984
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
985
|
+
|
986
|
+
stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name").
|
987
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
988
|
+
to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_travsing_no_expand.xml", __FILE__)), :headers => {})
|
989
|
+
|
990
|
+
stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name&$expand=Products").
|
991
|
+
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
992
|
+
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_expand.xml", __FILE__)), :headers => {})
|
993
|
+
end
|
994
|
+
|
995
|
+
it "doesn't error" do
|
996
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
997
|
+
svc.Categories.select "Name", "Products/Name"
|
998
|
+
expect { svc.execute }.to_not raise_error
|
999
|
+
end
|
1000
|
+
|
1001
|
+
it "returns the classes with the properties filled in" do
|
1002
|
+
svc = OData::Service.new "http://test.com/test.svc/"
|
1003
|
+
svc.Categories.select "Name", "Products/Name"
|
1004
|
+
results = svc.execute
|
1005
|
+
category = results.first
|
1006
|
+
category.Name.should eq "Category 0001"
|
1007
|
+
product = category.Products.first
|
1008
|
+
product.Name.should eq "Widget 0001"
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
end
|
707
1012
|
end
|
708
|
-
|
709
1013
|
describe_private OData::Service do
|
710
1014
|
describe "parse value" do
|
711
1015
|
before(:each) do
|
@@ -714,13 +1018,13 @@ module OData
|
|
714
1018
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
715
1019
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {})
|
716
1020
|
end
|
717
|
-
|
1021
|
+
|
718
1022
|
it "should not error on an 'out of range' date" do
|
719
1023
|
# This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32]
|
720
1024
|
svc = OData::Service.new "http://test.com/test.svc/"
|
721
1025
|
element_to_parse = Nokogiri::XML.parse('<d:AvailableFrom m:type="Edm.DateTime">2100-01-01T00:00:00</d:AvailableFrom>').elements[0]
|
722
|
-
lambda { svc.
|
1026
|
+
lambda { svc.parse_value_xml(element_to_parse) }.should_not raise_exception
|
723
1027
|
end
|
724
1028
|
end
|
725
1029
|
end
|
726
|
-
end
|
1030
|
+
end
|