Add first version of admin bulk change

This commit is contained in:
Alex Sherman 2021-02-01 15:35:44 +05:00
parent efdb445488
commit f3f89bedd7
5 changed files with 96 additions and 28 deletions

View file

@ -0,0 +1,40 @@
module Repp
module V1
module Domains
class AdminContactsController < BaseController
before_action :set_current_contact, only: [:update]
before_action :set_new_contact, only: [:update]
def set_current_contact
@current_contact = current_user.registrar.contacts.find_by!(code: contact_params[:current_contact_id])
end
def set_new_contact
@new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id])
end
def update
@epp_errors ||= []
@epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid?
unless @new_contact.identical_to?(@current_contact)
@epp_errors << { code: 2304, msg: 'Admin contacts must be identical' }
end
return handle_errors if @epp_errors.any?
affected, skipped = AdminDomainContact.replace(@current_contact, @new_contact)
@response = { affected_domains: affected, skipped_domains: skipped }
render_success(data: @response)
end
private
def contact_params
params.require(%i[current_contact_id new_contact_id])
params.permit(:current_contact_id, :new_contact_id)
end
end
end
end
end

View file

@ -1,2 +1,23 @@
class AdminDomainContact < DomainContact class AdminDomainContact < DomainContact
def self.replace(current_contact, new_contact)
affected_domains = []
skipped_domains = []
admin_contacts = where(contact: current_contact)
admin_contacts.each do |admin_contact|
if admin_contact.domain.discarded?
skipped_domains << admin_contact.domain.name
next
end
begin
admin_contact.contact = new_contact
admin_contact.save!
affected_domains << admin_contact.domain.name
rescue ActiveRecord::RecordNotUnique
skipped_domains << admin_contact.domain.name
end
end
[affected_domains.sort, skipped_domains.sort]
end
end end

View file

@ -20,6 +20,12 @@ module Concerns::Contact::Identical
.where.not(id: id).take .where.not(id: id).take
end end
def identical_to?(contact)
IDENTIFIABLE_ATTRIBUTES.all? do |attribute|
self.attributes[attribute] == contact.attributes[attribute]
end
end
private private
def identifiable_hash def identifiable_hash

View file

@ -64,6 +64,7 @@ Rails.application.routes.draw do
get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ }
post 'transfer', to: 'domains#transfer' post 'transfer', to: 'domains#transfer'
patch 'contacts', to: 'domains/contacts#update' patch 'contacts', to: 'domains/contacts#update'
patch 'admin_contacts', to: 'domains/admin_contacts#update'
post 'renew/bulk', to: 'domains/renews#bulk_renew' post 'renew/bulk', to: 'domains/renews#bulk_renew'
end end
end end

View file

@ -5,48 +5,48 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
@admin_current = domains(:shop).admin_contacts.find_by(code: 'jane-001') @admin_current = domains(:shop).admin_contacts.find_by(code: 'jane-001')
@admin_new = contacts(:william) @admin_new = contacts(:william)
@admin_new.update(ident: @admin_current.ident, @admin_new.update(ident: @admin_current.ident,
ident_type: @admin_current.ident_type, ident_type: @admin_current.ident_type,
ident_country_code: @admin_current.ident_country_code) ident_country_code: @admin_current.ident_country_code)
end end
def test_replace_all_admin_contacts_when_ident_data_doesnt_match def test_replace_all_admin_contacts_when_ident_data_doesnt_match
@admin_new.update(ident: '777' , @admin_new.update(ident: '777' ,
ident_type: 'priv', ident_type: 'priv',
ident_country_code: 'LV') ident_country_code: 'LV')
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :bad_request assert_response :bad_request
assert_equal ({ code: 2304, message: 'New admin contact must have same ident' }), assert_equal ({ code: 2304, message: 'Admin contacts must be identical', data: {} }),
JSON.parse(response.body, symbolize_names: true) JSON.parse(response.body, symbolize_names: true)
end end
def test_replace_all_admin_contacts_of_the_current_registrar def test_replace_all_admin_contacts_of_the_current_registrar
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_nil domains(:shop).admin_contacts.find_by(code: @admin_current) assert_nil domains(:shop).admin_contacts.find_by(code: @admin_current.code)
assert domains(:shop).admin_contacts.find_by(code: @admin_new) assert domains(:shop).admin_contacts.find_by(code: @admin_new.code)
assert domains(:airport).admin_contacts.find_by(code: @admin_new) assert domains(:airport).admin_contacts.find_by(code: @admin_new.code)
end end
def test_skip_discarded_domains def test_skip_discarded_domains
domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE]) domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert domains(:shop).admin_contacts.find_by(code: @admin_current) assert domains(:shop).admin_contacts.find_by(code: @admin_current.code)
end end
def test_return_affected_domains_in_alphabetical_order def test_return_affected_domains_in_alphabetical_order
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :ok assert_response :ok
@ -59,8 +59,8 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
domains(:shop).update!(statuses: [DomainStatus::DELETE_CANDIDATE]) domains(:shop).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE]) domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :ok assert_response :ok
@ -69,23 +69,23 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
end end
def test_keep_other_admin_contacts_intact def test_keep_other_admin_contacts_intact
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert domains(:airport).admin_contacts.find_by(code: 'john-001') assert domains(:airport).admin_contacts.find_by(code: 'john-001')
end end
def test_keep_tech_contacts_intact def test_keep_tech_contacts_intact
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_new }, new_contact_id: @admin_new.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert domains(:airport).tech_contacts.find_by(code: 'william-001') assert domains(:airport).tech_contacts.find_by(code: 'william-001')
end end
def test_restrict_contacts_to_the_current_registrar def test_restrict_contacts_to_the_current_registrar
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: 'william-002' }, new_contact_id: 'william-002' },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
@ -96,7 +96,7 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
def test_non_existent_current_contact def test_non_existent_current_contact
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: 'non-existent', patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: 'non-existent',
new_contact_id: @admin_new}, new_contact_id: @admin_new.code},
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :not_found assert_response :not_found
assert_equal ({ code: 2303, message: 'Object does not exist' }), assert_equal ({ code: 2303, message: 'Object does not exist' }),
@ -104,7 +104,7 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
end end
def test_non_existent_new_contact def test_non_existent_new_contact
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: 'non-existent' }, new_contact_id: 'non-existent' },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :not_found assert_response :not_found
@ -113,7 +113,7 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
end end
def test_disallow_invalid_new_contact def test_disallow_invalid_new_contact
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: 'invalid' }, new_contact_id: 'invalid' },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :bad_request assert_response :bad_request
@ -122,8 +122,8 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest
end end
def test_disallow_self_replacement def test_disallow_self_replacement
patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current, patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
new_contact_id: @admin_current }, new_contact_id: @admin_current.code },
headers: { 'HTTP_AUTHORIZATION' => http_auth_key } headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response :bad_request assert_response :bad_request
assert_equal ({ code: 2304, message: 'New contact must be different from current', data: {} }), assert_equal ({ code: 2304, message: 'New contact must be different from current', data: {} }),