Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export Import of Expression Methods #50

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/models/miq_ae_yaml_export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,19 @@ def write_all_methods(ns_fqname, class_obj)
class_obj.ae_methods.sort_by(&:fqname).each do |meth_obj|
export_file_hash['created_on'] = meth_obj.created_on
export_file_hash['updated_on'] = meth_obj.updated_on
write_method_file(meth_obj, export_file_hash) unless meth_obj.location == 'builtin'
if meth_obj.location == 'inline'
write_method_file(meth_obj, export_file_hash)
end
write_method_attributes(meth_obj, export_file_hash)
end
end

def write_method_attributes(method_obj, export_file_hash)
envelope_hash = setup_envelope(method_obj, METHOD_OBJ_TYPE)
envelope_hash['object']['inputs'] = method_obj.method_inputs
envelope_hash['object']['attributes'].delete('data')
if method_obj.location == "inline"
envelope_hash['object']['attributes'].delete('data')
end
if method_obj.embedded_methods.empty?
envelope_hash['object']['attributes'].delete('embedded_methods')
end
Expand Down
3 changes: 2 additions & 1 deletion app/models/miq_ae_yaml_import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ def process_instance(class_obj, instance_yaml)
def process_method(class_obj, ruby_method_file_name, method_yaml)
method_attributes = method_yaml.fetch_path('object', 'attributes')
if method_attributes['location'] == 'inline'
method_yaml.store_path('object', 'attributes', 'data', load_method_ruby(ruby_method_file_name))
data = load_method_ruby(ruby_method_file_name)
method_yaml.store_path('object', 'attributes', 'data', data) if data
end
method_obj = MiqAeMethod.find_by(:name => method_attributes['name'], :class_id => class_obj.id) unless class_obj.nil?
track_stats('method', method_obj)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
module MiqAeEngine
class MiqAeExpressionMethod
include ApplicationController::Filter::SubstMixin
def initialize(method_obj, obj, inputs)
@edit = {}
@name = method_obj.name
@workspace = obj.workspace
@inputs = inputs
@attributes = inputs['distinct'] || inputs['attributes'] || %w(name)
load_expression(method_obj.data)
process_filter
end

def run
@search_objects = Rbac.search(:filter => MiqExpression.new(@exp),
:class => @exp_object,
:results_format => :objects).first
@search_objects.empty? ? error_handler : set_result
end

private

def load_expression(data)
raise MiqAeException::MethodExpressionEmpty, "Empty expression" if data.blank?
begin
hash = YAML.load(data)
if hash[:expression] && hash[:db]
@exp = hash[:expression]
@exp_object = hash[:db]
else
raise MiqAeException::MethodExpressionInvalid, "Invalid expression #{data}"
end
rescue
raise MiqAeException::MethodExpressionInvalid, "Invalid expression #{data}"
end
end

def process_filter
exp_table = exp_build_table(@exp, true)
qs_tokens = create_tokens(exp_table, @exp)
values = get_args(qs_tokens.keys.length)
qs_tokens.keys.each_with_index { |token_at, index| qs_tokens[token_at][:value] = values[index] }
exp_replace_qs_tokens(@exp, qs_tokens)
end

def result_hash(obj)
@attributes.each_with_object({}) do |attr, hash|
hash[attr] = result_simple(obj, attr)
end
end

def result_array
multiple = @attributes.count > 1
result = @search_objects.collect do |obj|
multiple ? @attributes.collect { |attr| result_simple(obj, attr) } : result_simple(obj, @attributes.first)
end
@inputs['distinct'].blank? ? result : result.uniq
end

def result_dialog_hash
key = @inputs['key'] || 'id'
@search_objects.each_with_object({}) do |obj, hash|
hash[result_simple(obj, key)] = result_simple(obj, @attributes.first)
end
end

def result_simple(obj, attr)
raise MiqAeException::MethodNotDefined, "Undefined method #{attr} in class #{obj.class}" unless obj.respond_to?(attr.to_sym)
obj.send(attr.to_sym)
end

def set_result
target_object.attributes[attribute_name] = exp_value
@workspace.root['ae_result'] = 'ok'
end

def error_handler
disposition = @inputs['on_empty'] || 'error'
case disposition.to_sym
when :error
set_error
when :warn
set_warn
when :abort
set_abort
end
end

def set_error
$miq_ae_logger.error("Expression method ends")
@workspace.root['ae_result'] = 'error'
end

def set_abort
$miq_ae_logger.error("Expression method aborted")
raise MiqAeException::AbortInstantiation, "Expression method #{@name} aborted"
end

def set_warn
$miq_ae_logger.warn("Expression method ends")
@workspace.root['ae_result'] = 'warn'
set_default_value
end

def set_default_value
return unless @inputs.key?('default')
target_object.attributes[attribute_name] = @inputs['default']
end

def attribute_name
@inputs['result_attr'] || 'values'
end

def target_object
@workspace.get_obj_from_path(@inputs['result_obj'] || '.').tap do |obj|
raise MiqAeException::MethodExpressionTargetObjectMissing, @inputs['result_obj'] unless obj
end
end

def exp_value
type = @inputs['result_type'] || 'dialog_hash'
case type.downcase.to_sym
when :hash
result_hash(@search_objects.first)
when :dialog_hash
result_dialog_hash
when :array
result_array
when :simple
result_simple(@search_objects.first, @inputs['attributes'].first)
else
raise MiqAeException::MethodExpressionResultTypeInvalid, "Invalid Result type, should be hash, array or dialog_hash"
end
end

def get_args(num_token)
params = []
(1..num_token).each do |i|
key = "arg#{i}"
raise MiqAeException::MethodParameterNotFound, key unless @inputs.key?(key)
params[i - 1] = @inputs[key]
end
params
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ def self.invoke_inline(aem, obj, inputs)
raise MiqAeException::InvalidMethod, "Inline Method Language [#{aem.language}] not supported"
end

def self.invoke_expression(aem, obj, inputs)
exp_method = MiqAeEngine::MiqAeExpressionMethod.new(aem, obj, inputs)
exp_method.run
end

def self.invoke_uri(aem, obj, _inputs)
scheme, userinfo, host, port, registry, path, opaque, query, fragment = URI.split(aem.data)
raise MiqAeException::MethodNotFound, "Specified URI [#{aem.data}] in Method [#{aem.name}] has unsupported scheme of #{scheme}; supported scheme is file" unless scheme.downcase == "file"
Expand Down Expand Up @@ -45,7 +50,7 @@ def self.invoke(obj, aem, args)
aem.inputs.each do |f|
key = f.name
value = args[key]
value = obj.attributes[key] || f.default_value if value.nil?
value = obj.attributes[key] || obj.substitute_value(f.default_value) if value.nil?
inputs[key] = MiqAeObject.convert_value_based_on_datatype(value, f["datatype"])

if obj.attributes[key] && f["datatype"] != "string"
Expand All @@ -60,7 +65,7 @@ def self.invoke(obj, aem, args)

if obj.workspace.readonly?
$miq_ae_logger.info("Workspace Instantiation is READONLY -- skipping method [#{aem.fqname}] with inputs [#{inputs.inspect}]")
elsif ["inline", "builtin", "uri"].include?(aem.location.downcase.strip)
elsif %w(inline builtin uri expression).include?(aem.location.downcase.strip)
$miq_ae_logger.info("Invoking [#{aem.location}] method [#{aem.fqname}] with inputs [#{inputs.inspect}]")
return MiqAeEngine::MiqAeMethod.send("invoke_#{aem.location.downcase.strip}", aem, obj, inputs)
end
Expand Down
34 changes: 17 additions & 17 deletions lib/miq_automation_engine/engine/miq_ae_engine/miq_ae_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,23 @@ def uri2value(uri, required = false)
uri # if it was not processed, return the original uri
end

def substitute_value(value, _type = nil, required = false)
Benchmark.current_realtime[:substitution_count] += 1
Benchmark.realtime_block(:substitution_time) do
value = value.gsub(RE_SUBST) do |_s|
subst = uri2value($1, required)
subst &&= subst.to_s
# This encoding of relationship is not needed, until we can get a valid use case
# Based on RFC 3986 Section 2.4 "When to Encode or Decode"
# We are properly encoding when we send URL requests to external systems
# or building an automate request
# subst &&= URI.escape(subst, RE_URI_ESCAPE) if type == :aetype_relationship
subst
end unless value.nil?
return value
end
end

private

def call_method(obj, method)
Expand Down Expand Up @@ -541,23 +558,6 @@ def self.convert_value_based_on_datatype(value, datatype)
value
end

def substitute_value(value, _type = nil, required = false)
Benchmark.current_realtime[:substitution_count] += 1
Benchmark.realtime_block(:substitution_time) do
value = value.gsub(RE_SUBST) do |_s|
subst = uri2value($1, required)
subst &&= subst.to_s
# This encoding of relationship is not needed, until we can get a valid use case
# Based on RFC 3986 Section 2.4 "When to Encode or Decode"
# We are properly encoding when we send URL requests to external systems
# or building an automate request
# subst &&= URI.escape(subst, RE_URI_ESCAPE) if type == :aetype_relationship
subst
end unless value.nil?
return value
end
end

def process_assertion(f, message, args)
Benchmark.current_realtime[:assertion_count] += 1
Benchmark.realtime_block(:assertion_time) do
Expand Down
8 changes: 8 additions & 0 deletions lib/miq_automation_engine/miq_ae_exception.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ class FileExists < MiqAeDatastoreError; end
class DomainNotAccessible < MiqAeDatastoreError; end
class CannotLock < MiqAeDatastoreError; end
class CannotUnlock < MiqAeDatastoreError; end

class MethodExpressionNotFound < MiqAeEngineError; end
class MethodExpressionEmpty < MiqAeEngineError; end
class MethodExpressionInvalid < MiqAeEngineError; end
class MethodExpressionTargetObjectMissing < MiqAeEngineError; end
class MethodExpressionResultTypeInvalid < MiqAeEngineError; end
class MethodParameterNotFound < MiqAeEngineError; end
class MethodNotDefined < MiqAeEngineError; end
end

module MiqException
Expand Down
Loading