diff --git a/app/api/repp/nameservers_v1.rb b/app/api/repp/nameservers_v1.rb index 10e9930f3..fe391b704 100644 --- a/app/api/repp/nameservers_v1.rb +++ b/app/api/repp/nameservers_v1.rb @@ -10,20 +10,27 @@ module Repp requires :id, type: String, allow_blank: false requires :attributes, type: Hash do requires :hostname, type: String, allow_blank: false + requires :ipv4, type: Array + requires :ipv6, type: Array end end end - current_user.registrar.nameservers.where(hostname: params[:data][:id]).each do |nameserver| - nameserver.hostname = params[:data][:attributes][:hostname] - nameserver.ipv4 = params[:data][:attributes][:ipv4] - nameserver.ipv6 = params[:data][:attributes][:ipv6] - nameserver.save! - end + old_nameserver = current_user.registrar.nameservers.find_by(hostname: params[:data][:id]) + error!({ errors: [{ title: "Hostname #{params[:data][:id]} does not exist" }] }, 404) unless old_nameserver - status 204 - body false - @response = {} + new_nameserver = old_nameserver.dup + new_nameserver.hostname = params[:data][:attributes][:hostname] + new_nameserver.ipv4 = params[:data][:attributes][:ipv4] + new_nameserver.ipv6 = params[:data][:attributes][:ipv6] + + error!({ errors: [{ title: 'Invalid params' }] }, 400) unless new_nameserver.valid? + + current_user.registrar.replace_nameserver(old_nameserver, new_nameserver) + + status 200 + @response = { data: { type: 'nameserver', + id: new_nameserver.hostname, attributes: params[:data][:attributes] } } end end end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 7646b2ceb..299813dbe 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -167,6 +167,16 @@ class Registrar < ActiveRecord::Base white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) end + def replace_nameserver(old_nameserver, new_nameserver) + transaction do + nameservers.where(hostname: old_nameserver.hostname).find_each do |nameserver| + nameserver.update!(hostname: new_nameserver.hostname, + ipv4: new_nameserver.ipv4, + ipv6: new_nameserver.ipv6) # Audit log is needed, therefore no raw SQL + end + end + end + private def set_defaults diff --git a/doc/repp/v1/nameservers.md b/doc/repp/v1/nameservers.md index 1f47dc312..9c3fc9561 100644 --- a/doc/repp/v1/nameservers.md +++ b/doc/repp/v1/nameservers.md @@ -1,36 +1,42 @@ # Nameservers -## PUT /repp/v1/nameservers -Replaces nameservers +## PATCH /repp/v1/nameservers +Replaces all name servers of current registrar domains. #### Request ``` -PUT /repp/v1/nameservers +PATCH /repp/v1/nameservers Accept: application/json Content-Type: application/json Authorization: Basic dGVzdDp0ZXN0dGVzdA== - { - "data":{ - "nameservers":[ - { - "hostname":"ns1.example.com", - "ipv4":"192.0.2.1" - "ipv6":"2001:DB8::1" - }, - { - "hostname":"ns2.example.com", - "ipv4":"192.0.2.1" - "ipv6":"2001:DB8::1" - } - ] - } + "data":{ + "type": "nameserver", + "id": "ns1.example.com", + "attributes": { + "hostname": "new-ns1.example.com", + "ipv4": ["192.0.2.1", "192.0.2.2"], + "ipv6": ["2001:db8::1", "2001:db8::2"] + }, + } } ``` #### Response on success ``` -HTTP/1.1 204 +HTTP/1.1 200 +Content-Type: application/json +{ + "data":{ + "type": "nameserver", + "id": "new-ns1.example.com", + "attributes": { + "hostname": "new-ns1.example.com", + "ipv4": ["192.0.2.1", "192.0.2.2"], + "ipv6": ["2001:db8::1", "2001:db8::2"] + }, + } +} ``` #### Response on failure @@ -38,13 +44,13 @@ HTTP/1.1 204 HTTP/1.1 400 Content-Type: application/json { - "errors":[ - { - "title":"ns1.example.com does not exist" - }, - { - "title":"192.0.2.1 is not a valid IPv4 address" - } - ] + "errors":[ + { + "title":"ns1.example.com does not exist" + }, + { + "title":"192.0.2.1 is not a valid IPv4 address" + } + ] } ``` diff --git a/test/integration/api/nameservers/patch_test.rb b/test/integration/api/nameservers/patch_test.rb new file mode 100644 index 000000000..1633cc9d1 --- /dev/null +++ b/test/integration/api/nameservers/patch_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +class APINameserversPatchTest < ActionDispatch::IntegrationTest + def test_replaces_current_registrar_nameservers + request_params = { format: :json, data: { type: 'nameserver', id: 'ns1.bestnames.test', + attributes: { hostname: 'ns55.bestnames.test', + ipv4: ['192.0.2.55'], + ipv6: ['2001:db8::55'] } } } + put '/repp/v1/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key } + + new_nameserver = registrars(:bestnames).nameservers.find_by(hostname: 'ns55.bestnames.test') + assert_nil registrars(:bestnames).nameservers.find_by(hostname: 'ns1.bestnames.test') + assert_equal ['192.0.2.55'], new_nameserver.ipv4 + assert_equal ['2001:DB8::55'], new_nameserver.ipv6 + assert_response 200 + assert_equal ({ data: { type: 'nameserver', + id: 'ns55.bestnames.test', + attributes: { hostname: 'ns55.bestnames.test', + ipv4: ['192.0.2.55'], + ipv6: ['2001:db8::55'] } } }), + JSON.parse(response.body, symbolize_names: true) + end + + def test_honors_optional_params + request_params = { format: :json, data: { type: 'nameserver', id: 'ns1.bestnames.test', + attributes: { hostname: 'ns55.bestnames.test' } } } + put '/repp/v1/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key } + assert_response 200 + end + + def test_non_existent_nameserver_hostname + request_params = { format: :json, data: { type: 'nameserver', id: 'non-existent.test', + attributes: { hostname: 'any.bestnames.test' } } } + put '/repp/v1/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key } + + assert_response 404 + assert_equal ({ errors: [{ title: 'Hostname non-existent.test does not exist' }] }), + JSON.parse(response.body, symbolize_names: true) + end + + def test_invalid_request_params + request_params = { format: :json, data: { type: 'nameserver', id: 'ns1.bestnames.test', + attributes: { hostname: '' } } } + put '/repp/v1/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key } + + assert_response 400 + assert_equal ({ errors: [{ title: 'Invalid params' }] }), + JSON.parse(response.body, symbolize_names: true) + end + + def test_unauthenticated + put '/repp/v1/nameservers' + assert_response 401 + end + + private + + def http_auth_key + ActionController::HttpAuthentication::Basic.encode_credentials('test_bestnames', 'testtest') + end +end diff --git a/test/integration/api/nameservers/put_nameservers_test.rb b/test/integration/api/nameservers/put_nameservers_test.rb deleted file mode 100644 index e21eae1cc..000000000 --- a/test/integration/api/nameservers/put_nameservers_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'test_helper' - -class APIPutNameserversTest < ActionDispatch::IntegrationTest - def test_changes_nameservers_of_all_domains_of_current_registrar - ns2 = domains(:shop).nameservers.find_by(hostname: 'ns2.bestnames.test') - request_params = { format: :json, data: { type: 'nameservers', id: 'ns2.bestnames.test', - attributes: { hostname: 'ns3.bestnames.test', - ipv4: ['192.0.2.3'], - ipv6: ['2001:DB8::3'] } } } - put '/repp/v1/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key } - ns2.reload - assert_equal 'ns3.bestnames.test', ns2.hostname - assert_equal ['192.0.2.3'], ns2.ipv4 - assert_equal ['2001:DB8::3'], ns2.ipv6 - assert_response 204 - assert_empty response.body - end - - def test_unauthenticated - put '/repp/v1/nameservers' - assert_response 401 - end - - private - - def http_auth_key - ActionController::HttpAuthentication::Basic.encode_credentials('test_bestnames', 'testtest') - end -end