ruby_odata 0.1.0 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +7 -2
  4. data/.simplecov +10 -0
  5. data/.travis.yml +10 -4
  6. data/.yardopts +6 -0
  7. data/CHANGELOG.md +157 -0
  8. data/Guardfile +14 -0
  9. data/LICENSE +20 -22
  10. data/README.md +289 -0
  11. data/Rakefile +19 -9
  12. data/features/basic_auth.feature +3 -2
  13. data/features/batch_request.feature +7 -6
  14. data/features/cassettes/basic_auth_protected_resource.yml +57 -0
  15. data/features/cassettes/batch_request_additions.yml +69 -0
  16. data/features/cassettes/batch_request_deletes.yml +69 -0
  17. data/features/cassettes/batch_request_updates.yml +69 -0
  18. data/features/cassettes/clean_database_for_testing.yml +46 -0
  19. data/features/cassettes/cucumber_tags/basic_auth.yml +297 -0
  20. data/features/cassettes/cucumber_tags/batch_request.yml +1459 -0
  21. data/features/cassettes/cucumber_tags/complex_types.yml +326 -0
  22. data/features/cassettes/cucumber_tags/error_handling.yml +64 -0
  23. data/features/cassettes/cucumber_tags/query_builder.yml +2025 -0
  24. data/features/cassettes/cucumber_tags/service.yml +234 -0
  25. data/features/cassettes/cucumber_tags/service_manage.yml +937 -0
  26. data/features/cassettes/cucumber_tags/service_methods.yml +647 -0
  27. data/features/cassettes/cucumber_tags/ssl.yml +203 -0
  28. data/features/cassettes/cucumber_tags/type_conversion.yml +337 -0
  29. data/features/cassettes/service_manage_additions.yml +65 -0
  30. data/features/cassettes/service_manage_deletions.yml +58 -0
  31. data/features/cassettes/service_manage_deletions_2.yml +58 -0
  32. data/features/cassettes/unsecured_metadata.yml +89 -0
  33. data/features/complex_types.feature +4 -3
  34. data/features/error_handling.feature +13 -0
  35. data/features/query_builder.feature +30 -9
  36. data/features/service.feature +4 -3
  37. data/features/service_manage.feature +6 -5
  38. data/features/service_methods.feature +3 -2
  39. data/features/ssl.feature +8 -8
  40. data/features/step_definitions/service_steps.rb +32 -74
  41. data/features/support/env.rb +6 -3
  42. data/features/support/hooks.rb +3 -2
  43. data/features/support/pickle.rb +29 -18
  44. data/features/support/vcr.rb +24 -0
  45. data/features/type_conversion.feature +16 -17
  46. data/gemfiles/Gemfile.ruby187 +6 -0
  47. data/lib/ruby_odata.rb +20 -18
  48. data/lib/ruby_odata/association.rb +7 -6
  49. data/lib/ruby_odata/class_builder.rb +31 -14
  50. data/lib/ruby_odata/exceptions.rb +11 -0
  51. data/lib/ruby_odata/helpers.rb +17 -0
  52. data/lib/ruby_odata/operation.rb +5 -6
  53. data/lib/ruby_odata/property_metadata.rb +9 -7
  54. data/lib/ruby_odata/query_builder.rb +127 -63
  55. data/lib/ruby_odata/service.rb +265 -147
  56. data/lib/ruby_odata/version.rb +3 -1
  57. data/ruby_odata.gemspec +22 -13
  58. data/spec/fixtures/error_without_message.xml +5 -0
  59. data/spec/fixtures/int64_ids/edmx_boat_service.xml +19 -0
  60. data/spec/fixtures/int64_ids/edmx_car_service.xml +21 -0
  61. data/spec/fixtures/int64_ids/result_boats.xml +26 -0
  62. data/spec/fixtures/int64_ids/result_cars.xml +28 -0
  63. data/spec/fixtures/ms_system_center/edmx_ms_system_center.xml +1645 -0
  64. data/spec/fixtures/ms_system_center/edmx_ms_system_center_v2.xml +2120 -0
  65. data/spec/fixtures/ms_system_center/hardware_profiles.xml +61 -0
  66. data/spec/fixtures/ms_system_center/virtual_machines.xml +175 -0
  67. data/spec/fixtures/ms_system_center/vm_templates.xml +1193 -0
  68. data/spec/fixtures/nested_expands/edmx_northwind.xml +557 -0
  69. data/spec/fixtures/nested_expands/northwind_products_category_expands.xml +774 -0
  70. data/spec/fixtures/sample_service/result_select_categories_expand.xml +268 -0
  71. data/spec/fixtures/sample_service/result_select_categories_no_property.xml +6 -0
  72. data/spec/fixtures/sample_service/result_select_categories_travsing_no_expand.xml +6 -0
  73. data/spec/fixtures/sample_service/result_select_products_name_price.xml +92 -0
  74. data/spec/property_metadata_spec.rb +10 -10
  75. data/spec/query_builder_spec.rb +153 -14
  76. data/spec/revised_service_spec.rb +111 -6
  77. data/spec/service_spec.rb +389 -85
  78. data/spec/spec_helper.rb +3 -0
  79. data/spec/support/sample_service_matcher.rb +15 -0
  80. data/test/RubyODataService/RubyODataService/App_Data/.gitkeep +0 -0
  81. data/test/blueprints.rb +15 -9
  82. data/test/usage_samples/querying.rb +5 -1
  83. data/test/usage_samples/sample_data.rb +1 -3
  84. metadata +276 -76
  85. data/CHANGELOG.rdoc +0 -88
  86. 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
@@ -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(ArgumentError, "You cannot load a property on an entity that isn't tracked")
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(ArgumentError, "You cannot add a link on an entity that isn't tracked (Category)")
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(ArgumentError, "You cannot add a link on a child entity that isn't tracked (Product)")
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.parse_value(element_to_parse) }.should_not raise_exception
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