occi-core 5.0.0.alpha.4 → 5.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|