Skip to content

Commit

Permalink
Fixes #4127 - Global Parameters with types
Browse files Browse the repository at this point in the history
  • Loading branch information
kgaikwad committed Jan 16, 2019
1 parent 58d6e0f commit f2cc385
Show file tree
Hide file tree
Showing 31 changed files with 238 additions and 186 deletions.
2 changes: 1 addition & 1 deletion app/assets/javascripts/hidden_values.js
@@ -1,5 +1,5 @@
function turn_textarea_switch() {
$('.editor-container .ace_content').toggleClass('masked-input');
$('#common_parameter_value').toggleClass('masked-input');
}

function hidden_value_control(){
Expand Down
9 changes: 9 additions & 0 deletions app/assets/javascripts/parameter_override.js
@@ -1,13 +1,22 @@
function override_param(item){
var param = $(item).closest('tr').addClass('override-param');
var n = param.find('[id^=name_]').text();
var parameter_type_val = param.find('[id^=parameter_type_]').text();
var param_value = param.find('[id^=value_]');
var v = param_value.val();

$('#parameters').find('.btn-primary').click();
var new_param = $('#parameters').find('.fields').last();
new_param.find('[id$=_name]').val(n);
new_param.find('[id$=_parameter_type]').val(parameter_type_val);
new_param.find('[id$=_value]').val(v == param_value.data('hidden-value') ? '' : v);
if (param_value.hasClass('masked-input')) {
var alink = new_param.find('span.fa-eye-slash').closest('a'),
hiddenValueCheckBox = new_param.find('.set_hidden_value');
hiddenValueCheckBox.prop('checked', true);
hiddenValueCheckBox.val('1');
alink.click();
}
}

function override_class_param(item){
Expand Down
4 changes: 3 additions & 1 deletion app/assets/javascripts/taxonomy_edit.js
Expand Up @@ -12,7 +12,9 @@ function ignore_checked(item){
} else {
current_select.removeAttr('disabled');
}
$(current_select).multiSelect('refresh');
if (!$(current_select).hasClass('parameter_type_selection')) {
$(current_select).multiSelect('refresh');
}
multiSelectToolTips();
}

Expand Down
1 change: 1 addition & 0 deletions app/controllers/api/v2/common_parameters_controller.rb
Expand Up @@ -25,6 +25,7 @@ def show
param :common_parameter, Hash, :required => true, :action_aware => true do
param :name, String, :required => true
param :value, String, :required => true
param :parameter_type, Parameter::KEY_TYPES, :desc => N_("Type of value"), :required => true
param :hidden_value, [true, false]
end
end
Expand Down
1 change: 1 addition & 0 deletions app/controllers/api/v2/parameters_controller.rb
Expand Up @@ -62,6 +62,7 @@ def show
param :parameter, Hash, :required => true, :action_aware => true do
param :name, String, :required => true
param :value, String, :required => true
param :parameter_type, Parameter::KEY_TYPES, :desc => N_("Type of value"), :required => true
end
end

Expand Down
Expand Up @@ -17,6 +17,7 @@ def parameter_params_filter(type)
:nested,
:reference_id,
:value,
:parameter_type,
:nested => true

filter.permit_by_context :id,
Expand Down
17 changes: 11 additions & 6 deletions app/helpers/common_parameters_helper.rb
Expand Up @@ -12,7 +12,14 @@ def parameters_title
def parameter_value_field(value)
source_name = value[:source_name] ? "(#{value[:source_name]})" : nil
popover_tag = popover('', _("<b>Source:</b> %{type} %{name}") % { :type => _(value[:source]), :name => html_escape(source_name) }, :data => { :placement => 'top' })
content_tag(:div, parameter_value_content("value_#{value[:safe_value]}", value[:safe_value], :popover => popover_tag, :disabled => true) + fullscreen_input, :class => 'input-group')
content_tag(
:div,
parameter_value_content(
"value_#{value[:safe_value]}", value[:safe_value], :hidden_value? => value[:hidden_value?],
:popover => popover_tag, :disabled => true
) + fullscreen_input,
:class => 'input-group'
)
end

def parameter_value_content(id, value, options)
Expand Down Expand Up @@ -40,23 +47,21 @@ def hidden_value_field(f, field, disabled, options = {})
:class => html_class,
:rows => 1,
:id => dom_id(f.object) + '_value',
:placeholder => _("Value")))
:placeholder => _("Value"),
:value => f.object.value_before_type_cast))

input_group(input, input_group_btn(hidden_toggle(f.object.hidden_value?), fullscreen_button("$(this).closest('.input-group').find('input,textarea')")))
end

def lookup_key_field(id, value, options)
lookup_key = options[:lookup_key]

option_hash = { :rows => 1,
:class => 'form-control no-stretch',
:'data-property' => 'value',
:'data-hidden-value' => LookupKey.hidden_value,
:'data-inherited-value' => options[:inherited_value],
:name => options[:name].to_s,
:disabled => options[:disabled] }

option_hash[:class] += " masked-input" if lookup_key.present? && options[:lookup_key_hidden_value?]
option_hash[:class] += " masked-input" if options[:hidden_value?]

case options[:lookup_key_type]
when "boolean"
Expand Down
13 changes: 11 additions & 2 deletions app/helpers/form_helper.rb
Expand Up @@ -173,11 +173,20 @@ def time_zone_select_f(f, attr, default_timezone, select_options = {}, html_opti
def selectable_f(f, attr, array, select_options = {}, html_options = {})
html_options[:size] = 'col-md-10' if html_options[:multiple]
field(f, attr, html_options) do
addClass html_options, "form-control"
f.select attr, array, select_options, html_options
form_select_f(f, attr, array, select_options, html_options)
end
end

def form_select_f(f, attr, array, select_options = {}, html_options = {})
addClass html_options, "form-control"
f.select attr, array, select_options, html_options
end

def selectable_f_inline(f, attr, array, select_options = {}, html_options = {})
html_options[:size] = 'col-md-10' if html_options[:multiple]
form_select_f(f, attr, array, select_options, html_options)
end

def spinner_button_f(f, caption, action, html_options = {})
html_options[:class] ||= 'btn-default'
html_options[:class] = "btn btn-spinner #{html_options[:class]}"
Expand Down
29 changes: 29 additions & 0 deletions app/helpers/key_types_helper.rb
@@ -0,0 +1,29 @@
module KeyTypesHelper
def param_type_selector(f, only_inline_select = false, options = {})
common_extra_options = { :size => "col-md-4", :class => "parameter_type_selection without_select2",
:label_help => _("<dl>" +
"<dt>String</dt> <dd>Everything turns into a string.</dd>" +
"<dt>Boolean</dt> <dd>Common representation of boolean values are accepted.</dd>" +
"<dt>Integer</dt> <dd>Integer numbers only, can be negative.</dd>" +
"<dt>Real</dt> <dd>Accept any numerical input.</dd>" +
"<dt>Array</dt> <dd>A valid JSON or YAML input, that must evaluate to an array.</dd>" +
"<dt>Hash</dt> <dd>A valid JSON or YAML input, that must evaluate to an object/map/dict/hash.</dd>" +
"<dt>YAML</dt> <dd>Any valid YAML input.</dd>" +
"<dt>JSON</dt> <dd>Any valid JSON input.</dd>" +
"</dl>").html_safe,
:label_help_options => { :title => _("How values are validated") }}
if lookup_keys_table?(f)
common_extra_options[:disabled] = (f.object.puppet? && !f.object.override)
common_extra_options[:size] = 'col-md-8'
end
method_for_select_f = only_inline_select ? 'selectable_f_inline' : 'selectable_f'

send(method_for_select_f, f, :parameter_type,
options_for_select(LookupKey::KEY_TYPES.map { |e| [_(e), e] }, f.object.parameter_type), {},
options.merge(common_extra_options))
end

def lookup_keys_table?(f)
f.object.class.table_name == 'lookup_keys'
end
end
20 changes: 2 additions & 18 deletions app/helpers/lookup_keys_helper.rb
Expand Up @@ -32,22 +32,6 @@ def show_puppet_class(f)
end
end

def param_type_selector(f, options = {})
selectable_f f, :key_type, options_for_select(LookupKey::KEY_TYPES.map { |e| [_(e), e] }, f.object.key_type), {},
options.merge({ :disabled => (f.object.puppet? && !f.object.override), :size => "col-md-8", :class => "without_select2",
:label_help => _("<dl>" +
"<dt>String</dt> <dd>Everything is taken as a string.</dd>" +
"<dt>Boolean</dt> <dd>Common representation of boolean values are accepted.</dd>" +
"<dt>Integer</dt> <dd>Integer numbers only, can be negative.</dd>" +
"<dt>Real</dt> <dd>Accept any numerical input.</dd>" +
"<dt>Array</dt> <dd>A valid JSON or YAML input, that must evaluate to an array.</dd>" +
"<dt>Hash</dt> <dd>A valid JSON or YAML input, that must evaluate to an object/map/dict/hash.</dd>" +
"<dt>YAML</dt> <dd>Any valid YAML input.</dd>" +
"<dt>JSON</dt> <dd>Any valid JSON input.</dd>" +
"</dl>").html_safe,
:label_help_options => { :title => _("How values are validated") }})
end

def validator_type_selector(f)
selectable_f f, :validator_type, options_for_select(LookupKey::VALIDATOR_TYPES.map { |e| [_(e), e] }, f.object.validator_type), {:include_blank => _("None")},
{ :disabled => (f.object.puppet? && !f.object.override), :size => "col-md-8", :class => "without_select2",
Expand All @@ -69,7 +53,7 @@ def can_edit_params?

def lookup_key_with_diagnostic(obj, lookup_key, lookup_value)
value, matcher = value_matcher(obj, lookup_key)
inherited_value = lookup_key.value_before_type_cast(value)
inherited_value = lookup_key.value_before_type_casting(value)
effective_value = lookup_value.lookup_key_id.nil? ? inherited_value.to_s : lookup_value.value_before_type_cast.to_s
warnings = lookup_key_warnings(lookup_key.required, effective_value.present?)
popover_value = lookup_key.hidden_value? ? lookup_key.hidden_value : inherited_value
Expand All @@ -82,7 +66,7 @@ def lookup_key_with_diagnostic(obj, lookup_key, lookup_value)
:disabled => !lookup_key.overridden?(obj) || lookup_value.omit || !can_edit_params?,
:inherited_value => inherited_value,
:lookup_key => lookup_key,
:lookup_key_hidden_value? => lookup_key.hidden_value?,
:hidden_value? => lookup_key.hidden_value?,
:lookup_key_type => lookup_key.key_type)
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/hidden_value.rb
Expand Up @@ -4,7 +4,7 @@ module HiddenValue
HIDDEN_VALUE = "*" * 5

def safe_value
self.hidden_value? ? HIDDEN_VALUE : self.value
self.hidden_value? ? HIDDEN_VALUE : self.value_before_type_cast
end

def hidden_value
Expand Down
7 changes: 1 addition & 6 deletions app/models/concerns/host_params.rb
Expand Up @@ -26,12 +26,7 @@ def non_inherited_params_hash

def params_to_hash(params)
params.each_with_object({}) do |param, hash|
source = param.associated_type
options = {:value => param.value,
:source => source,
:safe_value => param.safe_value }
options[:source_name] = param.associated_label if source != 'global'
hash[param.name] = options
hash[param.name] = param.hash_for_include_source(param.associated_type)
end
end

Expand Down
46 changes: 1 addition & 45 deletions app/models/concerns/hostext/search.rb
Expand Up @@ -65,7 +65,7 @@ module Search

scoped_search :relation => :puppetclasses, :on => :name, :complete_value => true, :rename => :class, :only_explicit => true, :operators => ['= ', '~ '], :ext_method => :search_by_puppetclass
scoped_search :relation => :fact_values, :on => :value, :in_key => :fact_names, :on_key => :name, :rename => :facts, :complete_value => true, :only_explicit => true, :ext_method => :search_cast_facts
scoped_search :relation => :search_parameters, :on => :value, :on_key => :name, :complete_value => true, :rename => :params, :ext_method => :search_by_params, :only_explicit => true
scoped_search :relation => :search_parameters, :on => :name, :complete_value => true, :rename => :params, :only_explicit => true

if SETTINGS[:locations_enabled]
scoped_search :relation => :location, :on => :title, :rename => :location, :complete_value => true, :only_explicit => true
Expand Down Expand Up @@ -146,27 +146,6 @@ def search_by_hostgroup_and_descendants(key, operator, value)
{:conditions => opts}
end

def search_by_params(key, operator, value)
key_name = key.sub(/^.*\./, '')
condition = sanitize_sql_for_conditions(["name = ? and value #{operator} ?", key_name, value_to_sql(operator, value)])
p = Parameter.where(condition).reorder(:priority)
return {:conditions => '1 = 0'} if p.blank?

max = p.first.priority
condition = sanitize_sql_for_conditions(["name = ? and NOT(value #{operator} ?) and priority > ?", key_name, value_to_sql(operator, value), max])
n = Parameter.where(condition).reorder(:priority)

conditions = param_conditions(p)
negate = param_conditions(n)

conditions += " AND " unless conditions.blank? || negate.blank?
conditions += " NOT(#{negate})" if negate.present?
{
:joins => :primary_interface,
:conditions => conditions
}
end

def search_by_config_group(key, operator, value)
conditions = sanitize_sql_for_conditions(["config_groups.name #{operator} ?", value_to_sql(operator, value)])
host_ids = Host::Managed.where(conditions).joins(:config_groups).distinct.pluck('hosts.id')
Expand Down Expand Up @@ -201,29 +180,6 @@ def search_cast_facts(key, operator, value)
:conditions => "#{sanitize_sql_for_conditions(["fact_names_#{table_id}.name = ?", key.split('.')[1]])} AND #{cast_facts("fact_values_#{table_id}", key, operator, value)}"
}
end

private

def param_conditions(p)
conditions = []
p.each do |param|
case param.class.to_s
when 'CommonParameter'
conditions << "1 = 1" # include all Global parameters
when 'DomainParameter'
conditions << "nics.domain_id = #{param.reference_id}"
when 'OsParameter'
conditions << "hosts.operatingsystem_id = #{param.reference_id}"
when 'GroupParameter'
conditions << "hosts.hostgroup_id IN (#{param.hostgroup.subtree_ids.join(', ')})"
when 'HostParameter'
conditions << "hosts.id = #{param.reference_id}"
when 'SubnetParameter'
conditions << "nics.subnet_id = #{param.reference_id} OR nics.subnet6_id = #{param.reference_id}"
end
end
conditions.empty? ? "" : "( #{conditions.join(' OR ')} )"
end
end
end
end
24 changes: 24 additions & 0 deletions app/models/concerns/key_type.rb
@@ -0,0 +1,24 @@
module KeyType
extend ActiveSupport::Concern
KEY_TYPES = [N_("string"), N_("boolean"), N_("integer"), N_("real"), N_("array"), N_("hash"), N_("yaml"), N_("json")]

included do
validates :key_type, :inclusion => {:in => KEY_TYPES, :message => N_("invalid")}, :allow_blank => true, :allow_nil => true

alias_attribute :parameter_type, :key_type
end

def value_before_type_casting(val)
return val if val.nil? || val.contains_erb?
if key_type.present?
case key_type.to_sym
when :json, :array
val = JSON.dump(val)
when :yaml, :hash
val = YAML.dump val
val.sub!(/\A---\s*$\n/, '')
end
end
val
end
end
12 changes: 12 additions & 0 deletions app/models/concerns/key_value_validation.rb
@@ -0,0 +1,12 @@
module KeyValueValidation
extend ActiveSupport::Concern

def validate_and_cast_value(object_for_key_type = nil)
object_for_key_type ||= self
return if !self.value.is_a?(String) || value.contains_erb?
Foreman::Parameters::Caster.new(self, :attribute_name => :value, :to => object_for_key_type.key_type).cast!
rescue StandardError, SyntaxError => e
Foreman::Logging.exception("Error while parsing #{object_for_key_type}", e)
errors.add(:value, _("is invalid %s") % object_for_key_type.key_type)
end
end
9 changes: 7 additions & 2 deletions app/models/hostgroup.rb
Expand Up @@ -164,15 +164,20 @@ def parent_params(include_source = false)
# otherwise we might be overwriting the hash in the wrong order.
groups = Hostgroup.sort_by_ancestry(Hostgroup.includes(:group_parameters).find(ids))
groups.each do |hg|
hg.group_parameters.authorized(:view_params).each {|p| hash[p.name] = include_source ? {:value => p.value, :source => p.associated_type, :safe_value => p.safe_value, :source_name => hg.title} : p.value }
params_arr = hg.group_parameters.authorized(:view_params)
params_arr.each do |p|
hash[p.name] = include_source ? p.hash_for_include_source(p.associated_type, hg.title) : p.value
end
end
hash
end

# returns self and parent parameters as a hash
def parameters(include_source = false)
hash = parent_params(include_source)
group_parameters.each {|p| hash[p.name] = include_source ? {:value => p.value, :source => p.associated_type, :safe_value => p.safe_value, :source_name => title} : p.value }
group_parameters.each do |p|
hash[p.name] = include_source ? p.hash_for_include_source(p.associated_type, title) : p.value
end
hash
end

Expand Down

0 comments on commit f2cc385

Please sign in to comment.