odata4 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +1 -0
- data/lib/odata4/entity.rb +13 -5
- data/lib/odata4/properties/collection.rb +50 -0
- data/lib/odata4/properties.rb +1 -0
- data/lib/odata4/query.rb +7 -5
- data/lib/odata4/schema.rb +8 -2
- data/lib/odata4/service/request.rb +81 -0
- data/lib/odata4/{query/result → service/response}/atom.rb +9 -10
- data/lib/odata4/{query/result → service/response}/json.rb +9 -10
- data/lib/odata4/service/response/plain.rb +36 -0
- data/lib/odata4/service/response/xml.rb +40 -0
- data/lib/odata4/service/response.rb +160 -0
- data/lib/odata4/service.rb +12 -80
- data/lib/odata4/version.rb +1 -1
- data/lib/odata4.rb +0 -3
- data/spec/fixtures/files/entity_to_xml.xml +1 -1
- data/spec/fixtures/files/error.xml +5 -0
- data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
- data/spec/odata4/properties/collection_spec.rb +44 -0
- data/spec/odata4/query_spec.rb +2 -2
- data/spec/odata4/service/request_spec.rb +49 -0
- data/spec/odata4/service/response_spec.rb +77 -0
- metadata +19 -7
- data/lib/odata4/query/result.rb +0 -84
- data/spec/odata4/query/result_spec.rb +0 -53
data/lib/odata4/service.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'odata4/service/request'
|
2
|
+
require 'odata4/service/response'
|
3
|
+
|
1
4
|
module OData4
|
2
5
|
# Encapsulates the basic details and functionality needed to interact with an
|
3
6
|
# OData4 service.
|
@@ -14,6 +17,7 @@ module OData4
|
|
14
17
|
MIME_TYPES = {
|
15
18
|
atom: 'application/atom+xml',
|
16
19
|
json: 'application/json',
|
20
|
+
xml: 'application/xml',
|
17
21
|
plain: 'text/plain'
|
18
22
|
}
|
19
23
|
|
@@ -133,55 +137,11 @@ module OData4
|
|
133
137
|
|
134
138
|
# Execute a request against the service
|
135
139
|
#
|
136
|
-
# @param url_chunk [to_s] string to append to service
|
137
|
-
# @param
|
138
|
-
# @return [
|
139
|
-
def execute(url_chunk,
|
140
|
-
|
141
|
-
accept_header = {'Accept' => accept }
|
142
|
-
|
143
|
-
request_options = options[:typhoeus]
|
144
|
-
.merge({ method: :get })
|
145
|
-
.merge(additional_options)
|
146
|
-
|
147
|
-
# Don't overwrite Accept header if already present
|
148
|
-
unless request_options[:headers]['Accept']
|
149
|
-
request_options[:headers] = request_options[:headers].merge(accept_header)
|
150
|
-
end
|
151
|
-
|
152
|
-
request = ::Typhoeus::Request.new(
|
153
|
-
URI.join("#{service_url}/", URI.escape(url_chunk)),
|
154
|
-
request_options
|
155
|
-
)
|
156
|
-
logger.info "Requesting #{URI.unescape(request.url)}..."
|
157
|
-
request.run
|
158
|
-
|
159
|
-
response = request.response
|
160
|
-
logger.debug(response.headers)
|
161
|
-
logger.debug(response.body)
|
162
|
-
|
163
|
-
validate_response(response)
|
164
|
-
response
|
165
|
-
end
|
166
|
-
|
167
|
-
# Find a specific node in the given result set
|
168
|
-
#
|
169
|
-
# @param results [Typhoeus::Response]
|
170
|
-
# @return [Nokogiri::XML::Element]
|
171
|
-
def find_node(results, node_name)
|
172
|
-
document = ::Nokogiri::XML(results.body)
|
173
|
-
document.remove_namespaces!
|
174
|
-
document.xpath("//#{node_name}").first
|
175
|
-
end
|
176
|
-
|
177
|
-
# Find entity entries in a result set
|
178
|
-
#
|
179
|
-
# @param results [Typhoeus::Response]
|
180
|
-
# @return [Nokogiri::XML::NodeSet]
|
181
|
-
def find_entities(results)
|
182
|
-
document = ::Nokogiri::XML(results.body)
|
183
|
-
document.remove_namespaces!
|
184
|
-
document.xpath('//entry')
|
140
|
+
# @param url_chunk [to_s] string to append to service URL
|
141
|
+
# @param options [Hash] additional request options
|
142
|
+
# @return [OData4::Service::Response]
|
143
|
+
def execute(url_chunk, options = {})
|
144
|
+
Request.new(self, url_chunk, options).execute
|
185
145
|
end
|
186
146
|
|
187
147
|
# Get the property type for an entity from metadata.
|
@@ -256,57 +216,29 @@ module OData4
|
|
256
216
|
def default_options
|
257
217
|
{
|
258
218
|
typhoeus: {
|
259
|
-
headers: { '
|
219
|
+
headers: { 'OData-Version' => '4.0' },
|
260
220
|
timeout: HTTP_TIMEOUT
|
261
221
|
},
|
262
222
|
strict: true # strict property validation
|
263
223
|
}
|
264
224
|
end
|
265
225
|
|
266
|
-
def content_type(format)
|
267
|
-
if format == :auto
|
268
|
-
MIME_TYPES.values.join(',')
|
269
|
-
elsif MIME_TYPES.has_key? format
|
270
|
-
MIME_TYPES[format]
|
271
|
-
else
|
272
|
-
raise ArgumentError, "Unknown format '#{format}'"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
226
|
def read_metadata
|
277
|
-
response = nil
|
278
227
|
# From file, good for debugging
|
279
228
|
if options[:metadata_file]
|
280
229
|
data = File.read(options[:metadata_file])
|
281
230
|
::Nokogiri::XML(data).remove_namespaces!
|
282
231
|
else # From a URL
|
232
|
+
response = nil
|
283
233
|
METADATA_TIMEOUTS.each do |timeout|
|
284
|
-
response =
|
285
|
-
options[:typhoeus].merge(timeout: timeout))
|
234
|
+
response = execute(metadata_url, timeout: timeout)
|
286
235
|
break unless response.timed_out?
|
287
236
|
end
|
288
237
|
raise "Metadata Timeout" if response.timed_out?
|
289
|
-
validate_response(response)
|
290
238
|
::Nokogiri::XML(response.body).remove_namespaces!
|
291
239
|
end
|
292
240
|
end
|
293
241
|
|
294
|
-
def validate_response(response)
|
295
|
-
raise "Bad Request. #{error_message(response)}" if response.code == 400
|
296
|
-
raise "Access Denied" if response.code == 401
|
297
|
-
raise "Forbidden" if response.code == 403
|
298
|
-
raise "Not Found" if [0,404].include?(response.code)
|
299
|
-
raise "Method Not Allowed" if response.code == 405
|
300
|
-
raise "Not Acceptable" if response.code == 406
|
301
|
-
raise "Request Entity Too Large" if response.code == 413
|
302
|
-
raise "Internal Server Error" if response.code == 500
|
303
|
-
raise "Service Unavailable" if response.code == 503
|
304
|
-
end
|
305
|
-
|
306
|
-
def error_message(response)
|
307
|
-
OData4::Query::Result.new(nil, response).error_message
|
308
|
-
end
|
309
|
-
|
310
242
|
def register_custom_types
|
311
243
|
complex_types.each do |name, type|
|
312
244
|
::OData4::PropertyRegistry.add(name, type.property_class)
|
data/lib/odata4/version.rb
CHANGED
data/lib/odata4.rb
CHANGED
@@ -21,9 +21,6 @@ require 'odata4/enum_type'
|
|
21
21
|
require 'odata4/entity'
|
22
22
|
require 'odata4/entity_container'
|
23
23
|
require 'odata4/entity_set'
|
24
|
-
require 'odata4/query/criteria'
|
25
|
-
require 'odata4/query/result'
|
26
|
-
require 'odata4/query/in_batches'
|
27
24
|
require 'odata4/query'
|
28
25
|
require 'odata4/schema'
|
29
26
|
require 'odata4/service'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0"?>
|
2
2
|
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:data="http://docs.oasis-open.org/odata/ns/data" xmlns:metadata="http://docs.oasis-open.org/odata/ns/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xml:base="http://services.odata.org/V4/OData/OData.svc">
|
3
|
-
<category term="ODataDemo.
|
3
|
+
<category term="ODataDemo.Product" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
|
4
4
|
<author>
|
5
5
|
<name/>
|
6
6
|
</author>
|
@@ -0,0 +1,193 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://services.odata.org/V4/OData/OData.svc/Products
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Typhoeus - https://github.com/typhoeus/typhoeus
|
12
|
+
OData4-Version:
|
13
|
+
- '4.0'
|
14
|
+
Accept:
|
15
|
+
- application/atom+xml,application/json,application/xml,text/plain
|
16
|
+
Expect:
|
17
|
+
- ''
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Cache-Control:
|
24
|
+
- no-cache
|
25
|
+
Content-Length:
|
26
|
+
- '1981'
|
27
|
+
Content-Type:
|
28
|
+
- application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
|
29
|
+
Server:
|
30
|
+
- Microsoft-IIS/10.0
|
31
|
+
X-Content-Type-Options:
|
32
|
+
- nosniff
|
33
|
+
OData-Version:
|
34
|
+
- 4.0;
|
35
|
+
X-AspNet-Version:
|
36
|
+
- 4.0.30319
|
37
|
+
X-Powered-By:
|
38
|
+
- ASP.NET
|
39
|
+
Access-Control-Allow-Origin:
|
40
|
+
- "*"
|
41
|
+
Access-Control-Allow-Methods:
|
42
|
+
- GET
|
43
|
+
Access-Control-Allow-Headers:
|
44
|
+
- Accept, Origin, Content-Type, MaxDataServiceVersion
|
45
|
+
Access-Control-Expose-Headers:
|
46
|
+
- DataServiceVersion
|
47
|
+
Date:
|
48
|
+
- Thu, 15 Feb 2018 19:55:09 GMT
|
49
|
+
body:
|
50
|
+
encoding: UTF-8
|
51
|
+
string: '{"@odata.context":"http://services.odata.org/V4/OData/OData.svc/$metadata#Products","value":[{"ID":0,"Name":"Bread","Description":"Whole
|
52
|
+
grain bread","ReleaseDate":"1992-01-01T00:00:00Z","DiscontinuedDate":null,"Rating":4,"Price":2.5},{"ID":1,"Name":"Milk","Description":"Low
|
53
|
+
fat milk","ReleaseDate":"1995-10-01T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":3.5},{"ID":2,"Name":"Vint
|
54
|
+
soda","Description":"Americana Variety - Mix of 6 flavors","ReleaseDate":"2000-10-01T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":20.9},{"ID":3,"Name":"Havina
|
55
|
+
Cola","Description":"The Original Key Lime Cola","ReleaseDate":"2005-10-01T00:00:00Z","DiscontinuedDate":"2006-10-01T00:00:00Z","Rating":3,"Price":19.9},{"ID":4,"Name":"Fruit
|
56
|
+
Punch","Description":"Mango flavor, 8.3 Ounce Cans (Pack of 24)","ReleaseDate":"2003-01-05T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":22.99},{"ID":5,"Name":"Cranberry
|
57
|
+
Juice","Description":"16-Ounce Plastic Bottles (Pack of 12)","ReleaseDate":"2006-08-04T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":22.8},{"ID":6,"Name":"Pink
|
58
|
+
Lemonade","Description":"36 Ounce Cans (Pack of 3)","ReleaseDate":"2006-11-05T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":18.8},{"ID":7,"Name":"DVD
|
59
|
+
Player","Description":"1080P Upconversion DVD Player","ReleaseDate":"2006-11-15T00:00:00Z","DiscontinuedDate":null,"Rating":5,"Price":35.88},{"ID":8,"Name":"LCD
|
60
|
+
HDTV","Description":"42 inch 1080p LCD with Built-in Blu-ray Disc Player","ReleaseDate":"2008-05-08T00:00:00Z","DiscontinuedDate":null,"Rating":3,"Price":1088.8},{"@odata.type":"#ODataDemo.FeaturedProduct","ID":9,"Name":"Lemonade","Description":"Classic,
|
61
|
+
refreshing lemonade (Single bottle)","ReleaseDate":"1970-01-01T00:00:00Z","DiscontinuedDate":null,"Rating":7,"Price":1.01},{"@odata.type":"#ODataDemo.FeaturedProduct","ID":10,"Name":"Coffee","Description":"Bulk
|
62
|
+
size can of instant coffee","ReleaseDate":"1982-12-31T00:00:00Z","DiscontinuedDate":null,"Rating":1,"Price":6.99}]}'
|
63
|
+
http_version: '1.1'
|
64
|
+
adapter_metadata:
|
65
|
+
effective_url: http://services.odata.org/V4/OData/OData.svc/Products
|
66
|
+
recorded_at: Thu, 15 Feb 2018 19:55:10 GMT
|
67
|
+
- request:
|
68
|
+
method: get
|
69
|
+
uri: http://services.odata.org/V4/OData/OData.svc/$metadata
|
70
|
+
body:
|
71
|
+
encoding: US-ASCII
|
72
|
+
string: ''
|
73
|
+
headers:
|
74
|
+
User-Agent:
|
75
|
+
- Typhoeus - https://github.com/typhoeus/typhoeus
|
76
|
+
OData4-Version:
|
77
|
+
- '4.0'
|
78
|
+
Expect:
|
79
|
+
- ''
|
80
|
+
response:
|
81
|
+
status:
|
82
|
+
code: 200
|
83
|
+
message: OK
|
84
|
+
headers:
|
85
|
+
Cache-Control:
|
86
|
+
- no-cache
|
87
|
+
Content-Length:
|
88
|
+
- '6742'
|
89
|
+
Content-Type:
|
90
|
+
- application/xml;charset=utf-8
|
91
|
+
Server:
|
92
|
+
- Microsoft-IIS/10.0
|
93
|
+
X-Content-Type-Options:
|
94
|
+
- nosniff
|
95
|
+
OData-Version:
|
96
|
+
- 4.0;
|
97
|
+
X-AspNet-Version:
|
98
|
+
- 4.0.30319
|
99
|
+
X-Powered-By:
|
100
|
+
- ASP.NET
|
101
|
+
Access-Control-Allow-Origin:
|
102
|
+
- "*"
|
103
|
+
Access-Control-Allow-Methods:
|
104
|
+
- GET
|
105
|
+
Access-Control-Allow-Headers:
|
106
|
+
- Accept, Origin, Content-Type, MaxDataServiceVersion
|
107
|
+
Access-Control-Expose-Headers:
|
108
|
+
- DataServiceVersion
|
109
|
+
Date:
|
110
|
+
- Thu, 15 Feb 2018 20:48:14 GMT
|
111
|
+
body:
|
112
|
+
encoding: UTF-8
|
113
|
+
string: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema
|
114
|
+
Namespace="ODataDemo" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType
|
115
|
+
Name="Product"><Key><PropertyRef Name="ID" /></Key><Property Name="ID" Type="Edm.Int32"
|
116
|
+
Nullable="false" /><Property Name="Name" Type="Edm.String" /><Property Name="Description"
|
117
|
+
Type="Edm.String" /><Property Name="ReleaseDate" Type="Edm.DateTimeOffset"
|
118
|
+
Nullable="false" /><Property Name="DiscontinuedDate" Type="Edm.DateTimeOffset"
|
119
|
+
/><Property Name="Rating" Type="Edm.Int16" Nullable="false" /><Property Name="Price"
|
120
|
+
Type="Edm.Double" Nullable="false" /><NavigationProperty Name="Categories"
|
121
|
+
Type="Collection(ODataDemo.Category)" Partner="Products" /><NavigationProperty
|
122
|
+
Name="Supplier" Type="ODataDemo.Supplier" Partner="Products" /><NavigationProperty
|
123
|
+
Name="ProductDetail" Type="ODataDemo.ProductDetail" Partner="Product" /></EntityType><EntityType
|
124
|
+
Name="FeaturedProduct" BaseType="ODataDemo.Product"><NavigationProperty Name="Advertisement"
|
125
|
+
Type="ODataDemo.Advertisement" Partner="FeaturedProduct" /></EntityType><EntityType
|
126
|
+
Name="ProductDetail"><Key><PropertyRef Name="ProductID" /></Key><Property
|
127
|
+
Name="ProductID" Type="Edm.Int32" Nullable="false" /><Property Name="Details"
|
128
|
+
Type="Edm.String" /><NavigationProperty Name="Product" Type="ODataDemo.Product"
|
129
|
+
Partner="ProductDetail" /></EntityType><EntityType Name="Category" OpenType="true"><Key><PropertyRef
|
130
|
+
Name="ID" /></Key><Property Name="ID" Type="Edm.Int32" Nullable="false" /><Property
|
131
|
+
Name="Name" Type="Edm.String" /><NavigationProperty Name="Products" Type="Collection(ODataDemo.Product)"
|
132
|
+
Partner="Categories" /></EntityType><EntityType Name="Supplier"><Key><PropertyRef
|
133
|
+
Name="ID" /></Key><Property Name="ID" Type="Edm.Int32" Nullable="false" /><Property
|
134
|
+
Name="Name" Type="Edm.String" /><Property Name="Address" Type="ODataDemo.Address"
|
135
|
+
/><Property Name="Location" Type="Edm.GeographyPoint" SRID="Variable" /><Property
|
136
|
+
Name="Concurrency" Type="Edm.Int32" ConcurrencyMode="Fixed" Nullable="false"
|
137
|
+
/><NavigationProperty Name="Products" Type="Collection(ODataDemo.Product)"
|
138
|
+
Partner="Supplier" /></EntityType><ComplexType Name="Address"><Property Name="Street"
|
139
|
+
Type="Edm.String" /><Property Name="City" Type="Edm.String" /><Property Name="State"
|
140
|
+
Type="Edm.String" /><Property Name="ZipCode" Type="Edm.String" /><Property
|
141
|
+
Name="Country" Type="Edm.String" /></ComplexType><EntityType Name="Person"><Key><PropertyRef
|
142
|
+
Name="ID" /></Key><Property Name="ID" Type="Edm.Int32" Nullable="false" /><Property
|
143
|
+
Name="Name" Type="Edm.String" /><NavigationProperty Name="PersonDetail" Type="ODataDemo.PersonDetail"
|
144
|
+
Partner="Person" /></EntityType><EntityType Name="Customer" BaseType="ODataDemo.Person"><Property
|
145
|
+
Name="TotalExpense" Type="Edm.Decimal" Nullable="false" /></EntityType><EntityType
|
146
|
+
Name="Employee" BaseType="ODataDemo.Person"><Property Name="EmployeeID" Type="Edm.Int64"
|
147
|
+
Nullable="false" /><Property Name="HireDate" Type="Edm.DateTimeOffset" Nullable="false"
|
148
|
+
/><Property Name="Salary" Type="Edm.Single" Nullable="false" /></EntityType><EntityType
|
149
|
+
Name="PersonDetail"><Key><PropertyRef Name="PersonID" /></Key><Property Name="PersonID"
|
150
|
+
Type="Edm.Int32" Nullable="false" /><Property Name="Age" Type="Edm.Byte" Nullable="false"
|
151
|
+
/><Property Name="Gender" Type="Edm.Boolean" Nullable="false" /><Property
|
152
|
+
Name="Phone" Type="Edm.String" /><Property Name="Address" Type="ODataDemo.Address"
|
153
|
+
/><Property Name="Photo" Type="Edm.Stream" Nullable="false" /><NavigationProperty
|
154
|
+
Name="Person" Type="ODataDemo.Person" Partner="PersonDetail" /></EntityType><EntityType
|
155
|
+
Name="Advertisement" HasStream="true"><Key><PropertyRef Name="ID" /></Key><Property
|
156
|
+
Name="ID" Type="Edm.Guid" Nullable="false" /><Property Name="Name" Type="Edm.String"
|
157
|
+
/><Property Name="AirDate" Type="Edm.DateTimeOffset" Nullable="false" /><NavigationProperty
|
158
|
+
Name="FeaturedProduct" Type="ODataDemo.FeaturedProduct" Partner="Advertisement"
|
159
|
+
/></EntityType><EntityContainer Name="DemoService"><EntitySet Name="Products"
|
160
|
+
EntityType="ODataDemo.Product"><NavigationPropertyBinding Path="ODataDemo.FeaturedProduct/Advertisement"
|
161
|
+
Target="Advertisements" /><NavigationPropertyBinding Path="Categories" Target="Categories"
|
162
|
+
/><NavigationPropertyBinding Path="Supplier" Target="Suppliers" /><NavigationPropertyBinding
|
163
|
+
Path="ProductDetail" Target="ProductDetails" /></EntitySet><EntitySet Name="ProductDetails"
|
164
|
+
EntityType="ODataDemo.ProductDetail"><NavigationPropertyBinding Path="Product"
|
165
|
+
Target="Products" /></EntitySet><EntitySet Name="Categories" EntityType="ODataDemo.Category"><NavigationPropertyBinding
|
166
|
+
Path="Products" Target="Products" /></EntitySet><EntitySet Name="Suppliers"
|
167
|
+
EntityType="ODataDemo.Supplier"><NavigationPropertyBinding Path="Products"
|
168
|
+
Target="Products" /></EntitySet><EntitySet Name="Persons" EntityType="ODataDemo.Person"><NavigationPropertyBinding
|
169
|
+
Path="PersonDetail" Target="PersonDetails" /></EntitySet><EntitySet Name="PersonDetails"
|
170
|
+
EntityType="ODataDemo.PersonDetail"><NavigationPropertyBinding Path="Person"
|
171
|
+
Target="Persons" /></EntitySet><EntitySet Name="Advertisements" EntityType="ODataDemo.Advertisement"><NavigationPropertyBinding
|
172
|
+
Path="FeaturedProduct" Target="Products" /></EntitySet></EntityContainer><Annotations
|
173
|
+
Target="ODataDemo.DemoService"><Annotation Term="Org.OData.Display.V1.Description"
|
174
|
+
String="This is a sample OData service with vocabularies" /></Annotations><Annotations
|
175
|
+
Target="ODataDemo.Product"><Annotation Term="Org.OData.Display.V1.Description"
|
176
|
+
String="All Products available in the online store" /></Annotations><Annotations
|
177
|
+
Target="ODataDemo.Product/Name"><Annotation Term="Org.OData.Display.V1.DisplayName"
|
178
|
+
String="Product Name" /></Annotations><Annotations Target="ODataDemo.DemoService/Suppliers"><Annotation
|
179
|
+
Term="Org.OData.Publication.V1.PublisherName" String="Microsoft Corp." /><Annotation
|
180
|
+
Term="Org.OData.Publication.V1.PublisherId" String="MSFT" /><Annotation Term="Org.OData.Publication.V1.Keywords"
|
181
|
+
String="Inventory, Supplier, Advertisers, Sales, Finance" /><Annotation Term="Org.OData.Publication.V1.AttributionUrl"
|
182
|
+
String="http://www.odata.org/" /><Annotation Term="Org.OData.Publication.V1.AttributionDescription"
|
183
|
+
String="All rights reserved" /><Annotation Term="Org.OData.Publication.V1.DocumentationUrl
|
184
|
+
" String="http://www.odata.org/" /><Annotation Term="Org.OData.Publication.V1.TermsOfUseUrl"
|
185
|
+
String="All rights reserved" /><Annotation Term="Org.OData.Publication.V1.PrivacyPolicyUrl"
|
186
|
+
String="http://www.odata.org/" /><Annotation Term="Org.OData.Publication.V1.LastModified"
|
187
|
+
String="4/2/2013" /><Annotation Term="Org.OData.Publication.V1.ImageUrl "
|
188
|
+
String="http://www.odata.org/" /></Annotations></Schema></edmx:DataServices></edmx:Edmx>
|
189
|
+
http_version: '1.1'
|
190
|
+
adapter_metadata:
|
191
|
+
effective_url: http://services.odata.org/V4/OData/OData.svc/$metadata
|
192
|
+
recorded_at: Thu, 15 Feb 2018 20:48:14 GMT
|
193
|
+
recorded_with: VCR 4.0.0
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OData4::Properties::Collection do
|
4
|
+
let(:subject) { OData4::Properties::Collection.new('Names', names) }
|
5
|
+
let(:names) { %w[Dan Greg Jon] }
|
6
|
+
let(:new_names) { %w[Andrew Doug Paul] }
|
7
|
+
|
8
|
+
it { expect(subject.type).to eq('Collection(Edm.String)') }
|
9
|
+
it { expect(subject.value).to eq(['Dan', 'Greg', 'Jon']) }
|
10
|
+
it { expect(subject.url_value).to eq("['Dan','Greg','Jon']") }
|
11
|
+
|
12
|
+
describe '#value=' do
|
13
|
+
it 'allows an array of string values to be set' do
|
14
|
+
subject.value = new_names
|
15
|
+
expect(subject.value).to eq(new_names)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: Make collection type work properly with data types other than string
|
20
|
+
xcontext 'with value type other than Edm.String' do
|
21
|
+
let(:subject) { OData4::Properties::Collection.new('Bits', [1, 0, 1], value_type: 'Edm.Binary') }
|
22
|
+
|
23
|
+
it { expect(subject.type).to eq('Collection(Edm.Int32)') }
|
24
|
+
it { expect(subject.value).to eq([1, 0, 1]) }
|
25
|
+
it { expect(subject.url_value).to eq('[1,0,1]') }
|
26
|
+
|
27
|
+
it 'does not allow other property types to be set' do
|
28
|
+
expect {
|
29
|
+
subject.value = names
|
30
|
+
}.to raise_error(ArgumentError)
|
31
|
+
end
|
32
|
+
|
33
|
+
xdescribe 'lenient validation' do
|
34
|
+
let(:subject) do
|
35
|
+
OData4::Properties::Collection.new('Names', names, value_type: 'Edm.String', strict: false)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'ignores invalid values' do
|
39
|
+
subject.value = [1, 2, 3]
|
40
|
+
expect(subject.value).to eq([])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/odata4/query_spec.rb
CHANGED
@@ -132,7 +132,7 @@ describe OData4::Query, vcr: {cassette_name: 'query_specs'} do
|
|
132
132
|
|
133
133
|
describe '#execute' do
|
134
134
|
it { expect(subject).to respond_to(:execute) }
|
135
|
-
it { expect(subject.execute).to be_a(OData4::
|
135
|
+
it { expect(subject.execute).to be_a(OData4::Service::Response) }
|
136
136
|
end
|
137
137
|
|
138
138
|
describe '#count' do
|
@@ -166,7 +166,7 @@ describe OData4::Query, vcr: {cassette_name: 'query_specs'} do
|
|
166
166
|
batch_count = entity_count = 0
|
167
167
|
|
168
168
|
subject.in_batches(of: 5) do |batch|
|
169
|
-
expect(batch).to be_a(OData4::
|
169
|
+
expect(batch).to be_a(OData4::Service::Response)
|
170
170
|
expect(batch.count).to eq(5) unless batch_count == 2
|
171
171
|
|
172
172
|
batch.each do |entity|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OData4::Service::Request, vcr: {cassette_name: 'service/request_specs'} do
|
4
|
+
let(:subject) { OData4::Service::Request.new(service, 'Products') }
|
5
|
+
let(:service) { OData4::Service.open(service_url, name: 'ODataDemo', metadata_file: metadata_file) }
|
6
|
+
let(:service_url) { 'http://services.odata.org/V4/OData/OData.svc' }
|
7
|
+
let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
|
8
|
+
|
9
|
+
describe '#url' do
|
10
|
+
it 'returns the full request URL' do
|
11
|
+
expect(subject.url).to eq('http://services.odata.org/V4/OData/OData.svc/Products')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#method' do
|
16
|
+
it 'defaults to GET' do
|
17
|
+
expect(subject.method).to eq(:get)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#format' do
|
22
|
+
it 'defaults to :auto' do
|
23
|
+
expect(subject.format).to eq(:auto)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#content_type' do
|
28
|
+
it 'return all acceptable types when format = :auto' do
|
29
|
+
expect(subject.content_type).to eq(OData4::Service::MIME_TYPES.values.join(','))
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns the correct MIME type when format = :atom' do
|
33
|
+
subject.format = :atom
|
34
|
+
expect(subject.content_type).to eq('application/atom+xml')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns the correct MIME type when format = :json' do
|
38
|
+
subject.format = :json
|
39
|
+
expect(subject.content_type).to eq('application/json')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#execute' do
|
44
|
+
it 'returns a response object' do
|
45
|
+
expect(subject.execute).to be_a(OData4::Service::Response)
|
46
|
+
end
|
47
|
+
it 'retries on wrong content type'
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'a valid response' do
|
4
|
+
it { expect(subject).to be_success }
|
5
|
+
it { expect(subject.count).to eq(11) }
|
6
|
+
|
7
|
+
describe '#empty?' do
|
8
|
+
it { expect(subject).to respond_to(:empty?) }
|
9
|
+
it { expect(subject.empty?).to eq(false) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#each' do
|
13
|
+
it { expect(subject).to respond_to(:each) }
|
14
|
+
it 'returns just OData4::Entities of the right type' do
|
15
|
+
subject.each do |entity|
|
16
|
+
expect(entity).to be_a(OData4::Entity)
|
17
|
+
expect(entity.type).to eq('ODataDemo.Product')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe OData4::Service::Response, vcr: {cassette_name: 'service/response_specs'} do
|
24
|
+
let(:subject) { OData4::Service::Response.new(service, response, entity_set.query) }
|
25
|
+
let(:service) { OData4::Service.open(service_url, name: 'ODataDemo', metadata_file: metadata_file) }
|
26
|
+
let(:service_url) { 'http://services.odata.org/V4/OData/OData.svc' }
|
27
|
+
let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
|
28
|
+
let(:entity_set) { service['Products'] }
|
29
|
+
let(:response) do
|
30
|
+
response = double('response')
|
31
|
+
allow(response).to receive_messages(
|
32
|
+
headers: { 'Content-Type' => content_type },
|
33
|
+
code: response_status,
|
34
|
+
body: response_body
|
35
|
+
)
|
36
|
+
response
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with Atom result' do
|
40
|
+
let(:content_type) { 'application/atom+xml' }
|
41
|
+
let(:response_status) { 200 }
|
42
|
+
let(:response_body) { File.read('spec/fixtures/files/products.xml') }
|
43
|
+
|
44
|
+
it_behaves_like 'a valid response'
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with JSON result' do
|
48
|
+
let(:content_type) { 'application/json' }
|
49
|
+
let(:response_status) { 200 }
|
50
|
+
let(:response_body) { File.read('spec/fixtures/files/products.json') }
|
51
|
+
|
52
|
+
it_behaves_like 'a valid response'
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with XML result' do
|
56
|
+
let(:content_type) { 'application/xml' }
|
57
|
+
let(:response_status) { 200 }
|
58
|
+
let(:response_body) { File.read('spec/fixtures/files/error.xml') }
|
59
|
+
|
60
|
+
it 'contains no entities' do
|
61
|
+
expect(subject.empty?).to eq(true)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'contains error message' do
|
65
|
+
expect(subject.error_message).to match(/Resource not found/)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with plain text result' do
|
70
|
+
let(:content_type) { 'text/plain' }
|
71
|
+
let(:response_status) { 200 }
|
72
|
+
let(:response_body) { '123' }
|
73
|
+
|
74
|
+
it { expect(subject).to be_success }
|
75
|
+
it { expect(subject.body).to match(/123/) }
|
76
|
+
end
|
77
|
+
end
|