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.
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