frodata 0.9.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 +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,162 @@
|
|
|
1
|
+
require 'frodata/service/response/atom'
|
|
2
|
+
require 'frodata/service/response/json'
|
|
3
|
+
require 'frodata/service/response/plain'
|
|
4
|
+
require 'frodata/service/response/xml'
|
|
5
|
+
|
|
6
|
+
module FrOData
|
|
7
|
+
class Service
|
|
8
|
+
# The result of executing a FrOData::Service::Request.
|
|
9
|
+
class Response
|
|
10
|
+
include Enumerable
|
|
11
|
+
|
|
12
|
+
# The service that generated this response
|
|
13
|
+
attr_reader :service
|
|
14
|
+
# The underlying (raw) response
|
|
15
|
+
attr_reader :response
|
|
16
|
+
# The query that generated the response (optional)
|
|
17
|
+
attr_reader :query
|
|
18
|
+
|
|
19
|
+
# Create a new response given a service and a raw response.
|
|
20
|
+
# @param service [FrOData::Service] The executing service.
|
|
21
|
+
# @param query [FrOData::Query] The related query (optional).
|
|
22
|
+
# @param
|
|
23
|
+
def initialize(service, query = nil, &block)
|
|
24
|
+
@service = service
|
|
25
|
+
@query = query
|
|
26
|
+
@timed_out = false
|
|
27
|
+
execute(&block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the HTTP status code.
|
|
31
|
+
def status
|
|
32
|
+
response.status
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Whether the request was successful.
|
|
36
|
+
def success?
|
|
37
|
+
200 <= status && status < 300
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the content type of the resonse.
|
|
41
|
+
def content_type
|
|
42
|
+
response.headers['Content-Type'] || ''
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def is_atom?
|
|
46
|
+
content_type =~ /#{Regexp.escape FrOData::Service::MIME_TYPES[:atom]}/
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def is_json?
|
|
50
|
+
content_type =~ /#{Regexp.escape FrOData::Service::MIME_TYPES[:json]}/
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def is_plain?
|
|
54
|
+
content_type =~ /#{Regexp.escape FrOData::Service::MIME_TYPES[:plain]}/
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def is_xml?
|
|
58
|
+
content_type =~ /#{Regexp.escape FrOData::Service::MIME_TYPES[:xml]}/
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether the response contained any entities.
|
|
62
|
+
# @return [Boolean]
|
|
63
|
+
def empty?
|
|
64
|
+
@empty ||= find_entities.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Whether the response failed due to a timeout
|
|
68
|
+
def timed_out?
|
|
69
|
+
@timed_out
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Iterates over all entities in the response, using
|
|
73
|
+
# automatic paging if necessary.
|
|
74
|
+
# Provided for Enumerable functionality.
|
|
75
|
+
# @param block [block] a block to evaluate
|
|
76
|
+
# @return [FrOData::Entity] each entity in turn for the query result
|
|
77
|
+
def each(&block)
|
|
78
|
+
unless empty?
|
|
79
|
+
process_results(&block)
|
|
80
|
+
unless next_page.nil?
|
|
81
|
+
# ensure request gets executed with the same options
|
|
82
|
+
query.execute(URI.decode next_page_url).each(&block)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns the response body.
|
|
88
|
+
def body
|
|
89
|
+
response.body
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Validates the response. Throws an exception with
|
|
93
|
+
# an appropriate message if a 4xx or 5xx status code
|
|
94
|
+
# occured.
|
|
95
|
+
#
|
|
96
|
+
# @return [self]
|
|
97
|
+
def validate_response!
|
|
98
|
+
if error = FrOData::Errors::ERROR_MAP[status]
|
|
99
|
+
raise error, response, error_message
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def execute(&block)
|
|
106
|
+
@response = block.call
|
|
107
|
+
logger.debug <<-EOS
|
|
108
|
+
[FrOData: #{service.name}] Received response:
|
|
109
|
+
Headers: #{response.headers}
|
|
110
|
+
Body: #{response.body}
|
|
111
|
+
EOS
|
|
112
|
+
check_content_type
|
|
113
|
+
validate_response!
|
|
114
|
+
rescue Faraday::TimeoutError
|
|
115
|
+
logger.info "Request timed out."
|
|
116
|
+
@timed_out = true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def logger
|
|
120
|
+
service.logger
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def check_content_type
|
|
124
|
+
# Dynamically extend instance with methods for
|
|
125
|
+
# processing the current result type
|
|
126
|
+
if is_atom?
|
|
127
|
+
extend FrOData::Service::Response::Atom
|
|
128
|
+
elsif is_json?
|
|
129
|
+
extend FrOData::Service::Response::JSON
|
|
130
|
+
elsif is_xml?
|
|
131
|
+
extend FrOData::Service::Response::XML
|
|
132
|
+
elsif is_plain?
|
|
133
|
+
extend FrOData::Service::Response::Plain
|
|
134
|
+
elsif response.body.empty?
|
|
135
|
+
# Some services (*cough* Microsoft *cough*) return
|
|
136
|
+
# an empty response with no `Content-Type` header set.
|
|
137
|
+
# We catch that here and bypass content type detection.
|
|
138
|
+
@empty = true
|
|
139
|
+
else
|
|
140
|
+
raise RequestError, response, "Invalid response type '#{content_type}'"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def entity_options
|
|
145
|
+
if query
|
|
146
|
+
query.entity_set.entity_options
|
|
147
|
+
else
|
|
148
|
+
{
|
|
149
|
+
service_name: service.name,
|
|
150
|
+
}
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def process_results(&block)
|
|
155
|
+
find_entities.each do |entity_data|
|
|
156
|
+
entity = parse_entity(entity_data, entity_options)
|
|
157
|
+
block_given? ? block.call(entity) : yield(entity)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module FrOData
|
|
2
|
+
class Service
|
|
3
|
+
class Response
|
|
4
|
+
module Atom
|
|
5
|
+
def parse_entity(entity_xml, entity_options)
|
|
6
|
+
FrOData::Entity.from_xml(entity_xml, entity_options)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def next_page
|
|
10
|
+
result_xml.xpath("/feed/link[@rel='next']").first
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def next_page_url
|
|
14
|
+
next_page.attributes['href'].value.gsub(service.service_url, '')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def error_message
|
|
18
|
+
result_xml.xpath('//error/message').first.andand.text
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parsed_body
|
|
22
|
+
result_xml
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def result_xml
|
|
28
|
+
@result_xml ||= ::Nokogiri::XML(response.body).remove_namespaces!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Find entity entries in a result set
|
|
32
|
+
#
|
|
33
|
+
# @return [Nokogiri::XML::NodeSet]
|
|
34
|
+
def find_entities
|
|
35
|
+
result_xml.xpath('//entry')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module FrOData
|
|
2
|
+
class Service
|
|
3
|
+
class Response
|
|
4
|
+
module JSON
|
|
5
|
+
def parse_entity(entity_json, entity_options)
|
|
6
|
+
FrOData::Entity.from_json(entity_json, entity_options)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def next_page
|
|
10
|
+
result_json['@odata.nextLink']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def next_page_url
|
|
14
|
+
next_page.gsub(service.service_url, '')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def error_message
|
|
18
|
+
result_json['error'].andand['message']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parsed_body
|
|
22
|
+
result_json
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def result_json
|
|
28
|
+
@result_json ||= ::JSON.parse(response.body)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def single_entity?
|
|
32
|
+
result_json['@odata.context'] =~ /\$entity$/
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def find_entities
|
|
36
|
+
single_entity? ? [result_json] : result_json['value']
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module FrOData
|
|
2
|
+
class Service
|
|
3
|
+
class Response
|
|
4
|
+
module Plain
|
|
5
|
+
def parse_entity(entity_data, entity_options)
|
|
6
|
+
raise NotImplementedError, 'Not Available'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def next_page
|
|
10
|
+
raise NotImplementedError, 'Not available'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def next_page_url
|
|
14
|
+
raise NotImplementedError, 'Not available'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def error_message
|
|
18
|
+
response.body
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parsed_body
|
|
22
|
+
response.body
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Find entity entries in a response set
|
|
28
|
+
#
|
|
29
|
+
# @return [Array]
|
|
30
|
+
def find_entities
|
|
31
|
+
[]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module FrOData
|
|
2
|
+
class Service
|
|
3
|
+
class Response
|
|
4
|
+
module XML
|
|
5
|
+
def parse_entity(entity_data, entity_options)
|
|
6
|
+
raise NotImplementedError, 'Not Available'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def next_page
|
|
10
|
+
raise NotImplementedError, 'Not Available'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def next_page_url
|
|
14
|
+
raise NotImplementedError, 'Not Available'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def error_message
|
|
18
|
+
response_xml.xpath('//error/message').first.andand.text
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parsed_body
|
|
22
|
+
response_xml
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def response_xml
|
|
28
|
+
@response_xml ||= ::Nokogiri::XML(response.body).remove_namespaces!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Find entity entries in a response set
|
|
32
|
+
#
|
|
33
|
+
# @return [Array]
|
|
34
|
+
def find_entities
|
|
35
|
+
[]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module FrOData
|
|
4
|
+
# Provides a registry for keeping track of multiple FrOData::Service instances
|
|
5
|
+
class ServiceRegistry
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
# Add a service to the Registry
|
|
9
|
+
#
|
|
10
|
+
# @param service [FrOData::Service] service to add to the registry
|
|
11
|
+
def add(service)
|
|
12
|
+
initialize_instance_variables
|
|
13
|
+
@services << service if service.is_a?(FrOData::Service) && !@services.include?(service)
|
|
14
|
+
@services_by_name[service.name] = @services.find_index(service)
|
|
15
|
+
@services_by_url[service.service_url] = @services.find_index(service)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Lookup a service by URL or name
|
|
19
|
+
#
|
|
20
|
+
# @param lookup_key [String] the URL or name to lookup
|
|
21
|
+
# @return [FrOData::Service, nil] the FrOData::Service or nil
|
|
22
|
+
def [](lookup_key)
|
|
23
|
+
initialize_instance_variables
|
|
24
|
+
index = @services_by_name[lookup_key] || @services_by_url[lookup_key]
|
|
25
|
+
index.nil? ? nil : @services[index]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# (see #add)
|
|
29
|
+
def self.add(service)
|
|
30
|
+
FrOData::ServiceRegistry.instance.add(service)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# (see #[])
|
|
34
|
+
def self.[](lookup_key)
|
|
35
|
+
FrOData::ServiceRegistry.instance[lookup_key]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def initialize_instance_variables
|
|
41
|
+
@services ||= []
|
|
42
|
+
@services_by_name ||= {}
|
|
43
|
+
@services_by_url ||= {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def flush
|
|
47
|
+
@services = []
|
|
48
|
+
@services_by_name = {}
|
|
49
|
+
@services_by_url = {}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
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.Product" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
|
|
4
|
+
<author>
|
|
5
|
+
<name/>
|
|
6
|
+
</author>
|
|
7
|
+
<content type="application/xml">
|
|
8
|
+
<metadata:properties>
|
|
9
|
+
<data:Name metadata:type="Edm.String">Bread</data:Name>
|
|
10
|
+
<data:Description metadata:type="Edm.String">Whole grain bread</data:Description>
|
|
11
|
+
<data:ReleaseDate metadata:type="Edm.DateTimeOffset">1992-01-01T00:00:00Z</data:ReleaseDate>
|
|
12
|
+
<data:DiscontinuedDate metadata:type="Edm.DateTimeOffset" metadata:null="true"/>
|
|
13
|
+
<data:Rating metadata:type="Edm.Int16">4</data:Rating>
|
|
14
|
+
<data:Price metadata:type="Edm.Double">2.5</data:Price>
|
|
15
|
+
</metadata:properties>
|
|
16
|
+
</content>
|
|
17
|
+
</entry>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
|
|
3
|
+
<edmx:DataServices>
|
|
4
|
+
<Schema Namespace="ODataDemo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
|
|
5
|
+
<EntityType Name="Product">
|
|
6
|
+
<Key>
|
|
7
|
+
<PropertyRef Name="ID" />
|
|
8
|
+
</Key>
|
|
9
|
+
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
|
|
10
|
+
<Property Name="Name" Type="Edm.String" />
|
|
11
|
+
<Property Name="Description" Type="Edm.String" />
|
|
12
|
+
<Property Name="ReleaseDate" Type="Edm.DateTimeOffset" Nullable="false" />
|
|
13
|
+
<Property Name="DiscontinuedDate" Type="Edm.DateTimeOffset" />
|
|
14
|
+
<Property Name="Rating" Type="Edm.Int16" Nullable="false" />
|
|
15
|
+
<Property Name="Price" Type="Edm.Double" Nullable="false" />
|
|
16
|
+
<Property Name="ProductStatus" Type="ODataDemo.ProductStatus" Nullable="false" />
|
|
17
|
+
<NavigationProperty Name="Categories" Type="Collection(ODataDemo.Category)" Partner="Products" />
|
|
18
|
+
<NavigationProperty Name="Supplier" Type="ODataDemo.Supplier" Partner="Products" />
|
|
19
|
+
<NavigationProperty Name="ProductDetail" Type="ODataDemo.ProductDetail" Partner="Product" />
|
|
20
|
+
</EntityType>
|
|
21
|
+
<EnumType Name="ProductStatus" UnderlyingType="Edm.Byte">
|
|
22
|
+
<Member Name="Available" />
|
|
23
|
+
<Member Name="LowStock" />
|
|
24
|
+
<Member Name="Backordered" />
|
|
25
|
+
<Member Name="Discontinued" />
|
|
26
|
+
</EnumType>
|
|
27
|
+
<EntityType Name="FeaturedProduct" BaseType="ODataDemo.Product">
|
|
28
|
+
<NavigationProperty Name="Advertisement" Type="ODataDemo.Advertisement" Partner="FeaturedProduct" />
|
|
29
|
+
</EntityType>
|
|
30
|
+
<EntityType Name="ProductDetail">
|
|
31
|
+
<Key>
|
|
32
|
+
<PropertyRef Name="ProductID" />
|
|
33
|
+
</Key>
|
|
34
|
+
<Property Name="ProductID" Type="Edm.Int32" Nullable="false" />
|
|
35
|
+
<Property Name="Details" Type="Edm.String" />
|
|
36
|
+
<NavigationProperty Name="Product" Type="ODataDemo.Product" Partner="ProductDetail" />
|
|
37
|
+
</EntityType>
|
|
38
|
+
<EntityType Name="Category" OpenType="true">
|
|
39
|
+
<Key>
|
|
40
|
+
<PropertyRef Name="ID" />
|
|
41
|
+
</Key>
|
|
42
|
+
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
|
|
43
|
+
<Property Name="Name" Type="Edm.String" />
|
|
44
|
+
<NavigationProperty Name="Products" Type="Collection(ODataDemo.Product)" Partner="Categories" />
|
|
45
|
+
</EntityType>
|
|
46
|
+
<EntityType Name="Supplier">
|
|
47
|
+
<Key>
|
|
48
|
+
<PropertyRef Name="ID" />
|
|
49
|
+
</Key>
|
|
50
|
+
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
|
|
51
|
+
<Property Name="Name" Type="Edm.String" />
|
|
52
|
+
<Property Name="Address" Type="ODataDemo.Address" />
|
|
53
|
+
<Property Name="Location" Type="Edm.GeographyPoint" SRID="Variable" />
|
|
54
|
+
<Property Name="Concurrency" Type="Edm.Int32" ConcurrencyMode="Fixed" Nullable="false" />
|
|
55
|
+
<NavigationProperty Name="Products" Type="Collection(ODataDemo.Product)" Partner="Supplier" />
|
|
56
|
+
</EntityType>
|
|
57
|
+
<ComplexType Name="Address">
|
|
58
|
+
<Property Name="Street" Type="Edm.String" />
|
|
59
|
+
<Property Name="City" Type="Edm.String" />
|
|
60
|
+
<Property Name="State" Type="Edm.String" />
|
|
61
|
+
<Property Name="ZipCode" Type="Edm.String" />
|
|
62
|
+
<Property Name="Country" Type="Edm.String" />
|
|
63
|
+
</ComplexType>
|
|
64
|
+
<EntityType Name="Person">
|
|
65
|
+
<Key>
|
|
66
|
+
<PropertyRef Name="ID" />
|
|
67
|
+
</Key>
|
|
68
|
+
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
|
|
69
|
+
<Property Name="Name" Type="Edm.String" />
|
|
70
|
+
<NavigationProperty Name="PersonDetail" Type="ODataDemo.PersonDetail" Partner="Person" />
|
|
71
|
+
</EntityType>
|
|
72
|
+
<EntityType Name="Customer" BaseType="ODataDemo.Person">
|
|
73
|
+
<Property Name="TotalExpense" Type="Edm.Decimal" Nullable="false" />
|
|
74
|
+
</EntityType>
|
|
75
|
+
<EntityType Name="Employee" BaseType="ODataDemo.Person">
|
|
76
|
+
<Property Name="EmployeeID" Type="Edm.Int64" Nullable="false" />
|
|
77
|
+
<Property Name="HireDate" Type="Edm.DateTimeOffset" Nullable="false" />
|
|
78
|
+
<Property Name="Salary" Type="Edm.Single" Nullable="false" />
|
|
79
|
+
</EntityType>
|
|
80
|
+
<EntityType Name="PersonDetail">
|
|
81
|
+
<Key>
|
|
82
|
+
<PropertyRef Name="PersonID" />
|
|
83
|
+
</Key>
|
|
84
|
+
<Property Name="PersonID" Type="Edm.Int32" Nullable="false" />
|
|
85
|
+
<Property Name="Age" Type="Edm.Byte" Nullable="false" />
|
|
86
|
+
<Property Name="Gender" Type="Edm.Boolean" Nullable="false" />
|
|
87
|
+
<Property Name="Phone" Type="Edm.String" />
|
|
88
|
+
<Property Name="Address" Type="ODataDemo.Address" />
|
|
89
|
+
<Property Name="Photo" Type="Edm.Stream" Nullable="false" />
|
|
90
|
+
<NavigationProperty Name="Person" Type="ODataDemo.Person" Partner="PersonDetail" />
|
|
91
|
+
</EntityType>
|
|
92
|
+
<EntityType Name="Advertisement" HasStream="true">
|
|
93
|
+
<Key>
|
|
94
|
+
<PropertyRef Name="ID" />
|
|
95
|
+
</Key>
|
|
96
|
+
<Property Name="ID" Type="Edm.Guid" Nullable="false" />
|
|
97
|
+
<Property Name="Name" Type="Edm.String" />
|
|
98
|
+
<Property Name="AirDate" Type="Edm.DateTimeOffset" Nullable="false" />
|
|
99
|
+
<NavigationProperty Name="FeaturedProduct" Type="ODataDemo.FeaturedProduct" Partner="Advertisement" />
|
|
100
|
+
</EntityType>
|
|
101
|
+
<EntityContainer Name="DemoService">
|
|
102
|
+
<EntitySet Name="Products" EntityType="ODataDemo.Product">
|
|
103
|
+
<NavigationPropertyBinding Path="ODataDemo.FeaturedProduct/Advertisement" Target="Advertisements" />
|
|
104
|
+
<NavigationPropertyBinding Path="Categories" Target="Categories" />
|
|
105
|
+
<NavigationPropertyBinding Path="Supplier" Target="Suppliers" />
|
|
106
|
+
<NavigationPropertyBinding Path="ProductDetail" Target="ProductDetails" />
|
|
107
|
+
</EntitySet>
|
|
108
|
+
<EntitySet Name="ProductDetails" EntityType="ODataDemo.ProductDetail">
|
|
109
|
+
<NavigationPropertyBinding Path="Product" Target="Products" />
|
|
110
|
+
</EntitySet>
|
|
111
|
+
<EntitySet Name="Categories" EntityType="ODataDemo.Category">
|
|
112
|
+
<NavigationPropertyBinding Path="Products" Target="Products" />
|
|
113
|
+
</EntitySet>
|
|
114
|
+
<EntitySet Name="Suppliers" EntityType="ODataDemo.Supplier">
|
|
115
|
+
<NavigationPropertyBinding Path="Products" Target="Products" />
|
|
116
|
+
</EntitySet>
|
|
117
|
+
<EntitySet Name="Persons" EntityType="ODataDemo.Person">
|
|
118
|
+
<NavigationPropertyBinding Path="PersonDetail" Target="PersonDetails" />
|
|
119
|
+
</EntitySet>
|
|
120
|
+
<EntitySet Name="PersonDetails" EntityType="ODataDemo.PersonDetail">
|
|
121
|
+
<NavigationPropertyBinding Path="Person" Target="Persons" />
|
|
122
|
+
</EntitySet>
|
|
123
|
+
<EntitySet Name="Advertisements" EntityType="ODataDemo.Advertisement">
|
|
124
|
+
<NavigationPropertyBinding Path="FeaturedProduct" Target="Products" />
|
|
125
|
+
</EntitySet>
|
|
126
|
+
</EntityContainer>
|
|
127
|
+
<Annotations Target="ODataDemo.DemoService">
|
|
128
|
+
<Annotation Term="Org.OData.Display.V1.Description" String="This is a sample OData service with vocabularies" />
|
|
129
|
+
</Annotations>
|
|
130
|
+
<Annotations Target="ODataDemo.Product">
|
|
131
|
+
<Annotation Term="Org.OData.Display.V1.Description" String="All Products available in the online store" />
|
|
132
|
+
</Annotations>
|
|
133
|
+
<Annotations Target="ODataDemo.Product/Name">
|
|
134
|
+
<Annotation Term="Org.OData.Display.V1.DisplayName" String="Product Name" />
|
|
135
|
+
</Annotations>
|
|
136
|
+
<Annotations Target="ODataDemo.DemoService/Suppliers">
|
|
137
|
+
<Annotation Term="Org.OData.Publication.V1.PublisherName" String="Microsoft Corp." />
|
|
138
|
+
<Annotation Term="Org.OData.Publication.V1.PublisherId" String="MSFT" />
|
|
139
|
+
<Annotation Term="Org.OData.Publication.V1.Keywords" String="Inventory, Supplier, Advertisers, Sales, Finance" />
|
|
140
|
+
<Annotation Term="Org.OData.Publication.V1.AttributionUrl" String="http://www.odata.org/" />
|
|
141
|
+
<Annotation Term="Org.OData.Publication.V1.AttributionDescription" String="All rights reserved" />
|
|
142
|
+
<Annotation Term="Org.OData.Publication.V1.DocumentationUrl " String="http://www.odata.org/" />
|
|
143
|
+
<Annotation Term="Org.OData.Publication.V1.TermsOfUseUrl" String="All rights reserved" />
|
|
144
|
+
<Annotation Term="Org.OData.Publication.V1.PrivacyPolicyUrl" String="http://www.odata.org/" />
|
|
145
|
+
<Annotation Term="Org.OData.Publication.V1.LastModified" String="4/2/2013" />
|
|
146
|
+
<Annotation Term="Org.OData.Publication.V1.ImageUrl " String="http://www.odata.org/" />
|
|
147
|
+
</Annotations>
|
|
148
|
+
</Schema>
|
|
149
|
+
</edmx:DataServices>
|
|
150
|
+
</edmx:Edmx>
|