odata4 0.8.0 → 0.8.1
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.
- 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
|