0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-10 04:12:53 +01:00
discourse-custom-wizard/lib/custom_wizard/notice.rb

359 Zeilen
10 KiB
Ruby

2021-09-24 11:58:42 +02:00
# frozen_string_literal: true
class CustomWizard::Notice
include ActiveModel::Serialization
2021-10-05 14:54:06 +02:00
PLUGIN_STATUS_DOMAINS = {
"main" => "plugins.discourse.pavilion.tech",
"master" => "plugins.discourse.pavilion.tech",
2021-10-19 10:08:03 +02:00
"tests-passed" => "plugins.discourse.pavilion.tech",
"stable" => "stable.plugins.discourse.pavilion.tech"
2021-10-05 14:54:06 +02:00
}
2021-11-01 14:52:29 +01:00
SUBSCRIPTION_MESSAGE_DOMAIN = "test.thepavilion.io"
2021-10-05 14:54:06 +02:00
LOCALHOST_DOMAIN = "localhost:3000"
2021-09-24 11:58:42 +02:00
PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing)
2021-10-05 14:54:06 +02:00
CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable)
2021-11-01 14:52:29 +01:00
PAGE_LIMIT = 30
2021-09-24 11:58:42 +02:00
attr_reader :id,
2021-11-01 14:52:29 +01:00
:title,
2021-09-24 11:58:42 +02:00
:message,
:type,
2021-11-01 14:52:29 +01:00
:archetype,
2021-09-24 11:58:42 +02:00
:created_at
attr_accessor :retrieved_at,
2021-10-05 14:54:06 +02:00
:updated_at,
2021-09-24 11:58:42 +02:00
:dismissed_at,
2021-11-01 14:52:29 +01:00
:expired_at,
:hidden_at
2021-09-24 11:58:42 +02:00
def initialize(attrs)
2021-11-01 14:52:29 +01:00
@id = self.class.generate_notice_id(attrs[:title], attrs[:created_at])
@title = attrs[:title]
2021-09-24 11:58:42 +02:00
@message = attrs[:message]
@type = attrs[:type].to_i
2021-11-01 14:52:29 +01:00
@archetype = attrs[:archetype].to_i
2021-09-24 11:58:42 +02:00
@created_at = attrs[:created_at]
2021-10-05 14:54:06 +02:00
@updated_at = attrs[:updated_at]
2021-09-24 11:58:42 +02:00
@retrieved_at = attrs[:retrieved_at]
@dismissed_at = attrs[:dismissed_at]
@expired_at = attrs[:expired_at]
2021-11-01 14:52:29 +01:00
@hidden_at = attrs[:hidden_at]
2021-09-24 11:58:42 +02:00
end
2021-11-01 14:52:29 +01:00
def dismiss!
2021-09-24 11:58:42 +02:00
if dismissable?
self.dismissed_at = DateTime.now.iso8601(3)
2021-11-02 08:29:31 +01:00
self.save_and_publish
2021-09-24 11:58:42 +02:00
end
end
2021-11-01 14:52:29 +01:00
def hide!
if can_hide?
self.hidden_at = DateTime.now.iso8601(3)
2021-11-02 08:29:31 +01:00
self.save_and_publish
2021-11-01 14:52:29 +01:00
end
end
def expire!
if !expired?
self.expired_at = DateTime.now.iso8601(3)
2021-11-02 08:29:31 +01:00
self.save_and_publish
end
end
def save_and_publish
if self.save
2021-11-01 14:52:29 +01:00
self.class.publish_notice_count
2021-11-02 08:29:31 +01:00
true
else
false
2021-11-01 14:52:29 +01:00
end
2021-09-24 11:58:42 +02:00
end
def expired?
expired_at.present?
end
def dismissed?
dismissed_at.present?
end
def dismissable?
2021-11-01 14:52:29 +01:00
!expired? && !dismissed? && type === self.class.types[:info]
end
def hidden?
hidden_at.present?
end
def can_hide?
!hidden? && (
type === self.class.types[:connection_error] ||
type === self.class.types[:warning]
) && (
archetype === self.class.archetypes[:plugin_status]
)
2021-09-24 11:58:42 +02:00
end
def save
attrs = {
expired_at: expired_at,
2021-11-01 14:52:29 +01:00
updated_at: updated_at,
retrieved_at: retrieved_at,
2021-09-24 11:58:42 +02:00
created_at: created_at,
2021-11-01 14:52:29 +01:00
title: title,
2021-09-24 11:58:42 +02:00
message: message,
2021-11-01 14:52:29 +01:00
type: type,
archetype: archetype
2021-09-24 11:58:42 +02:00
}
if current = self.class.find(self.id)
attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at
2021-11-02 08:29:31 +01:00
attrs[:hidden_at] = current.hidden_at || self.hidden_at
2021-09-24 11:58:42 +02:00
end
self.class.store(id, attrs)
end
def self.types
@types ||= Enum.new(
info: 0,
2021-11-01 14:52:29 +01:00
warning: 1,
connection_error: 2
)
end
def self.archetypes
@archetypes ||= Enum.new(
subscription_message: 0,
plugin_status: 1
2021-09-24 11:58:42 +02:00
)
end
def self.update(skip_subscription: false, skip_plugin: false)
notices = []
if !skip_subscription
2021-11-01 14:52:29 +01:00
subscription_messages = request(:subscription_message)
2021-10-05 14:54:06 +02:00
2021-09-24 11:58:42 +02:00
if subscription_messages.present?
subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages])
notices.push(*subscription_notices)
end
end
2021-10-05 14:54:06 +02:00
if !skip_plugin && request_plugin_status?
plugin_status = request(:plugin_status)
2021-09-24 11:58:42 +02:00
2021-11-01 14:52:29 +01:00
if plugin_status.present? && plugin_status[:status].present?
plugin_notice = convert_plugin_status_to_notice(plugin_status)
2021-09-24 11:58:42 +02:00
notices.push(plugin_notice) if plugin_notice
end
end
2021-11-01 14:52:29 +01:00
if notices.any?
notices.each do |notice_data|
notice = new(notice_data)
notice.retrieved_at = DateTime.now.iso8601(3)
2021-11-01 14:52:29 +01:00
notice.save
end
2021-09-24 11:58:42 +02:00
end
publish_notice_count
2021-09-24 11:58:42 +02:00
end
2021-11-01 14:52:29 +01:00
def self.publish_notice_count
payload = {
active_notice_count: CustomWizard::Notice.active_count
}
MessageBus.publish("/custom-wizard/notices", payload, group_ids: [Group::AUTO_GROUPS[:admins]])
end
2021-09-24 11:58:42 +02:00
def self.convert_subscription_messages_to_notices(messages)
2021-11-01 14:52:29 +01:00
messages.reduce([]) do |result, message|
2021-11-02 08:29:31 +01:00
id = generate_notice_id(message[:title], message[:created_at])
result.push(
id: id,
title: message[:title],
message: message[:message],
type: types[message[:type].to_sym],
archetype: archetypes[:subscription_message],
created_at: message[:created_at],
expired_at: message[:expired_at]
)
2021-11-01 14:52:29 +01:00
result
2021-09-24 11:58:42 +02:00
end
end
def self.convert_plugin_status_to_notice(plugin_status)
notice = nil
if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status])
2021-11-01 14:52:29 +01:00
title = I18n.t('wizard.notice.compatibility_issue.title')
created_at = plugin_status[:status_changed_at]
id = generate_notice_id(title, created_at)
unless exists?(id)
message = I18n.t('wizard.notice.compatibility_issue.message', domain: plugin_status_domain)
notice = {
id: id,
title: title,
message: message,
type: types[:warning],
archetype: archetypes[:plugin_status],
created_at: created_at
}
end
2021-09-24 11:58:42 +02:00
else
2021-11-01 14:52:29 +01:00
expire_all(types[:warning], archetypes[:plugin_status])
2021-09-24 11:58:42 +02:00
end
notice
end
2021-11-01 14:52:29 +01:00
def self.notify_connection_errors(archetype)
domain = self.send("#{archetype.to_s}_domain")
title = I18n.t("wizard.notice.#{archetype.to_s}.connection_error.title")
notices = list(type: types[:connection_error], archetype: archetypes[archetype.to_sym], title: title)
2021-10-05 14:54:06 +02:00
if notices.any?
notice = notices.first
notice.updated_at = DateTime.now.iso8601(3)
2021-10-05 14:54:06 +02:00
notice.save
else
notice = new(
2021-11-01 14:52:29 +01:00
title: title,
message: I18n.t("wizard.notice.#{archetype.to_s}.connection_error.message", domain: domain),
archetype: archetypes[archetype.to_sym],
type: types[:connection_error],
created_at: DateTime.now.iso8601(3),
updated_at: DateTime.now.iso8601(3)
2021-10-05 14:54:06 +02:00
)
notice.save
end
end
def self.request_plugin_status?
CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development?
end
2021-11-01 14:52:29 +01:00
def self.subscription_message_domain
return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?)
SUBSCRIPTION_MESSAGE_DOMAIN
2021-09-24 11:58:42 +02:00
end
2021-11-01 14:52:29 +01:00
def self.subscription_message_url
2021-11-25 07:38:16 +01:00
"https://#{subscription_message_domain}/subscription-server/messages.json"
2021-09-24 11:58:42 +02:00
end
def self.plugin_status_domain
2021-10-05 14:54:06 +02:00
return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?)
PLUGIN_STATUS_DOMAINS[Discourse.git_branch]
2021-09-24 11:58:42 +02:00
end
def self.plugin_status_url
2021-11-25 07:38:16 +01:00
"https://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard"
2021-09-24 11:58:42 +02:00
end
2021-10-05 14:54:06 +02:00
2021-11-01 14:52:29 +01:00
def self.request(archetype)
url = self.send("#{archetype.to_s}_url")
2021-09-24 11:58:42 +02:00
2021-11-01 14:52:29 +01:00
begin
response = Excon.get(url)
rescue Excon::Error::Socket, Excon::Error::Timeout => e
response = nil
end
connection_error = CustomWizard::Notice::ConnectionError.new(archetype)
if response && response.status == 200
2021-10-05 14:54:06 +02:00
connection_error.expire!
2021-11-01 14:52:29 +01:00
expire_all(types[:connection_error], archetypes[archetype.to_sym])
2021-10-05 14:54:06 +02:00
2021-09-24 11:58:42 +02:00
begin
data = JSON.parse(response.body).deep_symbolize_keys
rescue JSON::ParserError
return nil
end
2021-10-05 14:54:06 +02:00
2021-09-24 11:58:42 +02:00
data
else
2021-10-05 14:54:06 +02:00
connection_error.create!
2021-11-01 14:52:29 +01:00
notify_connection_errors(archetype) if connection_error.reached_limit?
2021-09-24 11:58:42 +02:00
nil
end
end
def self.namespace
"#{CustomWizard::PLUGIN_NAME}_notice"
end
def self.find(id)
raw = PluginStore.get(namespace, id)
new(raw.symbolize_keys) if raw.present?
end
2021-11-01 14:52:29 +01:00
def self.exists?(id)
PluginStoreRow.where(plugin_name: namespace, key: id).exists?
end
2021-09-24 11:58:42 +02:00
def self.store(id, raw_notice)
PluginStore.set(namespace, id, raw_notice)
end
2021-11-02 08:29:31 +01:00
def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, page: nil, visible: false)
2021-09-24 11:58:42 +02:00
query = PluginStoreRow.where(plugin_name: namespace)
2021-11-01 14:52:29 +01:00
query = query.where("(value::json->>'hidden_at') IS NULL") if visible
query = query.where("(value::json->>'dismissed_at') IS NULL") unless include_all
2021-11-02 08:29:31 +01:00
query = query.where("(value::json->>'expired_at') IS NULL") unless include_all
2021-11-01 14:52:29 +01:00
query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype
if type
2021-11-17 13:48:11 +01:00
type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?"
2021-11-01 14:52:29 +01:00
query = query.where(type_query_str, type)
end
query = query.where("(value::json->>'title')::text = ?", title) if title
query = query.limit(PAGE_LIMIT).offset(page.to_i * PAGE_LIMIT) if !page.nil?
query.order("value::json->>'expired_at' DESC, value::json->>'updated_at' DESC,value::json->>'dismissed_at' DESC, value::json->>'created_at' DESC")
2021-09-24 11:58:42 +02:00
end
2021-11-02 08:29:31 +01:00
def self.list(type: nil, archetype: nil, title: nil, include_all: false, page: 0, visible: false)
list_query(type: type, archetype: archetype, title: title, include_all: include_all, page: page, visible: visible)
2021-09-24 11:58:42 +02:00
.map { |r| self.new(JSON.parse(r.value).symbolize_keys) }
end
2021-11-01 14:52:29 +01:00
def self.active_count
list_query.count
end
def self.dismiss_all
dismissed_count = PluginStoreRow.where("
plugin_name = '#{namespace}' AND
(value::json->>'type')::integer = #{types[:info]} AND
(value::json->>'expired_at') IS NULL AND
(value::json->>'dismissed_at') IS NULL
").update_all("
value = jsonb_set(value::jsonb, '{dismissed_at}', (to_json(now())::text)::jsonb, true)
")
publish_notice_count if dismissed_count.to_i > 0
dismissed_count
end
def self.expire_all(type, archetype)
expired_count = PluginStoreRow.where("
plugin_name = '#{namespace}' AND
(value::json->>'type')::integer = #{type} AND
(value::json->>'archetype')::integer = #{archetype} AND
(value::json->>'expired_at') IS NULL
").update_all("
value = jsonb_set(value::jsonb, '{expired_at}', (to_json(now())::text)::jsonb, true)
")
publish_notice_count if expired_count.to_i > 0
expired_count
end
def self.generate_notice_id(title, created_at)
Digest::SHA1.hexdigest("#{title}-#{created_at}")
end
2021-09-24 11:58:42 +02:00
end