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,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