diff --git a/app/controllers/epp/commands_controller.rb b/app/controllers/epp/commands_controller.rb index 310520317..ccaad7f32 100644 --- a/app/controllers/epp/commands_controller.rb +++ b/app/controllers/epp/commands_controller.rb @@ -2,6 +2,7 @@ class Epp::CommandsController < ApplicationController include Epp::Common include Epp::DomainsHelper include Epp::ContactsHelper + include Epp::PollHelper include Shared::UserStamper helper WhodunnitHelper @@ -9,14 +10,6 @@ class Epp::CommandsController < ApplicationController private - def poll - @message = current_epp_user.registrar.messages.last - if @message.attached_obj_type && @message.attached_obj_id - @object = Object.const_get(@message.attached_obj_type).find(@message.attached_obj_id) - end - render 'epp/poll' - end - def create send("create_#{OBJECT_TYPES[params_hash['epp']['xmlns:ns2']]}") end diff --git a/app/helpers/epp/poll_helper.rb b/app/helpers/epp/poll_helper.rb new file mode 100644 index 000000000..354455219 --- /dev/null +++ b/app/helpers/epp/poll_helper.rb @@ -0,0 +1,41 @@ +module Epp::PollHelper + def poll + req_poll if parsed_frame.css('poll').first['op'] == 'req' + ack_poll if parsed_frame.css('poll').first['op'] == 'ack' + end + + def req_poll + @message = current_epp_user.queued_messages.last + render 'epp/poll/poll_no_messages' and return unless @message + + if @message.attached_obj_type && @message.attached_obj_id + @object = Object.const_get(@message.attached_obj_type).find(@message.attached_obj_id) + end + render 'epp/poll/poll_req' + end + + def ack_poll + @message = current_epp_user.queued_messages.find_by(id: parsed_frame.css('poll').first['msgID']) + + unless @message + epp_errors << { + code: '2303', + msg: I18n.t('message_was_not_found'), + value: { obj: 'msgID', val: parsed_frame.css('poll').first['msgID'] } + } + handle_errors and return + end + + handle_errors(@message) and return unless @message.dequeue + render 'epp/poll/poll_ack' + end + + private + + def validate__poll_request + op = parsed_frame.css('poll').first[:op] + return true if %w(ack req).include?(op) + epp_errors << { code: '2306', msg: I18n.t('errors.messages.attribute_op_is_invalid') } + false + end +end diff --git a/app/models/message.rb b/app/models/message.rb index 2bc5fce09..9813479e4 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -4,4 +4,9 @@ class Message < ActiveRecord::Base before_create -> { self.queued = true } scope :queued, -> { where(queued: true) } + + def dequeue + self.queued = false + save + end end diff --git a/app/views/epp/poll/poll_ack.xml.builder b/app/views/epp/poll/poll_ack.xml.builder new file mode 100644 index 000000000..07a09cda1 --- /dev/null +++ b/app/views/epp/poll/poll_ack.xml.builder @@ -0,0 +1,11 @@ +xml.epp_head do + xml.response do + xml.result('code' => '1000') do + xml.msg 'Command completed successfully' + end + + xml.tag!('msgQ', 'count' => current_epp_user.queued_messages.count, 'id' => @message.id) + + xml << render('/epp/shared/trID') + end +end diff --git a/app/views/epp/poll/poll_no_messages.xml.builder b/app/views/epp/poll/poll_no_messages.xml.builder new file mode 100644 index 000000000..1e57a1216 --- /dev/null +++ b/app/views/epp/poll/poll_no_messages.xml.builder @@ -0,0 +1,9 @@ +xml.epp_head do + xml.response do + xml.result('code' => '1300') do + xml.msg 'Command completed successfully; no messages' + end + + xml << render('/epp/shared/trID') + end +end diff --git a/app/views/epp/poll.xml.builder b/app/views/epp/poll/poll_req.xml.builder similarity index 100% rename from app/views/epp/poll.xml.builder rename to app/views/epp/poll/poll_req.xml.builder diff --git a/config/locales/en.yml b/config/locales/en.yml index b4b3e529f..da8dc2c87 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -421,3 +421,4 @@ en: registrar: 'Registrar' transfer_requested: 'Transfer requested.' + message_was_not_found: 'Message was not found' diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index e75ac4afc..e96260737 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -103,10 +103,23 @@ describe 'EPP Domain', epp: true do # should show up in other registrar's poll - response = epp_request(EppXml::Session.poll, :xml, :elkdata) + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') msg_q = response[:parsed].css('msgQ') expect(msg_q.css('qDate').text).to_not be_blank expect(msg_q.css('msg').text).to eq('Transfer requested.') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('1') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') end it 'prohibits wrong registrar from approving transfer' do diff --git a/spec/epp/poll_spec.rb b/spec/epp/poll_spec.rb new file mode 100644 index 000000000..030b9b022 --- /dev/null +++ b/spec/epp/poll_spec.rb @@ -0,0 +1,134 @@ +require 'rails_helper' + +describe 'EPP Poll', epp: true do + let(:server_zone) { Epp::Server.new({ server: 'localhost', tag: 'zone', password: 'ghyt9e4fu', port: 701 }) } + let(:server_elkdata) { Epp::Server.new({ server: 'localhost', tag: 'elkdata', password: 'ghyt9e4fu', port: 701 }) } + let(:elkdata) { Fabricate(:registrar, { name: 'Elkdata', reg_no: '123' }) } + let(:zone) { Fabricate(:registrar) } + + before(:each) { create_settings } + + context 'with valid user' do + before(:each) do + Fabricate(:epp_user, username: 'zone', registrar: zone) + Fabricate(:epp_user, username: 'elkdata', registrar: elkdata) + end + + it 'returns no messages in poll' do + response = epp_request(EppXml::Session.poll, :xml) + + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + end + + it 'queues and dequeues messages' do + msg = zone.messages.create({ body: 'Balance low.' }) + + response = epp_request(EppXml::Session.poll, :xml, :elkdata) + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Balance low.') + expect(msg_q.first['count']).to eq('1') + expect(msg_q.first['id']).to eq(msg.id.to_s) + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:results][0][:msg]).to eq('Message was not found') + expect(response[:results][0][:result_code]).to eq('2303') + expect(response[:results][0][:value]).to eq(msg_q.first['id']) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') + + response = epp_request(xml, :xml, :zone) + expect(response[:results][0][:msg]).to eq('Message was not found') + expect(response[:results][0][:result_code]).to eq('2303') + expect(response[:results][0][:value]).to eq(msg_q.first['id']) + end + + it 'returns an error on incorrect op' do + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'bla' } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Attribute op is invalid') + end + + it 'dequeues multiple messages' do + zone.messages.create({ body: 'Balance low.' }) + zone.messages.create({ body: 'Something.' }) + zone.messages.create({ body: 'Smth else.' }) + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Smth else.') + expect(msg_q.first['count']).to eq('3') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('2') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Something.') + expect(msg_q.first['count']).to eq('2') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('1') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Balance low.') + expect(msg_q.first['count']).to eq('1') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + end + end +end