internetee/registry

View on GitHub
app/controllers/repp/v1/contacts_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
93%
require 'serializers/repp/contact'
module Repp
  module V1
    class ContactsController < BaseController # rubocop:disable Metrics/ClassLength
      before_action :find_contact, only: %i[show update destroy]
      skip_around_action :log_request, only: :search

      THROTTLED_ACTIONS = %i[index check search create show update destroy].freeze
      include Shunter::Integration::Throttle

      api :get, '/repp/v1/contacts'
      desc 'Get all existing contacts'
      def index
        authorize! :check, Epp::Contact
        records = current_user.registrar.contacts

        q = records.ransack(PartialSearchFormatter.format(search_params))
        q.sorts = 'created_at desc' if q.sorts.empty?
        contacts = q.result(distinct: true)

        limited_contacts = contacts.limit(limit).offset(offset)
                                   .includes(:domain_contacts, :registrant_domains, :registrar)

        render_success(data: { contacts: serialized_contacts(limited_contacts),
                               count: contacts.count, statuses: Contact::STATUSES,
                               ident_types: Contact::Ident.types })
      end

      # rubocop:disable Metrics/MethodLength
      api :get, '/repp/v1/contacts/search(/:id)'
      desc 'Search all existing contacts by optional id or query param'
      def search
        scope = current_user.registrar.contacts
        if params[:query]
          escaped_str = ActiveRecord::Base.connection.quote_string params[:query]
          scope = scope.where("name ilike '%#{escaped_str}%' OR code ilike '%#{escaped_str}%'
                               OR ident ilike '%#{escaped_str}%'")
        elsif params[:id]
          scope = scope.where(code: params[:id])
        end

        render_success(data: scope.limit(10)
                                  .map do |c|
                                    { value: c.code,
                                      label: "#{c.code} #{c.name}",
                                      selected: scope.size == 1 }
                                  end)
      end
      # rubocop:enable Metrics/MethodLength

      api :get, '/repp/v1/contacts/:contact_code'
      desc 'Get a specific contact'
      def show
        authorize! :check, Epp::Contact

        simple = params[:simple] == 'true' || false
        serializer = Serializers::Repp::Contact.new(@contact,
                                                    show_address: Contact.address_processing?,
                                                    domain_params: domain_filter_params,
                                                    simplify: simple)

        render_success(data: { contact: serializer.to_json })
      end

      api :get, '/repp/v1/contacts/check/:contact_code'
      desc 'Check contact code availability'
      def check
        contact = Epp::Contact.find_by(code: params[:id])
        data = { contact: { code: params[:id], available: contact.nil? } }

        render_success(data: data)
      end

      api :POST, '/repp/v1/contacts'
      desc 'Create a new contact'
      def create
        authorize! :create, Epp::Contact
        @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false)
        action = Actions::ContactCreate.new(@contact, contact_params[:legal_document],
                                            contact_ident_params)

        unless action.call
          handle_errors(@contact)
          return
        end

        render_success(**create_update_success_body)
      end

      api :PUT, '/repp/v1/contacts/:contact_code'
      desc 'Update existing contact'
      def update
        authorize! :update, Epp::Contact
        action = Actions::ContactUpdate.new(@contact, contact_params_with_address(required: false),
                                            contact_params[:legal_document],
                                            contact_ident_params(required: false), current_user)

        unless action.call
          handle_errors(@contact)
          return
        end

        render_success(**create_update_success_body)
      end

      api :DELETE, '/repp/v1/contacts/:contact_code'
      desc 'Delete a specific contact'
      def destroy
        authorize! :delete, Epp::Contact
        action = Actions::ContactDelete.new(@contact, params[:legal_document])
        unless action.call
          handle_errors(@contact)
          return
        end

        render_success
      end

      private

      def index_params
        params.permit(:id, :limit, :offset, :details, :q, :simple,
                      :page, :per_page, :domain_filter,
                      domain_filter: [],
                      q: %i[s name_matches code_eq ident_matches ident_type_eq
                            email_matches country_code_eq types_contains_array
                            updated_at_gteq created_at_gteq created_at_lteq
                            statuses_contains_array] + [s: []])
      end

      def search_params
        index_params.fetch(:q, {}) || {}
      end

      def domain_filter_params
        filter_params = index_params.slice(:id, :page, :per_page, :domain_filter).to_h
        filter_params.merge!({ sort: hashify(index_params[:q].fetch(:s)) }) if index_params[:q]
        filter_params
      end

      def hashify(sort)
        return unless sort

        sort_hash = {}
        if sort.is_a?(Array)
          sort.each do |s|
            sort_hash.merge!(Hash[*s.split(' ')])
          end
        else
          sort_hash.merge!(Hash[*sort.split(' ')])
        end
        sort_hash
      end

      def limit
        index_params[:limit]
      end

      def offset
        index_params[:offset] || 0
      end

      def serialized_contacts(contacts)
        return contacts.map(&:code) unless index_params[:details] == 'true'

        address_processing = Contact.address_processing?
        contacts.map do |c|
          Serializers::Repp::Contact.new(c, show_address: address_processing).to_json
        end
      end

      def contact_addr_present?
        return false unless contact_addr_params

        contact_addr_params.keys.any?
      end

      def create_update_success_body
        { code: opt_addr? ? 1100 : nil,
          data: { contact: { code: @contact.code } },
          message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil }
      end

      def opt_addr?
        !Contact.address_processing? && contact_addr_present?
      end

      def find_contact
        code = params[:id]
        @contact = Epp::Contact.find_by!(code: code, registrar: current_user.registrar)
      end

      def contact_params_with_address(required: true)
        return contact_create_params(required: required) unless contact_addr_present?

        contact_create_params(required: required).merge(contact_addr_params)
      end

      def contact_create_params(required: true)
        create_params = %i[name email phone]
        contact_params.require(create_params) if required
        contact_params.slice(:id, *create_params)
      end

      def contact_ident_params(required: true)
        ident_params = %i[ident ident_type ident_country_code]
        contact_params.require(:ident).require(ident_params) if required
        contact_params[:ident].to_h
      end

      def contact_addr_params
        return contact_params[:addr] unless Contact.address_processing?

        addr_params = %i[country_code city street zip]
        contact_params.require(:addr).require(addr_params)
        contact_params[:addr]
      end

      def contact_params
        params.require(:contact).permit(:id, :name, :email, :phone, :legal_document,
                                        legal_document: %i[body type],
                                        ident: [%i[ident ident_type ident_country_code]],
                                        addr: [%i[country_code city street zip state]])
      end
    end
  end
end