occi-core 5.0.0.alpha.4 → 5.0.0.beta.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/lib/occi/core/helpers/argument_validator.rb +1 -1
- data/lib/occi/core/helpers/hash_dereferencer.rb +2 -0
- data/lib/occi/core/helpers/identifier_validator.rb +3 -3
- data/lib/occi/core/helpers/instance_attribute_resetter.rb +3 -3
- data/lib/occi/core/helpers/parser_dereferencer.rb +6 -6
- data/lib/occi/core/helpers/raw_json_parser.rb +1 -1
- data/lib/occi/core/helpers/renderable.rb +1 -1
- data/lib/occi/core/helpers/yaml_summoner.rb +1 -1
- data/lib/occi/core/instance_builder.rb +4 -4
- data/lib/occi/core/model.rb +1 -1
- data/lib/occi/core/parsers/base_parser.rb +10 -1
- data/lib/occi/core/parsers/json/action_instance.rb +8 -2
- data/lib/occi/core/parsers/json/category.rb +18 -2
- data/lib/occi/core/parsers/json/entity.rb +22 -11
- data/lib/occi/core/parsers/json/validator.rb +13 -2
- data/lib/occi/core/parsers/json_parser.rb +14 -6
- data/lib/occi/core/parsers/text/category.rb +26 -5
- data/lib/occi/core/parsers/text/constants.rb +1 -1
- data/lib/occi/core/parsers/text/entity.rb +35 -18
- data/lib/occi/core/parsers/text/location.rb +18 -6
- data/lib/occi/core/parsers/text_parser.rb +45 -14
- data/lib/occi/core/renderer_factory.rb +5 -5
- data/lib/occi/core/version.rb +1 -1
- data/lib/occi/core/warehouse.rb +5 -5
- data/lib/occi/infrastructure/model.rb +1 -1
- data/lib/occi/infrastructure_ext/model.rb +1 -1
- data/lib/occi/monkey_island/hash.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bac82f913d97840096bf9d40d0e492fec5ed3b8f
|
4
|
+
data.tar.gz: dd0af74945ae6464ddf58e91cb4fa220d3409968
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fa653867bd073489c1b8f584125c6b5eff7d8675aad1298d0f5b1cef057d6327a076a7d938a70a0d1d1eb5a124d3258ccc57b8412c93fd6fe6a9cd150eb3a39
|
7
|
+
data.tar.gz: b8b8ce1728507099d3b932e4abf9388f53256a0692628868c96bb98ab16998ba2d57d74cf3ffb68fe1470e8074980d2f83b790a37e78cc2685b1bae42d9dc3c6
|
data/.rubocop.yml
CHANGED
@@ -8,7 +8,7 @@ module Occi
|
|
8
8
|
module ArgumentValidator
|
9
9
|
# :nodoc:
|
10
10
|
def default_args!(args)
|
11
|
-
logger.debug "#{self.class}
|
11
|
+
logger.debug "Merging #{self.class} args #{args.inspect} with defaults #{defaults.inspect}"
|
12
12
|
args.merge!(defaults) { |_, oldval, _| oldval }
|
13
13
|
sufficient_args!(args)
|
14
14
|
end
|
@@ -128,6 +128,7 @@ module Occi
|
|
128
128
|
# @param model [Occi::Core::Model] model instance for dereferencing (category look-up)
|
129
129
|
# @return [Occi::Core::Category] instance located in the model
|
130
130
|
def dereference_via_model(identifier, model)
|
131
|
+
logger.debug "Dereferencing #{identifier.inspect} by look-up in model"
|
131
132
|
model.find_by_identifier!(identifier)
|
132
133
|
end
|
133
134
|
|
@@ -138,6 +139,7 @@ module Occi
|
|
138
139
|
# @param hash [Hash] hash with known attribute definitions for dereferencing
|
139
140
|
# @return [Occi::Core::AttributeDefinition] definition located in the hash
|
140
141
|
def dereference_via_hash(identifier, hash)
|
142
|
+
logger.debug "Dereferencing #{identifier.inspect} by look-up in hash"
|
141
143
|
raise "Attribute definition #{identifier.inspect} not found in the hash" unless hash[identifier]
|
142
144
|
hash[identifier]
|
143
145
|
end
|
@@ -25,7 +25,7 @@ module Occi
|
|
25
25
|
begin
|
26
26
|
valid_term! term
|
27
27
|
rescue Occi::Core::Errors::CategoryValidationError => ex
|
28
|
-
logger.warn "#{self}
|
28
|
+
logger.warn "Term validation for #{self} failed with #{ex.message}" if respond_to?(:logger)
|
29
29
|
return false
|
30
30
|
end
|
31
31
|
|
@@ -58,7 +58,7 @@ module Occi
|
|
58
58
|
begin
|
59
59
|
valid_schema! schema
|
60
60
|
rescue URI::InvalidURIError, Occi::Core::Errors::CategoryValidationError => ex
|
61
|
-
logger.warn "#{self}
|
61
|
+
logger.warn "Schema validation for #{self} failed with #{ex.message}" if respond_to?(:logger)
|
62
62
|
return false
|
63
63
|
end
|
64
64
|
|
@@ -96,7 +96,7 @@ module Occi
|
|
96
96
|
begin
|
97
97
|
valid_identifier! identifier
|
98
98
|
rescue URI::InvalidURIError, Occi::Core::Errors::CategoryValidationError => ex
|
99
|
-
logger.warn "#{self}
|
99
|
+
logger.warn "Identifier validation for #{self} failed with #{ex.message}" if respond_to?(:logger)
|
100
100
|
return false
|
101
101
|
end
|
102
102
|
|
@@ -53,7 +53,7 @@ module Occi
|
|
53
53
|
name_cache = attribute_names
|
54
54
|
attributes.keep_if do |key, value|
|
55
55
|
defined = name_cache.include?(key) && value && value.attribute_definition
|
56
|
-
logger.debug "
|
56
|
+
logger.debug "Removing undefined attribute #{key.inspect} on #{self.class}" unless defined
|
57
57
|
defined
|
58
58
|
end
|
59
59
|
end
|
@@ -122,10 +122,10 @@ module Occi
|
|
122
122
|
# @param force [TrueClass, FalseClass] forcibly change attribute value to default
|
123
123
|
def reset_attribute(name, definition, force)
|
124
124
|
if attributes[name]
|
125
|
-
logger.debug "
|
125
|
+
logger.debug "Setting attribute definition for existing #{name.inspect} on #{self.class}"
|
126
126
|
attributes[name].attribute_definition = definition
|
127
127
|
else
|
128
|
-
logger.debug "
|
128
|
+
logger.debug "Creating attribute definition for new #{name.inspect} on #{self.class}"
|
129
129
|
attributes[name] = Attribute.new(nil, definition)
|
130
130
|
end
|
131
131
|
|
@@ -28,7 +28,7 @@ module Occi
|
|
28
28
|
# @param parsed [Array] list of original parsed category structures
|
29
29
|
def lookup_references!(cat, derefd, parsed)
|
30
30
|
parsed_cat = parsed.detect { |pcat| "#{pcat[:scheme]}#{pcat[:term]}" == cat.identifier }
|
31
|
-
raise Occi::Core::Errors::ParsingError, "
|
31
|
+
raise Occi::Core::Errors::ParsingError, "Category #{cat.identifier} not in the model" unless parsed_cat
|
32
32
|
lookup_action_references!(cat, derefd, parsed_cat[:actions])
|
33
33
|
|
34
34
|
if cat.is_a?(Occi::Core::Mixin)
|
@@ -44,7 +44,7 @@ module Occi
|
|
44
44
|
# @param derefd [Array] list of all available category instances
|
45
45
|
# @param parsed_actions [Array] textual representation of needed actions
|
46
46
|
def lookup_action_references!(cat, derefd, parsed_actions)
|
47
|
-
logger.debug "
|
47
|
+
logger.debug "Dereferencing actions #{parsed_actions.inspect} for #{cat.identifier.inspect}"
|
48
48
|
return if parsed_actions.blank?
|
49
49
|
parsed_actions.each { |action| cat.actions << first_or_die(derefd, action) }
|
50
50
|
end
|
@@ -54,11 +54,11 @@ module Occi
|
|
54
54
|
# @param parsed_rel [Array] textual representation of needed parent(s)
|
55
55
|
def lookup_parent_references!(kind, derefd, parsed_rel)
|
56
56
|
return if parsed_rel.blank? || kind.parent.is_a?(Occi::Core::Kind)
|
57
|
-
logger.debug "
|
57
|
+
logger.debug "Dereferencing parent #{parsed_rel.inspect} for #{kind.identifier.inspect}"
|
58
58
|
if parsed_rel.is_a?(Enumerable)
|
59
59
|
if parsed_rel.count > 1
|
60
60
|
raise Occi::Core::Errors::ParsingError,
|
61
|
-
"
|
61
|
+
"Kind #{kind} with multiple parents #{parsed_rel.inspect}"
|
62
62
|
end
|
63
63
|
parsed_rel = parsed_rel.first
|
64
64
|
end
|
@@ -85,11 +85,11 @@ module Occi
|
|
85
85
|
# @param what [String] identifier of the desired item
|
86
86
|
# @return [Object] desired item from `where`
|
87
87
|
def first_or_die(where, what)
|
88
|
-
logger.debug "
|
88
|
+
logger.debug "Looking for #{what.inspect} in #{where.class}"
|
89
89
|
found = where.detect { |elm| elm.identifier == what }
|
90
90
|
unless found
|
91
91
|
raise Occi::Core::Errors::ParsingError,
|
92
|
-
"
|
92
|
+
"Category #{what.to_s.inspect} referenced but not provided"
|
93
93
|
end
|
94
94
|
found
|
95
95
|
end
|
@@ -10,7 +10,7 @@ module Occi
|
|
10
10
|
def raw_hash(body)
|
11
11
|
JSON.parse body, symbolize_names: true
|
12
12
|
rescue => ex
|
13
|
-
raise Occi::Core::Errors::ParsingError, "
|
13
|
+
raise Occi::Core::Errors::ParsingError, "JSON parsing failed: #{ex.message}"
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -55,7 +55,7 @@ module Occi
|
|
55
55
|
# @param base [Class] class receiving this module
|
56
56
|
def self.included(base)
|
57
57
|
renderer_factory.formats.each do |format|
|
58
|
-
base.logger.debug "
|
58
|
+
base.logger.debug "Adding support for format #{format} to #{base}" if base.respond_to?(:logger)
|
59
59
|
base.send(:define_method, "to_#{format}", proc { render(format) })
|
60
60
|
end
|
61
61
|
end
|
@@ -25,7 +25,7 @@ module Occi
|
|
25
25
|
raise 'This method cannot be invoked on instances' unless is_a? Class
|
26
26
|
allowed_classes = respond_to?(:allowed_yaml_classes, true) ? allowed_yaml_classes : []
|
27
27
|
|
28
|
-
logger.debug "
|
28
|
+
logger.debug "Loading YAML definition for #{self} from #{path.inspect}"
|
29
29
|
object_args = YAML.safe_load(File.read(path), allowed_classes)
|
30
30
|
object_args.symbolize_keys!
|
31
31
|
object_args.dereference_with!(self, model, attribute_definitions) if needs_dereferencing?
|
@@ -42,7 +42,7 @@ module Occi
|
|
42
42
|
# @return [Object] constructed instance
|
43
43
|
# @return [NilClass] if such an instance could not be constructed
|
44
44
|
def build(identifier, args = {})
|
45
|
-
logger.debug "
|
45
|
+
logger.debug "Building instance of #{identifier.inspect} with #{args.inspect}"
|
46
46
|
k_args = args_with_kind(identifier, args)
|
47
47
|
klass(identifier, parent_klass(k_args[:kind])).new k_args
|
48
48
|
end
|
@@ -93,7 +93,7 @@ module Occi
|
|
93
93
|
"#{found_klass} is not a sub-type of #{known_ancestor}"
|
94
94
|
end
|
95
95
|
|
96
|
-
logger.debug "
|
96
|
+
logger.debug "Found class #{found_klass} for #{identifier.inspect}"
|
97
97
|
found_klass
|
98
98
|
end
|
99
99
|
|
@@ -119,10 +119,10 @@ module Occi
|
|
119
119
|
# @return [Class] located known parent class
|
120
120
|
def parent_klass(kind)
|
121
121
|
if kind.related? kind_instance(Occi::Core::Constants::RESOURCE_KIND)
|
122
|
-
logger.debug "
|
122
|
+
logger.debug "Identified #{kind.identifier} as Resource"
|
123
123
|
Occi::Core::Resource
|
124
124
|
elsif kind.related? kind_instance(Occi::Core::Constants::LINK_KIND)
|
125
|
-
logger.debug "
|
125
|
+
logger.debug "Identified #{kind.identifier} as Link"
|
126
126
|
Occi::Core::Link
|
127
127
|
else
|
128
128
|
raise Occi::Core::Errors::ModelLookupError,
|
data/lib/occi/core/model.rb
CHANGED
@@ -242,7 +242,7 @@ module Occi
|
|
242
242
|
# model = Occi::Core::Model.new
|
243
243
|
# model.load_core!
|
244
244
|
def load_core!
|
245
|
-
logger.debug
|
245
|
+
logger.debug 'Loading Core definitions from Core::Warehouse'
|
246
246
|
Occi::Core::Warehouse.bootstrap! self
|
247
247
|
end
|
248
248
|
|
@@ -18,6 +18,10 @@ module Occi
|
|
18
18
|
|
19
19
|
attr_accessor :model, :media_type
|
20
20
|
|
21
|
+
# Shortcuts to interesting methods on logger
|
22
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
23
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
24
|
+
|
21
25
|
# Constructs an instance of the parser that will use a particular model as the reference for every
|
22
26
|
# parsed instance. Only instances allowed by the model will be successfuly parsed. In case of
|
23
27
|
# `Occi::Core::Category` instances, only identifiers are parsed and existing instances from the model
|
@@ -32,6 +36,7 @@ module Occi
|
|
32
36
|
|
33
37
|
@model = args.fetch(:model)
|
34
38
|
@media_type = args.fetch(:media_type)
|
39
|
+
logger.debug "Initializing parser for #{media_type.inspect}"
|
35
40
|
|
36
41
|
post_initialize(args)
|
37
42
|
end
|
@@ -112,12 +117,16 @@ module Occi
|
|
112
117
|
def lookup(identifier, klass)
|
113
118
|
found = handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(identifier) }
|
114
119
|
unless found.is_a?(klass)
|
115
|
-
raise Occi::Core::Errors::ParsingError, "#{
|
120
|
+
raise Occi::Core::Errors::ParsingError, "#{identifier.inspect} is not of expected class #{klass}"
|
116
121
|
end
|
117
122
|
found
|
118
123
|
end
|
119
124
|
|
120
125
|
class << self
|
126
|
+
# Shortcuts to interesting methods on logger
|
127
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
128
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
129
|
+
|
121
130
|
# Returns a list of supported media types for this parser.
|
122
131
|
#
|
123
132
|
# @return [Array] list of supported media types
|
@@ -12,6 +12,10 @@ module Occi
|
|
12
12
|
extend Helpers::RawJsonParser
|
13
13
|
|
14
14
|
class << self
|
15
|
+
# Shortcuts to interesting methods on logger
|
16
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
17
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
18
|
+
|
15
19
|
# Parses action instances. Internal references between objects are converted from strings
|
16
20
|
# to actual objects. Actions have to be declared in the provided model.
|
17
21
|
#
|
@@ -20,12 +24,14 @@ module Occi
|
|
20
24
|
# @return [Occi::Core::ActionInstance] action instance
|
21
25
|
def json(body, model)
|
22
26
|
parsed = raw_hash(body)
|
23
|
-
|
27
|
+
action = handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier! parsed[:action] }
|
24
28
|
|
25
|
-
|
29
|
+
logger.debug "Identified #{action.class}[#{action.identifier}]"
|
26
30
|
ai = Occi::Core::ActionInstance.new(action: action)
|
31
|
+
ep = Entity.new(model: model)
|
27
32
|
ep.set_attributes!(ai.attributes, parsed[:attributes]) if parsed[:attributes]
|
28
33
|
|
34
|
+
logger.debug "Parsed into ActionInstance #{ai.inspect}" if logger_debug?
|
29
35
|
ai
|
30
36
|
end
|
31
37
|
end
|
@@ -27,6 +27,10 @@ module Occi
|
|
27
27
|
DEPENDS_KEY = :depends
|
28
28
|
|
29
29
|
class << self
|
30
|
+
# Shortcuts to interesting methods on logger
|
31
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
32
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
33
|
+
|
30
34
|
# Parses categories into instances of subtypes of `Occi::Core::Category`. Internal references
|
31
35
|
# between objects are converted from strings to actual objects. Categories provided in the model
|
32
36
|
# will be reused but have to be declared in the parsed model as well.
|
@@ -37,10 +41,12 @@ module Occi
|
|
37
41
|
def json(body, model)
|
38
42
|
parsed = raw_hash(body)
|
39
43
|
instantiate_hashes! parsed, model
|
44
|
+
logger.debug "Parsed into raw hashes #{parsed.inspect}" if logger_debug?
|
40
45
|
|
41
46
|
raw_categories = [parsed[:kinds], parsed[:mixins]].flatten.compact
|
42
47
|
dereference_identifiers! model.categories, raw_categories
|
43
48
|
|
49
|
+
logger.debug "Returning (updated) model #{model.inspect}" if logger_debug?
|
44
50
|
model
|
45
51
|
end
|
46
52
|
|
@@ -53,15 +59,19 @@ module Occi
|
|
53
59
|
|
54
60
|
# :nodoc:
|
55
61
|
def instatiate_hash(raw, klass)
|
62
|
+
logger.debug "Creating #{klass} from #{raw.inspect}" if logger_debug?
|
63
|
+
|
56
64
|
obj = klass.new(
|
57
65
|
term: raw[:term], schema: raw[:scheme], title: raw[:title],
|
58
66
|
attributes: attribute_definitions(raw[:attributes])
|
59
67
|
)
|
60
68
|
|
61
69
|
if obj.respond_to?(:location)
|
70
|
+
logger.debug "Setting location #{raw[:location].inspect}" if logger_debug?
|
62
71
|
obj.location = handle(Occi::Core::Errors::ParsingError) { URI.parse(raw[:location]) }
|
63
72
|
end
|
64
73
|
|
74
|
+
logger.debug "Created category #{obj.inspect}" if logger_debug?
|
65
75
|
obj
|
66
76
|
end
|
67
77
|
|
@@ -71,9 +81,10 @@ module Occi
|
|
71
81
|
|
72
82
|
attr_defs = {}
|
73
83
|
raw.each_pair do |k, v|
|
84
|
+
logger.debug "Creating AttributeDefinition for #{k.inspect} from #{v.inspect}" if logger_debug?
|
74
85
|
def_hsh = typecast(v)
|
75
86
|
unless def_hsh[:type]
|
76
|
-
raise Occi::Core::Errors::ParsingError, "
|
87
|
+
raise Occi::Core::Errors::ParsingError, "Attribute #{k.to_s.inspect} has no type"
|
77
88
|
end
|
78
89
|
attr_defs[k.to_s] = Occi::Core::AttributeDefinition.new def_hsh
|
79
90
|
end
|
@@ -85,18 +96,23 @@ module Occi
|
|
85
96
|
def typecast(hash)
|
86
97
|
hash = hash.clone
|
87
98
|
hash[:type] = TYPECASTER_HASH[hash[:type]]
|
88
|
-
|
99
|
+
|
100
|
+
return hash if hash[:pattern].blank?
|
101
|
+
hash[:pattern] = handle(Occi::Core::Errors::ParsingError) { Regexp.new(hash[:pattern]) }
|
102
|
+
|
89
103
|
hash
|
90
104
|
end
|
91
105
|
|
92
106
|
# :nodoc:
|
93
107
|
def lookup_applies_references!(mixin, derefd, parsed_rel)
|
108
|
+
logger.debug "Looking up applies from #{parsed_rel.inspect}" if logger_debug?
|
94
109
|
return if parsed_rel.blank?
|
95
110
|
parsed_rel.each { |kind| mixin.applies << first_or_die(derefd, kind) }
|
96
111
|
end
|
97
112
|
|
98
113
|
# :nodoc:
|
99
114
|
def lookup_depends_references!(mixin, derefd, parsed_rel)
|
115
|
+
logger.debug "Looking up depens from #{parsed_rel.inspect}" if logger_debug?
|
100
116
|
return if parsed_rel.blank?
|
101
117
|
parsed_rel.each { |mxn| mixin.depends << first_or_die(derefd, mxn) }
|
102
118
|
end
|
@@ -14,6 +14,10 @@ module Occi
|
|
14
14
|
include Helpers::ErrorHandler
|
15
15
|
extend Helpers::RawJsonParser
|
16
16
|
|
17
|
+
# Shortcuts to interesting methods on logger
|
18
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
19
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
20
|
+
|
17
21
|
# Constants
|
18
22
|
SINGLE_INSTANCE_TYPES = %i[resource link].freeze
|
19
23
|
MULTI_INSTANCE_TYPES = %i[entity-collection].freeze
|
@@ -46,14 +50,16 @@ module Occi
|
|
46
50
|
# @param type [Symbol] `:resource`, `:link`, or `:'entity-collection'`
|
47
51
|
# @return [Array] constructed instances
|
48
52
|
def json(body, type)
|
49
|
-
case type
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
symbol = case type
|
54
|
+
when *SINGLE_INSTANCE_TYPES
|
55
|
+
:json_single
|
56
|
+
when *MULTI_INSTANCE_TYPES
|
57
|
+
:json_collection
|
58
|
+
else
|
59
|
+
raise Occi::Core::Errors::ParserError, "#{type.inspect} is not a valid type"
|
60
|
+
end
|
61
|
+
|
62
|
+
send symbol, self.class.raw_hash(body)
|
57
63
|
end
|
58
64
|
|
59
65
|
# Builds an entity instance from the hash provided as input.
|
@@ -61,12 +67,14 @@ module Occi
|
|
61
67
|
# @param hash [Hash] Hash body for parsing
|
62
68
|
# @return [Array] constructed instances
|
63
69
|
def json_single(hash)
|
70
|
+
logger.debug "Converting #{hash.inspect} into a single instance" if logger_debug?
|
64
71
|
instance = @_ib.get hash[:kind], mixins: lookup(hash[:mixins]), actions: lookup(hash[:actions])
|
65
72
|
|
66
73
|
set_attributes! instance.attributes, hash[:attributes]
|
67
74
|
set_links! instance.links, hash[:links] if instance.respond_to?(:links)
|
68
75
|
set_target! instance, hash[:target] if instance.respond_to?(:target)
|
69
76
|
|
77
|
+
logger.debug "Created instance #{instance.inspect}" if logger_debug?
|
70
78
|
Set.new [instance]
|
71
79
|
end
|
72
80
|
|
@@ -77,10 +85,12 @@ module Occi
|
|
77
85
|
def json_collection(hash)
|
78
86
|
all = []
|
79
87
|
|
88
|
+
logger.debug "Converting #{hash.inspect} into multiple instances" if logger_debug?
|
80
89
|
all.concat hash[:resources] if hash[:resources]
|
81
90
|
all.concat hash[:links] if hash[:links]
|
82
91
|
all.map! { |a| json_single(a) }
|
83
92
|
|
93
|
+
logger.debug "Created instances #{all.inspect}" if logger_debug?
|
84
94
|
Set.new(all).flatten
|
85
95
|
end
|
86
96
|
|
@@ -96,10 +106,11 @@ module Occi
|
|
96
106
|
def set_attributes!(attributes, hash)
|
97
107
|
return if hash.blank?
|
98
108
|
hash.each_pair do |name, value|
|
109
|
+
logger.debug "Setting attribute #{name} to #{value.inspect}" if logger_debug?
|
99
110
|
attribute = attributes[name.to_s]
|
100
111
|
unless attribute
|
101
112
|
raise Occi::Core::Errors::ParsingError,
|
102
|
-
"
|
113
|
+
"Attribute #{name.inspect} is not allowed for this entity"
|
103
114
|
end
|
104
115
|
attribute.value = typecast(value, attribute.attribute_definition.type)
|
105
116
|
end
|
@@ -121,11 +132,11 @@ module Occi
|
|
121
132
|
# :nodoc:
|
122
133
|
def typecast(value, type)
|
123
134
|
if value.nil? || type.nil?
|
124
|
-
raise Occi::Core::Errors::ParsingError,
|
125
|
-
"#{self.class} -> Cannot typecast (un)set value to (un)set type"
|
135
|
+
raise Occi::Core::Errors::ParsingError, 'Cannot typecast (un)set value to (un)set type'
|
126
136
|
end
|
127
137
|
return value unless TYPECASTER_HASH.key?(type)
|
128
138
|
|
139
|
+
logger.debug "Typecasting value #{value.inspect} to #{type}" if logger_debug?
|
129
140
|
TYPECASTER_HASH[type].call(value)
|
130
141
|
end
|
131
142
|
|
@@ -7,12 +7,18 @@ module Occi
|
|
7
7
|
#
|
8
8
|
# @author Boris Parak <parak@cesnet.cz>
|
9
9
|
class Validator
|
10
|
+
include Yell::Loggable
|
11
|
+
|
10
12
|
# Repository constants
|
11
13
|
SCHEMA_DIR = 'validator'.freeze
|
12
14
|
SCHEMA_REPO = File.join(File.expand_path(File.dirname(__FILE__)), SCHEMA_DIR)
|
13
15
|
BASE_SCHEMAS = %i[occi-schema].freeze
|
14
16
|
|
15
17
|
class << self
|
18
|
+
# Shortcuts to interesting methods on logger
|
19
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
20
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
21
|
+
|
16
22
|
# Validates given `json` text with the appropriate schema for `type`.
|
17
23
|
# This method raises `Occi::Core::Errors::ParsingError` on failure.
|
18
24
|
#
|
@@ -20,10 +26,12 @@ module Occi
|
|
20
26
|
# @param type [Symbol] schema selector
|
21
27
|
# @raise [Occi::Core::Errors::ParsingError] on validation failure
|
22
28
|
def validate!(json, type)
|
29
|
+
logger.debug "Validating #{json.inspect} as #{type}" if logger_debug?
|
30
|
+
|
23
31
|
JSON::Validator.schema_reader = JSON::Schema::Reader.new(accept_uri: false, accept_file: true)
|
24
32
|
JSON::Validator.validate!(schema_for(type), json, json: true)
|
25
33
|
rescue JSON::Schema::JsonParseError, JSON::Schema::ValidationError => e
|
26
|
-
raise Occi::Core::Errors::ParsingError,
|
34
|
+
raise Occi::Core::Errors::ParsingError, e.message
|
27
35
|
end
|
28
36
|
|
29
37
|
# :nodoc:
|
@@ -66,7 +74,10 @@ module Occi
|
|
66
74
|
if type.blank? || BASE_SCHEMAS.include?(type)
|
67
75
|
raise Occi::Core::Errors::ParserError, "Schema type #{type.inspect} is not allowed"
|
68
76
|
end
|
69
|
-
File.join(SCHEMA_REPO, "#{type}.json")
|
77
|
+
schema_path = File.join(SCHEMA_REPO, "#{type}.json")
|
78
|
+
logger.debug "Found JSON schema for #{type} in #{schema_path}" if logger_debug?
|
79
|
+
|
80
|
+
schema_path
|
70
81
|
end
|
71
82
|
private :schema_for
|
72
83
|
end
|
@@ -34,13 +34,14 @@ module Occi
|
|
34
34
|
# @return [Set] set of instances
|
35
35
|
def entities(body, _headers = nil, expectation = nil)
|
36
36
|
expectation ||= Occi::Core::Entity
|
37
|
+
logger.debug "Parsing #{expectation}(s) from #{body.inspect}" if logger_debug?
|
37
38
|
type = validate_entities! body
|
38
39
|
|
39
40
|
entity_parser = Json::Entity.new(model: model)
|
40
41
|
entities = entity_parser.json body, type
|
41
42
|
entities.each do |entity|
|
42
43
|
unless entity.is_a?(expectation)
|
43
|
-
raise Occi::Core::Errors::ParsingError, "
|
44
|
+
raise Occi::Core::Errors::ParsingError, "Entity is not of type #{expectation}"
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
@@ -54,6 +55,7 @@ module Occi
|
|
54
55
|
# @param headers [Hash] raw headers as provided by the transport protocol
|
55
56
|
# @return [Set] set of parsed instances
|
56
57
|
def action_instances(body, _headers = nil)
|
58
|
+
logger.debug "Parsing Occi::Core::ActionInstance(s) from #{body.inspect}" if logger_debug?
|
57
59
|
Json::Validator.validate_action_instance! body
|
58
60
|
Set.new [Json::ActionInstance.json(body, model)]
|
59
61
|
end
|
@@ -67,6 +69,7 @@ module Occi
|
|
67
69
|
# @return [Set] set of instances
|
68
70
|
def categories(body, _headers = nil, expectation = nil)
|
69
71
|
expectation ||= Occi::Core::Category
|
72
|
+
logger.debug "Parsing #{expectation}(s) from #{body.inspect}" if logger_debug?
|
70
73
|
Json::Validator.validate_category_identifiers! body
|
71
74
|
|
72
75
|
cats = Set.new
|
@@ -81,14 +84,15 @@ module Occi
|
|
81
84
|
found = nil
|
82
85
|
|
83
86
|
%i[link resource entity-collection].each do |type|
|
87
|
+
logger.debug "Attempting to validate #{body.inspect} as #{type}" if logger_debug?
|
84
88
|
begin
|
85
89
|
Json::Validator.validate! body, type
|
86
90
|
found = type
|
87
91
|
rescue => ex
|
88
|
-
logger.debug "
|
92
|
+
logger.debug "Moving on, body does not contain valid #{type} - #{ex.message.inspect}"
|
89
93
|
end
|
90
94
|
end
|
91
|
-
raise Occi::Core::Errors::ParsingError,
|
95
|
+
raise Occi::Core::Errors::ParsingError, 'No acceptable entity rendering found' unless found
|
92
96
|
|
93
97
|
found
|
94
98
|
end
|
@@ -105,10 +109,13 @@ module Occi
|
|
105
109
|
def model(body, _headers, media_type, model)
|
106
110
|
unless media_types.include?(media_type)
|
107
111
|
raise Occi::Core::Errors::ParsingError,
|
108
|
-
"
|
112
|
+
"Model cannot be parsed from #{media_type.inspect}"
|
109
113
|
end
|
114
|
+
logger.debug "Parsing model from #{media_type.inspect} in #{body.inspect}" if logger_debug?
|
115
|
+
|
110
116
|
Json::Validator.validate_model! body
|
111
117
|
Json::Category.json body, model
|
118
|
+
|
112
119
|
model
|
113
120
|
end
|
114
121
|
|
@@ -120,9 +127,10 @@ module Occi
|
|
120
127
|
# @return [Array] list of extracted URIs
|
121
128
|
def locations(body, _headers, media_type)
|
122
129
|
unless media_types.include?(media_type)
|
123
|
-
raise Occi::Core::Errors::ParsingError,
|
124
|
-
"#{self} -> locations cannot be parsed from #{media_type.inspect}"
|
130
|
+
raise Occi::Core::Errors::ParsingError, "Locations cannot be parsed from #{media_type.inspect}"
|
125
131
|
end
|
132
|
+
logger.debug "Parsing locations from #{media_type.inspect} in #{body.inspect}" if logger_debug?
|
133
|
+
|
126
134
|
Json::Validator.validate_locations! body
|
127
135
|
handle(Occi::Core::Errors::ParsingError) { JSON.parse(body).map { |i| URI.parse(i) } }
|
128
136
|
end
|
@@ -20,6 +20,10 @@ module Occi
|
|
20
20
|
DEPENDS_KEY = :rel
|
21
21
|
|
22
22
|
class << self
|
23
|
+
# Shortcuts to interesting methods on logger
|
24
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
25
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
26
|
+
|
23
27
|
# Parses category lines into instances of subtypes of `Occi::Core::Category`. Internal references
|
24
28
|
# between objects are converted from strings to actual objects. Categories provided in the model
|
25
29
|
# will be reused but have to be declared in the parsed model as well. This mechanism can be used to
|
@@ -37,6 +41,7 @@ module Occi
|
|
37
41
|
end
|
38
42
|
dereference_identifiers! model.categories, raw_categories
|
39
43
|
|
44
|
+
logger.debug "Returning (updated) model #{model.inspect}" if logger_debug?
|
40
45
|
model
|
41
46
|
end
|
42
47
|
|
@@ -47,19 +52,33 @@ module Occi
|
|
47
52
|
# @param full [TrueClass, FalseClass] parse full definition, defaults to `true`
|
48
53
|
# @return [Hash] raw category hash for further processing
|
49
54
|
def plain_category(line, full = true)
|
55
|
+
logger.debug "Parsing line #{line.inspect}" if logger_debug?
|
50
56
|
matched = line.match(CATEGORY_REGEXP)
|
51
57
|
unless matched
|
52
|
-
raise Occi::Core::Errors::ParsingError,
|
53
|
-
"#{self} -> #{line.inspect} does not match expectations for Category"
|
58
|
+
raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match expectations for Category"
|
54
59
|
end
|
55
60
|
|
56
61
|
cat = matchdata_to_hash(matched)
|
57
|
-
|
62
|
+
full ? plain_category_extended(cat) : plain_category_partial(cat)
|
63
|
+
end
|
58
64
|
|
65
|
+
# Cleans up partially parsed hash. Removes all potentially inconsitent or unfinished data structures.
|
66
|
+
#
|
67
|
+
# @param cat [Hash] partially parsed hash
|
68
|
+
# @return [Hash] clean partially parsed hash
|
69
|
+
def plain_category_partial(cat)
|
70
|
+
%i[attributes rel actions].each { |el| cat[el] = nil }
|
71
|
+
cat
|
72
|
+
end
|
73
|
+
|
74
|
+
# Finishes parsing of attributes, actions, and referenced categories.
|
75
|
+
#
|
76
|
+
# @param cat [Hash] partially parsed hash
|
77
|
+
# @return [Hash] fully parsed hash
|
78
|
+
def plain_category_extended(cat)
|
59
79
|
cat[:attributes] = plain_attributes(cat[:attributes]) if cat[:attributes]
|
60
80
|
cat[:rel] = plain_identifiers(cat[:rel]) if cat[:rel]
|
61
81
|
cat[:actions] = plain_identifiers(cat[:actions]) if cat[:actions]
|
62
|
-
|
63
82
|
cat
|
64
83
|
end
|
65
84
|
|
@@ -77,6 +96,7 @@ module Occi
|
|
77
96
|
|
78
97
|
attributes = {}
|
79
98
|
line.split.each { |attribute| attributes.merge! plain_attribute(attribute) }
|
99
|
+
logger.debug "Matched attributes as #{attributes.inspect}" if logger_debug?
|
80
100
|
|
81
101
|
attributes
|
82
102
|
end
|
@@ -92,11 +112,12 @@ module Occi
|
|
92
112
|
def plain_attribute(line)
|
93
113
|
# TODO: find a better approach to fixing split
|
94
114
|
line.gsub!(/\{(immutable|required)_(required|immutable)\}/, '{\1 \2}')
|
115
|
+
logger.debug "Parsing attribute line #{line.inspect}" if logger_debug?
|
95
116
|
|
96
117
|
matched = line.match(ATTRIBUTE_REGEXP)
|
97
118
|
unless matched && matched[1]
|
98
119
|
raise Occi::Core::Errors::ParsingError,
|
99
|
-
"#{
|
120
|
+
"#{line.inspect} does not match expectations for Attribute"
|
100
121
|
end
|
101
122
|
|
102
123
|
{ matched[1] => plain_attribute_definition(matched[-2]) }
|
@@ -13,6 +13,10 @@ module Occi
|
|
13
13
|
include Helpers::ArgumentValidator
|
14
14
|
include Helpers::ErrorHandler
|
15
15
|
|
16
|
+
# Shortcuts to interesting methods on logger
|
17
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
18
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
19
|
+
|
16
20
|
# Regexp constants
|
17
21
|
ATTRIBUTE_REGEXP = /#{Constants::REGEXP_ATTRIBUTE}/
|
18
22
|
LINK_REGEXP = /#{Constants::REGEXP_LINK}/
|
@@ -57,7 +61,8 @@ module Occi
|
|
57
61
|
def plain(lines)
|
58
62
|
cats = plain_categories(lines)
|
59
63
|
kind = cats.detect { |c| c.is_a?(Occi::Core::Kind) }
|
60
|
-
raise Occi::Core::Errors::ParsingError,
|
64
|
+
raise Occi::Core::Errors::ParsingError, 'Entity does not specify its kind' unless kind
|
65
|
+
logger.debug "Identified entity kind #{kind.inspect}" if logger_debug?
|
61
66
|
|
62
67
|
entity = @_ib.build(kind.identifier)
|
63
68
|
cats.each { |cat| cat.is_a?(Occi::Core::Mixin) && entity << cat }
|
@@ -65,6 +70,7 @@ module Occi
|
|
65
70
|
plain_attributes! lines, entity.attributes
|
66
71
|
plain_links! lines, entity
|
67
72
|
|
73
|
+
logger.debug "Created instance #{entity.inspect}" if logger_debug?
|
68
74
|
entity
|
69
75
|
end
|
70
76
|
|
@@ -73,11 +79,12 @@ module Occi
|
|
73
79
|
# @param lines [Array] list of lines containing a single entity rendering
|
74
80
|
# @return [Array] list of identified category instances
|
75
81
|
def plain_categories(lines)
|
76
|
-
lines.map do |line|
|
82
|
+
categories = lines.map do |line|
|
77
83
|
next unless line.start_with?(TextParser::CATEGORY_KEYS.first)
|
78
84
|
cat = Category.plain_category(line, false)
|
79
85
|
handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!("#{cat[:scheme]}#{cat[:term]}") }
|
80
|
-
end
|
86
|
+
end
|
87
|
+
categories.compact
|
81
88
|
end
|
82
89
|
|
83
90
|
# Parses attributes from entity lines. Every attribute value is typed according to the attribute
|
@@ -89,15 +96,18 @@ module Occi
|
|
89
96
|
def plain_attributes!(lines, attributes)
|
90
97
|
lines.each do |line|
|
91
98
|
next unless line.start_with?(TextParser::ATTRIBUTE_KEYS.first)
|
99
|
+
|
92
100
|
name, value = raw_attribute(line)
|
93
101
|
unless attributes[name]
|
94
102
|
raise Occi::Core::Errors::ParsingError,
|
95
|
-
"
|
103
|
+
"Attribute #{name.inspect} is not allowed for this entity"
|
96
104
|
end
|
105
|
+
|
97
106
|
attributes[name].value = handle(Occi::Core::Errors::ParsingError) do
|
98
107
|
typecast value, attributes[name].attribute_definition.type
|
99
108
|
end
|
100
109
|
end
|
110
|
+
|
101
111
|
attributes
|
102
112
|
end
|
103
113
|
|
@@ -106,10 +116,10 @@ module Occi
|
|
106
116
|
# @param line [String] line containing a single entity attribute
|
107
117
|
# @return [Array] two-element array with name and value of the attribute
|
108
118
|
def raw_attribute(line)
|
119
|
+
logger.debug "Parsing attribute line #{line.inspect}" if logger_debug?
|
109
120
|
matched = line.match(ATTRIBUTE_REGEXP)
|
110
121
|
unless matched
|
111
|
-
raise Occi::Core::Errors::ParsingError,
|
112
|
-
"#{self.class} -> #{line.inspect} does not match expectations for Attribute"
|
122
|
+
raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match expectations for Attribute"
|
113
123
|
end
|
114
124
|
[matched[:name], matched[:string] || matched[:number] || matched[:bool]]
|
115
125
|
end
|
@@ -123,13 +133,16 @@ module Occi
|
|
123
133
|
def plain_links!(lines, entity)
|
124
134
|
lines.each do |line|
|
125
135
|
next unless line.start_with?(TextParser::LINK_KEYS.first)
|
136
|
+
logger.debug "Parsing link line #{line.inspect}" if logger_debug?
|
137
|
+
|
126
138
|
matched = line.match(LINK_REGEXP)
|
127
139
|
unless matched
|
128
|
-
raise Occi::Core::Errors::ParsingError,
|
129
|
-
"#{self.class} -> #{line.inspect} does not match expectations for Link"
|
140
|
+
raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match expectations for Link"
|
130
141
|
end
|
142
|
+
|
131
143
|
plain_link! matched, entity
|
132
144
|
end
|
145
|
+
|
133
146
|
entity
|
134
147
|
end
|
135
148
|
|
@@ -157,7 +170,7 @@ module Occi
|
|
157
170
|
def plain_oglink!(md, entity)
|
158
171
|
unless entity.respond_to?(:links)
|
159
172
|
raise Occi::Core::Errors::ParsingError,
|
160
|
-
"
|
173
|
+
"Cannot assign links to entity #{entity.id} which does not support them"
|
161
174
|
end
|
162
175
|
|
163
176
|
link = plain_oglink_instance(md)
|
@@ -169,24 +182,25 @@ module Occi
|
|
169
182
|
entity
|
170
183
|
end
|
171
184
|
|
172
|
-
# Constructs a single link instance based on the provided data. The returned instance does include
|
185
|
+
# Constructs a single link instance based on the provided data. The returned instance does include
|
173
186
|
# action instance attributes!
|
174
187
|
#
|
175
188
|
# @param md [MatchData] Hash-like structure with matched parts of the link
|
176
189
|
# @return [Occi::Core::Link] constructed link instance
|
177
190
|
def plain_oglink_instance(md)
|
178
191
|
if md[:category].blank? || md[:self].blank?
|
179
|
-
raise Occi::Core::Errors::ParsingError,
|
180
|
-
"#{self.class} -> Link #{md[:uri].inspect} is missing type and location information"
|
192
|
+
raise Occi::Core::Errors::ParsingError, "Link #{md[:uri].inspect} missing type and location information"
|
181
193
|
end
|
182
194
|
|
183
195
|
categories = md[:category].split
|
184
196
|
target_kind = handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(md[:rel]) }
|
185
197
|
link = @_ib.build(categories.shift, target_kind: target_kind)
|
186
198
|
categories.each do |mxn|
|
199
|
+
logger.debug "Adding mixin #{mxn.inspect} to link instance" if logger_debug?
|
187
200
|
link << handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(mxn) }
|
188
201
|
end
|
189
202
|
|
203
|
+
logger.debug "Created link instance #{link.inspect} from #{md.inspect}" if logger_debug?
|
190
204
|
link
|
191
205
|
end
|
192
206
|
|
@@ -196,13 +210,16 @@ module Occi
|
|
196
210
|
# @param link [Occi::Core::Link] partially constructed link instance to be updated
|
197
211
|
def plain_oglink_attributes!(md, link)
|
198
212
|
if md[:attributes].blank?
|
199
|
-
raise Occi::Core::Errors::ParsingError,
|
200
|
-
"#{self.class} -> Link #{link.id} is missing attribute information"
|
213
|
+
raise Occi::Core::Errors::ParsingError, "Link #{link.id} is missing attribute information"
|
201
214
|
end
|
215
|
+
logger.debug "Parsing inline link attributes from line #{md[:attributes].inspect}" if logger_debug?
|
202
216
|
|
217
|
+
regexp = Regexp.new "(\\s*#{Constants::REGEXP_ATTRIBUTE_REPR})"
|
203
218
|
line = md[:attributes].strip.gsub(/^;\s*/, '')
|
204
|
-
|
205
|
-
|
219
|
+
plain_attributes!(
|
220
|
+
line.scan(regexp).map { |attrb| "#{TextParser::ATTRIBUTE_KEYS.first}: #{attrb.first}" },
|
221
|
+
link.attributes
|
222
|
+
)
|
206
223
|
|
207
224
|
link
|
208
225
|
end
|
@@ -214,10 +231,10 @@ module Occi
|
|
214
231
|
# @return [Object] typecasted value
|
215
232
|
def typecast(value, type)
|
216
233
|
if value.nil? || type.nil?
|
217
|
-
raise Occi::Core::Errors::ParsingError,
|
218
|
-
"#{self.class} -> Cannot typecast (un)set value to (un)set type"
|
234
|
+
raise Occi::Core::Errors::ParsingError, 'Cannot typecast (un)set value to (un)set type'
|
219
235
|
end
|
220
236
|
|
237
|
+
logger.debug "Typecasting value #{value.inspect} to #{type}" if logger_debug?
|
221
238
|
self.class.typecaster[type].call(value)
|
222
239
|
end
|
223
240
|
|
@@ -11,6 +11,10 @@ module Occi
|
|
11
11
|
include Helpers::ErrorHandler
|
12
12
|
|
13
13
|
class << self
|
14
|
+
# Shortcuts to interesting methods on logger
|
15
|
+
DELEGATED = %i[debug? info? warn? error? fatal?].freeze
|
16
|
+
delegate(*DELEGATED, to: :logger, prefix: true)
|
17
|
+
|
14
18
|
# Parses text/plain OCCI locations into `URI` instances suitable for futher processing.
|
15
19
|
# Every location line is expected to begin with 'X-OCCI-Location'.
|
16
20
|
#
|
@@ -18,15 +22,19 @@ module Occi
|
|
18
22
|
# @return [Array] list of locations (URIs)
|
19
23
|
def plain(lines)
|
20
24
|
regexp = Regexp.new(Constants::REGEXP_LOCATION)
|
21
|
-
|
25
|
+
|
26
|
+
locations = lines.map do |line|
|
22
27
|
next if line.blank?
|
28
|
+
logger.debug "Parsing location from line #{line.inspect}" if logger_debug?
|
29
|
+
|
23
30
|
matched = line.match(regexp)
|
24
31
|
unless matched
|
25
|
-
raise Occi::Core::Errors::ParsingError,
|
26
|
-
"#{self} -> #{line.inspect} does not match 'X-OCCI-Location: URI'"
|
32
|
+
raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match 'X-OCCI-Location: URI'"
|
27
33
|
end
|
28
34
|
handle(Occi::Core::Errors::ParsingError) { URI.parse(matched[:location].strip) }
|
29
|
-
end
|
35
|
+
end
|
36
|
+
|
37
|
+
locations.compact
|
30
38
|
end
|
31
39
|
|
32
40
|
# Parses text/uri-list lines into `URI` instances suitable for futher processing.
|
@@ -35,10 +43,14 @@ module Occi
|
|
35
43
|
# @param lines [Array] list of lines to parse
|
36
44
|
# @return [Array] list of locations (URIs)
|
37
45
|
def uri_list(lines)
|
38
|
-
lines.map do |line|
|
46
|
+
uris = lines.map do |line|
|
39
47
|
next if line.blank? || line.start_with?('#')
|
48
|
+
logger.debug "Parsing location from line #{line.inspect}" if logger_debug?
|
49
|
+
|
40
50
|
handle(Occi::Core::Errors::ParsingError) { URI.parse(line.strip) }
|
41
|
-
end
|
51
|
+
end
|
52
|
+
|
53
|
+
uris.compact
|
42
54
|
end
|
43
55
|
end
|
44
56
|
end
|
@@ -50,14 +50,15 @@ module Occi
|
|
50
50
|
# @return [Set] set of instances
|
51
51
|
def entities(body, headers, expectation = nil)
|
52
52
|
expectation ||= Occi::Core::Entity
|
53
|
+
logger.debug "Parsing #{expectation} from #{body.inspect} and #{headers.inspect}" if logger_debug?
|
53
54
|
|
54
55
|
entity_parser = Text::Entity.new(model: model)
|
55
56
|
entity = entity_parser.plain transform(body, headers)
|
56
57
|
unless entity.is_a?(expectation)
|
57
|
-
raise Occi::Core::Errors::ParsingError, "
|
58
|
+
raise Occi::Core::Errors::ParsingError, "Entity is not of type #{expectation}"
|
58
59
|
end
|
59
60
|
|
60
|
-
|
61
|
+
setify(entity)
|
61
62
|
end
|
62
63
|
|
63
64
|
# Parses action instances from the given body/headers. Only actions already declared in the model are
|
@@ -67,16 +68,15 @@ module Occi
|
|
67
68
|
# @param headers [Hash] raw headers as provided by the transport protocol
|
68
69
|
# @return [Set] set of parsed instances
|
69
70
|
def action_instances(body, headers)
|
71
|
+
logger.debug "Parsing Occi::Core::ActionInstance from #{body.inspect} and #{headers.inspect}" if logger_debug?
|
70
72
|
entity_parser = Text::Entity.new(model: model)
|
71
73
|
tformed = transform(body, headers)
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
entity_parser.plain_attributes! tformed, ai.attributes
|
79
|
-
Set.new([ai].compact)
|
75
|
+
action_instance = Occi::Core::ActionInstance.new(
|
76
|
+
action: action_instance_category(entity_parser, tformed)
|
77
|
+
)
|
78
|
+
entity_parser.plain_attributes! tformed, action_instance.attributes
|
79
|
+
setify(action_instance)
|
80
80
|
end
|
81
81
|
|
82
82
|
# Parses categories from the given body/headers and returns corresponding instances
|
@@ -88,11 +88,14 @@ module Occi
|
|
88
88
|
# @return [Set] set of instances
|
89
89
|
def categories(body, headers, expectation = nil)
|
90
90
|
expectation ||= Occi::Core::Category
|
91
|
+
logger.debug "Parsing #{expectation} from #{body.inspect} and #{headers.inspect}" if logger_debug?
|
92
|
+
|
91
93
|
cats = transform(body, headers).map do |line|
|
92
94
|
cat = Text::Category.plain_category(line, false)
|
93
95
|
lookup "#{cat[:scheme]}#{cat[:term]}", expectation
|
94
96
|
end
|
95
|
-
|
97
|
+
|
98
|
+
setify(cats)
|
96
99
|
end
|
97
100
|
|
98
101
|
# Transforms `body` and `headers` into an array of lines parsable by 'text/plain'
|
@@ -116,13 +119,16 @@ module Occi
|
|
116
119
|
# @param model [Occi::Core::Model] `Model`-like instance to be populated (may contain existing categories)
|
117
120
|
# @return [Occi::Core::Model] model instance filled with parsed categories
|
118
121
|
def model(body, headers, media_type, model)
|
122
|
+
if logger_debug?
|
123
|
+
logger.debug "Parsing model from #{media_type.inspect} in #{body.inspect} and #{headers.inspect}"
|
124
|
+
end
|
125
|
+
|
119
126
|
if HEADERS_TEXT_TYPES.include? media_type
|
120
127
|
Text::Category.plain transform_headers(headers), model
|
121
128
|
elsif PLAIN_TEXT_TYPES.include? media_type
|
122
129
|
Text::Category.plain transform_body(body), model
|
123
130
|
else
|
124
|
-
raise Occi::Core::Errors::ParsingError,
|
125
|
-
"#{self} -> model cannot be parsed from #{media_type.inspect}"
|
131
|
+
raise Occi::Core::Errors::ParsingError, "Model cannot be parsed from #{media_type.inspect}"
|
126
132
|
end
|
127
133
|
end
|
128
134
|
|
@@ -133,6 +139,10 @@ module Occi
|
|
133
139
|
# @param media_type [String] media type string as provided by the transport protocol
|
134
140
|
# @return [Array] list of extracted URIs
|
135
141
|
def locations(body, headers, media_type)
|
142
|
+
if logger_debug?
|
143
|
+
logger.debug "Parsing locations from #{media_type.inspect} in #{body.inspect} and #{headers.inspect}"
|
144
|
+
end
|
145
|
+
|
136
146
|
if URI_LIST_TYPES.include? media_type
|
137
147
|
Text::Location.uri_list transform_body(body)
|
138
148
|
elsif HEADERS_TEXT_TYPES.include? media_type
|
@@ -148,6 +158,7 @@ module Occi
|
|
148
158
|
# @param body [String] multi-line body
|
149
159
|
# @return [Array] an array of lines
|
150
160
|
def transform_body(body)
|
161
|
+
return [] if body.blank?
|
151
162
|
lines = body.respond_to?(:lines) ? body.lines : body.split("\n")
|
152
163
|
lines.map(&:strip)
|
153
164
|
end
|
@@ -159,6 +170,7 @@ module Occi
|
|
159
170
|
# @param headers [Hash] hash with raw header key-value pairs
|
160
171
|
# @return [Array] an array of body-like lines
|
161
172
|
def transform_headers(headers)
|
173
|
+
logger.debug "Transforming headers #{headers.inspect}" if logger_debug?
|
162
174
|
unify_headers(
|
163
175
|
canonize_headers(
|
164
176
|
normalize_headers(headers)
|
@@ -172,11 +184,15 @@ module Occi
|
|
172
184
|
# @param headers [Hash] hash with raw header key-value pairs
|
173
185
|
# @return [Hash] a cleaner hash with relevant headers
|
174
186
|
def normalize_headers(headers)
|
187
|
+
return {} unless headers
|
188
|
+
|
175
189
|
headers = Hash[
|
176
190
|
headers.map { |k, v| [k.gsub(HEADER_HTTP_PREFIX, '').capitalize, v] }
|
177
191
|
] # remove 'HTTP_' prefix in keys
|
178
192
|
headers.delete_if { |_k, v| v.blank? } # remove pairs with empty values
|
179
193
|
headers.keep_if { |k, _v| OCCI_KEYS.include?(k) } # drop non-OCCI pairs
|
194
|
+
|
195
|
+
logger.debug "Normalized headers #{headers.inspect}" if logger_debug?
|
180
196
|
headers
|
181
197
|
end
|
182
198
|
|
@@ -194,6 +210,7 @@ module Occi
|
|
194
210
|
canonical[pref.first] = [canonize_header_value(value)].flatten
|
195
211
|
end
|
196
212
|
|
213
|
+
logger.debug "Canonized headers #{canonical.inspect}" if logger_debug?
|
197
214
|
canonical
|
198
215
|
end
|
199
216
|
|
@@ -219,6 +236,8 @@ module Occi
|
|
219
236
|
headers.each_pair do |k, v|
|
220
237
|
lines << v.map { |val| "#{k}: #{val}" }
|
221
238
|
end
|
239
|
+
|
240
|
+
logger.debug "Unified headers #{lines.inspect}" if logger_debug?
|
222
241
|
lines.flatten.sort
|
223
242
|
end
|
224
243
|
|
@@ -230,8 +249,7 @@ module Occi
|
|
230
249
|
def validate_header_keys!(headers_keys)
|
231
250
|
return unless key_name_groups.any? { |elm| (headers_keys & elm).count > 1 }
|
232
251
|
|
233
|
-
raise Occi::Core::Errors::ParsingError,
|
234
|
-
"#{self} -> Headers #{headers_keys.inspect} contain mixed key notations"
|
252
|
+
raise Occi::Core::Errors::ParsingError, "Headers #{headers_keys.inspect} contain mixed key notations"
|
235
253
|
end
|
236
254
|
|
237
255
|
# Returns a list of available key name groups accessible as constants by name on this class.
|
@@ -244,6 +262,19 @@ module Occi
|
|
244
262
|
|
245
263
|
protected
|
246
264
|
|
265
|
+
# :nodoc:
|
266
|
+
def action_instance_category(entity_parser, action_instance)
|
267
|
+
cats = entity_parser.plain_categories action_instance
|
268
|
+
action = cats.detect { |c| c.is_a? Occi::Core::Action }
|
269
|
+
raise Occi::Core::Errors::ParsingError, 'ActionInstance does not specify action' unless action
|
270
|
+
action
|
271
|
+
end
|
272
|
+
|
273
|
+
# :nodoc:
|
274
|
+
def setify(object)
|
275
|
+
object.is_a?(Enumerable) ? Set.new(object) : Set.new([object].compact)
|
276
|
+
end
|
277
|
+
|
247
278
|
# :nodoc:
|
248
279
|
def post_initialize(args)
|
249
280
|
super
|
@@ -31,7 +31,7 @@ module Occi
|
|
31
31
|
def initialize(args = {})
|
32
32
|
default_args! args
|
33
33
|
|
34
|
-
logger.debug "RendererFactory
|
34
|
+
logger.debug "Initializing RendererFactory with #{args.inspect}"
|
35
35
|
@required_methods = args.fetch(:required_methods)
|
36
36
|
@namespace = args.fetch(:namespace)
|
37
37
|
|
@@ -40,7 +40,7 @@ module Occi
|
|
40
40
|
|
41
41
|
#
|
42
42
|
def reload!
|
43
|
-
logger.debug 'RendererFactory
|
43
|
+
logger.debug 'Clearing RendererFactory cache for renderer reload'
|
44
44
|
@ravail_cache = nil
|
45
45
|
end
|
46
46
|
|
@@ -65,7 +65,7 @@ module Occi
|
|
65
65
|
@ravail_cache = {}
|
66
66
|
|
67
67
|
renderer_classes.each do |rndr_klass|
|
68
|
-
logger.debug "RendererFactory
|
68
|
+
logger.debug "RendererFactory registering #{rndr_klass} for #{rndr_klass.formats}"
|
69
69
|
rndr_klass.formats.each { |rndr_klass_f| @ravail_cache[rndr_klass_f] = rndr_klass }
|
70
70
|
end
|
71
71
|
|
@@ -113,7 +113,7 @@ module Occi
|
|
113
113
|
renderer_with_methods! candidate
|
114
114
|
renderer_with_formats! candidate
|
115
115
|
rescue Occi::Core::Errors::RendererError => ex
|
116
|
-
logger.debug "
|
116
|
+
logger.debug "Renderer validation failed with #{ex.message}"
|
117
117
|
return false
|
118
118
|
end
|
119
119
|
|
@@ -176,7 +176,7 @@ module Occi
|
|
176
176
|
raise Occi::Core::Errors::RendererError, "#{namespace.inspect} " \
|
177
177
|
'is not a Module'
|
178
178
|
end
|
179
|
-
logger.debug "RendererFactory
|
179
|
+
logger.debug "RendererFactory looking for renderers in #{namespace}"
|
180
180
|
namespace.constants.collect { |const| namespace.const_get(const) }
|
181
181
|
end
|
182
182
|
|
data/lib/occi/core/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Occi
|
|
3
3
|
MAJOR_VERSION = 5 # Major update constant
|
4
4
|
MINOR_VERSION = 0 # Minor update constant
|
5
5
|
PATCH_VERSION = 0 # Patch/Fix version constant
|
6
|
-
STAGE_VERSION = '
|
6
|
+
STAGE_VERSION = 'beta.1'.freeze # use `nil` for production releases
|
7
7
|
|
8
8
|
unless defined?(::Occi::Core::VERSION)
|
9
9
|
VERSION = [
|
data/lib/occi/core/warehouse.rb
CHANGED
@@ -29,7 +29,7 @@ module Occi
|
|
29
29
|
#
|
30
30
|
# @param model [Occi::Core::Model] model to be bootstrapped
|
31
31
|
def bootstrap!(model)
|
32
|
-
logger.debug "
|
32
|
+
logger.debug "Bootstrapping#{model.empty? ? ' empty' : ''} model"
|
33
33
|
actions! model
|
34
34
|
kinds! model
|
35
35
|
mixins! model
|
@@ -67,7 +67,7 @@ module Occi
|
|
67
67
|
def kinds!(model)
|
68
68
|
attribute_definitions = attribute_definitions_for(kinds_path)
|
69
69
|
yamls_in(kinds_path).each do |file|
|
70
|
-
logger.debug "
|
70
|
+
logger.debug "Warehouse loading kind from #{file.inspect}"
|
71
71
|
model << Occi::Core::Kind.from_yaml(file, model, attribute_definitions)
|
72
72
|
end
|
73
73
|
end
|
@@ -76,7 +76,7 @@ module Occi
|
|
76
76
|
def mixins!(model)
|
77
77
|
attribute_definitions = attribute_definitions_for(mixins_path)
|
78
78
|
yamls_in(mixins_path).each do |file|
|
79
|
-
logger.debug "
|
79
|
+
logger.debug "Warehouse loading mixin from #{file.inspect}"
|
80
80
|
model << Occi::Core::Mixin.from_yaml(file, model, attribute_definitions)
|
81
81
|
end
|
82
82
|
end
|
@@ -86,7 +86,7 @@ module Occi
|
|
86
86
|
# TODO: work with separate attribute definitions
|
87
87
|
attribute_definitions = {}
|
88
88
|
yamls_in(actions_path).each do |file|
|
89
|
-
logger.debug "
|
89
|
+
logger.debug "Warehouse loading action from #{file.inspect}"
|
90
90
|
model << Occi::Core::Action.from_yaml(file, model, attribute_definitions)
|
91
91
|
end
|
92
92
|
end
|
@@ -98,7 +98,7 @@ module Occi
|
|
98
98
|
attr_defs_path = File.join(categories_path, ATTRIBS)
|
99
99
|
yamls_in(attr_defs_path).each do |file|
|
100
100
|
name = attribute_name_from(file)
|
101
|
-
logger.debug "
|
101
|
+
logger.debug "Warehouse loading attribute definition for #{name.inspect} from #{file.inspect}"
|
102
102
|
attribute_definitions[name] = Occi::Core::AttributeDefinition.from_yaml(file)
|
103
103
|
end
|
104
104
|
|
@@ -10,7 +10,7 @@ module Occi
|
|
10
10
|
# model = Occi::Infrastructure::Model.new
|
11
11
|
# model.load_infrastructure!
|
12
12
|
def load_infrastructure!
|
13
|
-
logger.debug
|
13
|
+
logger.debug 'Loading Infrastructure from Infrastructure::Warehouse'
|
14
14
|
Occi::Infrastructure::Warehouse.bootstrap! self
|
15
15
|
self << Occi::Infrastructure::Mixins::OsTpl.new
|
16
16
|
self << Occi::Infrastructure::Mixins::ResourceTpl.new
|
@@ -10,7 +10,7 @@ module Occi
|
|
10
10
|
# model = Occi::InfrastructureExt::Model.new
|
11
11
|
# model.load_infrastructure_ext!
|
12
12
|
def load_infrastructure_ext!
|
13
|
-
logger.debug
|
13
|
+
logger.debug 'Loading InfrastructureExt from InfrastructureExt::Warehouse'
|
14
14
|
Occi::InfrastructureExt::Warehouse.bootstrap! self
|
15
15
|
self << Occi::InfrastructureExt::Mixins::AvailabilityZone.new
|
16
16
|
nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: occi-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boris Parak
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-06-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -566,7 +566,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
566
566
|
version: 1.3.1
|
567
567
|
requirements: []
|
568
568
|
rubyforge_project:
|
569
|
-
rubygems_version: 2.6.
|
569
|
+
rubygems_version: 2.6.10
|
570
570
|
signing_key:
|
571
571
|
specification_version: 4
|
572
572
|
summary: The rOCCI toolkit
|