mirror of
https://github.com/Evolix/chexpire.git
synced 2024-06-25 10:41:00 +02:00
Merge pull request #91 from Evolix/notifications-templates
Notifications templates
This commit is contained in:
commit
3aa1cc376e
|
@ -15,6 +15,18 @@ class ApplicationController < ActionController::Base
|
||||||
devise_parameter_sanitizer.permit(:account_update, keys: [:notifications_enabled, :locale])
|
devise_parameter_sanitizer.permit(:account_update, keys: [:notifications_enabled, :locale])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_sign_in_path_for(_resource)
|
||||||
|
checks_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_sign_up_path_for(_resource)
|
||||||
|
checks_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_sign_out_path_for(_resource)
|
||||||
|
root_path
|
||||||
|
end
|
||||||
|
|
||||||
def user_not_authorized
|
def user_not_authorized
|
||||||
flash[:alert] = I18n.t("user_not_authorized", scope: :flashes)
|
flash[:alert] = I18n.t("user_not_authorized", scope: :flashes)
|
||||||
redirect_to(request.referrer || root_path)
|
redirect_to(request.referrer || root_path)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
class ChecksController < ApplicationController
|
class ChecksController < ApplicationController # rubocop:disable Metrics/ClassLength
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_check, except: [:index, :new, :create, :supports]
|
before_action :set_check, except: [:index, :new, :create, :supports]
|
||||||
after_action :verify_authorized, except: :index
|
after_action :verify_authorized, except: :index
|
||||||
|
@ -29,16 +29,17 @@ class ChecksController < ApplicationController
|
||||||
build_empty_notification
|
build_empty_notification
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create # rubocop:disable Metrics/AbcSize
|
||||||
@check = Check.new(new_check_params)
|
@check = Check.new(new_check_params.merge(user: current_user))
|
||||||
@check.user = current_user
|
|
||||||
authorize @check
|
authorize @check
|
||||||
|
|
||||||
if @check.save
|
if @check.save
|
||||||
flash[:notice] = t(".saved")
|
flash[:notice] = t("checks.created", scope: :flashes)
|
||||||
redirect_to checks_path
|
redirect_to checks_path
|
||||||
else
|
else
|
||||||
flash.now[:alert] = t(".invalid")
|
flash.now[:alert] = t("checks.invalid", scope: :flashes)
|
||||||
|
|
||||||
|
fill_or_build_new_notification
|
||||||
render :new
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -49,11 +50,12 @@ class ChecksController < ApplicationController
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if @check.update(update_check_params)
|
if @check.update(update_check_params)
|
||||||
flash[:notice] = "Your check has been updated."
|
flash[:notice] = t("checks.updated", scope: :flashes)
|
||||||
redirect_to checks_path
|
redirect_to checks_path
|
||||||
else
|
else
|
||||||
flash.now[:alert] = "An error occured."
|
flash.now[:alert] = t("checks.invalid", scope: :flashes)
|
||||||
build_empty_notification
|
|
||||||
|
fill_or_build_new_notification
|
||||||
render :edit
|
render :edit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -61,7 +63,7 @@ class ChecksController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
@check.destroy!
|
@check.destroy!
|
||||||
|
|
||||||
flash[:notice] = "Your check has been destroyed."
|
flash[:notice] = t("checks.destroyed", scope: :flashes)
|
||||||
redirect_to checks_path
|
redirect_to checks_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -86,13 +88,39 @@ class ChecksController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_params(*others)
|
def check_params(*others)
|
||||||
params.require(:check)
|
permitted = params.require(:check)
|
||||||
.permit(:domain, :domain_expires_at, :comment, :vendor, :round_robin, *others,
|
.permit(:domain, :domain_expires_at, :comment, :vendor,
|
||||||
notifications_attributes: [:id, :channel, :recipient, :interval])
|
:round_robin, *others,
|
||||||
|
notification_ids: [],
|
||||||
|
notifications_attributes: [:channel, :label, :recipient, :interval])
|
||||||
|
|
||||||
|
merge_current_user!(permitted)
|
||||||
|
|
||||||
|
permitted
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_current_user!(permitted)
|
||||||
|
return unless permitted[:notifications_attributes].present?
|
||||||
|
|
||||||
|
permitted[:notifications_attributes].each_pair do |_key, attributes|
|
||||||
|
attributes.merge!(user: current_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_empty_notification
|
def build_empty_notification
|
||||||
@check.notifications.build
|
@new_notification = @check.notifications.build
|
||||||
|
@new_notification.recipient = current_user.email
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_or_build_new_notification
|
||||||
|
last_notification = @check.notifications.last
|
||||||
|
|
||||||
|
# user has filled a new notification: we use it for the form
|
||||||
|
if last_notification.new_record?
|
||||||
|
@new_notification = last_notification
|
||||||
|
else # otherwise, set a new empty notification
|
||||||
|
build_empty_notification
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_sort
|
def current_sort
|
||||||
|
|
|
@ -3,45 +3,61 @@
|
||||||
|
|
||||||
class NotificationsController < ApplicationController
|
class NotificationsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_notification, except: [:create]
|
before_action :set_notification, except: [:index, :new, :create]
|
||||||
|
after_action :verify_authorized, except: :index
|
||||||
|
after_action :verify_policy_scoped, only: :index
|
||||||
|
|
||||||
|
def index
|
||||||
|
@notifications = policy_scope(Notification).order(checks_count: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@notification = Notification.new
|
||||||
|
authorize @notification
|
||||||
|
@notification.recipient = current_user.email
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
check = Check.find(params[:check_id])
|
@notification = Notification.new(notification_params)
|
||||||
@notification = check.notifications.build(notification_params)
|
@notification.user = current_user
|
||||||
authorize @notification
|
authorize @notification
|
||||||
|
|
||||||
if @notification.save
|
if @notification.save
|
||||||
flash[:notice] = "Your notification has been saved."
|
flash[:notice] = t("notifications.created", scope: :flashes)
|
||||||
redirect_to check_path
|
redirect_to notifications_path
|
||||||
else
|
else
|
||||||
flash.now[:alert] = "An error occured."
|
flash.now[:alert] = t("notifications.invalid", scope: :flashes)
|
||||||
render "checks/edit"
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit; end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @notification.update(notification_params)
|
||||||
|
flash[:notice] = t("notifications.updated", scope: :flashes)
|
||||||
|
redirect_to notifications_path
|
||||||
|
else
|
||||||
|
flash.now[:alert] = t("notifications.error", scope: :flashes)
|
||||||
|
render :edit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@notification.destroy!
|
@notification.destroy!
|
||||||
|
|
||||||
respond_to do |format|
|
flash[:notice] = t("notifications.destroyed", scope: :flashes)
|
||||||
format.js
|
redirect_to notifications_path
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_notification
|
def set_notification
|
||||||
# joins the check because policy use the check relation
|
@notification = Notification.find(params[:id])
|
||||||
@notification = Notification
|
|
||||||
.joins(:check)
|
|
||||||
.find_by!(id: params[:id], check_id: params[:check_id])
|
|
||||||
authorize @notification
|
authorize @notification
|
||||||
end
|
end
|
||||||
|
|
||||||
def notification_params
|
def notification_params
|
||||||
params.require(:notification).permit(:channel, :recipient, :interval)
|
params.require(:notification).permit(:label, :recipient, :interval)
|
||||||
end
|
|
||||||
|
|
||||||
def check_path
|
|
||||||
edit_check_path(check_id: params[:check_id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
class PagesController < ApplicationController
|
class PagesController < ApplicationController
|
||||||
def home; end
|
def home
|
||||||
|
redirect_to checks_path if user_signed_in?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
51
app/frontend/scss/components/callouts.scss
Normal file
51
app/frontend/scss/components/callouts.scss
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Taken from Bootstrap 4 documentation
|
||||||
|
|
||||||
|
.bd-callout {
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-left-width: .25rem;
|
||||||
|
border-radius: .25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: .25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout p:last-child {
|
||||||
|
margin-bottom: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout code {
|
||||||
|
border-radius: .25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout+.bd-callout {
|
||||||
|
margin-top: -.25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-info {
|
||||||
|
border-left-color: #5bc0de
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-info h4 {
|
||||||
|
color: #5bc0de
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-warning {
|
||||||
|
border-left-color: #f0ad4e
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-warning h4 {
|
||||||
|
color: #f0ad4e
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-danger {
|
||||||
|
border-left-color: #d9534f
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-callout-danger h4 {
|
||||||
|
color: #d9534f
|
||||||
|
}
|
8
app/frontend/scss/components/notifications.scss
Normal file
8
app/frontend/scss/components/notifications.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
// License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
.notifications-table {
|
||||||
|
.action a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,5 +5,7 @@
|
||||||
@import '~bootstrap/scss/bootstrap';
|
@import '~bootstrap/scss/bootstrap';
|
||||||
@import 'layout';
|
@import 'layout';
|
||||||
@import 'icons';
|
@import 'icons';
|
||||||
|
@import 'components/callouts';
|
||||||
@import 'components/users';
|
@import 'components/users';
|
||||||
@import 'components/checks';
|
@import 'components/checks';
|
||||||
|
@import 'components/notifications';
|
||||||
|
|
5
app/helpers/check_notifications_helper.rb
Normal file
5
app/helpers/check_notifications_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
module CheckNotificationsHelper
|
||||||
|
end
|
|
@ -1,12 +1,9 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
|
||||||
|
|
||||||
module NotificationsHelper
|
module NotificationsHelper
|
||||||
def many_channels_available?
|
def many_channels_available?
|
||||||
Notification.channels.many?
|
Notification.channels.many?
|
||||||
end
|
end
|
||||||
|
|
||||||
def recipient_col_class
|
def notification_variable_col_class
|
||||||
many_channels_available? ? "col-md-7" : "col-md-9"
|
many_channels_available? ? "col-md-4" : "col-md-5"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,8 +5,9 @@ class NotificationsMailer < ApplicationMailer
|
||||||
helper :application
|
helper :application
|
||||||
|
|
||||||
before_action except: :recurrent_failures do
|
before_action except: :recurrent_failures do
|
||||||
@notification = params.fetch(:notification)
|
@check_notification = params.fetch(:check_notification)
|
||||||
@check = @notification.check
|
@check = @check_notification.check
|
||||||
|
@notification = @check_notification.notification
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_expires_soon
|
def domain_expires_soon
|
||||||
|
|
|
@ -34,10 +34,12 @@
|
||||||
class Check < ApplicationRecord
|
class Check < ApplicationRecord
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
has_many :logs, class_name: "CheckLog", dependent: :destroy
|
has_many :logs, class_name: "CheckLog", dependent: :destroy
|
||||||
has_many :notifications, validate: true, dependent: :destroy
|
has_many :check_notifications, dependent: :destroy
|
||||||
|
has_many :notifications, -> { order(checks_count: :desc) },
|
||||||
|
through: :check_notifications, validate: true
|
||||||
|
|
||||||
accepts_nested_attributes_for :notifications,
|
accepts_nested_attributes_for :notifications,
|
||||||
allow_destroy: true,
|
reject_if: lambda { |att| att["interval"].blank? }
|
||||||
reject_if: lambda { |at| at["recipient"].blank? && at["interval"].blank? }
|
|
||||||
|
|
||||||
enum kind: [:domain, :ssl]
|
enum kind: [:domain, :ssl]
|
||||||
enum mode: [:auto, :manual]
|
enum mode: [:auto, :manual]
|
||||||
|
@ -58,7 +60,7 @@ class Check < ApplicationRecord
|
||||||
|
|
||||||
before_save :reset_consecutive_failures
|
before_save :reset_consecutive_failures
|
||||||
before_save :set_mode
|
before_save :set_mode
|
||||||
after_update :reset_notifications
|
after_update :reset_check_notifications
|
||||||
after_save :enqueue_sync
|
after_save :enqueue_sync
|
||||||
|
|
||||||
scope :active, -> { where(active: true) }
|
scope :active, -> { where(active: true) }
|
||||||
|
@ -119,10 +121,10 @@ class Check < ApplicationRecord
|
||||||
ResyncJob.perform_later(id)
|
ResyncJob.perform_later(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_notifications
|
def reset_check_notifications
|
||||||
return unless (saved_changes.keys & %w[domain domain_expires_at]).present?
|
return unless (saved_changes.keys & %w[domain domain_expires_at]).present?
|
||||||
|
|
||||||
notifications.each(&:reset!)
|
check_notifications.each(&:reset!)
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_consecutive_failures
|
def reset_consecutive_failures
|
||||||
|
|
46
app/models/check_notification.rb
Normal file
46
app/models/check_notification.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: check_notifications
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# sent_at :datetime
|
||||||
|
# status :integer default("pending"), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# check_id :bigint(8)
|
||||||
|
# notification_id :bigint(8)
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_check_notifications_on_check_id (check_id)
|
||||||
|
# index_check_notifications_on_notification_id (notification_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (notification_id => notifications.id)
|
||||||
|
#
|
||||||
|
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
class CheckNotification < ApplicationRecord
|
||||||
|
belongs_to :check
|
||||||
|
belongs_to :notification, counter_cache: :checks_count
|
||||||
|
|
||||||
|
enum status: [:pending, :ongoing, :succeed, :failed]
|
||||||
|
|
||||||
|
scope :active_check, -> { Check.active }
|
||||||
|
scope :check_last_run_failed, -> { Check.last_run_failed }
|
||||||
|
|
||||||
|
def pending!
|
||||||
|
self.sent_at = nil
|
||||||
|
super
|
||||||
|
end
|
||||||
|
alias reset! pending!
|
||||||
|
|
||||||
|
def ongoing!
|
||||||
|
self.sent_at = Time.now
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,50 +1,45 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: notifications
|
# Table name: notifications
|
||||||
#
|
#
|
||||||
# id :bigint(8) not null, primary key
|
# id :bigint(8) not null, primary key
|
||||||
# channel :integer default("email"), not null
|
# channel :integer default("email"), not null
|
||||||
# interval :integer not null
|
# checks_count :integer default(0), not null
|
||||||
# recipient :string(255) not null
|
# interval :integer not null
|
||||||
# sent_at :datetime
|
# label :string(255)
|
||||||
# status :integer default("pending"), not null
|
# recipient :string(255) not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# check_id :bigint(8)
|
# check_id :bigint(8)
|
||||||
|
# user_id :bigint(8)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_notifications_on_check_id (check_id)
|
# index_notifications_on_check_id (check_id)
|
||||||
|
# index_notifications_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
# fk_rails_... (check_id => checks.id)
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
#
|
#
|
||||||
|
|
||||||
class Notification < ApplicationRecord
|
class Notification < ApplicationRecord
|
||||||
belongs_to :check
|
belongs_to :user
|
||||||
|
has_many :check_notifications, dependent: :destroy
|
||||||
|
has_many :checks, -> { order(domain_expires_at: :asc) }, through: :check_notifications
|
||||||
|
|
||||||
enum channel: [:email]
|
enum channel: [:email]
|
||||||
enum status: [:pending, :ongoing, :succeed, :failed]
|
|
||||||
|
|
||||||
validates :channel, presence: true
|
validates :channel, presence: true
|
||||||
validates :interval, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
|
validates :interval, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
|
||||||
validates :recipient, presence: true
|
validates :recipient, presence: true
|
||||||
|
|
||||||
scope :active_check, -> { Check.active }
|
def notifical_label
|
||||||
scope :check_last_run_failed, -> { Check.last_run_failed }
|
return label if label.present?
|
||||||
|
|
||||||
def pending!
|
"#{recipient} (#{interval})"
|
||||||
self.sent_at = nil
|
|
||||||
super
|
|
||||||
end
|
|
||||||
alias reset! pending!
|
|
||||||
|
|
||||||
def ongoing!
|
|
||||||
self.sent_at = Time.now
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,7 +39,8 @@ class User < ApplicationRecord
|
||||||
devise :database_authenticatable, :registerable,
|
devise :database_authenticatable, :registerable,
|
||||||
:recoverable, :rememberable, :validatable, :confirmable
|
:recoverable, :rememberable, :validatable, :confirmable
|
||||||
|
|
||||||
has_many :checks
|
has_many :checks, dependent: :destroy
|
||||||
|
has_many :notifications, dependent: :destroy
|
||||||
validates :tos_accepted, acceptance: true
|
validates :tos_accepted, acceptance: true
|
||||||
validates :locale, inclusion: { in: I18n.available_locales.map(&:to_s) }
|
validates :locale, inclusion: { in: I18n.available_locales.map(&:to_s) }
|
||||||
|
|
||||||
|
|
24
app/policies/check_notification_policy.rb
Normal file
24
app/policies/check_notification_policy.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
class CheckNotificationPolicy < ApplicationPolicy
|
||||||
|
class Scope < Scope
|
||||||
|
def resolve
|
||||||
|
scope.joins(:check).where(checks: { user: user })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
check_owner?
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_owner?
|
||||||
|
record.check.user == user
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,21 +4,25 @@
|
||||||
class NotificationPolicy < ApplicationPolicy
|
class NotificationPolicy < ApplicationPolicy
|
||||||
class Scope < Scope
|
class Scope < Scope
|
||||||
def resolve
|
def resolve
|
||||||
scope.joins(:check).where(checks: { user: user })
|
scope.where(user: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy?
|
def create?
|
||||||
check_owner?
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def show?
|
def update?
|
||||||
false
|
owner?
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
owner?
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_owner?
|
def owner?
|
||||||
record.check.user == user
|
record.user == user
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,26 +4,26 @@
|
||||||
module Notifier
|
module Notifier
|
||||||
module Channels
|
module Channels
|
||||||
class Base
|
class Base
|
||||||
def notify(notification) # rubocop:disable Metrics/MethodLength
|
def notify(check_notification) # rubocop:disable Metrics/MethodLength
|
||||||
return unless supports?(notification)
|
return unless supports?(check_notification)
|
||||||
|
|
||||||
notification.ongoing!
|
check_notification.ongoing!
|
||||||
|
|
||||||
case notification.check.kind.to_sym
|
case check_notification.check.kind.to_sym
|
||||||
when :domain
|
when :domain
|
||||||
domain_notify_expires_soon(notification)
|
domain_notify_expires_soon(check_notification)
|
||||||
when :ssl
|
when :ssl
|
||||||
ssl_notify_expires_soon(notification)
|
ssl_notify_expires_soon(check_notification)
|
||||||
else
|
else
|
||||||
fail ArgumentError,
|
fail ArgumentError,
|
||||||
"Invalid notification for check kind `#{notification.check.kind}`."
|
"Invalid notification for check kind `#{check_notification.check.kind}`."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# :nocov:
|
# :nocov:
|
||||||
def supports?(_notification)
|
def supports?(_check_notification)
|
||||||
fail NotImplementedError,
|
fail NotImplementedError,
|
||||||
"#{self.class.name} channel did not implemented method #{__callee__}"
|
"#{self.class.name} channel did not implemented method #{__callee__}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,17 +11,21 @@ module Notifier
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def supports?(_notification)
|
def supports?(_check_notification)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Expiration notifications
|
# Expiration notifications
|
||||||
def domain_notify_expires_soon(notification)
|
def domain_notify_expires_soon(check_notification)
|
||||||
NotificationsMailer.with(notification: notification).domain_expires_soon.deliver_now
|
NotificationsMailer.with(check_notification: check_notification)
|
||||||
|
.domain_expires_soon
|
||||||
|
.deliver_now
|
||||||
end
|
end
|
||||||
|
|
||||||
def ssl_notify_expires_soon(notification)
|
def ssl_notify_expires_soon(_notification)
|
||||||
NotificationsMailer.with(notification: notification).ssl_expires_soon.deliver_now
|
NotificationsMailer.with(check_notification: check_notification)
|
||||||
|
.ssl_expires_soon
|
||||||
|
.deliver_now
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,8 +19,8 @@ module Notifier
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_expires_soon
|
def process_expires_soon
|
||||||
resolver.notifications_expiring_soon.find_each do |notification|
|
resolver.notifications_expiring_soon.find_each do |check_notification|
|
||||||
notifier_channel_for(notification).notify(notification)
|
notifier_channel_for(check_notification.notification).notify(check_notification)
|
||||||
|
|
||||||
sleep configuration.interval
|
sleep configuration.interval
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,8 +21,8 @@ module Notifier
|
||||||
private
|
private
|
||||||
|
|
||||||
def scope
|
def scope
|
||||||
Notification
|
CheckNotification
|
||||||
.includes(:check)
|
.includes(:check, :notification)
|
||||||
.where(status: [:pending, :failed])
|
.where(status: [:pending, :failed])
|
||||||
.merge(Check.active)
|
.merge(Check.active)
|
||||||
.where.not(checks: { user: ignore_users })
|
.where.not(checks: { user: ignore_users })
|
||||||
|
|
|
@ -41,18 +41,17 @@
|
||||||
<%= f.input :active %>
|
<%= f.input :active %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<h2 class="mt-5"><%= t(".notifications") %></h2>
|
<p class="mt-5 bd-callout bd-callout-info"><%= t(".notifications_hint") %></p>
|
||||||
<p class="alert alert-light"><%= t(".notifications_hint") %></p>
|
|
||||||
|
|
||||||
<%- check.notifications.each_with_index do |notification, index| %>
|
<%= f.association :notifications, as: :check_boxes,
|
||||||
<div data-notification-id="<%= notification.id %>">
|
collection: policy_scope(Notification),
|
||||||
<%= f.fields_for :notifications, notification do |nf| %>
|
label_method: :notifical_label,
|
||||||
<%= render "notifications/nested_form_headers", f: nf if index.zero? %>
|
label_text: false %>
|
||||||
<%= render "notifications/nested_form", f: nf, check: check %>
|
|
||||||
<% end %>
|
<%= f.fields_for :notifications, new_notification do |nf| %>
|
||||||
</div>
|
<%= render "notifications/nested_form_headers", f: nf %>
|
||||||
|
<%= render "notifications/nested_form", f: nf, notification: new_notification %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<%= f.button :submit, class: "btn-primary mt-5" %>
|
<%= f.button :submit, class: "btn-primary mt-5" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -7,16 +7,20 @@
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
<%= t(".th.domain") %>
|
<%= t(".th.domain") %>
|
||||||
<span class="sort-links mx-sm-2 text-nowrap">
|
<% unless defined?(skip_sort) %>
|
||||||
<%== checks_sort_links(:domain) %>
|
<span class="sort-links mx-sm-2 text-nowrap">
|
||||||
</span>
|
<%== checks_sort_links(:domain) %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
<span class="d-none d-sm-inline"><%= t(".th.expiry_date") %></span>
|
<span class="d-none d-sm-inline"><%= t(".th.expiry_date") %></span>
|
||||||
<span class="d-inline d-sm-none"><%= t(".th.expiry_date_short") %></span>
|
<span class="d-inline d-sm-none"><%= t(".th.expiry_date_short") %></span>
|
||||||
<span class="sort-links mx-sm-2 text-nowrap">
|
<% unless defined?(skip_sort) %>
|
||||||
<%== checks_sort_links(:domain_expires_at) %>
|
<span class="sort-links mx-sm-2 text-nowrap">
|
||||||
</span>
|
<%== checks_sort_links(:domain_expires_at) %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" class="text-right"><%= t(".th.edit") %></th>
|
<th scope="col" class="text-right"><%= t(".th.edit") %></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -48,4 +52,4 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= paginate @checks %>
|
<%= paginate checks unless defined?(skip_pagination)%>
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-12 col-lg-10">
|
<div class="col-12 col-lg-10">
|
||||||
<h1>Edit your check</h1>
|
<h1><%= t(".title") %></h1>
|
||||||
|
|
||||||
<%= render "form", check: @check %>
|
<%= render "form", check: @check, new_notification: @new_notification %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-5 justify-content-center">
|
<div class="row mt-5 justify-content-center">
|
||||||
<div class="col-12 col-lg-10">
|
<div class="col-12 col-lg-10">
|
||||||
<%= button_to("Delete", check_path(@check), class: "btn btn-danger", method: :delete,
|
<%= button_to(t("helpers.submit.check.delete"), check_path(@check), class: "btn btn-danger", method: :delete,
|
||||||
data: { confirm: "Are you sure ?" }) %>
|
data: { confirm: t(".destroy_confirmation") }) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="col-12 col-lg-10">
|
<div class="col-12 col-lg-10">
|
||||||
<h1><%= t(".#{@check.kind}.title") %></h1>
|
<h1><%= t(".#{@check.kind}.title") %></h1>
|
||||||
|
|
||||||
<%= render "form", check: @check %>
|
<%= render "form", check: @check, new_notification: @new_notification %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
13
app/views/notifications/_form.html.erb
Normal file
13
app/views/notifications/_form.html.erb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
||||||
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
|
<%= simple_form_for(notification) do |f| %>
|
||||||
|
<%= f.input :recipient, as: :email,
|
||||||
|
input_html: { autocapitalize: :none, autocorrect: :off }
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%= f.input :interval, as: :integer, required: true %>
|
||||||
|
|
||||||
|
<%= f.input :label, hint: t(".label_hint") %>
|
||||||
|
|
||||||
|
<%= f.button :submit, class: "btn-primary mt-3" %>
|
||||||
|
<% end %>
|
|
@ -12,20 +12,16 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="form-group <%= recipient_col_class %>">
|
<div class="form-group <%= notification_variable_col_class %>">
|
||||||
<%= f.input :recipient, as: :email, label: false %>
|
<%= f.input :recipient, as: :email, label: false %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
<div class="form-group col-md-2">
|
||||||
<%= f.input :interval, as: :integer, label: false %>
|
<%= f.input :interval, as: :integer, label: false, required: true %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-1">
|
<div class="form-group <%= notification_variable_col_class %>">
|
||||||
<% if f.object.persisted? %>
|
<%= f.input :label, label: false %>
|
||||||
<%= link_to check_notification_path(check, f.object), method: :delete, remote: true, class: "btn btn-danger" do %>
|
|
||||||
<%== Octicons::Octicon.new("x", width: 15, height: 20).to_svg %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -7,11 +7,15 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="<%= recipient_col_class %>">
|
<div class="<%= notification_variable_col_class %>">
|
||||||
<%= f.label :recipient %>
|
<%= f.label :recipient %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<%= f.label :interval %>
|
<%= f.label :interval, required: true %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="<%= notification_variable_col_class %>">
|
||||||
|
<%= f.label :label %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
46
app/views/notifications/_table.html.erb
Normal file
46
app/views/notifications/_table.html.erb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
||||||
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
|
<div class="mb-4 table-responsive">
|
||||||
|
<table class="table notifications-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
<%= Notification.human_attribute_name("label") %>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<%= Notification.human_attribute_name("recipient") %>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<%= Notification.human_attribute_name("interval") %>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<%= Notification.human_attribute_name("checks_count") %>
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="text-right"><%= t(".th.edit") %></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% notifications.each do |notification| %>
|
||||||
|
<tr class="notification-row">
|
||||||
|
<td>
|
||||||
|
<strong><%= notification.label %></strong>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= notification.recipient %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= t(".interval_in_days", count: notification.interval) %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= notification.checks_count %>
|
||||||
|
</td>
|
||||||
|
<td class="action text-right">
|
||||||
|
<%= link_to edit_notification_path(notification), "data-turbolinks" => false do %>
|
||||||
|
<%== Octicons::Octicon.new("pencil").to_svg %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -1,3 +0,0 @@
|
||||||
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
|
||||||
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
|
||||||
document.querySelector("[data-notification-id='<%= @notification.id %>']").remove();
|
|
27
app/views/notifications/edit.html.erb
Normal file
27
app/views/notifications/edit.html.erb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
||||||
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-lg-10">
|
||||||
|
<h1><%= t(".title") %></h1>
|
||||||
|
|
||||||
|
<%= render "form", notification: @notification %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-5 justify-content-center">
|
||||||
|
<div class="col-12 col-lg-10">
|
||||||
|
<h4><%= t(".checks", count: @notification.checks_count) %></h4>
|
||||||
|
<% if @notification.checks_count.positive? %>
|
||||||
|
<%= render "checks/table", checks: @notification.checks, skip_sort: true, skip_pagination: true %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-5 mb-3 justify-content-center">
|
||||||
|
<div class="col-12 col-lg-10">
|
||||||
|
<%= button_to(t("helpers.submit.notification.delete"), notification_path(@notification), class: "btn btn-danger", method: :delete,
|
||||||
|
data: { confirm: t(".destroy_confirmation", count: @notification.checks_count) }) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
22
app/views/notifications/index.html.erb
Normal file
22
app/views/notifications/index.html.erb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
||||||
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-lg-10 col-xl-9">
|
||||||
|
<% if @notifications.empty? %>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<%= t(".no_notification_yet_html", new_path: new_notification_path) %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="row justify-content-md-end">
|
||||||
|
<%= link_to("Ajouter une notification", new_notification_path, class: "btn btn-primary") %>
|
||||||
|
</div>
|
||||||
|
<h1 class="mb-3 mb-sm-5"><%= t(".title") %></h1>
|
||||||
|
<%= render "table", notifications: @notifications %>
|
||||||
|
<% end %>
|
||||||
|
<div class="bd-callout bd-callout-info">
|
||||||
|
<p><%= t(".explanation") %></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
11
app/views/notifications/new.html.erb
Normal file
11
app/views/notifications/new.html.erb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<% # Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr> %>
|
||||||
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-lg-10">
|
||||||
|
<h1><%= t(".title") %></h1>
|
||||||
|
|
||||||
|
<%= render "form", notification: @notification %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -2,7 +2,7 @@
|
||||||
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
<% # License: GNU AGPL-3+ (see full text in LICENSE file) %>
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark justify-content-between">
|
<nav class="navbar navbar-expand-lg navbar-dark justify-content-between">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<%= link_to root_path , class: "navbar-brand" do %>
|
<%= link_to (user_signed_in? ? checks_path : root_path), class: "navbar-brand" do %>
|
||||||
<%= image_tag 'chexpire10.png', width: 200, alt: 'chexpire logo' %>
|
<%= image_tag 'chexpire10.png', width: 200, alt: 'chexpire logo' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
@ -24,6 +24,9 @@
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<%= link_to(t(".GitHub"), "https://github.com/Evolix/chexpire", class: "nav-link") %>
|
<%= link_to(t(".GitHub"), "https://github.com/Evolix/chexpire", class: "nav-link") %>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<%= link_to(t(".my_notifications"), notifications_path, class: "nav-link") %>
|
||||||
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,29 @@ en:
|
||||||
notifications:
|
notifications:
|
||||||
recipient: john@example.com
|
recipient: john@example.com
|
||||||
|
|
||||||
|
helpers:
|
||||||
|
submit:
|
||||||
|
check:
|
||||||
|
create: "Create Check"
|
||||||
|
update: "Update Check"
|
||||||
|
delete: "Destroy Check"
|
||||||
|
notification:
|
||||||
|
create: "Create Notification"
|
||||||
|
update: "Update Notification"
|
||||||
|
delete: "Destroy Notification"
|
||||||
|
|
||||||
flashes:
|
flashes:
|
||||||
user_not_authorized: "You are not authorized to access to this resource."
|
user_not_authorized: "You are not authorized to access to this resource."
|
||||||
|
checks:
|
||||||
|
create: The check has been saved.
|
||||||
|
updated: The check has been updated.
|
||||||
|
invalid: Please check the form.
|
||||||
|
destroyed: The check has been destroyed.
|
||||||
|
notifications:
|
||||||
|
created: The notification has been created.
|
||||||
|
updated: The notification has been updated."
|
||||||
|
destroyed: The notification has been destroyed.
|
||||||
|
invalid: Please check the form.
|
||||||
|
|
||||||
notifications_mailer:
|
notifications_mailer:
|
||||||
domain_expires_soon:
|
domain_expires_soon:
|
||||||
|
@ -68,6 +89,7 @@ en:
|
||||||
my_checks: "My checks"
|
my_checks: "My checks"
|
||||||
new_domain_check: "New domain check"
|
new_domain_check: "New domain check"
|
||||||
new_ssl_check: "New SSL check"
|
new_ssl_check: "New SSL check"
|
||||||
|
my_notifications: "My notifications"
|
||||||
sign_up: "Sign up"
|
sign_up: "Sign up"
|
||||||
sign_in: "Log in"
|
sign_in: "Log in"
|
||||||
sign_out: "Log out"
|
sign_out: "Log out"
|
||||||
|
@ -104,7 +126,9 @@ en:
|
||||||
title: New domain check
|
title: New domain check
|
||||||
ssl:
|
ssl:
|
||||||
title: New SSL check
|
title: New SSL check
|
||||||
|
edit:
|
||||||
|
title: Check edition
|
||||||
|
destroy_confirmation: Are you sure to destroy this check ?
|
||||||
create:
|
create:
|
||||||
saved: "Your check has been saved."
|
saved: "Your check has been saved."
|
||||||
invalid: "Please check the form."
|
invalid: "Please check the form."
|
||||||
|
@ -127,7 +151,7 @@ en:
|
||||||
domain: Hostname
|
domain: Hostname
|
||||||
notifications_hint: |
|
notifications_hint: |
|
||||||
Receive notifications to warn you when our system detects that the
|
Receive notifications to warn you when our system detects that the
|
||||||
expiration date is coming. The time is set in number of days.
|
expiration date is coming.
|
||||||
|
|
||||||
table:
|
table:
|
||||||
th:
|
th:
|
||||||
|
@ -143,3 +167,36 @@ en:
|
||||||
zero: "Last check successful: today"
|
zero: "Last check successful: today"
|
||||||
one: "Last check successful: yesterday"
|
one: "Last check successful: yesterday"
|
||||||
other: "Last check successful %{count} days ago"
|
other: "Last check successful %{count} days ago"
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
index:
|
||||||
|
title: "List of your notifications"
|
||||||
|
no_notification_yet_html: |
|
||||||
|
You have not set up a notification yet.
|
||||||
|
<a href="%{new_path}">Create your first notification</a>.
|
||||||
|
explanation: |
|
||||||
|
For each of your checks,
|
||||||
|
you can associate one or more notifications that you will receive
|
||||||
|
by email at the interval of your choice before the expiration date.
|
||||||
|
table:
|
||||||
|
th:
|
||||||
|
edit: Edit
|
||||||
|
interval_in_days:
|
||||||
|
one: 1 day
|
||||||
|
other: "%{count} days"
|
||||||
|
edit:
|
||||||
|
title: "Edit the notification"
|
||||||
|
checks:
|
||||||
|
zero: No check associated check.
|
||||||
|
one: Check associated
|
||||||
|
other: Checks associated
|
||||||
|
destroy_confirmation:
|
||||||
|
zero: "Are you sure ?"
|
||||||
|
one: |
|
||||||
|
Are you sure ?
|
||||||
|
You won't receive this notification for the associated check.
|
||||||
|
other: |
|
||||||
|
Are you sure ?
|
||||||
|
You won't receive this notification for the associated checks.
|
||||||
|
form:
|
||||||
|
label_hint: This label allows you to identify this notification and associate it with a check.
|
||||||
|
|
|
@ -13,8 +13,10 @@ fr:
|
||||||
domain_updated_at: "Date de modification"
|
domain_updated_at: "Date de modification"
|
||||||
domain_expires_at: "Date d'expiration"
|
domain_expires_at: "Date d'expiration"
|
||||||
notification:
|
notification:
|
||||||
|
label: Étiquette
|
||||||
interval: Délai
|
interval: Délai
|
||||||
recipient: Destinataire
|
recipient: Destinataire
|
||||||
|
checks_count: Vérifications
|
||||||
user:
|
user:
|
||||||
tos_accepted: "Conditions d'utilisation"
|
tos_accepted: "Conditions d'utilisation"
|
||||||
notifications_enabled: "Notifications activées"
|
notifications_enabled: "Notifications activées"
|
||||||
|
@ -25,8 +27,13 @@ fr:
|
||||||
helpers:
|
helpers:
|
||||||
submit:
|
submit:
|
||||||
check:
|
check:
|
||||||
create: "Créer"
|
create: "Créer la vérification"
|
||||||
update: "Valider"
|
update: "Modifier la vérification"
|
||||||
|
delete: "Supprimer la vérification"
|
||||||
|
notification:
|
||||||
|
create: "Créer la notification"
|
||||||
|
update: "Modifier la notification"
|
||||||
|
delete: "Supprimer la notification"
|
||||||
page_entries_info:
|
page_entries_info:
|
||||||
one_page:
|
one_page:
|
||||||
display_entries:
|
display_entries:
|
||||||
|
@ -71,6 +78,16 @@ fr:
|
||||||
|
|
||||||
flashes:
|
flashes:
|
||||||
user_not_authorized: "Vous n'êtes pas autorisé•e à accéder à cette ressouce."
|
user_not_authorized: "Vous n'êtes pas autorisé•e à accéder à cette ressouce."
|
||||||
|
checks:
|
||||||
|
create: La vérification est enregistrée.
|
||||||
|
updated: La vérification est mise à jour.
|
||||||
|
invalid: Veuillez vérifier le formulaire.
|
||||||
|
destroyed: La vérification est supprimée.
|
||||||
|
notifications:
|
||||||
|
created: La notification est créée.
|
||||||
|
updated: "La notification est mise à jour."
|
||||||
|
destroyed: La notification est supprimée.
|
||||||
|
invalid: Veuillez vérifier le formulaire.
|
||||||
|
|
||||||
notifications_mailer:
|
notifications_mailer:
|
||||||
domain_expires_soon:
|
domain_expires_soon:
|
||||||
|
@ -101,6 +118,7 @@ fr:
|
||||||
my_checks: "Mes vérifications"
|
my_checks: "Mes vérifications"
|
||||||
new_domain_check: "Nouveau nom de domaine"
|
new_domain_check: "Nouveau nom de domaine"
|
||||||
new_ssl_check: "Nouveau certificat SSL"
|
new_ssl_check: "Nouveau certificat SSL"
|
||||||
|
my_notifications: "Mes notifications"
|
||||||
sign_up: "Enregistrement"
|
sign_up: "Enregistrement"
|
||||||
sign_in: "Connexion"
|
sign_in: "Connexion"
|
||||||
sign_out: "Déconnexion"
|
sign_out: "Déconnexion"
|
||||||
|
@ -137,10 +155,9 @@ fr:
|
||||||
title: Nouvelle vérification d'un nom de domaine
|
title: Nouvelle vérification d'un nom de domaine
|
||||||
ssl:
|
ssl:
|
||||||
title: Nouvelle vérification d'un certificat SSL
|
title: Nouvelle vérification d'un certificat SSL
|
||||||
|
edit:
|
||||||
create:
|
title: Modification de la vérification
|
||||||
saved: La vérification est enregistrée.
|
destroy_confirmation: Confirmez-vous la suppression de cette vérification ?
|
||||||
invalid: Veuillez vérifier le formulaire.
|
|
||||||
|
|
||||||
filters:
|
filters:
|
||||||
kind_domain: Domaine
|
kind_domain: Domaine
|
||||||
|
@ -160,7 +177,7 @@ fr:
|
||||||
domain: Nom d'hôte
|
domain: Nom d'hôte
|
||||||
notifications_hint: |
|
notifications_hint: |
|
||||||
Recevez des notifications pour vous avertir lorsque notre système détecte
|
Recevez des notifications pour vous avertir lorsque notre système détecte
|
||||||
que la date d'expiration approche. Le délai est indiqué ennombre de jours.
|
que la date d'expiration approche.
|
||||||
|
|
||||||
table:
|
table:
|
||||||
th:
|
th:
|
||||||
|
@ -176,3 +193,38 @@ fr:
|
||||||
zero: "Dernière vérification réussie : aujourd'hui"
|
zero: "Dernière vérification réussie : aujourd'hui"
|
||||||
one: "Dernière vérification réussie : hier"
|
one: "Dernière vérification réussie : hier"
|
||||||
other: "Dernière vérification réussie il y a %{count} jours"
|
other: "Dernière vérification réussie il y a %{count} jours"
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
index:
|
||||||
|
title: "Liste de vos notifications"
|
||||||
|
no_notification_yet_html: |
|
||||||
|
Vous n'avez pas encore créé de notification.
|
||||||
|
<a href="%{new_domain_path}">Créez votre première notification</a>.
|
||||||
|
explanation: |
|
||||||
|
Pour chacune de vos vérifications,
|
||||||
|
vous pouvez associer une ou plusieurs notifications que vous recevrez
|
||||||
|
par email un certain nombre de jours avant la date d'expiration.
|
||||||
|
table:
|
||||||
|
th:
|
||||||
|
edit: Modifier
|
||||||
|
interval_in_days:
|
||||||
|
one: 1 jour
|
||||||
|
other: "%{count} jours"
|
||||||
|
new:
|
||||||
|
title: Nouvelle notification
|
||||||
|
edit:
|
||||||
|
title: "Modification d'une notification"
|
||||||
|
checks:
|
||||||
|
zero: Aucune vérification associée.
|
||||||
|
one: Vérification associée
|
||||||
|
other: Vérifications associées
|
||||||
|
destroy_confirmation:
|
||||||
|
zero: "Confirmez-vous la suppression de cette notification ?"
|
||||||
|
one: |
|
||||||
|
Confirmez-vous la suppression ?
|
||||||
|
Vous ne recevrez plus la notification associée à cette vérification.
|
||||||
|
other: |
|
||||||
|
Confirmez-vous la suppression ?
|
||||||
|
Vous ne recevrez plus les notifications associées à ces vérifications.
|
||||||
|
form:
|
||||||
|
label_hint: Ce nom vous permet d'identifier cette notification pour l'associer à une vérification.
|
||||||
|
|
|
@ -7,12 +7,13 @@ Rails.application.routes.draw do
|
||||||
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
|
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
|
||||||
|
|
||||||
resources :checks, except: [:show] do
|
resources :checks, except: [:show] do
|
||||||
resources :notifications, only: [:destroy]
|
|
||||||
collection do
|
collection do
|
||||||
post :supports, format: :json
|
post :supports, format: :json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :notifications, except: [:show]
|
||||||
|
|
||||||
devise_for :users
|
devise_for :users
|
||||||
root to: "pages#home"
|
root to: "pages#home"
|
||||||
|
|
||||||
|
@ -22,7 +23,6 @@ end
|
||||||
# == Route Map
|
# == Route Map
|
||||||
#
|
#
|
||||||
# Prefix Verb URI Pattern Controller#Action
|
# Prefix Verb URI Pattern Controller#Action
|
||||||
# check_notification DELETE /checks/:check_id/notifications/:id(.:format) notifications#destroy
|
|
||||||
# supports_checks POST /checks/supports(.:format) checks#supports
|
# supports_checks POST /checks/supports(.:format) checks#supports
|
||||||
# checks GET /checks(.:format) checks#index
|
# checks GET /checks(.:format) checks#index
|
||||||
# POST /checks(.:format) checks#create
|
# POST /checks(.:format) checks#create
|
||||||
|
@ -31,6 +31,13 @@ end
|
||||||
# check PATCH /checks/:id(.:format) checks#update
|
# check PATCH /checks/:id(.:format) checks#update
|
||||||
# PUT /checks/:id(.:format) checks#update
|
# PUT /checks/:id(.:format) checks#update
|
||||||
# DELETE /checks/:id(.:format) checks#destroy
|
# DELETE /checks/:id(.:format) checks#destroy
|
||||||
|
# notifications GET /notifications(.:format) notifications#index
|
||||||
|
# POST /notifications(.:format) notifications#create
|
||||||
|
# new_notification GET /notifications/new(.:format) notifications#new
|
||||||
|
# edit_notification GET /notifications/:id/edit(.:format) notifications#edit
|
||||||
|
# notification PATCH /notifications/:id(.:format) notifications#update
|
||||||
|
# PUT /notifications/:id(.:format) notifications#update
|
||||||
|
# DELETE /notifications/:id(.:format) notifications#destroy
|
||||||
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
|
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
|
||||||
# user_session POST /users/sign_in(.:format) devise/sessions#create
|
# user_session POST /users/sign_in(.:format) devise/sessions#create
|
||||||
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
|
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
class AddConsecutiveFailuresToChecks < ActiveRecord::Migration[5.2]
|
class AddConsecutiveFailuresToChecks < ActiveRecord::Migration[5.2]
|
||||||
def change
|
def change
|
||||||
add_column :checks, :consecutive_failures, :integer, default: 0, null: false
|
add_column :checks, :consecutive_failures, :integer, default: 0, null: false
|
||||||
|
|
15
db/migrate/20180829073920_create_check_notifications.rb
Normal file
15
db/migrate/20180829073920_create_check_notifications.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
class CreateCheckNotifications < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :check_notifications do |t|
|
||||||
|
t.references :check, foreign_key: true
|
||||||
|
t.references :notification, foreign_key: true
|
||||||
|
t.integer :status, null: false, default: 0, limit: 1
|
||||||
|
t.datetime :sent_at
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
db/migrate/20180829164227_add_fields_to_notifications.rb
Normal file
41
db/migrate/20180829164227_add_fields_to_notifications.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
class AddFieldsToNotifications < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_reference :notifications, :user, foreign_key: true
|
||||||
|
add_column :notifications, :label, :string
|
||||||
|
add_column :notifications, :checks_count, :integer, default: 0, null: false
|
||||||
|
|
||||||
|
reversible do |dir|
|
||||||
|
dir.up do
|
||||||
|
# first set user & label for *all* notifications
|
||||||
|
Notification.find_each do |notification|
|
||||||
|
check = Check.find(notification.check_id) # check relation does not exist anymore
|
||||||
|
|
||||||
|
notification.user_id = check.user_id
|
||||||
|
notification.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
# then build the equivalent check notification
|
||||||
|
Notification.find_each do |notification|
|
||||||
|
assoc_notification = Notification.where(
|
||||||
|
user_id: notification.user_id,
|
||||||
|
recipient: notification.recipient,
|
||||||
|
interval: notification.interval,
|
||||||
|
).order(checks_count: :desc).limit(1).first
|
||||||
|
|
||||||
|
CheckNotification.create!(
|
||||||
|
check_id: notification.check_id,
|
||||||
|
notification: assoc_notification,
|
||||||
|
status: notification.status,
|
||||||
|
sent_at: notification.sent_at
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# last delete duplicate notification templates not used
|
||||||
|
Notification.where(checks_count: 0).destroy_all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
class RemoveObsoleteFieldsToNotifications < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
remove_column :notifications, :status, :integer, null: false, limit: 1, default: 0
|
||||||
|
remove_column :notifications, :sent_at, :datetime
|
||||||
|
end
|
||||||
|
end
|
24
db/schema.rb
24
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2018_08_29_134404) do
|
ActiveRecord::Schema.define(version: 2018_08_30_083927) do
|
||||||
|
|
||||||
create_table "check_logs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
create_table "check_logs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
||||||
t.bigint "check_id"
|
t.bigint "check_id"
|
||||||
|
@ -24,6 +24,17 @@ ActiveRecord::Schema.define(version: 2018_08_29_134404) do
|
||||||
t.index ["check_id"], name: "index_check_logs_on_check_id"
|
t.index ["check_id"], name: "index_check_logs_on_check_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "check_notifications", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
||||||
|
t.bigint "check_id"
|
||||||
|
t.bigint "notification_id"
|
||||||
|
t.integer "status", limit: 1, default: 0, null: false
|
||||||
|
t.datetime "sent_at"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["check_id"], name: "index_check_notifications_on_check_id"
|
||||||
|
t.index ["notification_id"], name: "index_check_notifications_on_notification_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "checks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
create_table "checks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
||||||
t.bigint "user_id"
|
t.bigint "user_id"
|
||||||
t.integer "kind", null: false
|
t.integer "kind", null: false
|
||||||
|
@ -38,8 +49,8 @@ ActiveRecord::Schema.define(version: 2018_08_29_134404) do
|
||||||
t.boolean "active", default: true, null: false
|
t.boolean "active", default: true, null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.boolean "round_robin", default: true
|
|
||||||
t.integer "consecutive_failures", default: 0, null: false
|
t.integer "consecutive_failures", default: 0, null: false
|
||||||
|
t.boolean "round_robin", default: true
|
||||||
t.integer "mode", default: 0, null: false
|
t.integer "mode", default: 0, null: false
|
||||||
t.index ["user_id"], name: "index_checks_on_user_id"
|
t.index ["user_id"], name: "index_checks_on_user_id"
|
||||||
end
|
end
|
||||||
|
@ -49,11 +60,13 @@ ActiveRecord::Schema.define(version: 2018_08_29_134404) do
|
||||||
t.integer "channel", default: 0, null: false
|
t.integer "channel", default: 0, null: false
|
||||||
t.string "recipient", null: false
|
t.string "recipient", null: false
|
||||||
t.integer "interval", null: false
|
t.integer "interval", null: false
|
||||||
t.integer "status", default: 0, null: false
|
|
||||||
t.datetime "sent_at"
|
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.bigint "user_id"
|
||||||
|
t.string "label"
|
||||||
|
t.integer "checks_count", default: 0, null: false
|
||||||
t.index ["check_id"], name: "index_notifications_on_check_id"
|
t.index ["check_id"], name: "index_notifications_on_check_id"
|
||||||
|
t.index ["user_id"], name: "index_notifications_on_user_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
|
||||||
|
@ -82,6 +95,9 @@ ActiveRecord::Schema.define(version: 2018_08_29_134404) do
|
||||||
end
|
end
|
||||||
|
|
||||||
add_foreign_key "check_logs", "checks"
|
add_foreign_key "check_logs", "checks"
|
||||||
|
add_foreign_key "check_notifications", "checks"
|
||||||
|
add_foreign_key "check_notifications", "notifications"
|
||||||
add_foreign_key "checks", "users"
|
add_foreign_key "checks", "users"
|
||||||
add_foreign_key "notifications", "checks"
|
add_foreign_key "notifications", "checks"
|
||||||
|
add_foreign_key "notifications", "users"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
|
||||||
|
|
||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
class NotificationsControllerTest < ActionDispatch::IntegrationTest
|
class NotificationsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
|
44
test/factories/check_notifications.rb
Normal file
44
test/factories/check_notifications.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: check_notifications
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# sent_at :datetime
|
||||||
|
# status :integer default("pending"), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# check_id :bigint(8)
|
||||||
|
# notification_id :bigint(8)
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_check_notifications_on_check_id (check_id)
|
||||||
|
# index_check_notifications_on_notification_id (notification_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (notification_id => notifications.id)
|
||||||
|
#
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :check_notification do
|
||||||
|
check
|
||||||
|
notification
|
||||||
|
status :pending
|
||||||
|
sent_at nil
|
||||||
|
|
||||||
|
trait :ongoing do
|
||||||
|
status :ongoing
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :succeed do
|
||||||
|
status :succeed
|
||||||
|
sent_at { 1.day.ago }
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :failed do
|
||||||
|
status :failed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -86,7 +86,9 @@ FactoryBot.define do
|
||||||
|
|
||||||
trait :with_notifications do
|
trait :with_notifications do
|
||||||
after :create do |check|
|
after :create do |check|
|
||||||
create_list :notification, 2, check: check
|
create_list :check_notification, 2,
|
||||||
|
check: check,
|
||||||
|
notification: build(:notification, user: check.user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,53 +1,41 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: notifications
|
# Table name: notifications
|
||||||
#
|
#
|
||||||
# id :bigint(8) not null, primary key
|
# id :bigint(8) not null, primary key
|
||||||
# channel :integer default("email"), not null
|
# channel :integer default("email"), not null
|
||||||
# interval :integer not null
|
# checks_count :integer default(0), not null
|
||||||
# recipient :string(255) not null
|
# interval :integer not null
|
||||||
# sent_at :datetime
|
# label :string(255)
|
||||||
# status :integer default("pending"), not null
|
# recipient :string(255) not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# check_id :bigint(8)
|
# check_id :bigint(8)
|
||||||
|
# user_id :bigint(8)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_notifications_on_check_id (check_id)
|
# index_notifications_on_check_id (check_id)
|
||||||
|
# index_notifications_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
# fk_rails_... (check_id => checks.id)
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
#
|
#
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :notification do
|
factory :notification do
|
||||||
check
|
user
|
||||||
interval 30
|
interval 30
|
||||||
channel :email
|
channel :email
|
||||||
|
label { "#{recipient} (#{interval})" }
|
||||||
recipient "recipient@domain.fr"
|
recipient "recipient@domain.fr"
|
||||||
status :pending
|
|
||||||
sent_at nil
|
|
||||||
|
|
||||||
trait :email do
|
trait :email do
|
||||||
channel :email
|
channel :email
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :ongoing do
|
|
||||||
status :ongoing
|
|
||||||
end
|
|
||||||
|
|
||||||
trait :succeed do
|
|
||||||
status :succeed
|
|
||||||
sent_at { 1.day.ago }
|
|
||||||
end
|
|
||||||
|
|
||||||
trait :failed do
|
|
||||||
status :failed
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,10 +6,11 @@ require "test_helper"
|
||||||
class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics/ClassLength
|
class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics/ClassLength
|
||||||
test "domain_expires_soon" do
|
test "domain_expires_soon" do
|
||||||
check = create(:check, domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"))
|
check = create(:check, domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"))
|
||||||
notification = build(:notification, interval: 10, check: check, recipient: "colin@example.org")
|
notification = build(:notification, interval: 10, recipient: "colin@example.org")
|
||||||
|
check_notification = build(:check_notification, check: check, notification: notification)
|
||||||
|
|
||||||
Date.stub :today, Date.new(2018, 6, 2) do
|
Date.stub :today, Date.new(2018, 6, 2) do
|
||||||
mail = NotificationsMailer.with(notification: notification).domain_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).domain_expires_soon
|
||||||
|
|
||||||
assert_emails 1 do
|
assert_emails 1 do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
@ -37,10 +38,11 @@ class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics
|
||||||
check = create(:check,
|
check = create(:check,
|
||||||
domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"),
|
domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"),
|
||||||
user: build(:user, :fr))
|
user: build(:user, :fr))
|
||||||
notification = build(:notification, interval: 10, check: check, recipient: "colin@example.org")
|
notification = build(:notification, interval: 10, recipient: "colin@example.org")
|
||||||
|
check_notification = build(:check_notification, check: check, notification: notification)
|
||||||
|
|
||||||
Date.stub :today, Date.new(2018, 6, 2) do
|
Date.stub :today, Date.new(2018, 6, 2) do
|
||||||
mail = NotificationsMailer.with(notification: notification).domain_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).domain_expires_soon
|
||||||
|
|
||||||
assert_emails 1 do
|
assert_emails 1 do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
@ -69,9 +71,9 @@ class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics
|
||||||
domain_expires_at: 1.week.from_now,
|
domain_expires_at: 1.week.from_now,
|
||||||
comment: "My comment",
|
comment: "My comment",
|
||||||
vendor: "The vendor")
|
vendor: "The vendor")
|
||||||
notification = build(:notification, check: check)
|
check_notification = build(:check_notification, check: check)
|
||||||
|
|
||||||
mail = NotificationsMailer.with(notification: notification).domain_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).domain_expires_soon
|
||||||
|
|
||||||
parts = [mail.text_part.decode_body, mail.html_part.decode_body]
|
parts = [mail.text_part.decode_body, mail.html_part.decode_body]
|
||||||
|
|
||||||
|
@ -87,9 +89,9 @@ class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics
|
||||||
comment: "My comment",
|
comment: "My comment",
|
||||||
vendor: "The vendor",
|
vendor: "The vendor",
|
||||||
user: build(:user, :fr))
|
user: build(:user, :fr))
|
||||||
notification = build(:notification, check: check)
|
check_notification = build(:check_notification, check: check)
|
||||||
|
|
||||||
mail = NotificationsMailer.with(notification: notification).domain_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).domain_expires_soon
|
||||||
|
|
||||||
parts = [mail.text_part.decode_body, mail.html_part.decode_body]
|
parts = [mail.text_part.decode_body, mail.html_part.decode_body]
|
||||||
|
|
||||||
|
@ -158,10 +160,11 @@ class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics
|
||||||
|
|
||||||
test "ssl_expires_soon" do
|
test "ssl_expires_soon" do
|
||||||
check = create(:check, :ssl, domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"))
|
check = create(:check, :ssl, domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"))
|
||||||
notification = build(:notification, interval: 10, check: check, recipient: "colin@example.org")
|
notification = build(:notification, interval: 10, recipient: "colin@example.org")
|
||||||
|
check_notification = build(:check_notification, check: check, notification: notification)
|
||||||
|
|
||||||
Date.stub :today, Date.new(2018, 6, 2) do
|
Date.stub :today, Date.new(2018, 6, 2) do
|
||||||
mail = NotificationsMailer.with(notification: notification).ssl_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).ssl_expires_soon
|
||||||
|
|
||||||
assert_emails 1 do
|
assert_emails 1 do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
@ -190,10 +193,11 @@ class NotificationsMailerTest < ActionMailer::TestCase # rubocop:disable Metrics
|
||||||
check = create(:check, :ssl,
|
check = create(:check, :ssl,
|
||||||
domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"),
|
domain_expires_at: Time.new(2018, 6, 10, 12, 0, 5, "+02:00"),
|
||||||
user: build(:user, :fr))
|
user: build(:user, :fr))
|
||||||
notification = build(:notification, interval: 10, check: check, recipient: "colin@example.org")
|
notification = build(:notification, interval: 10, recipient: "colin@example.org")
|
||||||
|
check_notification = build(:check_notification, check: check, notification: notification)
|
||||||
|
|
||||||
Date.stub :today, Date.new(2018, 6, 2) do
|
Date.stub :today, Date.new(2018, 6, 2) do
|
||||||
mail = NotificationsMailer.with(notification: notification).ssl_expires_soon
|
mail = NotificationsMailer.with(check_notification: check_notification).ssl_expires_soon
|
||||||
|
|
||||||
assert_emails 1 do
|
assert_emails 1 do
|
||||||
mail.deliver_now
|
mail.deliver_now
|
||||||
|
|
30
test/models/check_notification_test.rb
Normal file
30
test/models/check_notification_test.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: check_notifications
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# sent_at :datetime
|
||||||
|
# status :integer default("pending"), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# check_id :bigint(8)
|
||||||
|
# notification_id :bigint(8)
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_check_notifications_on_check_id (check_id)
|
||||||
|
# index_check_notifications_on_notification_id (notification_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (notification_id => notifications.id)
|
||||||
|
#
|
||||||
|
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CheckNotificationTest < ActiveSupport::TestCase
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
|
@ -15,6 +15,7 @@
|
||||||
# kind :integer not null
|
# kind :integer not null
|
||||||
# last_run_at :datetime
|
# last_run_at :datetime
|
||||||
# last_success_at :datetime
|
# last_success_at :datetime
|
||||||
|
# mode :integer default("auto"), not null
|
||||||
# round_robin :boolean default(TRUE)
|
# round_robin :boolean default(TRUE)
|
||||||
# vendor :string(255)
|
# vendor :string(255)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
@ -35,23 +36,23 @@ require "test_helper"
|
||||||
class CheckTest < ActiveSupport::TestCase
|
class CheckTest < ActiveSupport::TestCase
|
||||||
test "notifications are resetted when domain expiration date has changed" do
|
test "notifications are resetted when domain expiration date has changed" do
|
||||||
check = create(:check)
|
check = create(:check)
|
||||||
notification = create(:notification, :succeed, check: check)
|
check_notification = create(:check_notification, :succeed, check: check)
|
||||||
|
|
||||||
check.comment = "Will not reset because of this attribute"
|
check.comment = "Will not reset because of this attribute"
|
||||||
check.save!
|
check.save!
|
||||||
|
|
||||||
notification.reload
|
check_notification.reload
|
||||||
|
|
||||||
assert notification.succeed?
|
assert check_notification.succeed?
|
||||||
assert_not_nil notification.sent_at
|
assert_not_nil check_notification.sent_at
|
||||||
|
|
||||||
check.domain_expires_at = 1.year.from_now
|
check.domain_expires_at = 1.year.from_now
|
||||||
check.save!
|
check.save!
|
||||||
|
|
||||||
notification.reload
|
check_notification.reload
|
||||||
|
|
||||||
assert notification.pending?
|
assert check_notification.pending?
|
||||||
assert_nil notification.sent_at
|
assert_nil check_notification.sent_at
|
||||||
end
|
end
|
||||||
|
|
||||||
test "days_from_last_success without any success" do
|
test "days_from_last_success without any success" do
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Jeremy Lecour <jlecour@evolix.fr>, 2018 Evolix <info@evolix.fr>
|
||||||
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: notifications
|
# Table name: notifications
|
||||||
#
|
#
|
||||||
# id :bigint(8) not null, primary key
|
# id :bigint(8) not null, primary key
|
||||||
# channel :integer default("email"), not null
|
# channel :integer default("email"), not null
|
||||||
# interval :integer not null
|
# checks_count :integer default(0), not null
|
||||||
# recipient :string(255) not null
|
# interval :integer not null
|
||||||
# sent_at :datetime
|
# label :string(255)
|
||||||
# status :integer default("pending"), not null
|
# recipient :string(255) not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# check_id :bigint(8)
|
# check_id :bigint(8)
|
||||||
|
# user_id :bigint(8)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_notifications_on_check_id (check_id)
|
# index_notifications_on_check_id (check_id)
|
||||||
|
# index_notifications_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
# fk_rails_... (check_id => checks.id)
|
# fk_rails_... (check_id => checks.id)
|
||||||
|
# fk_rails_... (user_id => users.id)
|
||||||
#
|
#
|
||||||
|
|
||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
35
test/policies/check_notification_policy_test.rb
Normal file
35
test/policies/check_notification_policy_test.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
|
||||||
|
# License: GNU AGPL-3+ (see full text in LICENSE file)
|
||||||
|
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CheckNotificationPolicyTest < ActiveSupport::TestCase
|
||||||
|
setup do
|
||||||
|
@owner, @other = create_list(:user, 2)
|
||||||
|
@check_notification = create(:check_notification, check: build(:check, user: @owner))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "permit to check user" do
|
||||||
|
assert_permit @owner, @check_notification, :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
test "disallow to anonymous and other user" do
|
||||||
|
refute_permit @other, @check_notification, :destroy
|
||||||
|
refute_permit nil, @check_notification, :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
test "scope only to user checks" do
|
||||||
|
other_notifications = create_list(:check_notification, 2, check: build(:check, user: @other))
|
||||||
|
|
||||||
|
assert_empty Pundit.policy_scope!(nil, CheckNotification)
|
||||||
|
assert_equal [@check_notification], Pundit.policy_scope!(@owner, CheckNotification)
|
||||||
|
assert_equal other_notifications, Pundit.policy_scope!(@other, CheckNotification)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "disabled actions" do
|
||||||
|
refute_permit @owner, @check_notification, :update
|
||||||
|
refute_permit @owner, @check_notification, :edit
|
||||||
|
refute_permit @owner, @check_notification, :create
|
||||||
|
refute_permit @owner, @check_notification, :index
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,30 +6,32 @@ require "test_helper"
|
||||||
class NotificationPolicyTest < ActiveSupport::TestCase
|
class NotificationPolicyTest < ActiveSupport::TestCase
|
||||||
setup do
|
setup do
|
||||||
@owner, @other = create_list(:user, 2)
|
@owner, @other = create_list(:user, 2)
|
||||||
@notification = create(:notification, check: build(:check, user: @owner))
|
@notification = create(:notification, user: @owner)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "permit to check user" do
|
test "create" do
|
||||||
|
assert_permit @other, Notification, :create
|
||||||
|
assert_permit @other, Notification, :new
|
||||||
|
end
|
||||||
|
|
||||||
|
test "permit to owner" do
|
||||||
|
assert_permit @owner, @notification, :edit
|
||||||
|
assert_permit @owner, @notification, :update
|
||||||
assert_permit @owner, @notification, :destroy
|
assert_permit @owner, @notification, :destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
test "disallow to anonymous and other user" do
|
test "disallow to anonymous and other user" do
|
||||||
refute_permit @other, @notification, :destroy
|
%i[update edit destroy].each do |action|
|
||||||
refute_permit nil, @notification, :destroy
|
refute_permit @other, @notification, action
|
||||||
|
refute_permit nil, @notification, action
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "scope only to user checks" do
|
test "scope only to owners" do
|
||||||
other_notifications = create_list(:notification, 2, check: build(:check, user: @other))
|
other_notifications = create_list(:notification, 2, user: @other)
|
||||||
|
|
||||||
assert_empty Pundit.policy_scope!(nil, Notification)
|
assert_empty Pundit.policy_scope!(nil, Notification)
|
||||||
assert_equal [@notification], Pundit.policy_scope!(@owner, Notification)
|
assert_equal [@notification], Pundit.policy_scope!(@owner, Notification)
|
||||||
assert_equal other_notifications, Pundit.policy_scope!(@other, Notification)
|
assert_equal other_notifications, Pundit.policy_scope!(@other, Notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "disabled actions" do
|
|
||||||
refute_permit @owner, @notification, :update
|
|
||||||
refute_permit @owner, @notification, :edit
|
|
||||||
refute_permit @owner, @notification, :create
|
|
||||||
refute_permit @owner, @notification, :index
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,8 @@ module Notifier
|
||||||
class BaseTest < ActiveSupport::TestCase
|
class BaseTest < ActiveSupport::TestCase
|
||||||
setup do
|
setup do
|
||||||
class FakeChannel < Base
|
class FakeChannel < Base
|
||||||
def supports?(notification)
|
def supports?(check_notification)
|
||||||
notification.interval < 1_000
|
check_notification.notification.interval < 1_000
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_notify_expires_soon(*); end
|
def domain_notify_expires_soon(*); end
|
||||||
|
@ -19,45 +19,46 @@ module Notifier
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notify change the status of the notification" do
|
test "#notify change the status of the notification" do
|
||||||
notification = create(:notification)
|
check_notification = create(:check_notification)
|
||||||
|
|
||||||
@channel.notify(notification)
|
@channel.notify(check_notification)
|
||||||
|
|
||||||
notification.reload
|
check_notification.reload
|
||||||
|
|
||||||
assert notification.ongoing?
|
assert check_notification.ongoing?
|
||||||
assert_just_now notification.sent_at
|
assert_just_now check_notification.sent_at
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notify raises an exception for a non supported check kind" do
|
test "#notify raises an exception for a non supported check kind" do
|
||||||
notification = Minitest::Mock.new
|
check_notification = Minitest::Mock.new
|
||||||
notification.expect :ongoing!, true
|
check_notification.expect :ongoing!, true
|
||||||
notification.expect :interval, 10
|
check_notification.expect :notification, OpenStruct.new(interval: 10)
|
||||||
|
|
||||||
check = Minitest::Mock.new
|
check = Minitest::Mock.new
|
||||||
check.expect(:kind, :invalid_kind)
|
check.expect(:kind, :invalid_kind)
|
||||||
check.expect(:kind, :invalid_kind) # twice (second call for exception message)
|
check.expect(:kind, :invalid_kind) # twice (second call for exception message)
|
||||||
|
|
||||||
notification.expect :check, check
|
check_notification.expect :check, check
|
||||||
notification.expect :check, check
|
check_notification.expect :check, check
|
||||||
|
|
||||||
assert_raises ArgumentError do
|
assert_raises ArgumentError do
|
||||||
@channel.notify(notification)
|
@channel.notify(check_notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
check.verify
|
check.verify
|
||||||
notification.verify
|
check_notification.verify
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notify does nothing when channel doesn't support a notification whatever the reason" do
|
test "#notify does nothing when channel doesn't support a notification whatever the reason" do
|
||||||
notification = create(:notification, interval: 10_000)
|
check_notification = create(:check_notification,
|
||||||
|
notification: build(:notification, interval: 10_000))
|
||||||
|
|
||||||
@channel.notify(notification)
|
@channel.notify(check_notification)
|
||||||
|
|
||||||
notification.reload
|
check_notification.reload
|
||||||
|
|
||||||
assert notification.pending?
|
assert check_notification.pending?
|
||||||
assert_nil notification.sent_at
|
assert_nil check_notification.sent_at
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,11 @@ require "test_helper"
|
||||||
|
|
||||||
module Notifier
|
module Notifier
|
||||||
class ProcessorTest < ActiveSupport::TestCase
|
class ProcessorTest < ActiveSupport::TestCase
|
||||||
|
# rubocop:disable Metrics/LineLength
|
||||||
test "#process_expires_soon sends an email for checks expiring soon" do
|
test "#process_expires_soon sends an email for checks expiring soon" do
|
||||||
create_list(:notification, 3, :email, check: build(:check, :expires_next_week))
|
create_list(:check_notification, 3, notification: email_notification, check: build(:check, :expires_next_week))
|
||||||
create(:notification, :email, check: build(:check, :nil_dates))
|
create(:check_notification, notification: email_notification, check: build(:check, :nil_dates))
|
||||||
create(:notification, :email, check: build(:check, :inactive))
|
create(:check_notification, notification: email_notification, check: build(:check, :inactive))
|
||||||
|
|
||||||
processor = Processor.new
|
processor = Processor.new
|
||||||
assert_difference "ActionMailer::Base.deliveries.size", +3 do
|
assert_difference "ActionMailer::Base.deliveries.size", +3 do
|
||||||
|
@ -20,7 +21,7 @@ module Notifier
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#process_expires_soon respects the interval configuration between sends" do
|
test "#process_expires_soon respects the interval configuration between sends" do
|
||||||
create_list(:notification, 3, :email, check: build(:check, :expires_next_week))
|
create_list(:check_notification, 3, notification: email_notification, check: build(:check, :expires_next_week))
|
||||||
test_interval_respected(:process_expires_soon, 3)
|
test_interval_respected(:process_expires_soon, 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,9 +31,14 @@ module Notifier
|
||||||
configuration.expect(:consecutive_failures, 4.2)
|
configuration.expect(:consecutive_failures, 4.2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/LineLength
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def email_notification
|
||||||
|
build(:notification, :email)
|
||||||
|
end
|
||||||
|
|
||||||
# rubocop:disable Metrics/MethodLength
|
# rubocop:disable Metrics/MethodLength
|
||||||
def test_interval_respected(process_method, count_expected)
|
def test_interval_respected(process_method, count_expected)
|
||||||
configuration = Minitest::Mock.new
|
configuration = Minitest::Mock.new
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
|
# rubocop:disable Metrics/LineLength
|
||||||
module Notifier
|
module Notifier
|
||||||
class ResolverTest < ActiveSupport::TestCase
|
class ResolverTest < ActiveSupport::TestCase
|
||||||
setup do
|
setup do
|
||||||
|
@ -10,74 +11,74 @@ module Notifier
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon ignores user having notification disabled" do
|
test "#notifications_expiring_soon ignores user having notification disabled" do
|
||||||
n1 = create(:notification, check: build(:check, :expires_next_week))
|
n1 = create(:check_notification, check: build(:check, :expires_next_week))
|
||||||
n1.check.user.update_attribute(:notifications_enabled, false)
|
n1.check.user.update_attribute(:notifications_enabled, false)
|
||||||
n2 = create(:notification, check: build(:check, :expires_next_week))
|
n2 = create(:check_notification, check: build(:check, :expires_next_week))
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_not_includes notifications, n1
|
assert_not_includes check_notifications, n1
|
||||||
assert_includes notifications, n2
|
assert_includes check_notifications, n2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon ignores inactive checks" do
|
test "#notifications_expiring_soon ignores inactive checks" do
|
||||||
n1 = create(:notification, check: build(:check, :expires_next_week, :inactive))
|
n1 = create(:check_notification, check: build(:check, :expires_next_week, :inactive))
|
||||||
n2 = create(:notification, check: build(:check, :expires_next_week))
|
n2 = create(:check_notification, check: build(:check, :expires_next_week))
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_not_includes notifications, n1
|
assert_not_includes check_notifications, n1
|
||||||
assert_includes notifications, n2
|
assert_includes check_notifications, n2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon gets only checks inside interval" do
|
test "#notifications_expiring_soon gets only checks inside interval" do
|
||||||
n1 = create(:notification, check: build(:check, :expires_next_week), interval: 6)
|
n1 = create(:check_notification, check: build(:check, :expires_next_week), notification: build(:notification, interval: 6))
|
||||||
n2 = create(:notification, check: build(:check, :expires_next_week), interval: 7)
|
n2 = create(:check_notification, check: build(:check, :expires_next_week), notification: build(:notification, interval: 7))
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_not_includes notifications, n1
|
assert_not_includes check_notifications, n1
|
||||||
assert_includes notifications, n2
|
assert_includes check_notifications, n2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon can gets several notifications for a same check" do
|
test "#notifications_expiring_soon can gets several notifications for a same check" do
|
||||||
check = create(:check, :expires_next_week)
|
check = create(:check, :expires_next_week)
|
||||||
n1 = create(:notification, check: check, interval: 3)
|
n1 = create(:check_notification, check: check, notification: build(:notification, interval: 3))
|
||||||
n2 = create(:notification, check: check, interval: 10)
|
n2 = create(:check_notification, check: check, notification: build(:notification, interval: 10))
|
||||||
n3 = create(:notification, check: check, interval: 30)
|
n3 = create(:check_notification, check: check, notification: build(:notification, interval: 30))
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_not_includes notifications, n1
|
assert_not_includes check_notifications, n1
|
||||||
assert_includes notifications, n2
|
assert_includes check_notifications, n2
|
||||||
assert_includes notifications, n3
|
assert_includes check_notifications, n3
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon takes care of the status" do
|
test "#notifications_expiring_soon takes care of the status" do
|
||||||
check = create(:check, :expires_next_week)
|
check = create(:check, :expires_next_week)
|
||||||
n1 = create(:notification, check: check)
|
n1 = create(:check_notification, check: check)
|
||||||
n2 = create(:notification, :failed, check: check)
|
n2 = create(:check_notification, :failed, check: check)
|
||||||
n3 = create(:notification, :ongoing, check: check)
|
n3 = create(:check_notification, :ongoing, check: check)
|
||||||
n4 = create(:notification, :succeed, check: check)
|
n4 = create(:check_notification, :succeed, check: check)
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_includes notifications, n1
|
assert_includes check_notifications, n1
|
||||||
assert_includes notifications, n2
|
assert_includes check_notifications, n2
|
||||||
assert_not_includes notifications, n3
|
assert_not_includes check_notifications, n3
|
||||||
assert_not_includes notifications, n4
|
assert_not_includes check_notifications, n4
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#notifications_expiring_soon ignores checks expired and without date" do
|
test "#notifications_expiring_soon ignores checks expired and without date" do
|
||||||
n1 = create(:notification, check: build(:check, :expires_next_week))
|
n1 = create(:check_notification, check: build(:check, :expires_next_week))
|
||||||
n2 = create(:notification, check: build(:check, domain_expires_at: 1.week.ago))
|
n2 = create(:check_notification, check: build(:check, domain_expires_at: 1.week.ago))
|
||||||
n3 = create(:notification, check: build(:check, :nil_dates))
|
n3 = create(:check_notification, check: build(:check, :nil_dates))
|
||||||
|
|
||||||
notifications = @resolver.notifications_expiring_soon
|
check_notifications = @resolver.notifications_expiring_soon
|
||||||
|
|
||||||
assert_includes notifications, n1
|
assert_includes check_notifications, n1
|
||||||
assert_not_includes notifications, n2
|
assert_not_includes check_notifications, n2
|
||||||
assert_not_includes notifications, n3
|
assert_not_includes check_notifications, n3
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#checks_recurrent_failures ignores inactive checks" do
|
test "#checks_recurrent_failures ignores inactive checks" do
|
||||||
|
@ -116,3 +117,4 @@ module Notifier
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/LineLength
|
||||||
|
|
|
@ -9,7 +9,7 @@ class ChecksTest < ApplicationSystemTestCase
|
||||||
login_as(@user)
|
login_as(@user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create a check and a notification without kind" do
|
test "create a check and a new notification without kind" do
|
||||||
visit new_check_path
|
visit new_check_path
|
||||||
|
|
||||||
choose "domain"
|
choose "domain"
|
||||||
|
@ -53,20 +53,34 @@ class ChecksTest < ApplicationSystemTestCase
|
||||||
fill_and_valid_new_check
|
fill_and_valid_new_check
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove a notification" do
|
test "dissociate a notification" do
|
||||||
check = create(:check, :with_notifications, domain: "dom-with-notif.net", user: @user)
|
check = create(:check, :with_notifications, user: @user)
|
||||||
|
notification = create(:notification, label: "label-notification", user: @user)
|
||||||
|
check.notifications << notification
|
||||||
|
|
||||||
visit edit_check_path(check)
|
visit edit_check_path(check)
|
||||||
notification = check.notifications.first
|
|
||||||
|
|
||||||
selector = "[data-notification-id=\"#{notification.id}\"]"
|
uncheck notification.label
|
||||||
|
|
||||||
assert_difference "Notification.where(check_id: #{check.id}).count", -1 do
|
click_button "Update Check"
|
||||||
within selector do
|
|
||||||
find(".btn-danger").click
|
|
||||||
end
|
|
||||||
|
|
||||||
page.has_no_content?(selector)
|
notification.reload
|
||||||
end
|
assert_equal 0, notification.checks_count
|
||||||
|
assert_equal 2, check.check_notifications.count
|
||||||
|
end
|
||||||
|
|
||||||
|
test "associate a notification" do
|
||||||
|
check = create(:check, user: @user)
|
||||||
|
notification = create(:notification, label: "label-notification", user: @user)
|
||||||
|
visit edit_check_path(check)
|
||||||
|
|
||||||
|
check notification.label
|
||||||
|
click_button "Update Check"
|
||||||
|
|
||||||
|
notification.reload
|
||||||
|
|
||||||
|
assert_equal 1, notification.checks_count
|
||||||
|
assert_equal 1, check.check_notifications.count
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update a check" do
|
test "update a check" do
|
||||||
|
@ -84,29 +98,6 @@ class ChecksTest < ApplicationSystemTestCase
|
||||||
assert_equal "My comment", check.comment
|
assert_equal "My comment", check.comment
|
||||||
end
|
end
|
||||||
|
|
||||||
test "add a notification" do
|
|
||||||
check = create(:check, :with_notifications, domain: "dom-with-notif.net", user: @user)
|
|
||||||
visit edit_check_path(check)
|
|
||||||
|
|
||||||
recipient = "recipient2@example.org"
|
|
||||||
fill_in("check[notifications_attributes][2][recipient]", with: recipient)
|
|
||||||
fill_in("check[notifications_attributes][2][interval]", with: 55)
|
|
||||||
|
|
||||||
assert_difference "Notification.where(check_id: #{check.id}).count", +1 do
|
|
||||||
click_button "Update Check"
|
|
||||||
|
|
||||||
assert_equal checks_path, page.current_path
|
|
||||||
end
|
|
||||||
|
|
||||||
assert page.has_css?(".alert-success")
|
|
||||||
|
|
||||||
notification = Notification.last
|
|
||||||
assert_equal recipient, notification.recipient
|
|
||||||
assert_equal 55, notification.interval
|
|
||||||
assert notification.email?
|
|
||||||
assert notification.pending?
|
|
||||||
end
|
|
||||||
|
|
||||||
test "list my checks" do
|
test "list my checks" do
|
||||||
create(:check, :domain, domain: "dom.com", domain_expires_at: Time.new(2018, 7, 5, 12), user: @user) # rubocop:disable Metrics/LineLength
|
create(:check, :domain, domain: "dom.com", domain_expires_at: Time.new(2018, 7, 5, 12), user: @user) # rubocop:disable Metrics/LineLength
|
||||||
create(:check, :ssl, domain: "ssldom.com", user: @user)
|
create(:check, :ssl, domain: "ssldom.com", user: @user)
|
||||||
|
@ -286,6 +277,8 @@ class ChecksTest < ApplicationSystemTestCase
|
||||||
fill_in("check[domain]", with: domain)
|
fill_in("check[domain]", with: domain)
|
||||||
|
|
||||||
recipient = "recipient@example.org"
|
recipient = "recipient@example.org"
|
||||||
|
label = "my new notificatiion"
|
||||||
|
fill_in("check[notifications_attributes][0][label]", with: label)
|
||||||
fill_in("check[notifications_attributes][0][recipient]", with: recipient)
|
fill_in("check[notifications_attributes][0][recipient]", with: recipient)
|
||||||
fill_in("check[notifications_attributes][0][interval]", with: 30)
|
fill_in("check[notifications_attributes][0][interval]", with: 30)
|
||||||
|
|
||||||
|
@ -297,10 +290,14 @@ class ChecksTest < ApplicationSystemTestCase
|
||||||
assert page.has_content?(domain)
|
assert page.has_content?(domain)
|
||||||
|
|
||||||
notification = Notification.last
|
notification = Notification.last
|
||||||
|
assert_equal label, notification.label
|
||||||
assert_equal recipient, notification.recipient
|
assert_equal recipient, notification.recipient
|
||||||
assert_equal 30, notification.interval
|
assert_equal 30, notification.interval
|
||||||
assert notification.email?
|
assert notification.email?
|
||||||
assert notification.pending?
|
|
||||||
|
check_notification = CheckNotification.last
|
||||||
|
assert check_notification.pending?
|
||||||
|
assert_nil check_notification.sent_at
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/AbcSize
|
# rubocop:enable Metrics/AbcSize
|
||||||
# rubocop:enable Metrics/MethodLength
|
# rubocop:enable Metrics/MethodLength
|
||||||
|
|
|
@ -52,7 +52,7 @@ class UsersTest < ApplicationSystemTestCase
|
||||||
|
|
||||||
click_button I18n.t("devise.sessions.new.sign_in")
|
click_button I18n.t("devise.sessions.new.sign_in")
|
||||||
|
|
||||||
assert_equal root_path, page.current_path
|
assert_equal checks_path, page.current_path
|
||||||
assert page.has_content?(@user.email)
|
assert page.has_content?(@user.email)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue