mirror of
https://github.com/Evolix/chexpire.git
synced 2024-05-03 01:10:50 +02:00
Check processor with task to update/sync dates
This commit is contained in:
parent
822da5c752
commit
142f0a6f1c
|
@ -52,6 +52,12 @@ class Check < ApplicationRecord
|
|||
after_update :reset_notifications
|
||||
after_save :enqueue_sync
|
||||
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :last_run_failed, -> {
|
||||
where("(last_success_at IS NULL AND last_run_at IS NOT NULL)
|
||||
OR (last_success_at <= DATE_SUB(last_run_at, INTERVAL 5 MINUTE))")
|
||||
}
|
||||
|
||||
private
|
||||
|
||||
def domain_created_at_past
|
||||
|
|
|
@ -31,6 +31,9 @@ class Notification < ApplicationRecord
|
|||
validates :delay, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
|
||||
validates :recipient, presence: true
|
||||
|
||||
scope :active_check, -> { Check.active }
|
||||
scope :check_last_run_failed, -> { Check.last_run_failed }
|
||||
|
||||
def pending!
|
||||
self.sent_at = nil
|
||||
super
|
||||
|
|
70
app/services/check_processor.rb
Normal file
70
app/services/check_processor.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
class CheckProcessor
|
||||
attr_reader :configuration
|
||||
|
||||
def initialize(configuration = nil)
|
||||
@configuration = configuration || default_configuration
|
||||
end
|
||||
|
||||
def sync_dates # rubocop:disable Metrics/MethodLength
|
||||
%i[
|
||||
resolve_last_run_failed
|
||||
resolve_expire_short_term
|
||||
resolve_expire_long_term
|
||||
resolve_unknown_expiry
|
||||
].each do |resolver|
|
||||
public_send(resolver).find_each(batch_size: 100).each do |check|
|
||||
process(check)
|
||||
|
||||
sleep configuration.interval
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_last_run_failed
|
||||
scope.last_run_failed
|
||||
end
|
||||
|
||||
def resolve_expire_long_term
|
||||
scope
|
||||
.where("DATE(domain_expires_at) >= DATE_ADD(CURDATE(), INTERVAL ? DAY)",
|
||||
configuration.long_term)
|
||||
.where("DATEDIFF(domain_expires_at, CURDATE()) MOD ? = 0",
|
||||
configuration.long_term_frequency)
|
||||
end
|
||||
|
||||
def resolve_expire_short_term
|
||||
scope.where("DATE(domain_expires_at) < DATE_ADD(CURDATE(), INTERVAL ? DAY)",
|
||||
configuration.long_term)
|
||||
end
|
||||
|
||||
def resolve_unknown_expiry
|
||||
scope.where("domain_expires_at IS NULL")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scope
|
||||
Check
|
||||
.active
|
||||
.where("last_run_at IS NULL OR last_run_at < DATE_SUB(NOW(), INTERVAL 12 HOUR)")
|
||||
end
|
||||
|
||||
def process(check)
|
||||
case check.kind.to_sym
|
||||
when :domain
|
||||
WhoisSyncJob.perform_now(check.id)
|
||||
else
|
||||
fail ArgumentError, "Unsupported check kind `#{check.kind}`"
|
||||
end
|
||||
end
|
||||
|
||||
def default_configuration
|
||||
config = Rails.configuration.chexpire.fetch("checks", {})
|
||||
|
||||
OpenStruct.new(
|
||||
interval: config.fetch("interval") { 0.00 },
|
||||
long_term: config.fetch("long_term") { 60 },
|
||||
long_term_frequency: config.fetch("long_term_frequency") { 10 },
|
||||
)
|
||||
end
|
||||
end
|
|
@ -12,7 +12,7 @@ module Notifier
|
|||
# Logical rules are in plain ruby inside processor
|
||||
scope
|
||||
.includes(check: :logs)
|
||||
.where("checks.last_success_at <= DATE_SUB(checks.last_run_at, INTERVAL 5 MINUTE)")
|
||||
.merge(Check.last_run_failed)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -20,7 +20,8 @@ module Notifier
|
|||
def scope
|
||||
Notification
|
||||
.includes(:check)
|
||||
.where(status: [:pending, :failed], checks: { active: true })
|
||||
.where(status: [:pending, :failed])
|
||||
.merge(Check.active)
|
||||
.where.not(checks: { user: ignore_users })
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ default: &default
|
|||
notifier:
|
||||
interval: 0.00
|
||||
failure_days: 3
|
||||
checks:
|
||||
interval: 0.5
|
||||
long_term: 60
|
||||
long_term_frequency: 10
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
|
|
|
@ -4,3 +4,7 @@ test:
|
|||
notifier:
|
||||
interval: 0.00
|
||||
failure_days: 3
|
||||
checks:
|
||||
interval: 0.00
|
||||
long_term: 60
|
||||
long_term_frequency: 10
|
||||
|
|
7
lib/tasks/checks.rake
Normal file
7
lib/tasks/checks.rake
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace :checks do
|
||||
desc "Refresh expiry dates for checks"
|
||||
task sync_dates: :environment do
|
||||
process = CheckProcessor.new
|
||||
process.sync_dates
|
||||
end
|
||||
end
|
|
@ -54,14 +54,18 @@ FactoryBot.define do
|
|||
domain_expires_at 1.week.from_now
|
||||
end
|
||||
|
||||
trait :expires_next_year do
|
||||
domain_expires_at 1.year.from_now
|
||||
end
|
||||
|
||||
trait :last_runs_failed do
|
||||
last_run_at Time.now - 90.minutes
|
||||
last_success_at 1.week.ago - 2.hours
|
||||
last_run_at 3.days.ago - 90.minutes
|
||||
last_success_at 7.days.ago - 2.hours
|
||||
end
|
||||
|
||||
trait :last_run_succeed do
|
||||
last_run_at 1.hour.ago
|
||||
last_success_at 1.hour.ago
|
||||
last_run_at 25.hour.ago
|
||||
last_success_at 25.hour.ago
|
||||
end
|
||||
|
||||
trait :inactive do
|
||||
|
|
133
test/services/check_processor_test.rb
Normal file
133
test/services/check_processor_test.rb
Normal file
|
@ -0,0 +1,133 @@
|
|||
require "test_helper"
|
||||
|
||||
class CheckProcessorTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@processor = CheckProcessor.new
|
||||
end
|
||||
|
||||
test "process WhoisSyncJob for domain checks" do
|
||||
domain = "domain.fr"
|
||||
check = create(:check, :domain, :nil_dates, domain: domain)
|
||||
|
||||
mock_system_command("whois", domain, stdout: file_fixture("whois/domain.fr.txt").read) do
|
||||
@processor.send(:process, check)
|
||||
end
|
||||
|
||||
check.reload
|
||||
|
||||
assert_equal Time.new(2019, 2, 17, 0, 0, 0, 0), check.domain_expires_at
|
||||
end
|
||||
|
||||
test "raises an error for an unsupported check kind" do
|
||||
check = build(:check)
|
||||
|
||||
check.stub :kind, :unknown do
|
||||
assert_raises ArgumentError do
|
||||
@processor.send(:process, check)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "resolve_last_run_failed includes already and never succeeded" do
|
||||
c1 = create(:check, :last_runs_failed)
|
||||
c2 = create(:check, :last_run_succeed)
|
||||
c3 = create(:check, last_run_at: 4.days.ago, last_success_at: nil)
|
||||
|
||||
checks = @processor.resolve_last_run_failed
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_not_includes checks, c2
|
||||
assert_includes checks, c3
|
||||
end
|
||||
|
||||
test "resolve_unknown_expiry" do
|
||||
c1 = create(:check, :nil_dates)
|
||||
c2 = create(:check)
|
||||
|
||||
checks = @processor.resolve_unknown_expiry
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_not_includes checks, c2
|
||||
end
|
||||
|
||||
test "resolve_expire_short_term" do
|
||||
c1 = create(:check, :expires_next_week)
|
||||
c2 = create(:check, :expires_next_year)
|
||||
|
||||
checks = @processor.resolve_expire_short_term
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_not_includes checks, c2
|
||||
end
|
||||
|
||||
test "resolve_expire_long_term returns checks with respect of frequency" do
|
||||
c1 = create(:check, domain_expires_at: 380.days.from_now)
|
||||
c2 = create(:check, domain_expires_at: 390.days.from_now)
|
||||
c3 = create(:check, domain_expires_at: 391.days.from_now)
|
||||
c4 = create(:check, domain_expires_at: 20.days.from_now)
|
||||
|
||||
checks = @processor.resolve_expire_long_term
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_includes checks, c2
|
||||
assert_not_includes checks, c3
|
||||
assert_not_includes checks, c4
|
||||
end
|
||||
|
||||
test "resolvers does not include checks recently executed" do
|
||||
c1 = create(:check, :expires_next_week)
|
||||
c2 = create(:check, :expires_next_week, last_run_at: 4.hours.ago)
|
||||
|
||||
checks = @processor.resolve_expire_short_term
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_not_includes checks, c2
|
||||
end
|
||||
|
||||
test "resolvers include checks never executed" do
|
||||
c1 = create(:check, :expires_next_week, last_run_at: 4.days.ago)
|
||||
|
||||
checks = @processor.resolve_expire_short_term
|
||||
|
||||
assert_includes checks, c1
|
||||
end
|
||||
|
||||
test "resolvers does not include inactive checks" do
|
||||
c1 = create(:check, :expires_next_week)
|
||||
c2 = create(:check, :expires_next_week, :inactive)
|
||||
|
||||
checks = @processor.resolve_expire_short_term
|
||||
|
||||
assert_includes checks, c1
|
||||
assert_not_includes checks, c2
|
||||
end
|
||||
|
||||
test "#sync_dates respects the interval configuration between sends" do
|
||||
create_list(:check, 3, :expires_next_week)
|
||||
|
||||
configuration = Minitest::Mock.new
|
||||
2.times do configuration.expect(:long_term, 60) end
|
||||
configuration.expect(:long_term_frequency, 10)
|
||||
|
||||
3.times do
|
||||
configuration.expect(:interval, 0.000001)
|
||||
end
|
||||
|
||||
processor = CheckProcessor.new(configuration)
|
||||
|
||||
mock = Minitest::Mock.new
|
||||
assert_stub = lambda { |actual_time|
|
||||
assert_equal 0.000001, actual_time
|
||||
mock
|
||||
}
|
||||
|
||||
processor.stub :process, nil do
|
||||
processor.stub :sleep, assert_stub do
|
||||
processor.sync_dates
|
||||
end
|
||||
end
|
||||
|
||||
configuration.verify
|
||||
mock.verify
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue