lib/odata4/service.rb in odata4-0.7.0 vs lib/odata4/service.rb in odata4-0.8.0
- old
+ new
@@ -50,92 +50,89 @@
# @return [String]
def metadata_url
"#{service_url}/$metadata"
end
- # Returns a list of entities exposed by the service
- def entity_types
- @entity_types ||= metadata.xpath('//EntityType').collect {|entity| entity.attributes['Name'].value}
+ # Returns the service's metadata definition.
+ # @return [Nokogiri::XML]
+ def metadata
+ @metadata ||= lambda { read_metadata }.call
end
- # Returns a hash of EntitySet names keyed to their respective EntityType name
- def entity_sets
- @entity_sets ||= metadata.xpath('//EntityContainer/EntitySet').collect {|entity|
+ # Returns all of the service's schemas.
+ # @return Hash<String, OData4::Schema>
+ def schemas
+ @schemas ||= metadata.xpath('//Schema').map do |schema_xml|
[
- entity.attributes['EntityType'].value.gsub("#{namespace}.", ''),
- entity.attributes['Name'].value
+ schema_xml.attributes['Namespace'].value,
+ Schema.new(schema_xml, self)
]
- }.to_h
+ end.to_h
end
- # Returns a list of ComplexTypes used by the service
- # @return [Hash<String, OData4::ComplexType>]
- def complex_types
- @complex_types ||= metadata.xpath('//ComplexType').map do |entity|
- [
- entity.attributes['Name'].value,
- ::OData4::ComplexType.new(entity, self)
- ]
- end.to_h
+ # Returns the service's EntityContainer (singleton)
+ # @return OData4::EntityContainer
+ def entity_container
+ @entity_container ||= EntityContainer.new(self)
end
- # Returns a list of EnumTypes used by the service
- # @return [Hash<String, OData4::EnumType>]
- def enum_types
- @enum_types ||= metadata.xpath('//EnumType').map do |entity|
- [
- entity.attributes['Name'].value,
- ::OData4::EnumType.new(entity, self)
- ]
- end.to_h
+ # Returns a hash of EntitySet names and their respective EntityType names
+ # @return Hash<String, String>
+ def entity_sets
+ entity_container.entity_sets
end
- # Returns a hash for finding an association through an entity type's defined
- # NavigationProperty elements.
- # @return [Hash<Hash<OData4::Association>>]
- def navigation_properties
- @navigation_properties ||= metadata.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,
- ::OData4::NavigationProperty.build(nav_property_def)
- ]
- end.to_h
- ]
- end.to_h
+ # Retrieves the EntitySet associated with a specific EntityType by name
+ #
+ # @param entity_set_name [to_s] the name of the EntitySet desired
+ # @return [OData4::EntitySet] an OData4::EntitySet to query
+ def [](entity_set_name)
+ entity_container[entity_set_name]
end
- # Returns the namespace defined on the service's schema
+ # Returns the default namespace, that is, the namespace of the schema
+ # that contains the service's EntityContainer.
+ # @return [String]
def namespace
- @namespace ||= metadata.xpath('//Schema').first.attributes['Namespace'].value
+ entity_container.namespace
end
+ # Returns a list of `EntityType`s exposed by the service
+ # @return Array<String>
+ def entity_types
+ @entity_types ||= schemas.map do |namespace, schema|
+ schema.entity_types.map do |entity_type|
+ "#{namespace}.#{entity_type}"
+ end
+ end.flatten
+ end
+
+ # Returns a list of `ComplexType`s used by the service.
+ # @return [Hash<String, OData4::ComplexType>]
+ 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
+
+ # Returns a list of `EnumType`s used by the service
+ # @return [Hash<String, OData4::EnumType>]
+ 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
+
# Returns a more compact inspection of the service object
def inspect
"#<#{self.class.name}:#{self.object_id} name='#{name}' service_url='#{self.service_url}'>"
end
- # Retrieves the EntitySet associated with a specific EntityType by name
- #
- # @param entity_set_name [to_s] the name of the EntitySet desired
- # @return [OData4::EntitySet] an OData4::EntitySet to query
- def [](entity_set_name)
- xpath_query = "//EntityContainer/EntitySet[@Name='#{entity_set_name}']"
- entity_set_node = metadata.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}\./, '')
- OData4::EntitySet.new(name: entity_set_name,
- namespace: namespace,
- type: entity_type_name.to_s,
- service_name: name,
- container: container_name)
- end
-
# Execute a request against the service
#
# @param url_chunk [to_s] string to append to service url
# @param additional_options [Hash] options to pass to Typhoeus
# @return [Typhoeus::Response]
@@ -187,39 +184,38 @@
document.xpath('//entry')
end
# Get the property type for an entity from metadata.
#
- # @param entity_name [to_s] the name of the relevant entity
+ # @param entity_name [to_s] the fully qualified entity name
# @param property_name [to_s] the property name needed
# @return [String] the name of the property's type
def get_property_type(entity_name, property_name)
- metadata.xpath("//EntityType[@Name='#{entity_name}']/Property[@Name='#{property_name}']").first.attributes['Type'].value
+ 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
# Get the primary key for the supplied Entity.
#
- # @param entity_name [to_s]
+ # @param entity_name [to_s] The fully qualified entity name
# @return [String]
def primary_key_for(entity_name)
- metadata.xpath("//EntityType[@Name='#{entity_name}']/Key/PropertyRef").first.attributes['Name'].value
+ namespace, _, entity_name = entity_name.rpartition('.')
+ raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
+ schemas[namespace].primary_key_for(entity_name)
end
# Get the list of properties and their various options for the supplied
# Entity name.
# @param entity_name [to_s]
# @return [Hash]
# @api private
def properties_for_entity(entity_name)
- type_definition = metadata.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
+ namespace, _, entity_name = entity_name.rpartition('.')
+ raise ArgumentError, 'Namespace missing' if namespace.nil? || namespace.empty?
+ schemas[namespace].properties_for_entity(entity_name)
end
# Returns the log level set via initial options, or the
# default log level (`Logger::WARN`) if none was specified.
# @see Logger
@@ -260,11 +256,12 @@
def default_options
{
typhoeus: {
headers: { 'OData4-Version' => '4.0' },
timeout: HTTP_TIMEOUT
- }
+ },
+ strict: true # strict property validation
}
end
def content_type(format)
if format == :auto
@@ -274,14 +271,10 @@
else
raise ArgumentError, "Unknown format '#{format}'"
end
end
- def metadata
- @metadata ||= lambda { read_metadata }.call
- end
-
def read_metadata
response = nil
# From file, good for debugging
if options[:metadata_file]
data = File.read(options[:metadata_file])
@@ -312,33 +305,16 @@
def error_message(response)
OData4::Query::Result.new(nil, response).error_message
end
- def process_property_from_xml(property_xml)
- property_name = property_xml.attributes['Name'].value
- value_type = property_xml.attributes['Type'].value
- property_options = {}
-
- klass = ::OData4::PropertyRegistry[value_type]
-
- if klass.nil?
- raise RuntimeError, "Unknown property type: #{value_type}"
- else
- property_options[:allows_nil] = false if property_xml.attributes['Nullable'] == 'false'
- property = klass.new(property_name, nil, property_options)
- end
-
- return [property_name, property]
- end
-
def register_custom_types
complex_types.each do |name, type|
- ::OData4::PropertyRegistry.add(type.type, type.property_class)
+ ::OData4::PropertyRegistry.add(name, type.property_class)
end
enum_types.each do |name, type|
- ::OData4::PropertyRegistry.add(type.type, type.property_class)
+ ::OData4::PropertyRegistry.add(name, type.property_class)
end
end
end
end