lib/eco/api/organization/presets_factory.rb in eco-helpers-2.0.13 vs lib/eco/api/organization/presets_factory.rb in eco-helpers-2.0.14

- old
+ new

@@ -2,145 +2,208 @@ module API module Organization class PresetsFactory ABILITIES = File.join(__dir__, 'presets_values.json') + INTEGRITY = File.join(__dir__, 'presets_integrity.json') DEFAULT_CUSTOM = 'presets_custom.json' DEFAULT_MAP = 'presets_map.json' def initialize(presets_custom: DEFAULT_CUSTOM, presets_map: DEFAULT_MAP, enviro: nil, policy_groups: nil) - @abilities = JSON.load(File.open(ABILITIES)) - @habilities = @abilities.map do |key, values| - h_values = values.map { |v| [v, true] }.to_h - [key, h_values] - end.to_h - fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment) - @enviro = enviro + @enviro = enviro - policy_groups = policy_groups || @enviro&.api&.policy_groups.to_a - if policy_groups.is_a?(Eco::API::Organization::PolicyGroups) - @policy_groups = policy_groups - else - @policy_groups = Eco::API::Organization::PolicyGroups.new(policy_groups) - end - - init_custom(presets_custom) - init_map(presets_map) + @policy_groups = policy_groups + @presets_custom_file = presets_custom || DEFAULT_CUSTOM + @presets_map_file = presets_map || DEFAULT_MAP end def new(*policy_group_ids_or_names) names = policy_group_ids_or_names.map do |id_name| - @policy_groups.to_name(id_name)&.downcase + policy_groups.to_name(id_name)&.downcase end.compact - if @presets_map - preset_names = names.map { |name| @presets_map.fetch(name, nil) } + if presets_map + preset_names = names.map { |name| presets_map.fetch(name, nil) } else # option to do not use preset mapping (so just the policy group name) preset_names = names end compile(*preset_names) end # @return [Array<String>] all the abilities def keys - @abilities.keys + abilities_model.keys end private - def init_custom(file = DEFAULT_CUSTOM) - @presets_custom = nil + def compile(*preset_names) + fatal("You need to specify an existing file for the custom presets.") if !@presets_custom + @presets_custom.values_at(*preset_names).compact.reduce({}) do |p1, p2| + merge(p1, p2) + end + end - return if !file - file = File.expand_path(file) + def merge(preset1, preset2) + keys = preset1.keys | preset2.keys - if File.exists?(file) - @presets_custom = JSON.load(File.open(file)) - - errors = @presets_custom.map do |key, preset| - (err = preset_errors(preset)) ? "{ '#{key}' preset -> #{err}}": nil - end.compact - - fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0 + abilities_model.each_with_object({}) do |(key, values), result| + next unless keys.include?(key) + idx = [ + values.index(preset1[key]), + values.index(preset2[key]) + ].compact.max + result[key] = idx && values[idx] end + end + # unsused: only play with the given abilities + def empty_model + JSON.parse(abilities_model.to_json).transform_values {|v| nil } end - def init_map(file = DEFAULT_MAP) - @presets_map = nil + def preset_errors(preset) + return "No preset given" if !preset + errors = preset.map do |k, v| + value_exists?(k, v) ? nil : "#{k}:#{v}" + end.compact + return " Unknown: {#{errors.join(", ")}}" if errors.length > 0 + nil + end - return if !file - file = File.expand_path(file) + def preset_integrity(preset) + preset.each_with_object([]) do |(ability, value), errors| + next unless checks = integrity_model[ability] - if File.exists?(file) - fatal("Maps file specified without custom presets file. Aborting!") if !@presets_custom - @presets_map = JSON.load(File.open(file)) + suberrors = [] - errors = [] - if @policy_groups.length > 0 - errors = @policy_groups.map do |pg| - exists = @presets_map[pg.name.downcase] || @presets_custom[pg.name.downcase] - exists ? nil : "'#{pg.name}'" - end.compact + checks.each do |check| + next unless check["value"] == value + check["conditions"].each do |cond, targets| + case cond + when "at_least" + targets.each do |other, minimum| + unless (ability_value_idx(other, minimum) <= ability_value_idx(other, preset[other])) + suberrors << "'#{other}' should be at least '#{minimum}'" + end + end + when "one_of" + unless targets.any? {|other, expected| preset[other] == expected} + suberrors << targets.each_with_object([]) do |(other, expected), out| + out << "'#{other}': '#{expected}'" + end.join(", ").yield_self do |msg| + "there should be at least one of: {#{msg}}" + end + end + else + warn("Unsuported integrity condition statement '#{cond}' in '#{ability}' with level '#{value}'") + end + end + end - warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0 + if suberrors.length > 0 + errors << "Incorrect value '#{value}' for '#{ability}' - reasons: {#{suberrors.join(", ")}}" end + end.yield_self do |errors| + " Integrity errors: { #{errors.join(", ")} }" if errors.length > 0 + end + end - errors = @presets_map.map do |source, dest| - @presets_custom[dest] ? nil : "'#{dest}'" - end.compact + def integrity_model + @integrity_model ||= JSON.load(File.open(INTEGRITY)) + end - warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0 + def value_exists?(ability, value) + abilities_model_inverted.dig(ability, value) + end + + def abilities_model_inverted + @abilities_model_inverted ||= abilities_model.each_with_object({}) do |(key, values), out| + out[key] = values.each_with_object({}) {|v, h| h[v] = true } end + end + def ability_value_idx(ability, value) + abilities_model[ability].index(value) end - def fatal(msg) - raise msg if !@enviro - @enviro.logger.fatal(msg) - raise msg + def abilities_model + @abilities_model ||= JSON.load(File.open(ABILITIES)) end - def warn(msg) - raise msg if !@enviro - @enviro.logger.warn(msg) + def policy_groups + return @policy_groups if @policy_groups.is_a?(Eco::API::Organization::PolicyGroups) + @policy_groups ||= @enviro&.api&.policy_groups.to_a + + unless @policy_groups.is_a?(Eco::API::Organization::PolicyGroups) + @policy_groups = Eco::API::Organization::PolicyGroups.new(@policy_groups) + end + @policy_groups end - def compile(*preset_names) - fatal("You need to specify an existing file for the custom presets.") if !@presets_custom - @presets_custom.values_at(*preset_names).compact.reduce({}) do |p1, p2| - merge(p1, p2) + def presets_custom + return @presets_custom if instance_variable_defined?(:@presets_custom) + @presets_custom = nil + if @presets_custom_file + if (file = File.expand_path(@presets_custom_file)) && File.exists?(file) + @presets_custom = JSON.load(File.open(file)).tap do |custom_presets| + errors = custom_presets.each_with_object([]) do |(key, preset), errors| + if err = preset_errors(preset) + errors << "{ '#{key}' preset -> #{err}}" + end + if err = preset_integrity(preset) + errors << "{ '#{key}' preset -> #{err}}" + end + end + + fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0 + end + end end end - def merge(preset1, preset2) - keys = preset1.keys | preset2.keys + def presets_map + return @presets_map if instance_variable_defined?(:@presets_map) + @presets_map = nil + if @presets_map_file + if (file = File.expand_path(@presets_map_file)) && File.exists?(file) + fatal("Maps file specified without 'presets_custom.json' file. Aborting!") if !presets_custom + @presets_map = JSON.load(File.open(file)).tap do |map_presets| - @abilities.each_with_object({}) do |(key, values), result| - next unless keys.include?(key) - idx = [ - values.index(preset1[key]), - values.index(preset2[key]) - ].compact.max - result[key] = idx && values[idx] + errors = [] + if policy_groups.length > 0 + errors = policy_groups.map do |pg| + exists = map_presets[pg.name.downcase] || presets_custom[pg.name.downcase] + exists ? nil : "'#{pg.name}'" + end.compact + + warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0 + end + + errors = map_presets.map do |source, dest| + presets_custom[dest] ? nil : "'#{dest}'" + end.compact + + warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0 + + end + end end end - # unsused: only play with the given abilities - def empty_model - JSON.parse(@abilities.to_json).transform_values {|v| nil } + def fatal(msg) + raise msg if !@enviro + @enviro.logger.fatal(msg) + raise msg end - def preset_errors(preset) - return "No preset given" if !preset - errors = preset.map do |k, v| - @habilities.dig(k, v) ? nil : "#{k}:#{v}" - end.compact - return " unknown: {#{errors.join(", ")}}" if errors.length > 0 - nil + def warn(msg) + raise msg if !@enviro + @enviro.logger.warn(msg) end + end end end end