# frozen_string_literal: true require 'active_support/hash_with_indifferent_access' module RSpec module Rails module Api # Helper methods class Utils def self.deep_get(hash, path) path.split('.').inject(hash) do |sub_hash, key| return nil unless sub_hash.is_a?(Hash) && sub_hash.key?(key.to_sym) sub_hash[key.to_sym] # rubocop:disable Lint/UnmodifiedReduceAccumulator end end def self.deep_set(hash, path, value) path = path.split('.') unless path.is_a? Array return value if path.count.zero? current_key = path.shift.to_sym hash[current_key] = {} unless hash[current_key].is_a?(Hash) hash[current_key] = deep_set(hash[current_key], path, value) hash end def self.check_value_type(type, value) return true if type == :boolean && (value.is_a?(TrueClass) || value.is_a?(FalseClass)) return true if type == :array && value.is_a?(Array) raise "Unknown type #{type}" unless PARAM_TYPES.key? type value.is_a? PARAM_TYPES[type][:class] end def self.validate_object_structure(actual, expected) # Check keys return false unless same_keys? actual, expected expected.each_key do |key| next unless expected[key][:required] expected_type = expected[key][:type] expected_attributes = expected[key][:attributes] # Type return false unless check_value_type expected_type, actual[key.to_s] # Deep object ? return false unless validate_deep_object expected_type, expected_attributes, actual[key.to_s] end true end # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength def self.validate_deep_object(expected_type, expected_attributes, actual) if %i[object array].include?(expected_type) && expected_attributes.is_a?(Hash) case expected_type when :object return false unless validate_object_structure actual, expected_attributes when :array actual.each do |array_entry| return false unless validate_object_structure array_entry, expected_attributes end end end true end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength def self.same_keys?(actual, expected) optional = expected.reject { |_key, value| value[:required] }.keys actual.symbolize_keys.keys.sort - optional == expected.keys.sort - optional end def self.check_attribute_type(type, except: []) keys = PARAM_TYPES.keys.reject { |key| except.include? key } keys.include?(type) end end end end end