Class: OData::Service

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

Overview

Encapsulates the basic details and functionality needed to interact with an OData 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 = {}) ⇒ OData::Service

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

Parameters:

  • service_url (String)

    the URL to the desired OData service

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

    options to pass to the service


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

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

Instance Attribute Details

#optionsObject (readonly)

Options to pass around


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

def options
  @options
end

#service_urlObject (readonly)

The OData Service's URL


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

def service_url
  @service_url
end

Class Method Details

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

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

Parameters:

  • service_url (String)

    the URL to the desired OData service

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

    options to pass to the service

Returns:


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

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

Instance Method Details

#[](entity_set_name) ⇒ OData::EntitySet

Retrieves the EntitySet associated with a specific EntityType by name

Parameters:

  • entity_set_name (to_s)

    the name of the EntitySet desired

Returns:

Raises:

  • (ArgumentError)

124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/odata/service.rb', line 124

def [](entity_set_name)
  xpath_query = "//EntityContainer/EntitySet[@Name='#{entity_set_name}']"
  entity_set_node = .xpath(xpath_query).first
  raise ArgumentError, "Unknown Entity Set: #{entity_set_name}" if entity_set_node.nil?
  container_name = entity_set_node.parent.attributes['Name'].value
  entity_type_name = entity_set_node.attributes['EntityType'].value.gsub(/#{namespace}\./, '')
  OData::EntitySet.new(name: entity_set_name,
                       namespace: namespace,
                       type: entity_type_name.to_s,
                       service_name: name,
                       container: container_name)
end

#complex_typesHash<String, OData::ComplexType>

Returns a list of ComplexTypes used by the service

Returns:


72
73
74
75
76
77
78
79
# File 'lib/odata/service.rb', line 72

def complex_types
  @complex_types ||= .xpath('//ComplexType').map do |entity|
    [
      entity.attributes['Name'].value,
      ::OData::ComplexType.new(entity, self)
    ]
  end.to_h
end

#entity_setsObject

Returns a hash of EntitySet names keyed to their respective EntityType name


61
62
63
64
65
66
67
68
# File 'lib/odata/service.rb', line 61

def entity_sets
  @entity_sets ||= .xpath('//EntityContainer/EntitySet').collect {|entity|
    [
      entity.attributes['EntityType'].value.gsub("#{namespace}.", ''),
      entity.attributes['Name'].value
    ]
  }.to_h
end

#entity_typesObject

Returns a list of entities exposed by the service


56
57
58
# File 'lib/odata/service.rb', line 56

def entity_types
  @entity_types ||= .xpath('//EntityType').collect {|entity| entity.attributes['Name'].value}
end

#enum_typesHash<String, OData::EnumType>

Returns a list of EnumTypes used by the service

Returns:


83
84
85
86
87
88
89
90
# File 'lib/odata/service.rb', line 83

def enum_types
  @enum_types ||= .xpath('//EnumType').map do |entity|
    [
      entity.attributes['Name'].value,
      ::OData::EnumType.new(entity, self)
    ]
  end.to_h
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)

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

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)

184
185
186
187
188
# File 'lib/odata/service.rb', line 184

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)

174
175
176
177
178
# File 'lib/odata/service.rb', line 174

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 name of the relevant entity

  • property_name (to_s)

    the property name needed

Returns:

  • (String)

    the name of the property's type


195
196
197
# File 'lib/odata/service.rb', line 195

def get_property_type(entity_name, property_name)
  .xpath("//EntityType[@Name='#{entity_name}']/Property[@Name='#{property_name}']").first.attributes['Type'].value
end

#inspectObject

Returns a more compact inspection of the service object


116
117
118
# File 'lib/odata/service.rb', line 116

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

227
228
229
# File 'lib/odata/service.rb', line 227

def log_level
  options[:log_level]
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:


240
241
242
243
244
245
246
247
248
249
250
# File 'lib/odata/service.rb', line 240

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)

254
255
256
# File 'lib/odata/service.rb', line 254

def logger=(custom_logger)
  @logger = custom_logger
end

#metadata_urlString

Returns the service's metadata URL.

Returns:

  • (String)

51
52
53
# File 'lib/odata/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/odata/service.rb', line 45

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

#namespaceObject

Returns the namespace defined on the service's schema


111
112
113
# File 'lib/odata/service.rb', line 111

def namespace
  @namespace ||= .xpath('//Schema').first.attributes['Namespace'].value
end

Returns a hash for finding an association through an entity type's defined NavigationProperty elements.

Returns:

  • (Hash<Hash<OData::Association>>)

95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/odata/service.rb', line 95

def navigation_properties
  @navigation_properties ||= .xpath('//EntityType').collect do |entity_type_def|
    entity_type_name = entity_type_def.attributes['Name'].value
    [
        entity_type_name,
        entity_type_def.xpath('./NavigationProperty').collect do |nav_property_def|
          [
              nav_property_def.attributes['Name'].value,
              ::OData::NavigationProperty.build(nav_property_def)
          ]
        end.to_h
    ]
  end.to_h
end

#primary_key_for(entity_name) ⇒ String

Get the primary key for the supplied Entity.

Parameters:

  • entity_name (to_s)

Returns:

  • (String)

203
204
205
# File 'lib/odata/service.rb', line 203

def primary_key_for(entity_name)
  .xpath("//EntityType[@Name='#{entity_name}']/Key/PropertyRef").first.attributes['Name'].value
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)

212
213
214
215
216
217
218
219
220
221
# File 'lib/odata/service.rb', line 212

def properties_for_entity(entity_name)
  type_definition = .xpath("//EntityType[@Name='#{entity_name}']").first
  raise ArgumentError, "Unknown EntityType: #{entity_name}" if type_definition.nil?
  properties_to_return = {}
  type_definition.xpath('./Property').each do |property_xml|
    property_name, property = process_property_from_xml(property_xml)
    properties_to_return[property_name] = property
  end
  properties_to_return
end