Class: OData4::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/odata4/service.rb

Overview

Encapsulates the basic details and functionality needed to interact with an OData4 service.

Constant Summary collapse

HTTP_TIMEOUT =
20
METADATA_TIMEOUTS =
[20, 60]
MIME_TYPES =
{
  atom:  'application/atom+xml',
  json:  'application/json',
  plain: 'text/plain'
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(service_url, options = {}) ⇒ OData4::Service

Opens the service based on the requested URL and adds the service to Registry

Parameters:

  • service_url (String)

    the URL to the desired OData4 service

  • options (Hash) (defaults to: {})

    options to pass to the service



26
27
28
29
30
31
# File 'lib/odata4/service.rb', line 26

def initialize(service_url, options = {})
  @service_url = service_url
  @options = default_options.merge(options)
  OData4::ServiceRegistry.add(self)
  register_custom_types
end

Instance Attribute Details

#optionsObject (readonly)

Options to pass around



8
9
10
# File 'lib/odata4/service.rb', line 8

def options
  @options
end

#service_urlObject (readonly)

The OData4 Service’s URL



6
7
8
# File 'lib/odata4/service.rb', line 6

def service_url
  @service_url
end

Class Method Details

.open(service_url, options = {}) ⇒ OData4::Service

Opens the service based on the requested URL and adds the service to Registry

Parameters:

  • service_url (String)

    the URL to the desired OData4 service

  • options (Hash) (defaults to: {})

    options to pass to the service

Returns:



39
40
41
# File 'lib/odata4/service.rb', line 39

def self.open(service_url, options = {})
  Service.new(service_url, options)
end

Instance Method Details

#[](entity_set_name) ⇒ OData4::EntitySet

Retrieves the EntitySet associated with a specific EntityType by name

Parameters:

  • entity_set_name (to_s)

    the name of the EntitySet desired

Returns:



88
89
90
# File 'lib/odata4/service.rb', line 88

def [](entity_set_name)
  entity_container[entity_set_name]
end

#complex_typesHash<String, OData4::ComplexType>

Returns a list of ‘ComplexType`s used by the service.

Returns:



111
112
113
114
115
116
117
# File 'lib/odata4/service.rb', line 111

def complex_types
  @complex_types ||= schemas.map do |namespace, schema|
    schema.complex_types.map do |name, complex_type|
      [ "#{namespace}.#{name}", complex_type ]
    end.to_h
  end.reduce({}, :merge)
end

#entity_containerObject

Returns the service’s EntityContainer (singleton)

Returns:

  • OData4::EntityContainer



74
75
76
# File 'lib/odata4/service.rb', line 74

def entity_container
  @entity_container ||= EntityContainer.new(self)
end

#entity_setsObject

Returns a hash of EntitySet names and their respective EntityType names



80
81
82
# File 'lib/odata4/service.rb', line 80

def entity_sets
  entity_container.entity_sets
end

#entity_typesObject

Returns a list of ‘EntityType`s exposed by the service



101
102
103
104
105
106
107
# File 'lib/odata4/service.rb', line 101

def entity_types
  @entity_types ||= schemas.map do |namespace, schema|
    schema.entity_types.map do |entity_type|
      "#{namespace}.#{entity_type}"
    end
  end.flatten
end

#enum_typesHash<String, OData4::EnumType>

Returns a list of ‘EnumType`s used by the service

Returns:



121
122
123
124
125
126
127
# File 'lib/odata4/service.rb', line 121

def enum_types
  @enum_types ||= schemas.map do |namespace, schema|
    schema.enum_types.map do |name, enum_type|
      [ "#{namespace}.#{name}", enum_type ]
    end.to_h
  end.reduce({}, :merge)
end

#execute(url_chunk, additional_options = {}) ⇒ Typhoeus::Response

Execute a request against the service

Parameters:

  • url_chunk (to_s)

    string to append to service url

  • additional_options (Hash) (defaults to: {})

    options to pass to Typhoeus

Returns:

  • (Typhoeus::Response)


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/odata4/service.rb', line 139

def execute(url_chunk, additional_options = {})
  accept = content_type(additional_options.delete(:format) || :auto)
  accept_header = {'Accept' => accept }

  request_options = options[:typhoeus]
    .merge({ method: :get })
    .merge(additional_options)

  # Don't overwrite Accept header if already present
  unless request_options[:headers]['Accept']
    request_options[:headers] = request_options[:headers].merge(accept_header)
  end

  request = ::Typhoeus::Request.new(
    URI.join("#{service_url}/", URI.escape(url_chunk)),
    request_options
  )
  logger.info "Requesting #{URI.unescape(request.url)}..."
  request.run

  response = request.response
  logger.debug(response.headers)
  logger.debug(response.body)

  validate_response(response)
  response
end

#find_entities(results) ⇒ Nokogiri::XML::NodeSet

Find entity entries in a result set

Parameters:

  • results (Typhoeus::Response)

Returns:

  • (Nokogiri::XML::NodeSet)


181
182
183
184
185
# File 'lib/odata4/service.rb', line 181

def find_entities(results)
  document = ::Nokogiri::XML(results.body)
  document.remove_namespaces!
  document.xpath('//entry')
end

#find_node(results, node_name) ⇒ Nokogiri::XML::Element

Find a specific node in the given result set

Parameters:

  • results (Typhoeus::Response)

Returns:

  • (Nokogiri::XML::Element)


171
172
173
174
175
# File 'lib/odata4/service.rb', line 171

def find_node(results, node_name)
  document = ::Nokogiri::XML(results.body)
  document.remove_namespaces!
  document.xpath("//#{node_name}").first
end

#get_property_type(entity_name, property_name) ⇒ String

Get the property type for an entity from metadata.

Parameters:

  • entity_name (to_s)

    the fully qualified entity name

  • property_name (to_s)

    the property name needed

Returns:

  • (String)

    the name of the property’s type

Raises:

  • (ArgumentError)


192
193
194
195
196
# File 'lib/odata4/service.rb', line 192

def get_property_type(entity_name, property_name)
  namespace, _, entity_name = entity_name.rpartition('.')
  raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
  schemas[namespace].get_property_type(entity_name, property_name)
end

#inspectObject

Returns a more compact inspection of the service object



130
131
132
# File 'lib/odata4/service.rb', line 130

def inspect
  "#<#{self.class.name}:#{self.object_id} name='#{name}' service_url='#{self.service_url}'>"
end

#log_levelFixnum|Symbol

Returns the log level set via initial options, or the default log level (‘Logger::WARN`) if none was specified.

Returns:

  • (Fixnum|Symbol)

See Also:

  • Logger


223
224
225
# File 'lib/odata4/service.rb', line 223

def log_level
  options[:log_level] || Logger::WARN
end

#loggerLogger

Returns the logger instance used by the service. When Ruby on Rails has been detected, the service will use ‘Rails.logger`. The log level will NOT be changed.

When no Rails has been detected, a default logger will be used that logs to STDOUT with the log level supplied via options, or the default log level if none was given.

Returns:

  • (Logger)

See Also:



236
237
238
239
240
241
242
243
244
245
246
# File 'lib/odata4/service.rb', line 236

def logger
  @logger ||= lambda do
    if defined?(Rails)
      Rails.logger
    else
      logger = Logger.new(STDOUT)
      logger.level = log_level
      logger
    end
  end.call
end

#logger=(custom_logger) ⇒ Object

Allows the logger to be set to a custom ‘Logger` instance.

Parameters:

  • custom_logger (Logger)


250
251
252
# File 'lib/odata4/service.rb', line 250

def logger=(custom_logger)
  @logger = custom_logger
end

#metadataNokogiri::XML

Returns the service’s metadata definition.

Returns:

  • (Nokogiri::XML)


57
58
59
# File 'lib/odata4/service.rb', line 57

def 
  @metadata ||= lambda {  }.call
end

#metadata_urlString

Returns the service’s metadata URL.

Returns:

  • (String)


51
52
53
# File 'lib/odata4/service.rb', line 51

def 
  "#{service_url}/$metadata"
end

#nameString

Returns user supplied name for service, or its URL

Returns:

  • (String)


45
46
47
# File 'lib/odata4/service.rb', line 45

def name
  @name ||= options[:name] || service_url
end

#namespaceString

Returns the default namespace, that is, the namespace of the schema that contains the service’s EntityContainer.

Returns:

  • (String)


95
96
97
# File 'lib/odata4/service.rb', line 95

def namespace
  entity_container.namespace
end

#primary_key_for(entity_name) ⇒ String

Get the primary key for the supplied Entity.

Parameters:

  • entity_name (to_s)

    The fully qualified entity name

Returns:

  • (String)

Raises:

  • (ArgumentError)


202
203
204
205
206
# File 'lib/odata4/service.rb', line 202

def primary_key_for(entity_name)
  namespace, _, entity_name = entity_name.rpartition('.')
  raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
  schemas[namespace].primary_key_for(entity_name)
end

#properties_for_entity(entity_name) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the list of properties and their various options for the supplied Entity name.

Parameters:

  • entity_name (to_s)

Returns:

  • (Hash)

Raises:

  • (ArgumentError)


213
214
215
216
217
# File 'lib/odata4/service.rb', line 213

def properties_for_entity(entity_name)
  namespace, _, entity_name = entity_name.rpartition('.')
  raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
  schemas[namespace].properties_for_entity(entity_name)
end

#schemasObject

Returns all of the service’s schemas.



63
64
65
66
67
68
69
70
# File 'lib/odata4/service.rb', line 63

def schemas
  @schemas ||= .xpath('//Schema').map do |schema_xml|
    [
      schema_xml.attributes['Namespace'].value,
      Schema.new(schema_xml, self)
    ]
  end.to_h
end