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.
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,79 @@
1
+ module FrOData
2
+ class Schema
3
+ # ComplexTypes are used in FrOData to either encapsulate richer data types for
4
+ # use as Entity properties. ComplexTypes are composed of properties the same
5
+ # way that Entities are and, so, the interface for working with the various
6
+ # properties of a ComplexType mimics that of Entities.
7
+ class ComplexType
8
+ # Creates a new ComplexType based on the supplied options.
9
+ # @param type_xml [Nokogiri::XML::Element]
10
+ # @param service [FrOData::Service]
11
+ def initialize(type_definition, schema)
12
+ @type_definition = type_definition
13
+ @schema = schema
14
+ end
15
+
16
+ # The name of the ComplexType
17
+ # @return [String]
18
+ def name
19
+ @name ||= type_definition.attributes['Name'].value
20
+ end
21
+
22
+ # Returns the namespaced type for the ComplexType.
23
+ # @return [String]
24
+ def type
25
+ "#{namespace}.#{name}"
26
+ end
27
+
28
+ # Returns the namespace this ComplexType belongs to.
29
+ # @return [String]
30
+ def namespace
31
+ @namespace ||= service.namespace
32
+ end
33
+
34
+ # Returns this ComplexType's properties.
35
+ # @return [Hash<String, FrOData::Property>]
36
+ def properties
37
+ @properties ||= collect_properties
38
+ end
39
+
40
+ # Returns a list of this ComplexType's property names.
41
+ # @return [Array<String>]
42
+ def property_names
43
+ @property_names ||= properties.keys
44
+ end
45
+
46
+ # Returns the property class that implements this `ComplexType`.
47
+ # @return [Class < FrOData::Properties::Complex]
48
+ def property_class
49
+ @property_class ||= lambda { |type, complex_type|
50
+ klass = Class.new ::FrOData::Properties::Complex
51
+ klass.send(:define_method, :type) { type }
52
+ klass.send(:define_method, :complex_type) { complex_type }
53
+ klass
54
+ }.call(type, self)
55
+ end
56
+
57
+ private
58
+
59
+ def schema
60
+ @schema
61
+ end
62
+
63
+ def service
64
+ @schema.service
65
+ end
66
+
67
+ def type_definition
68
+ @type_definition
69
+ end
70
+
71
+ def collect_properties
72
+ Hash[type_definition.xpath('./Property').map do |property_xml|
73
+ property_name, property = schema.send(:process_property_from_xml, property_xml)
74
+ [property_name, property]
75
+ end]
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,95 @@
1
+ module FrOData
2
+ class Schema
3
+ # Enumeration types are nominal types that represent a series of related values.
4
+ # Enumeration types expose these related values as members of the enumeration.
5
+ class EnumType
6
+ # Creates a new EnumType based on the supplied options.
7
+ # @param type_xml [Nokogiri::XML::Element]
8
+ # @param service [FrOData::Service]
9
+ # @return [self]
10
+ def initialize(type_definition, schema)
11
+ @type_definition = type_definition
12
+ @schema = schema
13
+ end
14
+
15
+ # The name of the EnumType
16
+ # @return [String]
17
+ def name
18
+ options['Name']
19
+ end
20
+
21
+ # Returns the namespaced type for the EnumType.
22
+ # @return [String]
23
+ def type
24
+ "#{namespace}.#{name}"
25
+ end
26
+
27
+ # Whether this EnumType supports setting multiple values.
28
+ # @return [Boolean]
29
+ def is_flags?
30
+ options['IsFlags'] == 'true'
31
+ end
32
+
33
+ # The underlying type of this EnumType.
34
+ # @return [String]
35
+ def underlying_type
36
+ options['UnderlyingType'] || 'Edm.Int32'
37
+ end
38
+
39
+ # Returns the namespace this EnumType belongs to.
40
+ # @return [String]
41
+ def namespace
42
+ @namespace ||= service.namespace
43
+ end
44
+
45
+ # Returns the members of this EnumType and their values.
46
+ # @return [Hash]
47
+ def members
48
+ @members ||= collect_members
49
+ end
50
+
51
+ # Returns the property class that implements this `EnumType`.
52
+ # @return [Class < FrOData::Properties::Enum]
53
+ def property_class
54
+ @property_class ||= lambda { |type, members, is_flags|
55
+ klass = Class.new ::FrOData::Properties::Enum
56
+ klass.send(:define_method, :type) { type }
57
+ klass.send(:define_method, :members) { members }
58
+ klass.send(:define_method, :is_flags?) { is_flags }
59
+ klass
60
+ }.call(type, members, is_flags?)
61
+ end
62
+
63
+ # Returns the value of the requested member.
64
+ # @param member_name [to_s]
65
+ # @return [*]
66
+ def [](member_name)
67
+ members.invert[member_name.to_s]
68
+ end
69
+
70
+ private
71
+
72
+ def service
73
+ @schema.service
74
+ end
75
+
76
+ def type_definition
77
+ @type_definition
78
+ end
79
+
80
+ def options
81
+ @options = type_definition.attributes.map do |name, attr|
82
+ [name, attr.value]
83
+ end.to_h
84
+ end
85
+
86
+ def collect_members
87
+ Hash[type_definition.xpath('./Member').map.with_index do |member_xml, index|
88
+ member_name = member_xml.attributes['Name'].value
89
+ member_value = member_xml.attributes['Value'].andand.value.andand.to_i
90
+ [member_value || index, member_name]
91
+ end]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,254 @@
1
+ require 'frodata/service/request'
2
+ require 'frodata/service/response'
3
+
4
+ module FrOData
5
+ # Encapsulates the basic details and functionality needed to interact with an
6
+ # FrOData service.
7
+ class Service
8
+ # The Faraday connection object used by the service to make requests
9
+ attr_reader :connection
10
+ # The FrOData Service's URL
11
+ attr_reader :service_url
12
+ # Service options
13
+ attr_reader :options
14
+
15
+ DEFAULT_TIMEOUT = 20
16
+
17
+ METADATA_TIMEOUTS = [20, 60]
18
+
19
+ MIME_TYPES = {
20
+ atom: 'application/atom+xml',
21
+ json: 'application/json',
22
+ xml: 'application/xml',
23
+ plain: 'text/plain'
24
+ }
25
+
26
+ # Opens the service based on the requested URL and adds the service to
27
+ # {FrOData::Registry}
28
+ #
29
+ # @param service_url [String|Faraday::Connection]
30
+ # The URL to the desired FrOData service, or a Faraday connection object
31
+ # @param options [Hash] options to pass to the service
32
+ # @return [FrOData::Service] an instance of the service
33
+ def initialize(service_url, options = {}, &block)
34
+ if service_url.is_a? Faraday::Connection
35
+ @connection = service_url
36
+ @service_url = connection.url_prefix
37
+ else
38
+ @service_url = service_url
39
+ @connection = Faraday.new(service_url, options[:connection], &block)
40
+ end
41
+ @options = default_options.merge(options)
42
+ FrOData::ServiceRegistry.add(self)
43
+ register_custom_types
44
+ end
45
+
46
+ # Opens the service based on the requested URL and adds the service to
47
+ # {FrOData::Registry}
48
+ # @deprecated Use {Service.new} instead.
49
+ #
50
+ # @param service_url [String] the URL to the desired FrOData service
51
+ # @param options [Hash] options to pass to the service
52
+ # @return [FrOData::Service] an instance of the service
53
+ def self.open(service_url, options = {}, &block)
54
+ Service.new(service_url, options, &block)
55
+ end
56
+
57
+ # Returns user supplied name for service, or its URL
58
+ # @return [String]
59
+ def name
60
+ @name ||= options[:name] || service_url
61
+ end
62
+
63
+ # Returns the service's metadata URL.
64
+ # @return [String]
65
+ def metadata_url
66
+ "#{service_url}/$metadata"
67
+ end
68
+
69
+ # Returns the service's metadata definition.
70
+ # @return [Nokogiri::XML]
71
+ def metadata
72
+ @metadata ||= lambda { read_metadata }.call
73
+ end
74
+
75
+ # Returns all of the service's schemas.
76
+ # @return Hash<String, FrOData::Schema>
77
+ def schemas
78
+ @schemas ||= metadata.xpath('//Schema').map do |schema_xml|
79
+ [
80
+ schema_xml.attributes['Namespace'].value,
81
+ Schema.new(schema_xml, self)
82
+ ]
83
+ end.to_h
84
+ end
85
+
86
+ # Returns the service's EntityContainer (singleton)
87
+ # @return FrOData::EntityContainer
88
+ def entity_container
89
+ @entity_container ||= EntityContainer.new(self)
90
+ end
91
+
92
+ # Returns a hash of EntitySet names and their respective EntityType names
93
+ # @return Hash<String, String>
94
+ def entity_sets
95
+ entity_container.entity_sets
96
+ end
97
+
98
+ # Retrieves the EntitySet associated with a specific EntityType by name
99
+ #
100
+ # @param entity_set_name [to_s] the name of the EntitySet desired
101
+ # @return [FrOData::EntitySet] an FrOData::EntitySet to query
102
+ def [](entity_set_name)
103
+ entity_container[entity_set_name]
104
+ end
105
+
106
+ # Returns the default namespace, that is, the namespace of the schema
107
+ # that contains the service's EntityContainer.
108
+ # @return [String]
109
+ def namespace
110
+ entity_container.namespace
111
+ end
112
+
113
+ # Returns a list of `EntityType`s exposed by the service
114
+ # @return Array<String>
115
+ def entity_types
116
+ @entity_types ||= schemas.map do |namespace, schema|
117
+ schema.entity_types.map do |entity_type|
118
+ "#{namespace}.#{entity_type}"
119
+ end
120
+ end.flatten
121
+ end
122
+
123
+ # Returns a list of `ComplexType`s used by the service.
124
+ # @return [Hash<String, FrOData::Schema::ComplexType>]
125
+ def complex_types
126
+ @complex_types ||= schemas.map do |namespace, schema|
127
+ schema.complex_types.map do |name, complex_type|
128
+ [ "#{namespace}.#{name}", complex_type ]
129
+ end.to_h
130
+ end.reduce({}, :merge)
131
+ end
132
+
133
+ # Returns a list of `EnumType`s used by the service
134
+ # @return [Hash<String, FrOData::Schema::EnumType>]
135
+ def enum_types
136
+ @enum_types ||= schemas.map do |namespace, schema|
137
+ schema.enum_types.map do |name, enum_type|
138
+ [ "#{namespace}.#{name}", enum_type ]
139
+ end.to_h
140
+ end.reduce({}, :merge)
141
+ end
142
+
143
+ # Returns a more compact inspection of the service object
144
+ def inspect
145
+ "#<#{self.class.name}:#{self.object_id} name='#{name}' service_url='#{self.service_url}'>"
146
+ end
147
+
148
+ # Execute a request against the service
149
+ #
150
+ # @param url_chunk [to_s] string to append to service URL
151
+ # @param options [Hash] additional request options
152
+ # @return [FrOData::Service::Response]
153
+ def execute(url_chunk, options = {})
154
+ Request.new(self, url_chunk, options).execute
155
+ end
156
+
157
+ # Get the property type for an entity from metadata.
158
+ #
159
+ # @param entity_name [to_s] the fully qualified entity name
160
+ # @param property_name [to_s] the property name needed
161
+ # @return [String] the name of the property's type
162
+ def get_property_type(entity_name, property_name)
163
+ namespace, _, entity_name = entity_name.rpartition('.')
164
+ raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
165
+ schemas[namespace].get_property_type(entity_name, property_name)
166
+ end
167
+
168
+ # Get the primary key for the supplied Entity.
169
+ #
170
+ # @param entity_name [to_s] The fully qualified entity name
171
+ # @return [String]
172
+ def primary_key_for(entity_name)
173
+ namespace, _, entity_name = entity_name.rpartition('.')
174
+ raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
175
+ schemas[namespace].primary_key_for(entity_name)
176
+ end
177
+
178
+ # Get the list of properties and their various options for the supplied
179
+ # Entity name.
180
+ # @param entity_name [to_s]
181
+ # @return [Hash]
182
+ # @api private
183
+ def properties_for_entity(entity_name)
184
+ namespace, _, entity_name = entity_name.rpartition('.')
185
+ raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
186
+ schemas[namespace].properties_for_entity(entity_name)
187
+ end
188
+
189
+ # Returns the logger instance used by the service.
190
+ # When Ruby on Rails has been detected, the service will
191
+ # use `Rails.logger`. The log level will NOT be changed.
192
+ #
193
+ # When no Rails has been detected, a default logger will
194
+ # be used that logs to STDOUT with the log level supplied
195
+ # via options, or the default log level if none was given.
196
+ # @return [Logger]
197
+ def logger
198
+ @logger ||= options[:logger] || if defined?(Rails)
199
+ Rails.logger
200
+ else
201
+ default_logger
202
+ end
203
+ end
204
+
205
+ # Allows the logger to be set to a custom `Logger` instance.
206
+ # @param custom_logger [Logger]
207
+ def logger=(custom_logger)
208
+ @logger = custom_logger
209
+ end
210
+
211
+ private
212
+
213
+ def default_options
214
+ {
215
+ request: {
216
+ timeout: DEFAULT_TIMEOUT
217
+ },
218
+ strict: true # strict property validation
219
+ }
220
+ end
221
+
222
+ def default_logger
223
+ Logger.new(STDOUT).tap do |logger|
224
+ logger.level = options[:log_level] || Logger::WARN
225
+ end
226
+ end
227
+
228
+ def read_metadata
229
+ # From file, good for debugging
230
+ if options[:metadata_file]
231
+ data = File.read(options[:metadata_file])
232
+ ::Nokogiri::XML(data).remove_namespaces!
233
+ else # From a URL
234
+ response = nil
235
+ METADATA_TIMEOUTS.each do |timeout|
236
+ response = execute(metadata_url, timeout: timeout)
237
+ break unless response.timed_out?
238
+ end
239
+ raise "Metadata Timeout" if response.timed_out?
240
+ ::Nokogiri::XML(response.body).remove_namespaces!
241
+ end
242
+ end
243
+
244
+ def register_custom_types
245
+ complex_types.each do |name, type|
246
+ ::FrOData::PropertyRegistry.add(name, type.property_class)
247
+ end
248
+
249
+ enum_types.each do |name, type|
250
+ ::FrOData::PropertyRegistry.add(name, type.property_class)
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,85 @@
1
+ module FrOData
2
+ class Service
3
+ # Encapsulates a single request to an OData service.
4
+ class Request
5
+ # The OData service against which the request is performed
6
+ attr_reader :service
7
+ # The FrOData::Query that generated this request (optional)
8
+ attr_reader :query
9
+ # The HTTP method for this request
10
+ attr_accessor :method
11
+ # The request format (`:atom`, `:json`, or `:auto`)
12
+ attr_accessor :format
13
+
14
+ # Create a new request
15
+ # @param service [FrOData::Service] Where the request will be sent
16
+ # @param url_chunk [String] Request path, relative to the service URL, including query params
17
+ # @param options [Hash] Additional request options
18
+ def initialize(service, url_chunk, options = {})
19
+ @service = service
20
+ @url_chunk = url_chunk
21
+ @method = options.delete(:method) || :get
22
+ @format = options.delete(:format) || :auto
23
+ @query = options.delete(:query)
24
+ @options = options
25
+ end
26
+
27
+ # Return the full request URL (including service base)
28
+ # @return [String]
29
+ def url
30
+ ::URI.join("#{service.service_url}/", ::URI.escape(url_chunk)).to_s
31
+ end
32
+
33
+ # The content type for this request. Depends on format.
34
+ # @return [String]
35
+ def content_type
36
+ if format == :auto
37
+ MIME_TYPES.values.join(',')
38
+ elsif MIME_TYPES.has_key? format
39
+ MIME_TYPES[format]
40
+ else
41
+ raise ArgumentError, "Unknown format '#{format}'"
42
+ end
43
+ end
44
+
45
+ # Execute the request
46
+ #
47
+ # @param additional_options [Hash] Request options to pass to Faraday
48
+ # @return [FrOData::Service::Response]
49
+ def execute(additional_options = {})
50
+ request_options = service.options[:request].merge(additional_options)
51
+
52
+ logger.info "Requesting #{method.to_s.upcase} #{url}..."
53
+ Response.new(service, query) do
54
+ connection.run_request(method, url_chunk, nil, headers) do |conn|
55
+ conn.options.merge! request_options
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :url_chunk
63
+
64
+ def connection
65
+ service.connection
66
+ end
67
+
68
+ def default_headers
69
+ {
70
+ 'Accept' => content_type,
71
+ 'Content-Type' => content_type,
72
+ 'OData-Version' => '4.0'
73
+ }
74
+ end
75
+
76
+ def headers
77
+ default_headers.merge(@options[:headers] || {})
78
+ end
79
+
80
+ def logger
81
+ service.logger
82
+ end
83
+ end
84
+ end
85
+ end