From e8fa79304f7e1b34846d74989deac1d753b6bbf7 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 15:23:23 +0500 Subject: [PATCH 1/5] Move DomainCron#clean_expired_pendings to interactor --- .../domains/expired_pendings/base.rb | 10 ++++ .../domains/expired_pendings/clean_all.rb | 35 +++++++++++ .../domains/expired_pendings/process_clean.rb | 60 +++++++++++++++++++ app/models/domain_cron.rb | 32 +--------- test/models/domain_cron_test.rb | 8 ++- 5 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 app/interactions/domains/expired_pendings/base.rb create mode 100644 app/interactions/domains/expired_pendings/clean_all.rb create mode 100644 app/interactions/domains/expired_pendings/process_clean.rb diff --git a/app/interactions/domains/expired_pendings/base.rb b/app/interactions/domains/expired_pendings/base.rb new file mode 100644 index 000000000..7faa32050 --- /dev/null +++ b/app/interactions/domains/expired_pendings/base.rb @@ -0,0 +1,10 @@ +module Domains + module ExpiredPendings + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/expired_pendings/clean_all.rb b/app/interactions/domains/expired_pendings/clean_all.rb new file mode 100644 index 000000000..1dbab266c --- /dev/null +++ b/app/interactions/domains/expired_pendings/clean_all.rb @@ -0,0 +1,35 @@ +module Domains + module ExpiredPendings + class CleanAll < Base + def execute + to_stdout('Clean expired domain pendings') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + count = 0 + expired_pending_domains.each do |domain| + log_error(domain) && next unless need_to_be_cleared?(domain) + count += 1 + Domains::ExpiredPendings::ProcessClean.run(domain: domain) + end + to_stdout("Successfully cancelled #{count} domain pendings") + end + + private + + def need_to_be_cleared?(domain) + domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation? + end + + def log_error(domain) + to_stdout("ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, "\ + 'but no pendingDelete/pendingUpdate state present!') + end + + def expired_pending_domains + expire_at = Setting.expire_pending_confirmation.hours.ago + Domain.where('registrant_verification_asked_at <= ?', expire_at) + end + end + end +end diff --git a/app/interactions/domains/expired_pendings/process_clean.rb b/app/interactions/domains/expired_pendings/process_clean.rb new file mode 100644 index 000000000..c6277c3c5 --- /dev/null +++ b/app/interactions/domains/expired_pendings/process_clean.rb @@ -0,0 +1,60 @@ +module Domains + module ExpiredPendings + class ProcessClean < Base + object :domain, + class: Domain + + def execute + check_notify + clean_pendings + + to_stdout("DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})") + UpdateWhoisRecordJob.enqueue domain.name, 'domain' + end + + private + + def notify_pending_update + RegistrantChangeMailer.expired(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_later + end + + def notify_pending_delete + DomainDeleteMailer.expired(domain).deliver_later + end + + def clean_pendings + clean_verification_data + domain.pending_json = {} + clean_statuses + domain.save + end + + def statuses_to_clean + [DomainStatus::PENDING_DELETE_CONFIRMATION, + DomainStatus::PENDING_UPDATE, + DomainStatus::PENDING_DELETE] + end + + def clean_statuses + domain.statuses = domain.statuses - statuses_to_clean + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end + + def clean_verification_data + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def check_notify + notify_pending_update if domain.pending_update? + + return unless domain.pending_delete? || domain.pending_delete_confirmation? + + notify_pending_delete + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index d09141d52..77de7062a 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -1,36 +1,6 @@ class DomainCron def self.clean_expired_pendings - STDOUT << "#{Time.zone.now.utc} - Clean expired domain pendings\n" unless Rails.env.test? - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - expire_at = Setting.expire_pending_confirmation.hours.ago - count = 0 - expired_pending_domains = Domain.where('registrant_verification_asked_at <= ?', expire_at) - expired_pending_domains.each do |domain| - unless domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation? - msg = "#{Time.zone.now.utc} - ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, " \ - "but no pendingDelete/pendingUpdate state present!\n" - STDOUT << msg unless Rails.env.test? - next - end - count += 1 - if domain.pending_update? - RegistrantChangeExpiredEmailJob.enqueue(domain.id) - end - if domain.pending_delete? || domain.pending_delete_confirmation? - DomainDeleteMailer.expired(domain).deliver_now - end - - domain.preclean_pendings - domain.clean_pendings! - - unless Rails.env.test? - STDOUT << "#{Time.zone.now.utc} DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})\n" - end - UpdateWhoisRecordJob.enqueue domain.name, 'domain' - end - STDOUT << "#{Time.zone.now.utc} - Successfully cancelled #{count} domain pendings\n" unless Rails.env.test? - count + Domains::ExpiredPendings::CleanAll.run! end def self.start_expire_period diff --git a/test/models/domain_cron_test.rb b/test/models/domain_cron_test.rb index 0224b1a61..c417df04f 100644 --- a/test/models/domain_cron_test.rb +++ b/test/models/domain_cron_test.rb @@ -19,7 +19,9 @@ class DomainCronTest < ActiveSupport::TestCase registrant_verification_token: 'test', statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - DomainCron.clean_expired_pendings + perform_enqueued_jobs do + DomainCron.clean_expired_pendings + end assert_emails 1 end @@ -84,7 +86,9 @@ class DomainCronTest < ActiveSupport::TestCase assert @domain.pending_update? @domain.reload - DomainCron.clean_expired_pendings + perform_enqueued_jobs do + DomainCron.clean_expired_pendings + end @domain.reload assert_not @domain.pending_update? From 849010b118addcba6057caef1d2b7685e2568fef Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 16:44:31 +0500 Subject: [PATCH 2/5] Move #process_expired to interactor --- .../domains/expire_period/base.rb | 10 +++++++ .../domains/expire_period/process_expired.rb | 28 +++++++++++++++++++ .../domains/expire_period/start.rb | 19 +++++++++++++ app/models/domain.rb | 6 ---- app/models/domain_cron.rb | 22 +-------------- 5 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 app/interactions/domains/expire_period/base.rb create mode 100644 app/interactions/domains/expire_period/process_expired.rb create mode 100644 app/interactions/domains/expire_period/start.rb diff --git a/app/interactions/domains/expire_period/base.rb b/app/interactions/domains/expire_period/base.rb new file mode 100644 index 000000000..eab8171bf --- /dev/null +++ b/app/interactions/domains/expire_period/base.rb @@ -0,0 +1,10 @@ +module Domains + module ExpirePeriod + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/expire_period/process_expired.rb b/app/interactions/domains/expire_period/process_expired.rb new file mode 100644 index 000000000..595de1228 --- /dev/null +++ b/app/interactions/domains/expire_period/process_expired.rb @@ -0,0 +1,28 @@ +module Domains + module ExpirePeriod + class ProcessExpired < Base + object :domain, + class: Domain, + description: 'Domain to set expiration' + + def execute + set_graceful_expired + to_stdout("start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}") + + saved = domain.save(validate: false) + + DomainExpireEmailJob.enqueue(domain.id, run_at: send_time) if saved + end + + def set_graceful_expired + domain.outzone_at = domain.expire_time + Domain.expire_warning_period + domain.delete_date = domain.outzone_at + Domain.redemption_grace_period + domain.statuses |= [DomainStatus::EXPIRED] + end + + def send_time + domain.valid_to + Setting.expiration_reminder_mail.to_i.days + end + end + end +end diff --git a/app/interactions/domains/expire_period/start.rb b/app/interactions/domains/expire_period/start.rb new file mode 100644 index 000000000..1ed2342f1 --- /dev/null +++ b/app/interactions/domains/expire_period/start.rb @@ -0,0 +1,19 @@ +module Domains + module ExpirePeriod + class Start < Base + def execute + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + count = 0 + + Domain.expired.each do |domain| + next unless domain.expirable? + + count += 1 + Domains::ExpirePeriod::ProcessExpired.run(domain: domain) + end + + to_stdout("Successfully expired #{count}") + end + end + end +end diff --git a/app/models/domain.rb b/app/models/domain.rb index dc7d86da8..33d2dcd58 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -482,12 +482,6 @@ class Domain < ApplicationRecord Registrant.find_by(id: pending_json['new_registrant_id']) end - def set_graceful_expired - self.outzone_at = expire_time + self.class.expire_warning_period - self.delete_date = outzone_at + self.class.redemption_grace_period - self.statuses |= [DomainStatus::EXPIRED] - end - def pending_update? statuses.include?(DomainStatus::PENDING_UPDATE) end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index 77de7062a..cd063f901 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -4,27 +4,7 @@ class DomainCron end def self.start_expire_period - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - domains = Domain.expired - marked = 0 - real = 0 - - domains.each do |domain| - next unless domain.expirable? - real += 1 - domain.set_graceful_expired - STDOUT << "#{Time.zone.now.utc} DomainCron.start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test? - - send_time = domain.valid_to + Setting.expiration_reminder_mail.to_i.days - saved = domain.save(validate: false) - - if saved - DomainExpireEmailJob.enqueue(domain.id, run_at: send_time) - marked += 1 - end - end - - STDOUT << "#{Time.zone.now.utc} - Successfully expired #{marked} of #{real} domains\n" unless Rails.env.test? + Domains::ExpirePeriod::Start.run! end def self.start_redemption_grace_period From d17e26c28b81e7ea9dc75d1fecd19ed14df1f623 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 17:04:19 +0500 Subject: [PATCH 3/5] Move #redemption_grace_period to interactor --- .../domains/redemption_grace_period/base.rb | 10 ++++++++++ .../process_grace_period.rb | 20 +++++++++++++++++++ .../domains/redemption_grace_period/start.rb | 20 +++++++++++++++++++ app/models/domain_cron.rb | 19 +----------------- 4 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 app/interactions/domains/redemption_grace_period/base.rb create mode 100644 app/interactions/domains/redemption_grace_period/process_grace_period.rb create mode 100644 app/interactions/domains/redemption_grace_period/start.rb diff --git a/app/interactions/domains/redemption_grace_period/base.rb b/app/interactions/domains/redemption_grace_period/base.rb new file mode 100644 index 000000000..5d1ede289 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/base.rb @@ -0,0 +1,10 @@ +module Domains + module RedemptionGracePeriod + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/redemption_grace_period/process_grace_period.rb b/app/interactions/domains/redemption_grace_period/process_grace_period.rb new file mode 100644 index 000000000..0f120a996 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/process_grace_period.rb @@ -0,0 +1,20 @@ +module Domains + module RedemptionGracePeriod + class ProcessGracePeriod < Base + object :domain, + class: Domain + + def execute + domain.statuses << DomainStatus::SERVER_HOLD + to_stdout(process_msg) + domain.save(validate: false) + end + + private + + def process_msg + "start_redemption_grace_period: #{domain.id} (#{domain.name}) #{domain.changes}" + end + end + end +end diff --git a/app/interactions/domains/redemption_grace_period/start.rb b/app/interactions/domains/redemption_grace_period/start.rb new file mode 100644 index 000000000..ef7f42e58 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/start.rb @@ -0,0 +1,20 @@ +module Domains + module RedemptionGracePeriod + class Start < Base + def execute + to_stdout('Setting server_hold to domains') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + count = 0 + + Domain.outzone_candidates.each do |domain| + next unless domain.server_holdable? + + count += 1 + Domains::RedemptionGracePeriod::ProcessGracePeriod.run(domain: domain) + end + to_stdout("Successfully set server_hold to #{count} of domains") + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index cd063f901..e936c29e6 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -8,24 +8,7 @@ class DomainCron end def self.start_redemption_grace_period - STDOUT << "#{Time.zone.now.utc} - Setting server_hold to domains\n" unless Rails.env.test? - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - - domains = Domain.outzone_candidates - marked = 0 - real = 0 - - domains.each do |domain| - next unless domain.server_holdable? - real += 1 - domain.statuses << DomainStatus::SERVER_HOLD - STDOUT << "#{Time.zone.now.utc} DomainCron.start_redemption_grace_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test? - domain.save(validate: false) and marked += 1 - end - - STDOUT << "#{Time.zone.now.utc} - Successfully set server_hold to #{marked} of #{real} domains\n" unless Rails.env.test? - marked + Domains::RedemptionGracePeriod::Start.run! end def self.start_client_hold From 167b37c61fb4aefae70cbf7e3c1c281322f094ba Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 2 Dec 2020 14:31:37 +0500 Subject: [PATCH 4/5] Add tests for DOmainCrone#expire_period --- test/interactions/expire_period/start_test.rb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/interactions/expire_period/start_test.rb diff --git a/test/interactions/expire_period/start_test.rb b/test/interactions/expire_period/start_test.rb new file mode 100644 index 000000000..168b255bb --- /dev/null +++ b/test/interactions/expire_period/start_test.rb @@ -0,0 +1,28 @@ +require 'test_helper' + +class StartTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + setup do + @domain = domains(:shop) + @domain.update(expire_time: Time.zone.now - 1.day) + ActionMailer::Base.deliveries.clear + end + + def test_sets_expired + job_count = lambda do + QueJob.where("args->>0 = '#{@domain.id}'", job_class: DomainExpireEmailJob.name).count + end + + assert_difference job_count, 1 do + perform_enqueued_jobs do + DomainCron.start_expire_period + end + end + + @domain.reload + assert @domain.statuses.include?(DomainStatus::EXPIRED) + assert_equal @domain.outzone_at, @domain.expire_time + Domain.expire_warning_period + assert_equal @domain.delete_date, (@domain.outzone_at + Domain.redemption_grace_period).to_date + end +end From ac3860bbd8fc66b7eaa7ba2254516a0482642bad Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 2 Dec 2020 14:54:37 +0500 Subject: [PATCH 5/5] Add test for redemption grace period --- .../redemption_grace_period/start_test.rb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/interactions/redemption_grace_period/start_test.rb diff --git a/test/interactions/redemption_grace_period/start_test.rb b/test/interactions/redemption_grace_period/start_test.rb new file mode 100644 index 000000000..8958030f0 --- /dev/null +++ b/test/interactions/redemption_grace_period/start_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class StartTest < ActiveSupport::TestCase + + setup do + @domain = domains(:shop) + @domain.update(outzone_time: Time.zone.now - 1.day) + end + + def test_sets_server_hold + DomainCron.start_redemption_grace_period + + @domain.reload + assert @domain.statuses.include?(DomainStatus::SERVER_HOLD) + end + + def test_doesnt_sets_server_hold_if_not_outzone + @domain.update(outzone_time: nil) + @domain.reload + DomainCron.start_redemption_grace_period + + @domain.reload + assert_not @domain.statuses.include?(DomainStatus::SERVER_HOLD) + end +end