module Eco module API module Organization class PresetsFactory ABILITIES = File.join(__dir__, 'presets_values.json') INTEGRITY = File.join(__dir__, 'presets_integrity.json') class << self def all_abilities(hash = {}) Hash[abilities.each_with_object(nil).to_a].merge(hash) end def abilities_model @abilities_model ||= JSON.load(File.open(ABILITIES)) end def integrity_model @integrity_model ||= JSON.load(File.open(INTEGRITY)) end def abilities @abilities ||= abilities_model.keys end end def initialize(enviro: nil, policy_groups: nil) fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment) @enviro = enviro @policy_groups = policy_groups end # @return [Array] all the abilities def keys self.class.abilities end def valid?(preset) validate(perset).length == 0 end def validate(preset) [].tap do |errors| if err = preset_errors(preset) errors << "{ '#{key}' preset -> #{err}}" end if err = preset_integrity(preset) errors << "{ '#{key}' preset -> #{err}}" end end end private def compile(*presets) presets.compact.reduce({}) do |p1, p2| merge(p1, p2) end end def merge(preset1, preset2) keys = preset1.keys | preset2.keys 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 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 def preset_integrity(preset) preset.each_with_object([]) do |(ability, value), errors| next unless checks = integrity_model[ability] suberrors = [] 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 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 def integrity_model self.class.integrity_model end 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) || -1 end def abilities_model self.class.abilities_model end 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 fatal(msg) raise msg if !@enviro @enviro.logger.fatal(msg) raise msg end def warn(msg) raise msg if !@enviro @enviro.logger.warn(msg) end end end end end