ruby_odata 0.1.0 → 0.1.1
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 +3 -2
- data/.travis.yml +2 -1
- data/.yardopts +6 -0
- data/CHANGELOG.md +102 -0
- data/Guardfile +14 -0
- data/README.md +285 -0
- data/Rakefile +0 -7
- 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 +14 -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 +38 -24
- data/features/support/env.rb +1 -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/lib/ruby_odata/association.rb +7 -6
- data/lib/ruby_odata/class_builder.rb +6 -7
- data/lib/ruby_odata/exceptions.rb +4 -0
- data/lib/ruby_odata/helpers.rb +11 -0
- data/lib/ruby_odata/operation.rb +5 -6
- data/lib/ruby_odata/property_metadata.rb +4 -5
- data/lib/ruby_odata/query_builder.rb +98 -63
- data/lib/ruby_odata/service.rb +118 -103
- data/lib/ruby_odata/version.rb +3 -1
- data/lib/ruby_odata.rb +20 -18
- data/ruby_odata.gemspec +16 -12
- data/spec/query_builder_spec.rb +78 -14
- data/spec/service_spec.rb +83 -83
- 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 +213 -39
- data/CHANGELOG.rdoc +0 -88
- data/README.rdoc +0 -259
data/spec/query_builder_spec.rb
CHANGED
@@ -1,34 +1,98 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
module OData
|
4
4
|
describe QueryBuilder do
|
5
5
|
describe "#initialize" do
|
6
|
-
it "handles additional parameters" do
|
7
|
-
builder = QueryBuilder.new
|
6
|
+
it "handles additional parameters" do
|
7
|
+
builder = QueryBuilder.new "Products", { :x=>1, :y=>2 }
|
8
8
|
builder.query.should eq "Products?x=1&y=2"
|
9
9
|
end
|
10
|
-
it "handles empty additional parameters" do
|
11
|
-
builder = QueryBuilder.new
|
10
|
+
it "handles empty additional parameters" do
|
11
|
+
builder = QueryBuilder.new "Products"
|
12
12
|
builder.query.should eq "Products"
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
it "should append additional parameters to the end" do
|
17
|
-
builder = QueryBuilder.new 'Products', { :x=>1, :y=>2 }
|
13
|
+
end
|
14
|
+
it "should append additional parameters to the end of the query" do
|
15
|
+
builder = QueryBuilder.new "Products", { :x=>1, :y=>2 }
|
18
16
|
builder.top(10)
|
19
17
|
builder.query.should eq "Products?$top=10&x=1&y=2"
|
20
18
|
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#links" do
|
21
22
|
it "should properly handle queries for links" do
|
22
|
-
builder = QueryBuilder.new
|
23
|
-
builder.links(
|
23
|
+
builder = QueryBuilder.new "Categories(1)"
|
24
|
+
builder.links("Products")
|
24
25
|
builder.query.should eq "Categories(1)/$links/Products"
|
25
26
|
end
|
26
27
|
it "should properly handle queries for links with additional operations" do
|
27
|
-
builder = QueryBuilder.new
|
28
|
-
builder.links(
|
28
|
+
builder = QueryBuilder.new "Categories(1)"
|
29
|
+
builder.links("Products")
|
29
30
|
builder.top(5)
|
30
31
|
builder.query.should eq "Categories(1)/$links/Products?$top=5"
|
31
32
|
end
|
33
|
+
it "should throw an execption if count was already called on the builder" do
|
34
|
+
builder = QueryBuilder.new "Categories(1)"
|
35
|
+
builder.count
|
36
|
+
lambda { builder.links("Products") }.should raise_error(OData::NotSupportedError, "You cannot call both the `links` method and the `count` method in the same query.")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#count" do
|
41
|
+
it "should accept the count method" do
|
42
|
+
builder = QueryBuilder.new "Products"
|
43
|
+
lambda { builder.count }.should_not raise_error
|
44
|
+
end
|
45
|
+
it "should properly handle the count method" do
|
46
|
+
builder = QueryBuilder.new "Products"
|
47
|
+
builder.count
|
48
|
+
builder.query.should eq "Products/$count"
|
49
|
+
end
|
50
|
+
it "should properly handle the count method with additional operators" do
|
51
|
+
builder = QueryBuilder.new "Products"
|
52
|
+
builder.filter("Name eq 'Widget 1'")
|
53
|
+
builder.count
|
54
|
+
builder.query.should eq "Products/$count?$filter=Name+eq+%27Widget+1%27"
|
55
|
+
end
|
56
|
+
it "should throw an execption if links was already called on the builder" do
|
57
|
+
builder = QueryBuilder.new "Categories(1)"
|
58
|
+
builder.links("Products")
|
59
|
+
lambda { builder.count }.should raise_error(OData::NotSupportedError, "You cannot call both the `links` method and the `count` method in the same query.")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#navigate" do
|
64
|
+
it "should allow a user to drill down into a navigaion property on an initial query" do
|
65
|
+
builder = QueryBuilder.new "Genres('Horror Movies')"
|
66
|
+
builder.navigate("Titles")
|
67
|
+
builder.filter("Name eq 'Halloween'")
|
68
|
+
builder.query.should eq "Genres('Horror%20Movies')/Titles?$filter=Name+eq+%27Halloween%27"
|
69
|
+
end
|
70
|
+
it "should allow for multiple levels of drill down" do
|
71
|
+
builder = QueryBuilder.new "Genres('Horror Movies')"
|
72
|
+
builder.navigate("Titles('6aBu')")
|
73
|
+
builder.navigate("Awards")
|
74
|
+
builder.filter("Type eq 'Afi'")
|
75
|
+
builder.query.should eq "Genres('Horror%20Movies')/Titles('6aBu')/Awards?$filter=Type+eq+%27Afi%27"
|
76
|
+
end
|
77
|
+
it "should allow for a drill down plus links" do
|
78
|
+
builder = QueryBuilder.new "Genres('Horror Movies')"
|
79
|
+
builder.navigate("Titles('6aBu')")
|
80
|
+
builder.links("Awards")
|
81
|
+
builder.query.should eq "Genres('Horror%20Movies')/Titles('6aBu')/$links/Awards"
|
82
|
+
end
|
83
|
+
it "should allow for a drill down plus count" do
|
84
|
+
builder = QueryBuilder.new "Genres('Horror Movies')"
|
85
|
+
builder.navigate("Titles")
|
86
|
+
builder.count
|
87
|
+
builder.query.should eq "Genres('Horror%20Movies')/Titles/$count"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#query" do
|
92
|
+
it "should encode spaces in IDs" do
|
93
|
+
builder = QueryBuilder.new "Categories('Cool Stuff')"
|
94
|
+
builder.query.should eq "Categories('Cool%20Stuff')"
|
95
|
+
end
|
32
96
|
end
|
33
97
|
end
|
34
98
|
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
|
@@ -52,7 +52,7 @@ module OData
|
|
52
52
|
end
|
53
53
|
it "should pass the parameters as part of a save" do
|
54
54
|
svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
|
55
|
-
new_flight = ZDemoFlight.new
|
55
|
+
new_flight = ZDemoFlight.new
|
56
56
|
svc.AddToflight_dataCollection(new_flight)
|
57
57
|
svc.save_changes
|
58
58
|
a_request(:post, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made
|
@@ -75,9 +75,9 @@ module OData
|
|
75
75
|
end
|
76
76
|
it "should pass the parameters as part of a batch save" do
|
77
77
|
svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
|
78
|
-
new_flight = ZDemoFlight.new
|
78
|
+
new_flight = ZDemoFlight.new
|
79
79
|
svc.AddToflight_dataCollection(new_flight)
|
80
|
-
new_flight2 = ZDemoFlight.new
|
80
|
+
new_flight2 = ZDemoFlight.new
|
81
81
|
svc.AddToflight_dataCollection(new_flight2)
|
82
82
|
svc.save_changes
|
83
83
|
a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made
|
@@ -103,7 +103,7 @@ module OData
|
|
103
103
|
a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made
|
104
104
|
end
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
describe "lowercase collections" do
|
108
108
|
before(:each) do
|
109
109
|
# Required for the build_classes method
|
@@ -111,12 +111,12 @@ module OData
|
|
111
111
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
112
112
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {})
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
it "should respond_to a lowercase collection" do
|
116
116
|
svc = OData::Service.new "http://test.com/test.svc"
|
117
117
|
svc.respond_to?('acronyms').should be_true
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
it "should allow a lowercase collections to be queried" do
|
121
121
|
svc = OData::Service.new "http://test.com/test.svc"
|
122
122
|
lambda { svc.send('acronyms') }.should_not raise_error
|
@@ -142,19 +142,19 @@ module OData
|
|
142
142
|
results.first.should be_a_kind_of(ZDemoFlight)
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
describe "collections, objects, metadata etc" do
|
147
147
|
before(:each) do
|
148
148
|
# Metadata
|
149
149
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
150
150
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
151
151
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/edmx_feed_customization.xml", __FILE__)), :headers => {})
|
152
|
-
|
152
|
+
|
153
153
|
# Content - Products
|
154
154
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products(?:.*)/).
|
155
155
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
156
156
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_products_expand.xml", __FILE__)), :headers => {})
|
157
|
-
|
157
|
+
|
158
158
|
# Content - Categories expanded Products
|
159
159
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Categories(?:.*)/).
|
160
160
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
@@ -208,9 +208,9 @@ module OData
|
|
208
208
|
meta.nullable.should eq true
|
209
209
|
meta.fc_target_path.should eq "SyndicationSummary"
|
210
210
|
meta.fc_keep_in_content.should eq false
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
214
|
describe "single class" do
|
215
215
|
it "should handle properties where a property is represented in the syndication title instead of the properties collection" do
|
216
216
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -224,46 +224,46 @@ module OData
|
|
224
224
|
results = svc.execute
|
225
225
|
results.first.Description.should eq "Whole grain bread"
|
226
226
|
end
|
227
|
-
end
|
228
|
-
|
227
|
+
end
|
228
|
+
|
229
229
|
describe "expanded inline class" do
|
230
230
|
it "should handle properties where a property is represented in the syndication title instead of the properties collection" do
|
231
231
|
svc = OData::Service.new "http://test.com/test.svc/"
|
232
232
|
svc.Categories
|
233
233
|
results = svc.execute
|
234
|
-
|
234
|
+
|
235
235
|
beverages = results[1]
|
236
|
-
|
236
|
+
|
237
237
|
milk = beverages.Products.first
|
238
238
|
milk.Name.should eq "Milk"
|
239
239
|
milk.Description.should eq "Low fat milk"
|
240
|
-
|
240
|
+
|
241
241
|
lemonade = beverages.Products.last
|
242
242
|
lemonade.Name.should eq "Pink Lemonade"
|
243
243
|
lemonade.Description.should eq "36 Ounce Cans (Pack of 3)"
|
244
244
|
end
|
245
245
|
end
|
246
246
|
end
|
247
|
-
|
247
|
+
|
248
248
|
describe "handling inline collections/properties" do
|
249
249
|
it "should make plural named properties arrays and not a single class" do
|
250
250
|
svc = OData::Service.new "http://test.com/test.svc/"
|
251
251
|
svc.Categories
|
252
252
|
results = svc.execute
|
253
253
|
food = results[0]
|
254
|
-
|
254
|
+
|
255
255
|
food.Products.should be_an Array
|
256
256
|
end
|
257
|
-
|
257
|
+
|
258
258
|
it "should not make an array if the navigation property name is singular" do
|
259
259
|
svc = OData::Service.new "http://test.com/test.svc/"
|
260
260
|
svc.Products
|
261
261
|
results = svc.execute
|
262
262
|
product = results.first
|
263
263
|
product.Category.should_not be_an Array
|
264
|
-
end
|
264
|
+
end
|
265
265
|
end
|
266
|
-
|
266
|
+
|
267
267
|
describe "navigation properties" do
|
268
268
|
it "should fill in PropertyMetadata for navigation properties" do
|
269
269
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -271,27 +271,27 @@ module OData
|
|
271
271
|
end
|
272
272
|
end
|
273
273
|
end
|
274
|
-
|
274
|
+
|
275
275
|
describe "single layer inheritance" do
|
276
276
|
before(:each) do
|
277
277
|
# Metadata
|
278
278
|
stub_request(:get, "http://test.com/test.svc/$metadata").
|
279
279
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
280
280
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {})
|
281
|
-
|
281
|
+
|
282
282
|
# Content - Courses
|
283
283
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/).
|
284
284
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
285
285
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {})
|
286
286
|
end
|
287
|
-
|
287
|
+
|
288
288
|
it "should build all inherited attributes" do
|
289
289
|
OData::Service.new "http://test.com/test.svc/"
|
290
290
|
methods = Course.instance_methods.reject {|m| Object.methods.index(m)}
|
291
|
-
|
291
|
+
|
292
292
|
# Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data
|
293
293
|
methods.map! {|m| m.to_sym}
|
294
|
-
|
294
|
+
|
295
295
|
methods.should include(:Title)
|
296
296
|
methods.should include(:Description)
|
297
297
|
methods.should include(:VideoLength)
|
@@ -319,7 +319,7 @@ module OData
|
|
319
319
|
course.Category.should_not be_nil
|
320
320
|
end
|
321
321
|
end
|
322
|
-
|
322
|
+
|
323
323
|
describe "handling partial collections" do
|
324
324
|
before(:each) do
|
325
325
|
# Metadata
|
@@ -361,14 +361,14 @@ module OData
|
|
361
361
|
results.count.should eq 3
|
362
362
|
end
|
363
363
|
end
|
364
|
-
|
364
|
+
|
365
365
|
describe "link queries" do
|
366
366
|
before(:each) do
|
367
367
|
# Required for the build_classes method
|
368
368
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
|
369
369
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
370
370
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
371
|
-
|
371
|
+
|
372
372
|
stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products").
|
373
373
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
374
374
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {})
|
@@ -381,72 +381,72 @@ module OData
|
|
381
381
|
results.first.should be_a_kind_of(URI)
|
382
382
|
results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)"
|
383
383
|
results[1].path.should eq "/SampleService/RubyOData.svc/Products(2)"
|
384
|
-
results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)"
|
384
|
+
results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)"
|
385
385
|
end
|
386
386
|
end
|
387
|
-
|
387
|
+
|
388
388
|
describe "sample service" do
|
389
389
|
before(:each) do
|
390
390
|
# Required for the build_classes method
|
391
391
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
|
392
392
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
393
393
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
|
394
|
-
|
394
|
+
|
395
395
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/).
|
396
396
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
397
397
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product.xml", __FILE__)), :headers => {})
|
398
|
-
|
398
|
+
|
399
399
|
stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/).
|
400
400
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
401
401
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product_not_found.xml", __FILE__)), :headers => {})
|
402
|
-
|
402
|
+
|
403
403
|
stub_request(:get, "http://test.com/test.svc/Products(1)/Category").
|
404
404
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
405
405
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
|
406
|
-
|
406
|
+
|
407
407
|
stub_request(:get, "http://test.com/test.svc/Categories(1)").
|
408
408
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
409
409
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
|
410
|
-
|
410
|
+
|
411
411
|
stub_request(:get, "http://test.com/test.svc/Categories(1)/Products").
|
412
412
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
413
413
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_multiple_category_products.xml", __FILE__)), :headers => {})
|
414
|
-
|
414
|
+
|
415
415
|
stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204)
|
416
416
|
stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202)
|
417
417
|
end
|
418
|
-
|
418
|
+
|
419
419
|
describe "lazy loading" do
|
420
420
|
after(:each) do
|
421
421
|
Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
|
422
422
|
Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
|
423
423
|
end
|
424
|
-
|
424
|
+
|
425
425
|
it "should have a load property method" do
|
426
426
|
svc = OData::Service.new "http://test.com/test.svc/"
|
427
427
|
svc.should respond_to(:load_property)
|
428
428
|
end
|
429
|
-
|
429
|
+
|
430
430
|
it "should throw an exception if the object isn't tracked" do
|
431
431
|
svc = OData::Service.new "http://test.com/test.svc/"
|
432
432
|
new_object = Product.new
|
433
|
-
lambda { svc.load_property(new_object, "Category") }.should raise_error(
|
433
|
+
lambda { svc.load_property(new_object, "Category") }.should raise_error(NotSupportedError, "You cannot load a property on an entity that isn't tracked")
|
434
434
|
end
|
435
|
-
|
435
|
+
|
436
436
|
it "should throw an exception if there isn't a method matching the navigation property passed in" do
|
437
437
|
svc = OData::Service.new "http://test.com/test.svc/"
|
438
438
|
svc.Products(1)
|
439
439
|
product = svc.execute.first
|
440
|
-
lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property")
|
440
|
+
lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property")
|
441
441
|
end
|
442
|
-
|
442
|
+
|
443
443
|
it "should throw an exception if the method passed in is a standard property (non-navigation)" do
|
444
444
|
svc = OData::Service.new "http://test.com/test.svc/"
|
445
445
|
svc.Products(1)
|
446
446
|
product = svc.execute.first
|
447
|
-
lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property")
|
447
|
+
lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property")
|
448
448
|
end
|
449
|
-
|
449
|
+
|
450
450
|
it "should fill a single navigation property" do
|
451
451
|
svc = OData::Service.new "http://test.com/test.svc/"
|
452
452
|
svc.Products(1)
|
@@ -456,7 +456,7 @@ module OData
|
|
456
456
|
product.Category.Id.should eq 1
|
457
457
|
product.Category.Name.should eq 'Category 1'
|
458
458
|
end
|
459
|
-
|
459
|
+
|
460
460
|
it "should fill a collection navigation property" do
|
461
461
|
svc = OData::Service.new "http://test.com/test.svc/"
|
462
462
|
svc.Categories(1)
|
@@ -467,58 +467,58 @@ module OData
|
|
467
467
|
category.Products[1].Id.should eq 2
|
468
468
|
end
|
469
469
|
end
|
470
|
-
|
470
|
+
|
471
471
|
describe "find, create, add, update, and delete" do
|
472
472
|
after(:each) do
|
473
473
|
Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
|
474
474
|
Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
|
475
475
|
end
|
476
|
-
|
476
|
+
|
477
477
|
it "should implement an AddTo method for collection" do
|
478
478
|
svc = OData::Service.new "http://test.com/test.svc/"
|
479
479
|
svc.should respond_to :AddToCategories
|
480
480
|
svc.should respond_to :AddToProducts
|
481
481
|
end
|
482
|
-
|
482
|
+
|
483
483
|
it "should create objects with an initialize method that can build the object from a hash" do
|
484
484
|
svc = OData::Service.new "http://test.com/test.svc/"
|
485
485
|
product = Product.new 'Id' => 1000, 'Name' => 'New Product'
|
486
486
|
product.Id.should eq 1000
|
487
487
|
product.Name.should eq 'New Product'
|
488
488
|
end
|
489
|
-
|
489
|
+
|
490
490
|
it "should create objects that rejects keys that don't have corresponding methods" do
|
491
491
|
svc = OData::Service.new "http://test.com/test.svc/"
|
492
492
|
lambda { Product.new 'NotAProperty' => true }.should raise_error NoMethodError
|
493
493
|
end
|
494
|
-
|
494
|
+
|
495
495
|
it "should create objects that expose a properties class method that lists the properties for the object" do
|
496
496
|
svc = OData::Service.new "http://test.com/test.svc/"
|
497
497
|
Product.properties.should include 'Id'
|
498
498
|
Product.properties.should include 'Name'
|
499
|
-
Product.properties.should include 'Category'
|
499
|
+
Product.properties.should include 'Category'
|
500
500
|
end
|
501
|
-
|
501
|
+
|
502
502
|
it "should have full metadata for a property returned from the properties method" do
|
503
503
|
svc = OData::Service.new "http://test.com/test.svc/"
|
504
504
|
Product.properties['Category'].should be_a PropertyMetadata
|
505
505
|
Product.properties['Category'].nav_prop.should be_true
|
506
506
|
end
|
507
|
-
|
507
|
+
|
508
508
|
it "should create objects that expose an id property" do
|
509
509
|
svc = OData::Service.new "http://test.com/test.svc/"
|
510
510
|
svc.Products(1)
|
511
511
|
product = svc.execute.first
|
512
512
|
product.should respond_to :id
|
513
513
|
end
|
514
|
-
|
514
|
+
|
515
515
|
it "should extract the id from the metadata" do
|
516
516
|
svc = OData::Service.new "http://test.com/test.svc/"
|
517
517
|
svc.Products(1)
|
518
518
|
product = svc.execute.first
|
519
519
|
product.id.should eq 1
|
520
520
|
end
|
521
|
-
|
521
|
+
|
522
522
|
describe "Class.first method" do
|
523
523
|
it "should exist on the create server objects" do
|
524
524
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -528,7 +528,7 @@ module OData
|
|
528
528
|
svc = OData::Service.new "http://test.com/test.svc/"
|
529
529
|
product = Product.first(nil)
|
530
530
|
product.should be_nil
|
531
|
-
end
|
531
|
+
end
|
532
532
|
it "should return nil if an id isn't found" do
|
533
533
|
svc = OData::Service.new "http://test.com/test.svc/"
|
534
534
|
product = Product.first(1234567890)
|
@@ -541,29 +541,29 @@ module OData
|
|
541
541
|
end
|
542
542
|
end
|
543
543
|
end
|
544
|
-
|
544
|
+
|
545
545
|
describe "namespaces" do
|
546
546
|
after(:each) do
|
547
547
|
VisoftInc::Sample::Models.send(:remove_const, 'Product') if VisoftInc::Sample::Models.const_defined? 'Product'
|
548
548
|
VisoftInc::Sample::Models.send(:remove_const, 'Category') if VisoftInc::Sample::Models.const_defined? 'Category'
|
549
|
-
|
549
|
+
|
550
550
|
VisoftInc::Sample.send(:remove_const, 'Models') if VisoftInc::Sample.const_defined? 'Models'
|
551
|
-
VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample'
|
551
|
+
VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample'
|
552
552
|
Object.send(:remove_const, 'VisoftInc') if Object.const_defined? 'VisoftInc'
|
553
553
|
end
|
554
|
-
|
554
|
+
|
555
555
|
it "should create models in the specified namespace if the option is set (using a .NET style namespace with dots)" do
|
556
556
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc.Sample.Models' }
|
557
557
|
defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
|
558
558
|
defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
|
559
559
|
end
|
560
|
-
|
560
|
+
|
561
561
|
it "should create models in the specified namespace if the option is set (using Ruby style namespaces with double colons)" do
|
562
562
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
563
563
|
defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
|
564
564
|
defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
|
565
565
|
end
|
566
|
-
|
566
|
+
|
567
567
|
it "should fill object defined in a namespace" do
|
568
568
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
569
569
|
svc.Categories(1)
|
@@ -573,19 +573,19 @@ module OData
|
|
573
573
|
category.Id.should eq 1
|
574
574
|
category.Name.should eq 'Category 1'
|
575
575
|
end
|
576
|
-
|
576
|
+
|
577
577
|
it "should fill the class_metadata hash" do
|
578
578
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
579
579
|
svc.class_metadata.should_not be_empty
|
580
580
|
end
|
581
|
-
|
581
|
+
|
582
582
|
it "should add a key (based on the name) for each property class_metadata hash" do
|
583
583
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
584
584
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Id'
|
585
585
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Name'
|
586
586
|
svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Description'
|
587
587
|
end
|
588
|
-
|
588
|
+
|
589
589
|
it "should lazy load objects defined in a namespace" do
|
590
590
|
svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
|
591
591
|
svc.Categories(1)
|
@@ -593,7 +593,7 @@ module OData
|
|
593
593
|
svc.load_property category, 'Products'
|
594
594
|
category.Products.should_not be_nil
|
595
595
|
category.Products.first.Id.should eq 1
|
596
|
-
category.Products.first.Name.should eq 'Widget 1'
|
596
|
+
category.Products.first.Name.should eq 'Widget 1'
|
597
597
|
end
|
598
598
|
end
|
599
599
|
|
@@ -602,15 +602,15 @@ module OData
|
|
602
602
|
svc = OData::Service.new "http://test.com/test.svc/"
|
603
603
|
svc.should respond_to(:add_link)
|
604
604
|
end
|
605
|
-
|
605
|
+
|
606
606
|
it "shouldn't be allowed if a parent isn't tracked" do
|
607
607
|
svc = OData::Service.new "http://test.com/test.svc/"
|
608
608
|
category = Category.new :Name => 'New Category'
|
609
609
|
property = nil # Not needed for this test
|
610
610
|
product = nil # Not needed for this test
|
611
|
-
lambda { svc.add_link(category, property, product) }.should raise_error(
|
611
|
+
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
612
|
end
|
613
|
-
|
613
|
+
|
614
614
|
it "shouldn't be allowed if a property isn't found on the parent" do
|
615
615
|
svc = OData::Service.new "http://test.com/test.svc/"
|
616
616
|
svc.Categories(1)
|
@@ -619,7 +619,7 @@ module OData
|
|
619
619
|
product = nil # Not needed for this test
|
620
620
|
lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'NotAProperty' is not a valid navigation property for Category")
|
621
621
|
end
|
622
|
-
|
622
|
+
|
623
623
|
it "shouldn't be allowed if a property isn't a navigation property on the parent" do
|
624
624
|
svc = OData::Service.new "http://test.com/test.svc/"
|
625
625
|
svc.Categories(1)
|
@@ -628,16 +628,16 @@ module OData
|
|
628
628
|
product = nil # Not needed for this test
|
629
629
|
lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'Name' is not a valid navigation property for Category")
|
630
630
|
end
|
631
|
-
|
631
|
+
|
632
632
|
it "shouldn't be allowed if a child isn't tracked" do
|
633
633
|
svc = OData::Service.new "http://test.com/test.svc/"
|
634
634
|
svc.Categories(1)
|
635
635
|
category = svc.execute.first
|
636
636
|
property = 'Products'
|
637
|
-
product = Product.new :Name => 'Widget 1'
|
638
|
-
lambda { svc.add_link(category, property, product) }.should raise_error(
|
637
|
+
product = Product.new :Name => 'Widget 1'
|
638
|
+
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
639
|
end
|
640
|
-
|
640
|
+
|
641
641
|
it "should perform a post against the correct URL with the correct body on a single_save" do
|
642
642
|
svc = OData::Service.new "http://test.com/test.svc/"
|
643
643
|
svc.Categories(1)
|
@@ -705,7 +705,7 @@ module OData
|
|
705
705
|
end
|
706
706
|
end
|
707
707
|
end
|
708
|
-
|
708
|
+
|
709
709
|
describe_private OData::Service do
|
710
710
|
describe "parse value" do
|
711
711
|
before(:each) do
|
@@ -714,7 +714,7 @@ module OData
|
|
714
714
|
with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
|
715
715
|
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {})
|
716
716
|
end
|
717
|
-
|
717
|
+
|
718
718
|
it "should not error on an 'out of range' date" do
|
719
719
|
# 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
720
|
svc = OData::Service.new "http://test.com/test.svc/"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module OData
|
4
|
+
module Support
|
5
|
+
class SampleServiceMatcher
|
6
|
+
def self.call(req1, req2)
|
7
|
+
regexp = /^(https?:\/\/(?:[^@]*@)?)[^:]*(:\d+\/.*$)/i
|
8
|
+
request1 = req1.uri.match(regexp)
|
9
|
+
request2 = req2.uri.match(regexp)
|
10
|
+
|
11
|
+
(request1[1] == request2[1]) && (request1[2] == request2[2])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
File without changes
|