Drop system notifications tables and remove logic

This commit is contained in:
Ivan Kljun 2023-07-25 14:48:03 +02:00
parent b516382fc4
commit 18fb319351
38 changed files with 14 additions and 1144 deletions

View file

@ -147,7 +147,6 @@
noRecentText.show();
}
bindSystemNotificationAjax();
SystemNotificationsMarkAsSeen();
}
});
$('#count-system-notifications').hide();

View file

@ -1,45 +0,0 @@
'use strict';
// update selected notiifcations
function SystemNotificationsMarkAsSeen() {
if ($('.system-notification[data-new="1"]').length > 0) {
$.post('/system_notifications/mark_as_seen');
}
}
function bindSystemNotificationAjax() {
var SystemNotificationModal = null;
var SystemNotificationModalBody = null;
var SystemNotificationModalTitle = null;
SystemNotificationModal = $('#manage-module-system-notification-modal');
SystemNotificationModalBody = SystemNotificationModal.find('.modal-body');
SystemNotificationModalTitle = SystemNotificationModal.find('#manage-module-system-notification-modal-label');
$('.modal-system-notification')
.on('ajax:success', function(ev, data) {
var SystemNotification = $('.system-notification[data-system-notification-id=' + data.id + ']');
SystemNotificationModalBody.html(data.modal_body);
SystemNotificationModalTitle.text(data.modal_title);
$('.dropdown.system-notifications').removeClass('open');
// Open modal
SystemNotificationModal.modal('show');
});
}
function initSystemNotificationsButton() {
$('.btn-more-system-notifications')
.on('ajax:success', function(e, data) {
$(data.html).insertAfter($('.system-notifications-container .system-notification').last());
bindSystemNotificationAjax();
if (data.more_url) {
$(this).attr('href', data.more_url);
} else {
$(this).remove();
}
});
}
initSystemNotificationsButton();
SystemNotificationsMarkAsSeen();
bindSystemNotificationAjax();

View file

@ -1 +0,0 @@
$('#manage-module-system-notification-modal').modal('show');

View file

@ -58,16 +58,6 @@
}
}
.sci--navigation--notificaitons-flyout-tab {
cursor: pointer;
padding: .5rem .625rem;
position: relative;
&.active {
color: $brand-focus;
}
}
hr {
margin: .625rem 0;
}

View file

@ -1,56 +0,0 @@
# frozen_string_literal: true
class SystemNotificationsController < ApplicationController
def show
current_user.user_system_notifications.mark_as_read(params[:id])
render json: current_user.system_notifications.modals
.find_by_id(params[:id]) || {}
end
# Update seen_at parameter for system notifications
def mark_as_seen
current_user.user_system_notifications.mark_as_seen
render json: { result: 'ok' }
rescue StandardError
render json: { result: 'failed' }
end
# Update read_at parameter for system notifications
def mark_as_read
current_user.user_system_notifications.mark_as_read(params[:id])
render json: { result: 'ok' }
rescue StandardError
render json: { result: 'failed' }
end
def unseen_counter
render json: {
notificationNmber: current_user.user_system_notifications.unseen.count
}
end
private
def prepare_notifications
page = (params[:page] || 1).to_i
query = params[:search_queue]
per_page = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT
notifications = SystemNotification.last_notifications(current_user, query)
.page(page)
.per(per_page)
unless notifications.blank? || notifications.last_page?
more_url = url_for(
system_notifications_url(
format: :json,
page: page + 1,
search_queue: query
)
)
end
@system_notifications = {
notifications: notifications,
more_notifications_url: more_url
}
end
end

View file

@ -13,44 +13,20 @@ class UserNotificationsController < ApplicationController
UserNotification.where(
notification_id: notifications.except(:select).where.not(type_of: 2).select(:id)
).seen_by_user(current_user)
current_user.user_system_notifications.where(
system_notification_id: notifications.except(:select).where(type_of: 2).select(:id)
).mark_as_seen
end
def unseen_counter
render json: {
unseen: load_notifications.where(checked: false).size
unseen: load_notifications.where('user_notifications.checked = ?', false).size
}
end
private
def load_notifications
user_notifications = current_user.notifications
.select(:id, :type_of, :title, :message, :created_at, 'user_notifications.checked')
system_notifications = current_user.system_notifications
.select(
:id,
'2 AS type_of',
:title,
'description AS message',
:created_at,
'CASE WHEN seen_at IS NULL THEN false ELSE true END AS checked'
)
notifications =
case params[:type]
when 'message'
user_notifications
when 'system'
Notification.from("(#{system_notifications.to_sql}) AS notifications")
else
Notification.from(
"((#{user_notifications.to_sql}) UNION ALL (#{system_notifications.to_sql})) AS notifications"
)
end
notifications.order(created_at: :desc)
current_user.notifications
.select(:id, :type_of, :title, :message, :created_at, 'user_notifications.checked')
.order(created_at: :desc)
end
def notification_serializer(notifications)
@ -62,8 +38,7 @@ class UserNotificationsController < ApplicationController
message: notification.message,
created_at: I18n.l(notification.created_at, format: :full),
today: notification.created_at.today?,
checked: notification.checked,
action_url: (system_notification_path(notification.id) if notification.type_of == 'system_message')
checked: notification.checked
}
end
end

View file

@ -2,7 +2,6 @@
class Users::SessionsController < Devise::SessionsController
layout :session_layout
after_action :after_sign_in, only: %i(create authenticate_with_two_factor)
before_action :remove_authenticate_mesasge_if_root_path, only: :new
prepend_before_action :skip_timeout, only: :expire_in
@ -58,10 +57,6 @@ class Users::SessionsController < Devise::SessionsController
@initial_page = stored_location_for(:user)
end
def after_sign_in
flash[:system_notification_modal] = true
end
def authenticate_with_two_factor
user = User.find_by(id: session[:otp_user_id])

View file

@ -9,8 +9,7 @@
<div class="sci-navigation--notificaitons-flyout-notification-title"
v-html="notification.title"
:data-seen="notification.checked"></div>
<div v-if="notification.type_of !== 'system_message'" v-html="notification.message" class="sci-navigation--notificaitons-flyout-notification-message"></div>
<a v-else @click="showSystemNotification()" class="sci-navigation--notificaitons-flyout-notification-message" data-notification="system">{{ i18n.t('nav.notifications.read_more') }}</a>
<div v-html="notification.message" class="sci-navigation--notificaitons-flyout-notification-message"></div>
</div>
</template>
@ -25,26 +24,12 @@ export default {
switch(this.notification.type_of) {
case 'deliver':
return 'fa-truck';
case 'system_message':
return 'fa-gift';
case 'assignment':
return 'fa-list-alt';
case 'recent_changes':
return 'fa-list-alt';
}
}
},
methods: {
showSystemNotification() {
$.get(this.notification.action_url, (data) => {
let systemNotificationModal = $('#manage-module-system-notification-modal');
let systemNotificationModalBody = systemNotificationModal.find('.modal-body');
let systemNotificationModalTitle = systemNotificationModal.find('#manage-module-system-notification-modal-label');
systemNotificationModalBody.html(data.modal_body);
systemNotificationModalTitle.text(data.modal_title);
systemNotificationModal.modal('show');
});
}
}
}
</script>

View file

@ -67,7 +67,7 @@ export default {
if (this.nextPage == null || this.loadingPage) return;
this.loadingPage = true;
$.getJSON(this.notificationsUrl, { type: this.activeTab, page: this.nextPage }, (result) => {
$.getJSON(this.notificationsUrl, { page: this.nextPage }, (result) => {
this.notifications = this.notifications.concat(result.notifications);
this.nextPage = result.next_page;
this.loadingPage = false;

View file

@ -22,16 +22,4 @@ class AppMailer < Devise::Mailer
}.merge(opts)
mail(headers)
end
def system_notification(user, system_notification, opts = {})
@user = user
@system_notification = system_notification
headers = {
to: @user.email,
subject: t('system_notifications.emails.subject')
}.merge(opts)
mail(headers)
end
end

View file

@ -1,51 +0,0 @@
# frozen_string_literal: true
class SystemNotification < ApplicationRecord
include PgSearch::Model
scope :modals, -> { select(:modal_title, :modal_body, :id) }
# Full text postgreSQL search configuration
pg_search_scope :search_notifications, against: %i(title description),
using: {
tsearch: {
dictionary: 'english'
}
}
# ignoring: :accents
has_many :user_system_notifications, dependent: :destroy
has_many :users, through: :user_system_notifications
validates :title, :modal_title, :modal_body, :description, :source_created_at, :source_id, :last_time_changed_at,
presence: true
validates :title, :description, :modal_title, length: { maximum: Constants::NAME_MAX_LENGTH }
validates :modal_body, length: { maximum: Constants::RICH_TEXT_MAX_LENGTH }
def self.last_notifications(
user,
query = nil
)
notifications = order(created_at: :DESC)
notifications = notifications.search_notifications(query) if query.present?
notifications.joins(:user_system_notifications)
.where('user_system_notifications.user_id = ?', user.id)
.select(
'system_notifications.id',
:title,
:description,
:last_time_changed_at,
:created_at,
'user_system_notifications.seen_at',
'user_system_notifications.read_at'
)
end
def self.last_sync_timestamp
# If no notifications are present, the created_at of the
# first user is used as the "initial sync time-point"
SystemNotification
.order(last_time_changed_at: :desc)
.first&.last_time_changed_at&.to_i ||
User.order(created_at: :asc).first&.created_at&.to_i
end
end

View file

@ -310,8 +310,6 @@ class User < ApplicationRecord
has_many :user_notifications, inverse_of: :user
has_many :notifications, through: :user_notifications
has_many :user_system_notifications, dependent: :destroy
has_many :system_notifications, through: :user_system_notifications
has_many :zip_exports, inverse_of: :user, dependent: :destroy
has_many :datatables_teams, class_name: '::Views::Datatables::DatatablesTeam'
has_many :view_states, dependent: :destroy
@ -560,12 +558,6 @@ class User < ApplicationRecord
user_identities.exists?(provider: provider)
end
# This method must be overwriten for addons that will be installed
def show_login_system_notification?
user_system_notifications.show_on_login.present? &&
(ENV['ENABLE_TUTORIAL'] != 'true' || settings['tutorial_completed'])
end
# json friendly attributes
NOTIFICATIONS_TYPES = %w(assignments_notification recent_notification
assignments_email_notification

View file

@ -1,45 +0,0 @@
# frozen_string_literal: true
class UserSystemNotification < ApplicationRecord
belongs_to :user
belongs_to :system_notification
validates :system_notification, uniqueness: { scope: :user }
scope :unseen, -> { where(seen_at: nil) }
def self.mark_as_seen
unseen.update_all(seen_at: Time.now)
end
def self.mark_as_read(notification_id)
notification = find_by_system_notification_id(notification_id)
notification.update(read_at: Time.now) if notification && notification.read_at.nil?
end
def self.show_on_login(update_read_time = false)
# for notification check leave update_read_time empty
notification = joins(:system_notification)
.where('system_notifications.show_on_login = true')
.order('system_notifications.created_at DESC')
.select(
:modal_title,
:modal_body,
'user_system_notifications.id',
:read_at,
:user_id,
:system_notification_id,
:created_at
)
.first
if notification && notification.read_at.nil?
if update_read_time
notification.update(
read_at: Time.now,
seen_at: Time.now
)
end
notification
end
end
end

View file

@ -1,27 +0,0 @@
# frozen_string_literal: true
module Notifications
class HandleSystemNotificationInCommunicationChannelService
extend Service
attr_reader :errors
def initialize(system_notification)
@system_notification = system_notification
@errors = {}
end
def call
@system_notification.user_system_notifications.find_each do |usn|
user = usn.user
AppMailer.delay.system_notification(user, @system_notification) if user.system_message_email_notification
end
self
end
def succeed?
@errors.none?
end
end
end

View file

@ -1,40 +0,0 @@
# frozen_string_literal: true
module Notifications
class PushToCommunicationChannelService
extend Service
WHITELISTED_ITEM_TYPES = %w(SystemNotification).freeze
attr_reader :errors
def initialize(item_id:, item_type:)
@item_type = item_type
@item = item_type.constantize.find item_id
@errors = {}
end
def call
return self unless valid?
"Notifications::Handle#{@item_type}InCommunicationChannelService".constantize.call(@item)
self
end
def succeed?
@errors.none?
end
private
def valid?
raise 'Dont know how to handle this type of items' unless WHITELISTED_ITEM_TYPES.include?(@item_type)
if @item.nil?
@errors[:invalid_arguments] = 'Can\'t find item' if @item.nil?
return false
end
true
end
end
end

View file

@ -1,117 +0,0 @@
# frozen_string_literal: true
module Notifications
class SyncSystemNotificationsService
extend Service
include HTTParty
base_uri Rails.application.secrets.system_notifications_uri
SYNC_TIMESTAMP_CACHE_KEY = 'system_notifications_last_sync_timestamp'
attr_reader :errors
def initialize
@errors = {}
end
def call
call_api
save_new_notifications if succeed?
self
end
def succeed?
@errors.none?
end
def self.available?
channel = Rails.application.secrets.system_notifications_channel
query = { query: { last_sync_timestamp: Time.now.to_i, channels_slug: channel },
headers: { 'accept': 'application/vnd.system-notifications.1+json' } }
response = get('/api/system_notifications', query)
response.code < Rack::Utils::SYMBOL_TO_STATUS_CODE[:bad_request]
end
private
def call_api
last_sync =
Rails.cache.fetch(SYNC_TIMESTAMP_CACHE_KEY, expires_in: 24.hours, skip_nil: true) do
SystemNotification.last_sync_timestamp
end
channel = Rails.application.secrets.system_notifications_channel
unless last_sync
@errors[:last_sync_timestamp] = 'Cannot find last_sync_timestamp'
return false
end
query = { query: { last_sync_timestamp: last_sync,
channels_slug: channel },
headers: { 'accept':
'application/vnd.system-notifications.1+json' } }
# rubocop:disable Lint/ShadowedException:
begin
@api_call = self.class.get('/api/system_notifications', query)
if @api_call.response.code.to_i != 200
@errors[:api_error] =
[@api_call.response.code.to_s, @api_call.response.message].join('-')
# Add message for logging if exists
if @api_call.parsed_response.try('error')
@errors[:api_error] += ': ' + @api_call
.parsed_response['error']
.flatten&.join(' - ').to_s
end
end
rescue SocketError, HTTParty::Error, StandardError => e
@errors[e.class.to_s.downcase.to_sym] = e.message
end
# rubocop:enable Lint/ShadowedException:
end
def save_new_notifications
received_notifications = @api_call.parsed_response['notifications']
return if received_notifications.blank?
received_notifications.each do |received_notification|
# Save new notification if not exists or override old 1
attrs = received_notification
.slice('title', 'description', 'modal_title', 'modal_body', 'show_on_login', 'source_id')
.merge('source_created_at': Time.zone.parse(received_notification['source_created_at']),
'last_time_changed_at': Time.zone.parse(received_notification['last_time_changed_at']))
.symbolize_keys
notification = SystemNotification.where(source_id: attrs[:source_id]).first_or_initialize(attrs)
if notification.new_record?
save_notification(notification)
elsif notification.last_time_changed_at < attrs[:last_time_changed_at]
notification.update!(attrs)
end
end
Rails.cache.delete(SYNC_TIMESTAMP_CACHE_KEY)
end
def save_notification(notification)
ActiveRecord::Base.transaction do
notification.save!
User.find_in_batches do |user_ids|
user_system_notifications = user_ids.pluck(:id).collect do |item|
Hash[:user_id, item, :system_notification_id, notification.id]
end
UserSystemNotification.import user_system_notifications, validate: false
end
end
Notifications::PushToCommunicationChannelService.delay.call(item_id: notification.id,
item_type: notification.class.name)
end
end
end

View file

@ -111,12 +111,6 @@
<%= render "label_printers/label_printer_modal" %>
<% end %>
<% if user_signed_in? && flash[:system_notification_modal] && current_user.show_login_system_notification? %>
<%= render partial: "/system_notifications/system_notification_modal", locals: { notification: current_user.user_system_notifications.show_on_login(true) } %>
<% else %>
<%= render partial: "/system_notifications/system_notification_modal", locals: { notification: nil} %>
<% end %>
<span style="display: none;" data-hook="application-body-end-html"></span>
<%= javascript_include_tag 'prism' %>

View file

@ -1,19 +0,0 @@
<div class="modal" id="manage-module-system-notification-modal" tabindex="-1" role="dialog" aria-labelledby="manage-module-system-notification-modal-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="manage-module-system-notification-modal-label">
<%= notification ? notification.modal_title.html_safe : '' %>
</h4>
</div>
<div class="modal-body">
<%= notification ? notification.modal_body.html_safe : '' %>
</div>
</div>
</div>
</div>
<% if notification %>
<%= javascript_include_tag 'system_notifications/system_notification_modal' %>
<% end %>

View file

@ -1,10 +0,0 @@
<p>
<%= I18n.t("system_notifications.emails.intro_paragraph", user_name: @user.name) %>
</p>
<p>
<%= link_to @system_notification.title.html_safe, system_notifications_url %>
</p>
<p>
<%= @system_notification.description.html_safe %>
</p>

View file

@ -77,7 +77,6 @@ Rails.application.config.assets.precompile += %w(jszip.min.js)
Rails.application.config.assets.precompile += %w(comments.js)
Rails.application.config.assets.precompile += %w(projects/show.js)
Rails.application.config.assets.precompile += %w(notifications.js)
Rails.application.config.assets.precompile += %w(system_notifications/index.js)
Rails.application.config.assets.precompile += %w(users/invite_users_modal.js)
Rails.application.config.assets.precompile += %w(search.js)
Rails.application.config.assets.precompile += %w(label_printers/index.js)
@ -102,7 +101,6 @@ Rails.application.config.assets.precompile += %w(users/login_disclaimer.js)
Rails.application.config.assets.precompile += %w(assets/office_form.js)
Rails.application.config.assets.precompile += %w(global_activities/date_picker.js)
Rails.application.config.assets.precompile += %w(shared/color_picker_select.js)
Rails.application.config.assets.precompile += %w(system_notifications/system_notification_modal.js)
Rails.application.config.assets.precompile += %w(users/confirmation/new.js)
Rails.application.config.assets.precompile += %w(users/invitations/team_errors.js)
Rails.application.config.assets.precompile += %w(users/invitations/resource_errors.js)

View file

@ -16,23 +16,6 @@ if ENV['ENABLE_TEMPLATES_SYNC'] == 'true'
end
end
if Rails.application.secrets.system_notifications_uri.present? &&
Rails.application.secrets.system_notifications_channel.present?
# System notifications periodic task
scheduler.every '1h' do
Rails.logger.info('System Notifications syncing')
Rails.logger.info(Process.pid)
result = Notifications::SyncSystemNotificationsService.call
if result.errors.any?
Rails.logger.info('System Notifications sync error: ')
Rails.logger.info(result.errors.to_s)
else
Rails.logger.info('System Notifications sync done')
end
end
end
if ENV['ENABLE_FLUICS_SYNC'] == 'true'
scheduler.every '24h' do
LabelPrinters::Fluics::SyncService.new.sync_templates! if LabelPrinter.fluics.any?

View file

@ -2300,19 +2300,6 @@ en:
my_to_team_message: 'My protocols to Team protocols'
team_to_my_message: 'Team protocols to My protocols'
system_notifications:
navbar:
tooltip: 'Whats new notifications'
emails:
subject: "You've received a What's new notification"
intro_paragraph: "Hi %{user_name}, you've received What's new notification in SciNote:"
index:
whats_new_html: "What's New"
more_notifications: "Show more notifications"
no_notifications: "No more notifications"
settings: "Settings"
see_all: "show all"
user_my_modules:
new:
head_title: "%{project} | %{module} | Add user"

View file

@ -568,17 +568,6 @@ Rails.application.routes.draw do
end
end
# System notifications routes
resources :system_notifications, only: %i(show) do
collection do
post 'mark_as_seen'
get 'unseen_counter'
end
member do
post 'mark_as_read'
end
end
# tinyMCE image uploader endpoint
resources :tiny_mce_assets, only: [:create] do
member do

View file

@ -62,23 +62,17 @@ common: &common
development:
secret_key_base: 22f2adf8f5cb73351da28f2292daa840cc2a414ae00ae605b175a8d5c73932f7e5b8ff8ef8f1554a7f1064f9869b15347f7709f0daa6ccb24c50f3cace304f64
system_notifications_uri: <%= ENV["SYSTEM_NOTIFICATIONS_URI"] %>
system_notifications_channel: <%= ENV["SYSTEM_NOTIFICATIONS_CHANNEL"] %>
export_all_limit_24h: <%= ENV['EXPORT_ALL_LIMIT_24_HOURS'] %>
<<: *common
test:
secret_key_base: f3719934e04fa8871cf5d33d5c60f05e1b8995e0315265aef9f8b878da49bd2d386eb25ce35545b469a94ccf22f91e0052b93a15194b4f57b0c8d6ce8b150e1e
system_notifications_uri: 'system-notifications-service.test'
system_notifications_channel: 'test-channel'
export_all_limit_24h: '3'
<<: *common
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
system_notifications_uri: <%= ENV["SYSTEM_NOTIFICATIONS_URI"] %>
system_notifications_channel: <%= ENV["SYSTEM_NOTIFICATIONS_CHANNEL"] %>
export_all_limit_24h: <%= ENV['EXPORT_ALL_LIMIT_24_HOURS'] %>
<<: *common

View file

@ -0,0 +1,6 @@
class DropSystemNotifications < ActiveRecord::Migration[7.0]
def up
drop_table :user_system_notifications
drop_table :system_notifications
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
ActiveRecord::Schema[7.0].define(version: 2023_07_24_094959) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gist"
enable_extension "pg_trgm"
@ -106,15 +106,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
t.index ["team_id"], name: "index_assets_on_team_id"
end
create_table "bmt_filters", force: :cascade do |t|
t.string "name", null: false
t.json "filters", null: false
t.bigint "created_by_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["created_by_id"], name: "index_bmt_filters_on_created_by_id"
end
create_table "checklist_items", force: :cascade do |t|
t.string "text", null: false
t.boolean "checked", default: false, null: false
@ -1006,22 +997,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
t.index ["user_id"], name: "index_steps_on_user_id"
end
create_table "system_notifications", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "modal_title"
t.text "modal_body"
t.boolean "show_on_login", default: false
t.datetime "source_created_at", precision: nil
t.bigint "source_id"
t.datetime "last_time_changed_at", precision: nil, null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.index ["last_time_changed_at"], name: "index_system_notifications_on_last_time_changed_at"
t.index ["source_created_at"], name: "index_system_notifications_on_source_created_at"
t.index ["source_id"], name: "index_system_notifications_on_source_id", unique: true
end
create_table "tables", force: :cascade do |t|
t.binary "contents", null: false
t.datetime "created_at", precision: nil, null: false
@ -1177,20 +1152,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
t.index ["last_modified_by_id"], name: "index_user_roles_on_last_modified_by_id"
end
create_table "user_system_notifications", force: :cascade do |t|
t.bigint "user_id"
t.bigint "system_notification_id"
t.datetime "seen_at", precision: nil
t.datetime "read_at", precision: nil
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.index ["read_at"], name: "index_user_system_notifications_on_read_at"
t.index ["seen_at"], name: "index_user_system_notifications_on_seen_at"
t.index ["system_notification_id"], name: "index_user_system_notifications_on_system_notification_id"
t.index ["user_id", "system_notification_id"], name: "index_user_system_notifications_on_user_and_notification_id", unique: true
t.index ["user_id"], name: "index_user_system_notifications_on_user_id"
end
create_table "user_teams", force: :cascade do |t|
t.integer "role", default: 1, null: false
t.bigint "user_id", null: false
@ -1315,7 +1276,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
add_foreign_key "asset_text_data", "assets"
add_foreign_key "assets", "users", column: "created_by_id"
add_foreign_key "assets", "users", column: "last_modified_by_id"
add_foreign_key "bmt_filters", "users", column: "created_by_id"
add_foreign_key "checklist_items", "checklists"
add_foreign_key "checklist_items", "users", column: "created_by_id"
add_foreign_key "checklist_items", "users", column: "last_modified_by_id"
@ -1480,8 +1440,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_16_140951) do
add_foreign_key "user_projects", "users", column: "assigned_by_id"
add_foreign_key "user_roles", "users", column: "created_by_id"
add_foreign_key "user_roles", "users", column: "last_modified_by_id"
add_foreign_key "user_system_notifications", "system_notifications"
add_foreign_key "user_system_notifications", "users"
add_foreign_key "user_teams", "teams"
add_foreign_key "user_teams", "users"
add_foreign_key "user_teams", "users", column: "assigned_by_id"

View file

@ -1,76 +0,0 @@
# frozen_string_literal: true
# rubocop:disable Metrics/BlockLength
namespace :notifications do
desc 'Copies system notifications to newly created data structure.' \
'IT SHOULD BE RUN ONE TIME ONLY'
task copy_system_notifications: :environment do
t0 = Time.now
system_notifications = Notification
.where(type_of: :system_message)
.where(generator_user_id: nil)
.where.not('title like ?', 'Congratulations%')
system_notifications.each do |system_notification|
new_notification = SystemNotification.create!(
source_id: -1,
title: system_notification.title,
description: system_notification.message,
modal_title: system_notification.title,
modal_body: system_notification.message,
show_on_login: false,
source_created_at: system_notification.created_at,
last_time_changed_at: system_notification.created_at
)
created_at = system_notification.created_at
sql = ' INSERT INTO user_system_notifications
(
user_id,
created_at,
updated_at,
system_notification_id,
seen_at,
read_at
)
VALUES
'
user_notifications = UserNotification
.where(notification_id: system_notification.id)
values_array = user_notifications.map do |user_notification|
user_notification
.slice(:user_id, :created_at, :updated_at)
.merge(system_notification_id: new_notification.id)
.merge(seen_at: created_at, read_at: created_at)
.values
.map { |v| "'#{v}'" }
.join(',')
end
values_sql = values_array
.map { |v| "(#{v})" }
.join(',')
sql += values_sql
ActiveRecord::Base.connection.execute(sql)
end
t1 = Time.now
puts "Task took #{t1 - t0}"
end
desc 'Removes obsolete system notifications from notifications table.'
task delete_obsolete_system_notifications: :environment do
system_notifications = Notification
.where(type_of: :system_message)
.where(generator_user_id: nil)
.where.not('title like ?', 'Congratulations%')
UserNotification
.where(notification_id: system_notifications.pluck(:id))
.delete_all
system_notifications.delete_all
end
end
# rubocop:enable Metrics/BlockLength

View file

@ -24,22 +24,4 @@ describe Api::ApiController, type: :controller do
end
end
end
describe 'GET #health' do
before do
stub_request(:get, Rails.application.secrets.system_notifications_uri + '/api/system_notifications')
.with(query: hash_including('channels_slug': Rails.application.secrets.system_notifications_channel),
headers: { 'Accept': 'application/vnd.system-notifications.1+json' })
.to_return(status: 200, body: '', headers: {})
get :health
end
it 'Returns HTTP success' do
expect(response).to have_http_status(200)
end
it 'Response with the correct plain text' do
expect(response.body).to match('RUNNING')
end
end
end

View file

@ -1,75 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe SystemNotificationsController, type: :controller do
login_user
render_views
let(:user) { subject.current_user }
describe 'Methods' do
let(:notifcation_one) { create :system_notification }
let(:notifcation_two) { create :system_notification, title: 'Special one' }
before do
create :user_system_notification,
user: user,
system_notification: notifcation_one
create :user_system_notification,
user: user,
system_notification: notifcation_two
end
it '#show return right result format' do
params = {
id: user.user_system_notifications.first.system_notification_id
}
get :show, format: :json, params: params
expect(response).to have_http_status(:ok)
body = JSON.parse(response.body)
expect(body).to include('id', 'modal_title', 'modal_body')
end
it '#mark_as_seen correctly set seen_at' do
params = {
notifications: user.user_system_notifications
.map(&:system_notification_id).to_s
}
get :mark_as_seen, format: :json, params: params
expect(response).to have_http_status(:ok)
body = JSON.parse(response.body)
expect(body['result']).to eq 'ok'
expect(user.user_system_notifications.where(seen_at: nil).count).to eq 0
end
it '#mark_as_read correctly set read_at' do
params = {
id: user.user_system_notifications.first.system_notification_id
}
get :mark_as_read, format: :json, params: params
expect(response).to have_http_status(:ok)
body = JSON.parse(response.body)
expect(body['result']).to eq 'ok'
expect(user.user_system_notifications.where(read_at: nil).count).to eq 1
end
it '#unseen_counter return right result' do
get :unseen_counter, format: :json
expect(response).to have_http_status(:ok)
body = JSON.parse(response.body)
expect(body['notificationNmber']).to eq 2
end
it '#index check next page link' do
notifications = create_list :system_notification, 50
notifications.each do |i|
create :user_system_notification,
user: user,
system_notification: i
end
get :index, format: :json
expect(response).to have_http_status(:ok)
body = JSON.parse(response.body)
expect(body['more_url']).to include('system_notifications.json?page=2')
end
end
end

View file

@ -1,16 +0,0 @@
# frozen_string_literal: true
FactoryBot.define do
factory :system_notification do
sequence(:title) { |n| "System notification #{n}" }
description { Faker::ChuckNorris.fact[0..255] }
modal_title { Faker::Name.first_name }
modal_body { Faker::Lorem.paragraphs(number: 4).map { |pr| "<p>#{pr}</p>" }.join }
source_created_at { Faker::Time.between(from: 3.days.ago, to: Date.today) }
source_id { SystemNotification.order(source_id: :desc).first&.source_id.to_i + 1 }
last_time_changed_at { Time.now }
trait :show_on_login do
show_on_login { true }
end
end
end

View file

@ -1,18 +0,0 @@
# frozen_string_literal: true
FactoryBot.define do
factory :user_system_notification do
user
system_notification
trait :seen do
seen_at { Faker::Time.between(from: 3.days.ago, to: Date.today) }
end
trait :read do
read_at { Faker::Time.between(from: 3.days.ago, to: Date.today) }
end
trait :seen_and_read do
seen_at { Faker::Time.between(from: 3.days.ago, to: Date.today) }
read_at { Faker::Time.between(from: seen_at, to: Date.today) }
end
end
end

View file

@ -1,77 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe SystemNotification do
let(:system_notification) { build :system_notification }
it 'is valid' do
expect(system_notification).to be_valid
end
describe 'Validations' do
describe '#title' do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_length_of(:title).is_at_most(255) }
end
describe '#modal_title' do
it { is_expected.to validate_presence_of(:modal_title) }
it { is_expected.to validate_length_of(:modal_title).is_at_most(255) }
end
describe '#modal_body' do
it { is_expected.to validate_presence_of(:modal_body) }
it { is_expected.to validate_length_of(:modal_body).is_at_most(100000) }
end
describe '#description' do
it { is_expected.to validate_presence_of(:description) }
it { is_expected.to validate_length_of(:description).is_at_most(255) }
end
describe '#source_id' do
it { is_expected.to validate_presence_of(:source_id) }
end
describe '#source_created_at' do
it { is_expected.to validate_presence_of(:source_created_at) }
end
describe '#last_time_changed_at' do
it { is_expected.to validate_presence_of(:last_time_changed_at) }
end
end
describe 'Associations' do
it { is_expected.to have_many(:users) }
end
describe 'self.last_sync_timestamp' do
context 'when there is no users or system notifications in db' do
it 'returns nil' do
expect(described_class.last_sync_timestamp).to be_nil
end
end
context 'when there is no system notifications in db' do
it 'returns first users created_at' do
create :user
create :user, created_at: Time.now + 5.seconds
expect(described_class.last_sync_timestamp)
.to be == User.first.created_at.to_i
end
end
context 'when have some system notifications' do
it 'returns last system notifications last_time_changed_at timestamp' do
create :user
create :system_notification
expect(described_class.last_sync_timestamp)
.to be SystemNotification.last.last_time_changed_at.to_i
end
end
end
end

View file

@ -318,10 +318,6 @@ describe User, type: :model do
end
end
describe 'Associations' do
it { is_expected.to have_many(:system_notifications) }
end
describe 'Email downcase' do
it 'downcases email before validating and saving user' do
user = User.new(email: 'Test@Email.com')

View file

@ -1,60 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe UserSystemNotification do
let(:user_system_notification) { build :user_system_notification }
it 'is valid' do
expect(user_system_notification).to be_valid
end
describe 'Associations' do
it { should belong_to :user }
it { should belong_to :system_notification }
end
describe 'Methods' do
let(:user) { create :user }
let(:notifcation_one) { create :system_notification }
let(:notifcation_two) { create :system_notification }
let(:notifcation_three) { create :system_notification, :show_on_login }
it 'make_as_seen update seen_at' do
usn = create :user_system_notification,
user: user,
system_notification: notifcation_one
notifications_to_update = [usn.system_notification_id]
user.user_system_notifications.mark_as_seen
expect(UserSystemNotification.find(usn.id).seen_at).not_to be_nil
end
it 'make_as_read update read_at' do
usn = create :user_system_notification,
user: user,
system_notification: notifcation_one
user.user_system_notifications.mark_as_read(usn.system_notification_id)
expect(UserSystemNotification.find(usn.id).read_at).not_to be_nil
end
it 'show_on_login method only check any notifications' do
usn = create :user_system_notification,
user: user,
system_notification: notifcation_three
result = user.user_system_notifications.show_on_login
expect(result).not_to be_nil
expect(UserSystemNotification.find(usn.id).seen_at).to be_nil
expect(UserSystemNotification.find(usn.id).read_at).to be_nil
end
it 'show_on_login method update notification read and seen time' do
usn = create :user_system_notification,
user: user,
system_notification: notifcation_three
result = user.user_system_notifications.show_on_login(true)
expect(result).not_to be_nil
expect(UserSystemNotification.find(usn.id).seen_at).not_to be_nil
expect(UserSystemNotification.find(usn.id).read_at).not_to be_nil
end
end
end

View file

@ -1,34 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Notifications::HandleSystemNotificationInCommunicationChannelService do
let(:system_notification) { create :system_notification }
let!(:user_system_notification) do
create :user_system_notification, user: user, system_notification: system_notification
end
let(:user) { create :user }
let(:service_call) do
Notifications::HandleSystemNotificationInCommunicationChannelService.call(system_notification)
end
context 'when user has enabled notifications' do
it 'calls AppMailer' do
allow_any_instance_of(User).to receive(:system_message_email_notification).and_return(true)
expect(AppMailer).to receive(:system_notification).and_return(double('Mailer', deliver: true))
service_call
end
end
context 'when user has disabled notifications' do
it 'does not call AppMailer' do
allow_any_instance_of(User).to receive(:system_message_email_notification).and_return(false)
expect(AppMailer).not_to receive(:system_notification)
service_call
end
end
end

View file

@ -1,36 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Notifications::PushToCommunicationChannelService do
let(:system_notification) { create :system_notification }
let(:service_call) do
Notifications::PushToCommunicationChannelService.call(item_id: system_notification.id,
item_type: system_notification.class.name)
end
context 'when call with valid items' do
it 'call service to to handle sending out' do
expect(Notifications::HandleSystemNotificationInCommunicationChannelService)
.to receive(:call).with(system_notification)
service_call
end
end
context 'when call with not valid items' do
it 'returns error with key invalid_arguments when system notification not exists' do
allow(SystemNotification).to receive(:find).and_return(nil)
expect(service_call.errors).to have_key(:invalid_arguments)
end
it 'raise error when have not listed object' do
u = create :user
expect do
Notifications::PushToCommunicationChannelService.call(item_id: u.id, item_type: 'User')
end.to(raise_error('Dont know how to handle this type of items'))
end
end
end

View file

@ -1,119 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe Notifications::SyncSystemNotificationsService do
url = 'http://system-notifications-service.test/api/system_notifications'
let!(:user) { create :user }
let(:service_call) do
allow_any_instance_of(Notifications::PushToCommunicationChannelService).to receive(:call).and_return(nil)
Notifications::SyncSystemNotificationsService.call
end
let(:first_call_result) do
notifications = (1..10).map do |id|
FactoryBot.attributes_for(:system_notification)
.merge('source_id': id)
end
{ notifications: notifications }
end
before(:all) do
Timecop.freeze
end
after(:all) do
Timecop.return
end
context 'when request is successful' do
before do |test|
if test.metadata[:add_notifications_before]
create :system_notification,
source_id: 10,
last_time_changed_at: 10.days.ago.to_datetime
end
stub_request(:get, url)
.with(query: { 'last_sync_timestamp':
SystemNotification.last_sync_timestamp,
'channels_slug': 'test-channel' },
headers: { 'accept':
'application/vnd.system-notifications.1+json' })
.to_return(body: first_call_result.to_json,
status: 200,
headers: { 'Content-Type': 'application/json' })
end
it 'adds 10 notifictions into db' do
expect { service_call }.to(change { SystemNotification.all.count }.by(10))
end
it 'does not add 10 notifications because ther are already in DB' do
first_call_result[:notifications].each do |sn|
SystemNotification.create(sn)
end
expect { service_call }.not_to(change { SystemNotification.all.count })
end
it 'updates existing notification', add_notifications_before: true do
expect { service_call }
.to(change { SystemNotification.last.last_time_changed_at })
end
it 'add only 3 notifications' do
first_call_result[:notifications][2..8].each do |sn|
SystemNotification.create(sn)
end
expect { service_call }.to(change { SystemNotification.all.count }.by(3))
end
it 'return error when last_sync_timestamp is nil' do
allow(SystemNotification).to receive(:last_sync_timestamp).and_return(nil)
expect(service_call.errors).to have_key(:last_sync_timestamp)
end
it 'adds 20 user_system_notifications records' do
create :user # add another user, so have 2 users in DB
expect { service_call }.to change { UserSystemNotification.count }.by(20)
end
it 'calls service to notify users about notification' do
expect(Notifications::PushToCommunicationChannelService).to receive(:call).exactly(10)
service_call
end
end
context 'when request is unsuccessful' do
before do
stub_request(:get, url)
.with(query: { 'last_sync_timestamp':
SystemNotification.last_sync_timestamp,
'channels_slug': 'test-channel' })
.to_return(status: [500, 'Internal Server Error'])
end
it 'returns api_error with message' do
expect(service_call.errors).to have_key(:api_error)
end
it 'returns error with description about itself' do
allow(Notifications::SyncSystemNotificationsService)
.to receive(:get).and_raise(SocketError)
expect(service_call.errors).to have_key(:socketerror)
end
it 'does not call service to notify users about notification' do
expect(Notifications::PushToCommunicationChannelService).to_not receive(:call)
service_call
end
end
end

View file

@ -57,19 +57,6 @@ class AppMailerPreview < ActionMailer::Preview
)
end
# <b>DEPRECATED:</b> Please use <tt>system_notification</tt> instead.
def system_message_notification
AppMailer.notification(
fake_user,
Notification.new(
type_of: :system_message,
title: 'SciNote 9.1 released!',
message: '<a href="#" target="_blank">View release notes</a>',
created_at: Time.now
)
)
end
def delivery_notification
AppMailer.notification(
fake_user,
@ -83,12 +70,6 @@ class AppMailerPreview < ActionMailer::Preview
)
end
def system_notification
sn = FactoryBot.build(:system_notification)
user = FactoryBot.build(:user)
AppMailer.system_notification(user, sn)
end
private
def fake_user