internetee/registry

View on GitHub
app/models/concerns/epp_errors.rb

Summary

Maintainability
A
2 hrs
Test Coverage
A
100%
module EppErrors
  extend ActiveSupport::Concern
  included do
    attr_accessor :epp_errors
  end

  def construct_epp_errors
    epp_errors = ActiveModel::Errors.new(self)
    errors.each do |error|
      attr = error.attribute.to_s.split('.')[0].to_sym
      next if attr == :epp_errors

      if self.class.reflect_on_association(attr)
        collect_child_errors(attr).each do |child_error|
          epp_errors.import child_error
        end
      end

      if self.class.reflect_on_aggregation(attr)
        aggregation = send(attr)
        collect_aggregation_errors(aggregation).each do |aggregation_error|
          epp_errors.import aggregation_error
        end
        next
      end
      collect_parent_errors(attr, error.message).each do |parent_error|
        epp_errors.import parent_error
      end
    end
    epp_errors.each { |epp_error| errors.import epp_error }
    errors
  end

  def collect_parent_errors(attr, errors)
    errors = [errors] if errors.is_a?(String)

    epp_errors = ActiveModel::Errors.new(self)
    errors.each do |err|
      code, value = find_epp_code_and_value(err)
      next unless code
      msg = attr.to_sym == :base ? err : "#{err} [#{attr}]"
      epp_errors.add(attr, code: code, msg: msg, value: value)
    end
    epp_errors
  end

  def collect_child_errors(attr)
    macro = self.class.reflect_on_association(attr).macro
    multi = [:has_and_belongs_to_many, :has_many]

    epp_errors = ActiveModel::Errors.new(self)

    if multi.include?(macro)
      send(attr).each do |x|
        x.errors.each do |error|
          x.collect_parent_errors(error.attribute, error.message).each do |parent_error|
            epp_errors.import parent_error
          end
        end
      end
    end

    epp_errors
  end

  def collect_aggregation_errors(aggregation)
    epp_errors = ActiveModel::Errors.new(self)

    aggregation.errors.details.each do |attr, error_details|
      error_details.each do |error_detail|
        aggregation.class.epp_code_map.each do |epp_code, attr_to_error|
          epp_code_found = attr_to_error.any? { |i| i == [attr, error_detail[:error]] }

          next unless epp_code_found

          message = aggregation.errors.generate_message(attr, error_detail[:error], error_detail)
          message = aggregation.errors.full_message(attr, message)

          if attr != :base
            message = "#{aggregation.model_name.human} #{message.camelize(:lower)}"
          end

          epp_errors.add(attr, code: epp_code, msg: message)
        end
      end
    end

    epp_errors
  end

  def find_epp_code_and_value(msg)
    epp_code_map.each do |code, values|
      values.each do |x|
        msg_args, value = construct_msg_args_and_value(x)
        t = errors.generate_message(*msg_args)
        return [code, value] if t == msg
      end
    end
    nil
  rescue NameError
    nil
  end

  def construct_msg_args_and_value(epp_error_args)
    args = {}
    args = epp_error_args.delete_at(-1) if epp_error_args.last.is_a?(Hash)
    msg_args = epp_error_args

    value = args.delete(:value) if args.key?(:value)

    interpolation = args[:interpolation] || args

    msg_args << interpolation

    [msg_args, value]
  end

  def add_epp_error(code, obj, val, msg)
    t = errors.generate_message(*msg) if msg.is_a?(Array)
    t = msg if msg.is_a?(String)
    err = { code: code, msg: t }
    val = check_for_status(code, obj, val)
    err[:value] = { val: val, obj: obj } if val.present?
    errors.add(:epp_errors, **err)
  end

  def check_for_status(code, obj, val)
    if code == '2304' && val.present? && val == DomainStatus::SERVER_DELETE_PROHIBITED &&
       obj == 'status'
      DomainStatus::PENDING_UPDATE
    else
      val
    end
  end
end