odata4 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +120 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +287 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/lib/odata4.rb +37 -0
  15. data/lib/odata4/complex_type.rb +76 -0
  16. data/lib/odata4/complex_type/property.rb +114 -0
  17. data/lib/odata4/entity.rb +319 -0
  18. data/lib/odata4/entity_set.rb +162 -0
  19. data/lib/odata4/enum_type.rb +95 -0
  20. data/lib/odata4/enum_type/property.rb +62 -0
  21. data/lib/odata4/navigation_property.rb +29 -0
  22. data/lib/odata4/navigation_property/proxy.rb +76 -0
  23. data/lib/odata4/properties.rb +25 -0
  24. data/lib/odata4/properties/binary.rb +50 -0
  25. data/lib/odata4/properties/boolean.rb +37 -0
  26. data/lib/odata4/properties/date.rb +27 -0
  27. data/lib/odata4/properties/date_time.rb +83 -0
  28. data/lib/odata4/properties/date_time_offset.rb +17 -0
  29. data/lib/odata4/properties/decimal.rb +50 -0
  30. data/lib/odata4/properties/float.rb +67 -0
  31. data/lib/odata4/properties/geography.rb +13 -0
  32. data/lib/odata4/properties/geography/base.rb +162 -0
  33. data/lib/odata4/properties/geography/line_string.rb +33 -0
  34. data/lib/odata4/properties/geography/point.rb +31 -0
  35. data/lib/odata4/properties/geography/polygon.rb +38 -0
  36. data/lib/odata4/properties/guid.rb +17 -0
  37. data/lib/odata4/properties/integer.rb +107 -0
  38. data/lib/odata4/properties/number.rb +14 -0
  39. data/lib/odata4/properties/string.rb +72 -0
  40. data/lib/odata4/properties/time.rb +40 -0
  41. data/lib/odata4/properties/time_of_day.rb +27 -0
  42. data/lib/odata4/property.rb +118 -0
  43. data/lib/odata4/property_registry.rb +41 -0
  44. data/lib/odata4/query.rb +231 -0
  45. data/lib/odata4/query/criteria.rb +92 -0
  46. data/lib/odata4/query/criteria/comparison_operators.rb +49 -0
  47. data/lib/odata4/query/criteria/date_functions.rb +61 -0
  48. data/lib/odata4/query/criteria/geography_functions.rb +21 -0
  49. data/lib/odata4/query/criteria/lambda_operators.rb +27 -0
  50. data/lib/odata4/query/criteria/string_functions.rb +40 -0
  51. data/lib/odata4/query/in_batches.rb +58 -0
  52. data/lib/odata4/query/result.rb +84 -0
  53. data/lib/odata4/query/result/atom.rb +41 -0
  54. data/lib/odata4/query/result/json.rb +42 -0
  55. data/lib/odata4/railtie.rb +19 -0
  56. data/lib/odata4/service.rb +344 -0
  57. data/lib/odata4/service_registry.rb +52 -0
  58. data/lib/odata4/version.rb +3 -0
  59. data/odata4.gemspec +34 -0
  60. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  61. data/spec/fixtures/files/metadata.xml +150 -0
  62. data/spec/fixtures/files/product_0.json +10 -0
  63. data/spec/fixtures/files/product_0.xml +28 -0
  64. data/spec/fixtures/files/products.json +106 -0
  65. data/spec/fixtures/files/products.xml +308 -0
  66. data/spec/fixtures/files/supplier_0.json +26 -0
  67. data/spec/fixtures/files/supplier_0.xml +32 -0
  68. data/spec/fixtures/vcr_cassettes/complex_type_specs.yml +127 -0
  69. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1348 -0
  70. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  71. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  72. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  73. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  74. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  75. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  76. data/spec/fixtures/vcr_cassettes/query_specs.yml +663 -0
  77. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  78. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  79. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +749 -0
  80. data/spec/odata4/complex_type_spec.rb +116 -0
  81. data/spec/odata4/entity/shared_examples.rb +82 -0
  82. data/spec/odata4/entity_set_spec.rb +168 -0
  83. data/spec/odata4/entity_spec.rb +151 -0
  84. data/spec/odata4/enum_type_spec.rb +134 -0
  85. data/spec/odata4/navigation_property/proxy_spec.rb +44 -0
  86. data/spec/odata4/navigation_property_spec.rb +55 -0
  87. data/spec/odata4/properties/binary_spec.rb +50 -0
  88. data/spec/odata4/properties/boolean_spec.rb +72 -0
  89. data/spec/odata4/properties/date_spec.rb +23 -0
  90. data/spec/odata4/properties/date_time_offset_spec.rb +30 -0
  91. data/spec/odata4/properties/date_time_spec.rb +23 -0
  92. data/spec/odata4/properties/decimal_spec.rb +24 -0
  93. data/spec/odata4/properties/float_spec.rb +45 -0
  94. data/spec/odata4/properties/geography/line_string_spec.rb +33 -0
  95. data/spec/odata4/properties/geography/point_spec.rb +29 -0
  96. data/spec/odata4/properties/geography/polygon_spec.rb +55 -0
  97. data/spec/odata4/properties/geography/shared_examples.rb +72 -0
  98. data/spec/odata4/properties/guid_spec.rb +17 -0
  99. data/spec/odata4/properties/integer_spec.rb +58 -0
  100. data/spec/odata4/properties/string_spec.rb +46 -0
  101. data/spec/odata4/properties/time_of_day_spec.rb +23 -0
  102. data/spec/odata4/properties/time_spec.rb +15 -0
  103. data/spec/odata4/property_registry_spec.rb +16 -0
  104. data/spec/odata4/property_spec.rb +32 -0
  105. data/spec/odata4/query/criteria_spec.rb +229 -0
  106. data/spec/odata4/query/result_spec.rb +53 -0
  107. data/spec/odata4/query_spec.rb +196 -0
  108. data/spec/odata4/service_registry_spec.rb +18 -0
  109. data/spec/odata4/service_spec.rb +80 -0
  110. data/spec/odata4/usage_example_spec.rb +176 -0
  111. data/spec/spec_helper.rb +32 -0
  112. data/spec/support/coverage.rb +2 -0
  113. data/spec/support/vcr.rb +9 -0
  114. metadata +380 -0
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe OData4::ComplexType, vcr: {cassette_name: 'complex_type_specs'} do
4
+ before(:example) do
5
+ OData4::Service.open('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
6
+ end
7
+
8
+ let(:service) { OData4::ServiceRegistry['ODataDemo'] }
9
+
10
+ describe '.new' do
11
+ it 'requires type name' do
12
+ expect {
13
+ OData4::ComplexType.new(service: service)
14
+ }.to raise_error(ArgumentError)
15
+ end
16
+
17
+ it 'requires service instance' do
18
+ expect {
19
+ OData4::ComplexType.new(name: 'Address')
20
+ }.to raise_error(ArgumentError)
21
+ end
22
+
23
+ it 'requires name to refer to a valid complex type' do
24
+ expect {
25
+ OData4::ComplexType.new(name: 'NotAType', service: service)
26
+ }.to raise_error(ArgumentError)
27
+ end
28
+ end
29
+
30
+ let(:address) { {
31
+ 'Street' => '123 Main St',
32
+ 'City' => 'Huntington Beach',
33
+ 'State' => 'CA',
34
+ 'ZipCode' => '92648',
35
+ 'Country' => 'USA'
36
+ } }
37
+
38
+ let(:complex_type) { service.complex_types['Address'] }
39
+ let(:subject) { complex_type.property_class.new('Address', nil) }
40
+
41
+ describe 'is properly parsed from service metadata' do
42
+ it { expect(complex_type.name).to eq('Address') }
43
+ it { expect(complex_type.namespace).to eq('ODataDemo') }
44
+ it { expect(complex_type.type).to eq('ODataDemo.Address') }
45
+ it { expect(complex_type.property_names).to eq(%w{Street City State ZipCode Country}) }
46
+ end
47
+
48
+ # Check property instance inheritance hierarchy
49
+ it { expect(subject).to be_a(OData4::Property) }
50
+ it { expect(subject).to be_a(OData4::ComplexType::Property) }
51
+
52
+ it { expect(subject).to respond_to(:name) }
53
+ it { expect(subject).to respond_to(:type) }
54
+ it { expect(subject).to respond_to(:property_names) }
55
+ it { expect(subject).to respond_to(:[]) }
56
+ it { expect(subject).to respond_to(:[]=) }
57
+
58
+ it { expect(subject[ 'Street']).to be_nil }
59
+ it { expect(subject[ 'City']).to be_nil }
60
+ it { expect(subject[ 'State']).to be_nil }
61
+ it { expect(subject['ZipCode']).to be_nil }
62
+ it { expect(subject['Country']).to be_nil }
63
+
64
+ describe '#[]=' do
65
+ before do
66
+ address.each { |key, val| subject[key] = val }
67
+ end
68
+
69
+ it { expect(subject.value).to eq(address) }
70
+
71
+ it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
72
+ it { expect(subject[ 'City']).to eq(address[ 'City']) }
73
+ it { expect(subject[ 'State']).to eq(address[ 'State']) }
74
+ it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
75
+ it { expect(subject['Country']).to eq(address['Country']) }
76
+ end
77
+
78
+ describe '#value=' do
79
+ before { subject.value = address }
80
+
81
+ it { expect(subject.value).to eq(address) }
82
+
83
+ it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
84
+ it { expect(subject[ 'City']).to eq(address[ 'City']) }
85
+ it { expect(subject[ 'State']).to eq(address[ 'State']) }
86
+ it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
87
+ it { expect(subject['Country']).to eq(address['Country']) }
88
+ end
89
+
90
+ describe '#to_xml' do
91
+ let(:builder) do
92
+ Nokogiri::XML::Builder.new do |xml|
93
+ xml.entry(
94
+ 'xmlns' => 'http://www.w3.org/2005/Atom',
95
+ 'xmlns:data' => 'http://docs.oasis-open.org/odata/ns/data',
96
+ 'xmlns:metadata' => 'http://docs.oasis-open.org/odata/ns/metadata',
97
+ ) do
98
+ subject.to_xml(xml)
99
+ end
100
+ end
101
+ end
102
+ let(:xml) { Nokogiri::XML(builder.to_xml) }
103
+
104
+ before(:each) do
105
+ subject.value = address
106
+ xml.remove_namespaces!
107
+ end
108
+
109
+ it { expect(xml.xpath("/entry/Address[@type='ODataDemo.Address']").count).to eq(1) }
110
+ it { expect(xml.xpath('/entry/Address/Street').count).to eq(1) }
111
+ it { expect(xml.xpath('/entry/Address/City').count).to eq(1) }
112
+ it { expect(xml.xpath('/entry/Address/State').count).to eq(1) }
113
+ it { expect(xml.xpath('/entry/Address/ZipCode').count).to eq(1) }
114
+ it { expect(xml.xpath('/entry/Address/Country').count).to eq(1) }
115
+ end
116
+ end
@@ -0,0 +1,82 @@
1
+ shared_examples 'a valid product' do
2
+ it { expect(subject).to be_a(OData4::Entity) }
3
+
4
+ it { expect(subject.name).to eq('Product') }
5
+ it { expect(subject.type).to eq('ODataDemo.Product') }
6
+ it { expect(subject.namespace).to eq('ODataDemo') }
7
+ it { expect(subject.service_name).to eq('ODataDemo') }
8
+ it { expect(subject.context).to eq('http://services.odata.org/V4/OData/OData.svc/$metadata#Products/$entity') }
9
+ it { expect(subject.id).to eq('Products(0)') }
10
+
11
+ # Check property types
12
+ it { expect(subject.get_property('ID')).to be_a(OData4::Properties::Integer) }
13
+ it { expect(subject.get_property('Name')).to be_a(OData4::Properties::String) }
14
+ it { expect(subject.get_property('Description')).to be_a(OData4::Properties::String) }
15
+ it { expect(subject.get_property('ReleaseDate')).to be_a(OData4::Properties::DateTimeOffset) }
16
+ it { expect(subject.get_property('DiscontinuedDate')).to be_a(OData4::Properties::DateTimeOffset) }
17
+ it { expect(subject.get_property('Rating')).to be_a(OData4::Properties::Integer) }
18
+ it { expect(subject.get_property('Price')).to be_a(OData4::Properties::Double) }
19
+
20
+ # Navigation property proxies
21
+ it { expect(subject.get_property('Categories')).to be_a(OData4::NavigationProperty::Proxy)}
22
+ it { expect(subject.get_property('ProductDetail')).to be_a(OData4::NavigationProperty::Proxy)}
23
+ it { expect(subject.get_property('Supplier')).to be_a(OData4::NavigationProperty::Proxy)}
24
+
25
+ # Check property values
26
+ it { expect(subject['ID']).to eq(0) }
27
+ it { expect(subject['Name']).to eq('Bread') }
28
+ it { expect(subject['Description']).to eq('Whole grain bread') }
29
+ it { expect(subject['ReleaseDate']).to eq(DateTime.new(1992,1,1,0,0,0,0)) }
30
+ it { expect(subject['DiscontinuedDate']).to be_nil }
31
+ it { expect(subject['Rating']).to eq(4) }
32
+ it { expect(subject['Price']).to eq(2.5) }
33
+
34
+ # Navigation properties
35
+ it { expect(subject['Categories']).to be_a(Enumerable) }
36
+ it { expect(subject['ProductDetail']).to be_nil }
37
+ it { expect(subject['Supplier']).to be_a(OData4::Entity) }
38
+
39
+ it { expect {subject['NonExistant']}.to raise_error(ArgumentError) }
40
+ it { expect {subject['NonExistant'] = 5}.to raise_error(ArgumentError) }
41
+
42
+ describe '#links' do
43
+ let(:links) do
44
+ {
45
+ 'Categories' => {type: :collection, href: 'Products(0)/Categories'},
46
+ 'Supplier' => {type: :entity, href: 'Products(0)/Supplier'},
47
+ 'ProductDetail' => {type: :entity, href: 'Products(0)/ProductDetail'}
48
+ }
49
+ end
50
+
51
+ it { expect(subject).to respond_to(:links) }
52
+ it { expect(subject.links.size).to eq(3) }
53
+ it { expect(subject.links).to eq(links) }
54
+ end
55
+ end
56
+
57
+ shared_examples 'a valid supplier' do
58
+ it { expect(subject.name).to eq('Supplier') }
59
+ it { expect(subject.type).to eq('ODataDemo.Supplier') }
60
+ it { expect(subject.namespace).to eq('ODataDemo') }
61
+ it { expect(subject.service_name).to eq('ODataDemo') }
62
+ it { expect(subject.context).to eq('http://services.odata.org/V4/OData/OData.svc/$metadata#Suppliers/$entity') }
63
+ it { expect(subject.id).to eq('Suppliers(0)') }
64
+
65
+ # Check property types
66
+ it { expect(subject.get_property('ID')).to be_a(OData4::Properties::Integer) }
67
+ it { expect(subject.get_property('Name')).to be_a(OData4::Properties::String) }
68
+ it { expect(subject.get_property('Address')).to be_a(OData4::ComplexType::Property) }
69
+ it { expect(subject.get_property('Location')).to be_a(OData4::Properties::Geography::Point) }
70
+ # it { expect(subject.get_property('Products')).to be_a(OData4::NavigationProperty::Proxy)}
71
+
72
+ # Check property values
73
+ it { expect(subject['ID']).to eq(0) }
74
+ it { expect(subject['Name']).to eq('Exotic Liquids') }
75
+ it { expect(subject['Address'][ 'Street']).to eq('NE 228th') }
76
+ it { expect(subject['Address'][ 'City']).to eq('Sammamish') }
77
+ it { expect(subject['Address'][ 'State']).to eq('WA') }
78
+ it { expect(subject['Address']['ZipCode']).to eq('98074') }
79
+ it { expect(subject['Address']['Country']).to eq('USA') }
80
+ xit { expect(subject['Location']).to eq([47.6316604614258, -122.03547668457]) }
81
+ xit { expect(subject['Products']).to be_a(OData4::Entity) }
82
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+
3
+ describe OData4::EntitySet, vcr: {cassette_name: 'entity_set_specs'} do
4
+ before(:example) do
5
+ OData4::Service.open('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
6
+ end
7
+
8
+ let(:subject) { OData4::EntitySet.new(options) }
9
+ let(:options) { {
10
+ container: 'DemoService', namespace: 'ODataDemo', name: 'Products',
11
+ type: 'Product', service_name: 'ODataDemo'
12
+ } }
13
+
14
+ it { expect(subject).to respond_to(:name) }
15
+ it { expect(subject).to respond_to(:type) }
16
+ it { expect(subject).to respond_to(:container) }
17
+ it { expect(subject).to respond_to(:namespace) }
18
+ it { expect(subject).to respond_to(:service_name) }
19
+ it { expect(subject).to respond_to(:new_entity) }
20
+ it { expect(subject).to respond_to(:[]) }
21
+ it { expect(subject).to respond_to(:<<) }
22
+
23
+ it { expect(subject.name).to eq('Products') }
24
+ it { expect(subject.container).to eq('DemoService') }
25
+ it { expect(subject.namespace).to eq('ODataDemo') }
26
+ it { expect(subject.service_name).to eq('ODataDemo') }
27
+ it { expect(subject.type).to eq('Product') }
28
+
29
+ describe '#each' do
30
+ it { expect(subject).to respond_to(:each) }
31
+ it { expect(lambda {
32
+ @counter = 0
33
+ subject.each {|entity| @counter += 1}
34
+ @counter
35
+ }.call).to eq(11) }
36
+ it { expect(lambda {
37
+ @entities = []
38
+ subject.each {|entity| @entities << entity}
39
+ @entities
40
+ }.call.shuffle.first).to be_a(OData4::Entity) }
41
+ end
42
+
43
+ describe '#first' do
44
+ it { expect(subject).to respond_to(:first) }
45
+
46
+ describe 'retrieving a single entity' do
47
+ it { expect(subject.first).to be_a(OData4::Entity) }
48
+ it { expect(subject.first['ID']).to eq(0) }
49
+ end
50
+
51
+ describe 'retrieving multiple entities' do
52
+ it { expect(subject.first(5)).to be_a(Array) }
53
+ it { expect(subject.first(5).length).to eq(5) }
54
+ it do
55
+ subject.first(5).each do |entity|
56
+ expect(entity).to be_a(OData4::Entity)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#count' do
63
+ it { expect(subject).to respond_to(:count) }
64
+ it { expect(subject.count).to eq(11) }
65
+ end
66
+
67
+ describe '#query' do
68
+ it { expect(subject).to respond_to(:query) }
69
+ it { expect(subject.query).to be_a(OData4::Query) }
70
+ end
71
+
72
+ describe '#new_entity' do
73
+ let(:new_entity) { subject.new_entity(properties) }
74
+ let(:release_date) { DateTime.new(2014,7,5) }
75
+ let(:properties) { {
76
+ Name: 'Widget',
77
+ Description: 'Just a simple widget',
78
+ ReleaseDate: release_date,
79
+ DiscontinuedDate: nil,
80
+ Rating: 4,
81
+ Price: 3.5
82
+ } }
83
+
84
+ it { expect(new_entity.entity_set).to eq(subject) }
85
+ it { expect(new_entity['ID']).to be_nil }
86
+ it { expect(new_entity['Name']).to eq('Widget') }
87
+ it { expect(new_entity['Description']).to eq('Just a simple widget') }
88
+ it { expect(new_entity['ReleaseDate']).to eq(release_date) }
89
+ it { expect(new_entity['DiscontinuedDate']).to be_nil }
90
+ it { expect(new_entity['Rating']).to eq(4) }
91
+ it { expect(new_entity['Price']).to eq(3.5) }
92
+ end
93
+
94
+ describe '#[]' do
95
+ let(:existing_entity) { subject[0] }
96
+ let(:nonexistant_entity) { subject[99] }
97
+
98
+ it 'finds an entity by its primary key' do
99
+ expect(existing_entity).to be_a(OData4::Entity)
100
+ expect(existing_entity['ID']).to eq(0)
101
+ end
102
+
103
+ it 'raises an error when no entity was found' do
104
+ expect { nonexistant_entity }.to raise_error(RuntimeError, /Not Found/)
105
+ end
106
+
107
+ describe 'eager loading' do
108
+ it 'works with a single property' do
109
+ product_with_categories = subject[1, expand: 'Categories']
110
+
111
+ expect(product_with_categories['Categories']).to eq([
112
+ { "ID" => 0, "Name" => "Food" },
113
+ { "ID" => 1, "Name" => "Beverages" }
114
+ ])
115
+ end
116
+
117
+ it 'works with multiple properties' do
118
+ product_with_details = subject[1, expand: %w[Categories Supplier]]
119
+
120
+ expect(product_with_details['Supplier']).to include('Name' => 'Exotic Liquids')
121
+ expect(product_with_details['Categories']).to be_a(Array)
122
+ end
123
+
124
+ it 'works with special shortcut for all properties' do
125
+ product_with_all_details = subject[1, expand: :all]
126
+
127
+ expect(product_with_all_details['Supplier']).to include('Name' => 'Exotic Liquids')
128
+ expect(product_with_all_details['Categories']).to be_a(Array)
129
+ expect(product_with_all_details['ProductDetail']).to include('Details' => 'Details of product 1')
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#<<' do
135
+ let(:new_entity) { subject.new_entity(properties) }
136
+ let(:bad_entity) { subject.new_entity }
137
+ let(:existing_entity) { subject.first }
138
+ let(:properties) { {
139
+ Name: 'Widget',
140
+ Description: 'Just a simple widget',
141
+ ReleaseDate: DateTime.now.new_offset(0),
142
+ DiscontinuedDate: nil,
143
+ Rating: 4,
144
+ Price: 3.5
145
+ } }
146
+
147
+ xdescribe 'with an existing entity', vcr: {cassette_name: 'entity_set_specs/existing_entry'} do
148
+ before(:each) do
149
+ subject << existing_entity
150
+ end
151
+
152
+ it { expect(existing_entity.any_errors?).to eq(false) }
153
+ end
154
+
155
+ xdescribe 'with a new entity', vcr: {cassette_name: 'entity_set_specs/new_entry'} do
156
+ it do
157
+ expect(new_entity['ID']).to be_nil
158
+ expect {subject << new_entity}.to_not raise_error
159
+ expect(new_entity['ID']).to_not be_nil
160
+ expect(new_entity['ID']).to eq(9999)
161
+ end
162
+ end
163
+
164
+ xdescribe 'with a bad entity', vcr: {cassette_name: 'entity_set_specs/bad_entry'} do
165
+ it { expect{subject << bad_entity}.to raise_error }
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+ require_relative 'entity/shared_examples'
3
+
4
+ describe OData4::Entity, vcr: {cassette_name: 'entity_specs'} do
5
+ before(:example) do
6
+ OData4::Service.open('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
7
+ end
8
+
9
+ let(:subject) { OData4::Entity.new(options) }
10
+ let(:options) { {
11
+ type: 'ODataDemo.Product',
12
+ namespace: 'ODataDemo',
13
+ service_name: 'ODataDemo'
14
+ } }
15
+
16
+ it { expect(subject).to respond_to(:name, :type, :namespace, :service_name) }
17
+
18
+ it { expect(subject.name).to eq('Product') }
19
+ it { expect(subject.type).to eq('ODataDemo.Product') }
20
+ it { expect(subject.namespace).to eq('ODataDemo') }
21
+ it { expect(subject.service_name).to eq('ODataDemo') }
22
+
23
+ describe '.with_properties' do
24
+ let(:subject) { OData4::Entity.with_properties(properties, options) }
25
+ let(:properties) { {
26
+ "ID" => 0,
27
+ "Name" => "Bread",
28
+ "Description" => "Whole grain bread",
29
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
30
+ "DiscontinuedDate" => nil,
31
+ "Rating" => 4,
32
+ "Price" => 2.5
33
+ } }
34
+ let(:entity_set) {
35
+ OData4::EntitySet.new(
36
+ container: 'DemoService',
37
+ namespace: 'ODataDemo',
38
+ name: 'Products',
39
+ type: 'Product',
40
+ service_name: 'ODataDemo')
41
+ }
42
+ let(:options) { {
43
+ type: 'ODataDemo.Product',
44
+ namespace: 'ODataDemo',
45
+ service_name: 'ODataDemo',
46
+ entity_set: entity_set
47
+ } }
48
+
49
+ it_behaves_like 'a valid product'
50
+ end
51
+
52
+ describe '.from_xml' do
53
+ let(:subject) { OData4::Entity.from_xml(product_xml, options) }
54
+ let(:product_xml) {
55
+ document = ::Nokogiri::XML(File.open('spec/fixtures/files/product_0.xml'))
56
+ document.remove_namespaces!
57
+ document.xpath('//entry').first
58
+ }
59
+
60
+ it { expect(OData4::Entity).to respond_to(:from_xml) }
61
+
62
+ it_behaves_like 'a valid product'
63
+
64
+ context 'with a complex type property' do
65
+ let(:options) { {
66
+ type: 'ODataDemo.Supplier',
67
+ namespace: 'ODataDemo',
68
+ service_name: 'ODataDemo'
69
+ } }
70
+
71
+ let(:subject) { OData4::Entity.from_xml(supplier_xml, options) }
72
+ let(:supplier_xml) {
73
+ document = ::Nokogiri::XML(File.open('spec/fixtures/files/supplier_0.xml'))
74
+ document.remove_namespaces!
75
+ document.xpath('//entry').first
76
+ }
77
+
78
+ it_behaves_like 'a valid supplier'
79
+ end
80
+ end
81
+
82
+ describe '#to_xml' do
83
+ let(:subject) { OData4::Entity.with_properties(properties, options) }
84
+ let(:properties) { {
85
+ "ID" => 0,
86
+ "Name" => "Bread",
87
+ "Description" => "Whole grain bread",
88
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
89
+ "DiscontinuedDate" => nil,
90
+ "Rating" => 4,
91
+ "Price" => 2.5
92
+ } }
93
+ let(:options) { {
94
+ type: 'ODataDemo.Product',
95
+ namespace: 'ODataDemo',
96
+ service_name: 'ODataDemo'
97
+ } }
98
+ let(:product_xml) {
99
+ File.read('spec/fixtures/files/entity_to_xml.xml')
100
+ }
101
+
102
+ # TODO: parse the XML and veryify property values instead?
103
+ # TODO: explicitly assert namespace URIs?
104
+ it { expect(subject.to_xml).to eq(product_xml) }
105
+ end
106
+
107
+ describe '.from_json' do
108
+ let(:subject) { OData4::Entity.from_json(product_json, options) }
109
+ let(:product_json) {
110
+ File.read('spec/fixtures/files/product_0.json')
111
+ }
112
+
113
+ it { expect(OData4::Entity).to respond_to(:from_json) }
114
+ it_behaves_like 'a valid product'
115
+
116
+ context 'with a complex type property' do
117
+ let(:options) { {
118
+ type: 'ODataDemo.Supplier',
119
+ namespace: 'ODataDemo',
120
+ service_name: 'ODataDemo'
121
+ } }
122
+
123
+ let(:subject) { OData4::Entity.from_json(supplier_json, options) }
124
+ let(:supplier_json) {
125
+ File.read('spec/fixtures/files/supplier_0.json')
126
+ }
127
+
128
+ it_behaves_like 'a valid supplier'
129
+ end
130
+ end
131
+
132
+ describe '#to_json' do
133
+ let(:subject) { OData4::Entity.with_properties(properties, options) }
134
+ let(:properties) { {
135
+ "ID" => 0,
136
+ "Name" => "Bread",
137
+ "Description" => "Whole grain bread",
138
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
139
+ "DiscontinuedDate" => nil,
140
+ "Rating" => 4,
141
+ "Price" => 2.5
142
+ } }
143
+ let(:options) { {
144
+ type: 'ODataDemo.Product',
145
+ namespace: 'ODataDemo',
146
+ service_name: 'ODataDemo'
147
+ } }
148
+
149
+ it { expect(subject.to_json).to eq(properties.to_json) }
150
+ end
151
+ end