frodata 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +2 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +75 -0
- data/CHANGELOG.md +150 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +427 -0
- data/Rakefile +7 -0
- data/TODO.md +55 -0
- data/frodata.gemspec +34 -0
- data/lib/frodata.rb +36 -0
- data/lib/frodata/entity.rb +332 -0
- data/lib/frodata/entity_container.rb +75 -0
- data/lib/frodata/entity_set.rb +161 -0
- data/lib/frodata/errors.rb +68 -0
- data/lib/frodata/navigation_property.rb +29 -0
- data/lib/frodata/navigation_property/proxy.rb +80 -0
- data/lib/frodata/properties.rb +32 -0
- data/lib/frodata/properties/binary.rb +50 -0
- data/lib/frodata/properties/boolean.rb +37 -0
- data/lib/frodata/properties/collection.rb +50 -0
- data/lib/frodata/properties/complex.rb +114 -0
- data/lib/frodata/properties/date.rb +27 -0
- data/lib/frodata/properties/date_time.rb +83 -0
- data/lib/frodata/properties/date_time_offset.rb +17 -0
- data/lib/frodata/properties/decimal.rb +50 -0
- data/lib/frodata/properties/enum.rb +62 -0
- data/lib/frodata/properties/float.rb +67 -0
- data/lib/frodata/properties/geography.rb +13 -0
- data/lib/frodata/properties/geography/base.rb +162 -0
- data/lib/frodata/properties/geography/line_string.rb +33 -0
- data/lib/frodata/properties/geography/point.rb +31 -0
- data/lib/frodata/properties/geography/polygon.rb +38 -0
- data/lib/frodata/properties/guid.rb +17 -0
- data/lib/frodata/properties/integer.rb +107 -0
- data/lib/frodata/properties/number.rb +14 -0
- data/lib/frodata/properties/string.rb +72 -0
- data/lib/frodata/properties/time.rb +40 -0
- data/lib/frodata/properties/time_of_day.rb +27 -0
- data/lib/frodata/property.rb +139 -0
- data/lib/frodata/property_registry.rb +41 -0
- data/lib/frodata/query.rb +233 -0
- data/lib/frodata/query/criteria.rb +92 -0
- data/lib/frodata/query/criteria/comparison_operators.rb +49 -0
- data/lib/frodata/query/criteria/date_functions.rb +61 -0
- data/lib/frodata/query/criteria/geography_functions.rb +21 -0
- data/lib/frodata/query/criteria/lambda_operators.rb +27 -0
- data/lib/frodata/query/criteria/string_functions.rb +40 -0
- data/lib/frodata/query/in_batches.rb +58 -0
- data/lib/frodata/railtie.rb +19 -0
- data/lib/frodata/schema.rb +155 -0
- data/lib/frodata/schema/complex_type.rb +79 -0
- data/lib/frodata/schema/enum_type.rb +95 -0
- data/lib/frodata/service.rb +254 -0
- data/lib/frodata/service/request.rb +85 -0
- data/lib/frodata/service/response.rb +162 -0
- data/lib/frodata/service/response/atom.rb +40 -0
- data/lib/frodata/service/response/json.rb +41 -0
- data/lib/frodata/service/response/plain.rb +36 -0
- data/lib/frodata/service/response/xml.rb +40 -0
- data/lib/frodata/service_registry.rb +52 -0
- data/lib/frodata/version.rb +3 -0
- data/spec/fixtures/files/entity_to_xml.xml +17 -0
- data/spec/fixtures/files/error.xml +5 -0
- data/spec/fixtures/files/metadata.xml +150 -0
- data/spec/fixtures/files/product_0.json +10 -0
- data/spec/fixtures/files/product_0.xml +28 -0
- data/spec/fixtures/files/products.json +106 -0
- data/spec/fixtures/files/products.xml +308 -0
- data/spec/fixtures/files/supplier_0.json +26 -0
- data/spec/fixtures/files/supplier_0.xml +32 -0
- data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1635 -0
- data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
- data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
- data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
- data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
- data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
- data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
- data/spec/fixtures/vcr_cassettes/query_specs.yml +1060 -0
- data/spec/fixtures/vcr_cassettes/schema/complex_type_specs.yml +127 -0
- data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
- data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
- data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
- data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +1330 -0
- data/spec/frodata/entity/shared_examples.rb +82 -0
- data/spec/frodata/entity_container_spec.rb +38 -0
- data/spec/frodata/entity_set_spec.rb +168 -0
- data/spec/frodata/entity_spec.rb +151 -0
- data/spec/frodata/errors_spec.rb +48 -0
- data/spec/frodata/navigation_property/proxy_spec.rb +44 -0
- data/spec/frodata/navigation_property_spec.rb +55 -0
- data/spec/frodata/properties/binary_spec.rb +50 -0
- data/spec/frodata/properties/boolean_spec.rb +72 -0
- data/spec/frodata/properties/collection_spec.rb +44 -0
- data/spec/frodata/properties/date_spec.rb +23 -0
- data/spec/frodata/properties/date_time_offset_spec.rb +30 -0
- data/spec/frodata/properties/date_time_spec.rb +23 -0
- data/spec/frodata/properties/decimal_spec.rb +51 -0
- data/spec/frodata/properties/float_spec.rb +45 -0
- data/spec/frodata/properties/geography/line_string_spec.rb +33 -0
- data/spec/frodata/properties/geography/point_spec.rb +29 -0
- data/spec/frodata/properties/geography/polygon_spec.rb +55 -0
- data/spec/frodata/properties/geography/shared_examples.rb +72 -0
- data/spec/frodata/properties/guid_spec.rb +17 -0
- data/spec/frodata/properties/integer_spec.rb +58 -0
- data/spec/frodata/properties/string_spec.rb +46 -0
- data/spec/frodata/properties/time_of_day_spec.rb +23 -0
- data/spec/frodata/properties/time_spec.rb +15 -0
- data/spec/frodata/property_registry_spec.rb +16 -0
- data/spec/frodata/property_spec.rb +71 -0
- data/spec/frodata/query/criteria_spec.rb +229 -0
- data/spec/frodata/query_spec.rb +199 -0
- data/spec/frodata/schema/complex_type_spec.rb +96 -0
- data/spec/frodata/schema/enum_type_spec.rb +112 -0
- data/spec/frodata/schema_spec.rb +97 -0
- data/spec/frodata/service/request_spec.rb +49 -0
- data/spec/frodata/service/response_spec.rb +85 -0
- data/spec/frodata/service_registry_spec.rb +18 -0
- data/spec/frodata/service_spec.rb +191 -0
- data/spec/frodata/usage_example_spec.rb +188 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/coverage.rb +2 -0
- data/spec/support/vcr.rb +9 -0
- metadata +401 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'an operator criterium' do |operator, value, to_s|
|
4
|
+
it { expect(criteria).to eq(subject) }
|
5
|
+
it { expect(criteria.operator).to eq(operator) }
|
6
|
+
it { expect(criteria.value).to eq(value) }
|
7
|
+
it { expect(criteria.to_s).to eq(to_s) }
|
8
|
+
end
|
9
|
+
|
10
|
+
shared_examples 'a function criterium' do |function, argument, to_s|
|
11
|
+
it { expect(criteria).to eq(subject) }
|
12
|
+
it { expect(criteria.function).to eq(function) }
|
13
|
+
it { expect(criteria.argument).to eq(argument) }
|
14
|
+
it { expect(criteria.to_s).to eq(to_s) }
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples 'an operator-function criterium' do |function, operator, value, to_s|
|
18
|
+
it { expect(criteria).to eq(subject) }
|
19
|
+
it { expect(criteria.function).to eq(function) }
|
20
|
+
it { expect(criteria.operator).to eq(operator) }
|
21
|
+
it { expect(criteria.value).to eq(value) }
|
22
|
+
it { expect(criteria.to_s).to eq(to_s) }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe FrOData::Query::Criteria do
|
26
|
+
let(:string_property) { FrOData::Properties::String.new(:Name, nil) }
|
27
|
+
let(:integer_property) { FrOData::Properties::Integer.new(:Age, nil) }
|
28
|
+
let(:datetime_property) { FrOData::Properties::DateTime.new(:BirthDate, nil) }
|
29
|
+
let(:geo_property) { FrOData::Properties::Geography::Point.new(:Position, nil) }
|
30
|
+
# TODO: use actual `NavigationProperty` class here
|
31
|
+
let(:navigation_property) { FrOData::Property.new(:Items, nil) }
|
32
|
+
|
33
|
+
let(:subject) { FrOData::Query::Criteria.new(property: string_property) }
|
34
|
+
|
35
|
+
it { expect(subject).to respond_to(:property) }
|
36
|
+
it { expect(subject.property).to eq(string_property)}
|
37
|
+
|
38
|
+
it { expect(subject).to respond_to(:operator) }
|
39
|
+
it { expect(subject).to respond_to(:value) }
|
40
|
+
it { expect(subject).to respond_to(:to_s) }
|
41
|
+
|
42
|
+
describe 'comparison operators' do
|
43
|
+
describe '#eq' do
|
44
|
+
let(:criteria) { subject.eq('Bread') }
|
45
|
+
|
46
|
+
it { expect(subject).to respond_to(:eq) }
|
47
|
+
it_behaves_like 'an operator criterium', :eq, 'Bread', "Name eq 'Bread'"
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#ne' do
|
51
|
+
let(:criteria) { subject.ne('Bread') }
|
52
|
+
|
53
|
+
it { expect(subject).to respond_to(:ne) }
|
54
|
+
it_behaves_like 'an operator criterium', :ne, 'Bread', "Name ne 'Bread'"
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#gt' do
|
58
|
+
let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
|
59
|
+
let(:criteria) { subject.gt(5) }
|
60
|
+
|
61
|
+
it { expect(subject).to respond_to(:gt) }
|
62
|
+
it_behaves_like 'an operator criterium', :gt, 5, "Age gt 5"
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#ge' do
|
66
|
+
let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
|
67
|
+
let(:criteria) { subject.ge(5) }
|
68
|
+
|
69
|
+
it { expect(subject).to respond_to(:ge) }
|
70
|
+
it_behaves_like 'an operator criterium', :ge, 5, 'Age ge 5'
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#lt' do
|
74
|
+
let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
|
75
|
+
let(:criteria) { subject.lt(5) }
|
76
|
+
|
77
|
+
it { expect(subject).to respond_to(:lt) }
|
78
|
+
it_behaves_like 'an operator criterium', :lt, 5, 'Age lt 5'
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#le' do
|
82
|
+
let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
|
83
|
+
let(:criteria) { subject.le(5) }
|
84
|
+
|
85
|
+
it { expect(subject).to respond_to(:le) }
|
86
|
+
it_behaves_like 'an operator criterium', :le, 5, 'Age le 5'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'string functions' do
|
91
|
+
describe '#contains' do
|
92
|
+
let(:criteria) { subject.contains('freds') }
|
93
|
+
|
94
|
+
it { expect(subject).to respond_to(:contains) }
|
95
|
+
it_behaves_like 'a function criterium', :contains, 'freds', "contains(Name,'freds')"
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#startswith' do
|
99
|
+
let(:criteria) { subject.startswith('Alfreds') }
|
100
|
+
|
101
|
+
it { expect(subject).to respond_to(:startswith) }
|
102
|
+
it_behaves_like 'a function criterium', :startswith, 'Alfreds', "startswith(Name,'Alfreds')"
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#endswith' do
|
106
|
+
let(:criteria) { subject.endswith('Futterkiste') }
|
107
|
+
|
108
|
+
it { expect(subject).to respond_to(:endswith) }
|
109
|
+
it_behaves_like 'a function criterium', :endswith, 'Futterkiste', "endswith(Name,'Futterkiste')"
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '#tolower' do
|
113
|
+
let(:criteria) { subject.tolower.eq('alfreds futterkiste') }
|
114
|
+
|
115
|
+
it { expect(subject).to respond_to(:tolower) }
|
116
|
+
it_behaves_like 'an operator-function criterium', :tolower, :eq, 'alfreds futterkiste', "tolower(Name) eq 'alfreds futterkiste'"
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#toupper' do
|
120
|
+
let(:criteria) { subject.toupper.eq('ALFREDS FUTTERKISTE') }
|
121
|
+
|
122
|
+
it { expect(subject).to respond_to(:toupper) }
|
123
|
+
it_behaves_like 'an operator-function criterium', :toupper, :eq, 'ALFREDS FUTTERKISTE', "toupper(Name) eq 'ALFREDS FUTTERKISTE'"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'date functions' do
|
128
|
+
let(:subject) { FrOData::Query::Criteria.new(property: datetime_property) }
|
129
|
+
|
130
|
+
describe '#year' do
|
131
|
+
let(:criteria) { subject.year.eq(1981) }
|
132
|
+
|
133
|
+
it { expect(subject).to respond_to(:year) }
|
134
|
+
it_behaves_like 'an operator-function criterium', :year, :eq, 1981, 'year(BirthDate) eq 1981'
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#month' do
|
138
|
+
let(:criteria) { subject.month.eq(8) }
|
139
|
+
|
140
|
+
it { expect(subject).to respond_to(:month) }
|
141
|
+
it_behaves_like 'an operator-function criterium', :month, :eq, 8, 'month(BirthDate) eq 8'
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#day' do
|
145
|
+
let(:criteria) { subject.day.eq(27) }
|
146
|
+
|
147
|
+
it { expect(subject).to respond_to(:day) }
|
148
|
+
it_behaves_like 'an operator-function criterium', :day, :eq, 27, 'day(BirthDate) eq 27'
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#hour' do
|
152
|
+
let(:criteria) { subject.hour.eq(9) }
|
153
|
+
|
154
|
+
it { expect(subject).to respond_to(:hour) }
|
155
|
+
it_behaves_like 'an operator-function criterium', :hour, :eq, 9, 'hour(BirthDate) eq 9'
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#minute' do
|
159
|
+
let(:criteria) { subject.minute.eq(35) }
|
160
|
+
|
161
|
+
it { expect(subject).to respond_to(:minute) }
|
162
|
+
it_behaves_like 'an operator-function criterium', :minute, :eq, 35, 'minute(BirthDate) eq 35'
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#second' do
|
166
|
+
let(:criteria) { subject.second.eq(11) }
|
167
|
+
|
168
|
+
it { expect(subject).to respond_to(:second) }
|
169
|
+
it_behaves_like 'an operator-function criterium', :second, :eq, 11, 'second(BirthDate) eq 11'
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#fractionalseconds' do
|
173
|
+
let(:criteria) { subject.fractionalseconds.eq(0) }
|
174
|
+
|
175
|
+
it { expect(subject).to respond_to(:fractionalseconds) }
|
176
|
+
it_behaves_like 'an operator-function criterium', :fractionalseconds, :eq, 0, 'fractionalseconds(BirthDate) eq 0'
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#date' do
|
180
|
+
let(:criteria) { subject.date.ne('date(EndTime)') }
|
181
|
+
|
182
|
+
it { expect(subject).to respond_to(:date) }
|
183
|
+
it_behaves_like 'an operator-function criterium', :date, :ne, 'date(EndTime)', 'date(BirthDate) ne date(EndTime)'
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#time' do
|
187
|
+
let(:criteria) { subject.time.le('StartOfDay') }
|
188
|
+
|
189
|
+
it { expect(subject).to respond_to(:time) }
|
190
|
+
it_behaves_like 'an operator-function criterium', :time, :le, 'StartOfDay', 'time(BirthDate) le StartOfDay'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'geospatial functions' do
|
195
|
+
let(:subject) { FrOData::Query::Criteria.new(property: geo_property) }
|
196
|
+
|
197
|
+
describe '#distance' do
|
198
|
+
let(:criteria) { subject.distance('TargetPosition').le(42) }
|
199
|
+
|
200
|
+
it { expect(subject).to respond_to(:distance) }
|
201
|
+
it_behaves_like 'an operator-function criterium', :'geo.distance', :le, 42, 'geo.distance(Position,TargetPosition) le 42'
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#intersects' do
|
205
|
+
let(:criteria) { subject.intersects('TargetArea') }
|
206
|
+
|
207
|
+
it { expect(subject).to respond_to(:intersects) }
|
208
|
+
it_behaves_like 'a function criterium', :'geo.intersects', 'TargetArea', 'geo.intersects(Position,TargetArea)'
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe 'lambda operators' do
|
213
|
+
let(:subject) { FrOData::Query::Criteria.new(property: navigation_property) }
|
214
|
+
|
215
|
+
describe '#any' do
|
216
|
+
let(:criteria) { subject.any('Quantity').gt(100) }
|
217
|
+
|
218
|
+
it { expect(subject).to respond_to(:any) }
|
219
|
+
it_behaves_like 'an operator-function criterium', :any, :gt, 100, 'Items/any(d:d/Quantity gt 100)'
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '#all' do
|
223
|
+
let(:criteria) { subject.all('Quantity').gt(100) }
|
224
|
+
|
225
|
+
it { expect(subject).to respond_to(:all) }
|
226
|
+
it_behaves_like 'an operator-function criterium', :all, :gt, 100, 'Items/all(d:d/Quantity gt 100)'
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FrOData::Query, vcr: {cassette_name: 'query_specs'} do
|
4
|
+
before(:example) do
|
5
|
+
FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:subject) { FrOData::Query.new(entity_set) }
|
9
|
+
let(:entity_set) { FrOData::EntitySet.new(options) }
|
10
|
+
let(:options) { {
|
11
|
+
service_name: 'ODataDemo',
|
12
|
+
container: 'DemoService',
|
13
|
+
namespace: 'ODataDemo',
|
14
|
+
name: 'Products',
|
15
|
+
type: 'ODataDemo.Product'
|
16
|
+
} }
|
17
|
+
|
18
|
+
it { expect(subject).to respond_to(:to_s) }
|
19
|
+
it { expect(subject.to_s).to eq('Products')}
|
20
|
+
|
21
|
+
it { expect(subject).to respond_to(:[]) }
|
22
|
+
describe '#[]' do
|
23
|
+
it { expect(subject[:Name]).to be_a(FrOData::Query::Criteria) }
|
24
|
+
it { expect(subject[:Name].property).to be_a(FrOData::Property) }
|
25
|
+
end
|
26
|
+
|
27
|
+
it { expect(subject).to respond_to(:find) }
|
28
|
+
describe '#find' do
|
29
|
+
let(:product) { subject.find(0) }
|
30
|
+
|
31
|
+
it 'finds an entity by its ID' do
|
32
|
+
expect(product).to be_a(FrOData::Entity)
|
33
|
+
expect(product['ID']).to eq(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'allows expanding navigational properties' do
|
37
|
+
product_with_categories = subject.expand('Categories').find(0)
|
38
|
+
expect(product_with_categories['Categories']).to eq([
|
39
|
+
{ "ID" => 0, "Name" => "Food" }
|
40
|
+
])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it { expect(subject).to respond_to(:where) }
|
45
|
+
describe '#where' do
|
46
|
+
let(:criteria) { subject[:Name].eq('Bread') }
|
47
|
+
let(:query_string) { "Products?$filter=Name eq 'Bread'" }
|
48
|
+
|
49
|
+
it { expect(subject.where(criteria)).to eq(subject) }
|
50
|
+
it { expect(subject.where(criteria).to_s).to eq(query_string) }
|
51
|
+
end
|
52
|
+
|
53
|
+
it { expect(subject).to respond_to(:search) }
|
54
|
+
describe '#search' do
|
55
|
+
let(:term) { '"mountain bike"' }
|
56
|
+
let(:query_string) { 'Products?$search="mountain bike"' }
|
57
|
+
|
58
|
+
it { expect(subject.search(term)).to eq(subject) }
|
59
|
+
it { expect(subject.search(term).to_s).to eq(query_string) }
|
60
|
+
|
61
|
+
describe 'with multiple terms' do
|
62
|
+
let(:query_string) { 'Products?$search="mountain bike" AND NOT clothing' }
|
63
|
+
|
64
|
+
it { expect(subject.search(term).search('NOT clothing').to_s).to eq(query_string) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#it { expect(subject).to respond_to(:and) }
|
69
|
+
describe '#and' do
|
70
|
+
it { pending; fail }
|
71
|
+
end
|
72
|
+
|
73
|
+
#it { expect(subject).to respond_to(:or) }
|
74
|
+
describe '#or' do
|
75
|
+
it { pending; fail }
|
76
|
+
end
|
77
|
+
|
78
|
+
it { expect(subject).to respond_to(:skip) }
|
79
|
+
describe '#skip' do
|
80
|
+
it { expect(subject.skip(5)).to eq(subject) }
|
81
|
+
it 'properly formats query with skip specified' do
|
82
|
+
subject.skip(5)
|
83
|
+
expect(subject.to_s).to eq('Products?$skip=5')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it { expect(subject).to respond_to(:limit) }
|
88
|
+
describe '#limit' do
|
89
|
+
|
90
|
+
it { expect(subject.limit(5)).to eq(subject) }
|
91
|
+
it 'properly formats query with limit specified' do
|
92
|
+
subject.limit(5)
|
93
|
+
expect(subject.to_s).to eq('Products?$top=5')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it { expect(subject).to respond_to(:include_count) }
|
98
|
+
describe '#include_count' do
|
99
|
+
it { expect(subject.include_count).to eq(subject) }
|
100
|
+
it 'properly formats query with include_count specified' do
|
101
|
+
subject.include_count
|
102
|
+
expect(subject.to_s).to eq('Products?$count=true')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it { expect(subject).to respond_to(:select) }
|
107
|
+
describe '#select' do
|
108
|
+
it { expect(subject.select(:Name, :Price)).to eq(subject) }
|
109
|
+
it 'properly formats query with select operation specified' do
|
110
|
+
subject.select(:Name, :Price)
|
111
|
+
expect(subject.to_s).to eq('Products?$select=Name,Price')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it { expect(subject).to respond_to(:expand) }
|
116
|
+
describe '#expand' do
|
117
|
+
it { expect(subject.expand(:Supplier)).to eq(subject) }
|
118
|
+
it 'properly formats query with expand operation specified' do
|
119
|
+
subject.expand(:Supplier)
|
120
|
+
expect(subject.to_s).to eq('Products?$expand=Supplier')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it { expect(subject).to respond_to(:order_by) }
|
125
|
+
describe '#order_by' do
|
126
|
+
it { expect(subject.order_by(:Name, :Price)).to eq(subject) }
|
127
|
+
it 'properly formats query with orderby operation specified' do
|
128
|
+
subject.order_by(:Name, :Price)
|
129
|
+
expect(subject.to_s).to eq('Products?$orderby=Name,Price')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#execute' do
|
134
|
+
it { expect(subject).to respond_to(:execute) }
|
135
|
+
it { expect(subject.execute).to be_a(FrOData::Service::Response) }
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#count' do
|
139
|
+
it { expect(subject).to respond_to(:count) }
|
140
|
+
it { expect(subject.count).to be_a(Integer) }
|
141
|
+
it { expect(subject.count).to eq(11) }
|
142
|
+
|
143
|
+
context 'with filters' do
|
144
|
+
let(:criteria) { subject[:Name].eq('Bread') }
|
145
|
+
|
146
|
+
it { expect(subject.where(criteria).count).to eq(1) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#empty?' do
|
151
|
+
it { expect(subject).to respond_to(:empty?) }
|
152
|
+
it { expect(subject.empty?).to eq(false) }
|
153
|
+
|
154
|
+
context 'with filters' do
|
155
|
+
let(:non_empty_criteria) { subject[:Name].eq('Bread') }
|
156
|
+
let(:empty_criteria) { subject[:Name].eq('NonExistent') }
|
157
|
+
|
158
|
+
it { expect(subject.where(non_empty_criteria).empty?).to eq(false) }
|
159
|
+
it { expect(subject.where(empty_criteria).empty?).to eq(true) }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#in_batches' do
|
164
|
+
it { expect(subject).to respond_to(:in_batches) }
|
165
|
+
it 'returns FrOData::Entities in batches of specified size' do
|
166
|
+
batch_count = entity_count = 0
|
167
|
+
|
168
|
+
subject.in_batches(of: 5) do |batch|
|
169
|
+
expect(batch).to be_a(FrOData::Service::Response)
|
170
|
+
expect(batch.count).to eq(5) unless batch_count == 2
|
171
|
+
|
172
|
+
batch.each do |entity|
|
173
|
+
expect(entity).to be_a(FrOData::Entity)
|
174
|
+
expect(entity.type).to eq('ODataDemo.Product')
|
175
|
+
entity_count += 1
|
176
|
+
end
|
177
|
+
|
178
|
+
batch_count += 1
|
179
|
+
end
|
180
|
+
|
181
|
+
expect(batch_count).to eq(3)
|
182
|
+
expect(entity_count).to eq(11)
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '#each' do
|
186
|
+
it 'returns FrOData::Entities' do
|
187
|
+
entity_count = 0
|
188
|
+
|
189
|
+
subject.in_batches(of: 5).each do |entity|
|
190
|
+
expect(entity).to be_a(FrOData::Entity)
|
191
|
+
expect(entity.type).to eq('ODataDemo.Product')
|
192
|
+
entity_count += 1
|
193
|
+
end
|
194
|
+
|
195
|
+
expect(entity_count).to eq(11)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FrOData::Schema::ComplexType, vcr: {cassette_name: 'schema/complex_type_specs'} do
|
4
|
+
before(:example) do
|
5
|
+
FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:service) { FrOData::ServiceRegistry['ODataDemo'] }
|
9
|
+
|
10
|
+
let(:address) { {
|
11
|
+
'Street' => '123 Main St',
|
12
|
+
'City' => 'Huntington Beach',
|
13
|
+
'State' => 'CA',
|
14
|
+
'ZipCode' => '92648',
|
15
|
+
'Country' => 'USA'
|
16
|
+
} }
|
17
|
+
|
18
|
+
let(:complex_type) { service.complex_types['ODataDemo.Address'] }
|
19
|
+
let(:subject) { complex_type.property_class.new('Address', nil) }
|
20
|
+
|
21
|
+
describe 'is properly parsed from service metadata' do
|
22
|
+
it { expect(complex_type.name).to eq('Address') }
|
23
|
+
it { expect(complex_type.namespace).to eq('ODataDemo') }
|
24
|
+
it { expect(complex_type.type).to eq('ODataDemo.Address') }
|
25
|
+
it { expect(complex_type.property_names).to eq(%w{Street City State ZipCode Country}) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Check property instance inheritance hierarchy
|
29
|
+
it { expect(subject).to be_a(FrOData::Property) }
|
30
|
+
it { expect(subject).to be_a(FrOData::Properties::Complex) }
|
31
|
+
|
32
|
+
it { expect(subject).to respond_to(:name) }
|
33
|
+
it { expect(subject).to respond_to(:type) }
|
34
|
+
it { expect(subject).to respond_to(:property_names) }
|
35
|
+
it { expect(subject).to respond_to(:[]) }
|
36
|
+
it { expect(subject).to respond_to(:[]=) }
|
37
|
+
|
38
|
+
it { expect(subject[ 'Street']).to be_nil }
|
39
|
+
it { expect(subject[ 'City']).to be_nil }
|
40
|
+
it { expect(subject[ 'State']).to be_nil }
|
41
|
+
it { expect(subject['ZipCode']).to be_nil }
|
42
|
+
it { expect(subject['Country']).to be_nil }
|
43
|
+
|
44
|
+
describe '#[]=' do
|
45
|
+
before do
|
46
|
+
address.each { |key, val| subject[key] = val }
|
47
|
+
end
|
48
|
+
|
49
|
+
it { expect(subject.value).to eq(address) }
|
50
|
+
|
51
|
+
it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
|
52
|
+
it { expect(subject[ 'City']).to eq(address[ 'City']) }
|
53
|
+
it { expect(subject[ 'State']).to eq(address[ 'State']) }
|
54
|
+
it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
|
55
|
+
it { expect(subject['Country']).to eq(address['Country']) }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#value=' do
|
59
|
+
before { subject.value = address }
|
60
|
+
|
61
|
+
it { expect(subject.value).to eq(address) }
|
62
|
+
|
63
|
+
it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
|
64
|
+
it { expect(subject[ 'City']).to eq(address[ 'City']) }
|
65
|
+
it { expect(subject[ 'State']).to eq(address[ 'State']) }
|
66
|
+
it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
|
67
|
+
it { expect(subject['Country']).to eq(address['Country']) }
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#to_xml' do
|
71
|
+
let(:builder) do
|
72
|
+
Nokogiri::XML::Builder.new do |xml|
|
73
|
+
xml.entry(
|
74
|
+
'xmlns' => 'http://www.w3.org/2005/Atom',
|
75
|
+
'xmlns:data' => 'http://docs.oasis-open.org/odata/ns/data',
|
76
|
+
'xmlns:metadata' => 'http://docs.oasis-open.org/odata/ns/metadata',
|
77
|
+
) do
|
78
|
+
subject.to_xml(xml)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
let(:xml) { Nokogiri::XML(builder.to_xml) }
|
83
|
+
|
84
|
+
before(:each) do
|
85
|
+
subject.value = address
|
86
|
+
xml.remove_namespaces!
|
87
|
+
end
|
88
|
+
|
89
|
+
it { expect(xml.xpath("/entry/Address[@type='ODataDemo.Address']").count).to eq(1) }
|
90
|
+
it { expect(xml.xpath('/entry/Address/Street').count).to eq(1) }
|
91
|
+
it { expect(xml.xpath('/entry/Address/City').count).to eq(1) }
|
92
|
+
it { expect(xml.xpath('/entry/Address/State').count).to eq(1) }
|
93
|
+
it { expect(xml.xpath('/entry/Address/ZipCode').count).to eq(1) }
|
94
|
+
it { expect(xml.xpath('/entry/Address/Country').count).to eq(1) }
|
95
|
+
end
|
96
|
+
end
|