frodata 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +150 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +427 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/frodata.gemspec +34 -0
  15. data/lib/frodata.rb +36 -0
  16. data/lib/frodata/entity.rb +332 -0
  17. data/lib/frodata/entity_container.rb +75 -0
  18. data/lib/frodata/entity_set.rb +161 -0
  19. data/lib/frodata/errors.rb +68 -0
  20. data/lib/frodata/navigation_property.rb +29 -0
  21. data/lib/frodata/navigation_property/proxy.rb +80 -0
  22. data/lib/frodata/properties.rb +32 -0
  23. data/lib/frodata/properties/binary.rb +50 -0
  24. data/lib/frodata/properties/boolean.rb +37 -0
  25. data/lib/frodata/properties/collection.rb +50 -0
  26. data/lib/frodata/properties/complex.rb +114 -0
  27. data/lib/frodata/properties/date.rb +27 -0
  28. data/lib/frodata/properties/date_time.rb +83 -0
  29. data/lib/frodata/properties/date_time_offset.rb +17 -0
  30. data/lib/frodata/properties/decimal.rb +50 -0
  31. data/lib/frodata/properties/enum.rb +62 -0
  32. data/lib/frodata/properties/float.rb +67 -0
  33. data/lib/frodata/properties/geography.rb +13 -0
  34. data/lib/frodata/properties/geography/base.rb +162 -0
  35. data/lib/frodata/properties/geography/line_string.rb +33 -0
  36. data/lib/frodata/properties/geography/point.rb +31 -0
  37. data/lib/frodata/properties/geography/polygon.rb +38 -0
  38. data/lib/frodata/properties/guid.rb +17 -0
  39. data/lib/frodata/properties/integer.rb +107 -0
  40. data/lib/frodata/properties/number.rb +14 -0
  41. data/lib/frodata/properties/string.rb +72 -0
  42. data/lib/frodata/properties/time.rb +40 -0
  43. data/lib/frodata/properties/time_of_day.rb +27 -0
  44. data/lib/frodata/property.rb +139 -0
  45. data/lib/frodata/property_registry.rb +41 -0
  46. data/lib/frodata/query.rb +233 -0
  47. data/lib/frodata/query/criteria.rb +92 -0
  48. data/lib/frodata/query/criteria/comparison_operators.rb +49 -0
  49. data/lib/frodata/query/criteria/date_functions.rb +61 -0
  50. data/lib/frodata/query/criteria/geography_functions.rb +21 -0
  51. data/lib/frodata/query/criteria/lambda_operators.rb +27 -0
  52. data/lib/frodata/query/criteria/string_functions.rb +40 -0
  53. data/lib/frodata/query/in_batches.rb +58 -0
  54. data/lib/frodata/railtie.rb +19 -0
  55. data/lib/frodata/schema.rb +155 -0
  56. data/lib/frodata/schema/complex_type.rb +79 -0
  57. data/lib/frodata/schema/enum_type.rb +95 -0
  58. data/lib/frodata/service.rb +254 -0
  59. data/lib/frodata/service/request.rb +85 -0
  60. data/lib/frodata/service/response.rb +162 -0
  61. data/lib/frodata/service/response/atom.rb +40 -0
  62. data/lib/frodata/service/response/json.rb +41 -0
  63. data/lib/frodata/service/response/plain.rb +36 -0
  64. data/lib/frodata/service/response/xml.rb +40 -0
  65. data/lib/frodata/service_registry.rb +52 -0
  66. data/lib/frodata/version.rb +3 -0
  67. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  68. data/spec/fixtures/files/error.xml +5 -0
  69. data/spec/fixtures/files/metadata.xml +150 -0
  70. data/spec/fixtures/files/product_0.json +10 -0
  71. data/spec/fixtures/files/product_0.xml +28 -0
  72. data/spec/fixtures/files/products.json +106 -0
  73. data/spec/fixtures/files/products.xml +308 -0
  74. data/spec/fixtures/files/supplier_0.json +26 -0
  75. data/spec/fixtures/files/supplier_0.xml +32 -0
  76. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1635 -0
  77. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  78. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  79. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  80. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  81. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  82. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  83. data/spec/fixtures/vcr_cassettes/query_specs.yml +1060 -0
  84. data/spec/fixtures/vcr_cassettes/schema/complex_type_specs.yml +127 -0
  85. data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
  86. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  87. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  88. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +1330 -0
  89. data/spec/frodata/entity/shared_examples.rb +82 -0
  90. data/spec/frodata/entity_container_spec.rb +38 -0
  91. data/spec/frodata/entity_set_spec.rb +168 -0
  92. data/spec/frodata/entity_spec.rb +151 -0
  93. data/spec/frodata/errors_spec.rb +48 -0
  94. data/spec/frodata/navigation_property/proxy_spec.rb +44 -0
  95. data/spec/frodata/navigation_property_spec.rb +55 -0
  96. data/spec/frodata/properties/binary_spec.rb +50 -0
  97. data/spec/frodata/properties/boolean_spec.rb +72 -0
  98. data/spec/frodata/properties/collection_spec.rb +44 -0
  99. data/spec/frodata/properties/date_spec.rb +23 -0
  100. data/spec/frodata/properties/date_time_offset_spec.rb +30 -0
  101. data/spec/frodata/properties/date_time_spec.rb +23 -0
  102. data/spec/frodata/properties/decimal_spec.rb +51 -0
  103. data/spec/frodata/properties/float_spec.rb +45 -0
  104. data/spec/frodata/properties/geography/line_string_spec.rb +33 -0
  105. data/spec/frodata/properties/geography/point_spec.rb +29 -0
  106. data/spec/frodata/properties/geography/polygon_spec.rb +55 -0
  107. data/spec/frodata/properties/geography/shared_examples.rb +72 -0
  108. data/spec/frodata/properties/guid_spec.rb +17 -0
  109. data/spec/frodata/properties/integer_spec.rb +58 -0
  110. data/spec/frodata/properties/string_spec.rb +46 -0
  111. data/spec/frodata/properties/time_of_day_spec.rb +23 -0
  112. data/spec/frodata/properties/time_spec.rb +15 -0
  113. data/spec/frodata/property_registry_spec.rb +16 -0
  114. data/spec/frodata/property_spec.rb +71 -0
  115. data/spec/frodata/query/criteria_spec.rb +229 -0
  116. data/spec/frodata/query_spec.rb +199 -0
  117. data/spec/frodata/schema/complex_type_spec.rb +96 -0
  118. data/spec/frodata/schema/enum_type_spec.rb +112 -0
  119. data/spec/frodata/schema_spec.rb +97 -0
  120. data/spec/frodata/service/request_spec.rb +49 -0
  121. data/spec/frodata/service/response_spec.rb +85 -0
  122. data/spec/frodata/service_registry_spec.rb +18 -0
  123. data/spec/frodata/service_spec.rb +191 -0
  124. data/spec/frodata/usage_example_spec.rb +188 -0
  125. data/spec/spec_helper.rb +32 -0
  126. data/spec/support/coverage.rb +2 -0
  127. data/spec/support/vcr.rb +9 -0
  128. 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,3 @@
1
+ module FrOData
2
+ VERSION = '0.9.1'
3
+ 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,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <m:error xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
3
+ <m:code />
4
+ <m:message>Resource not found for the segment 'Products'.</m:message>
5
+ </m:error>
@@ -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>