0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00

DEV: Add notice specs and UI updates

Dieser Commit ist enthalten in:
angusmcleod 2021-10-05 20:54:06 +08:00
Ursprung 084c6f4a7a
Commit 925c8c009a
23 geänderte Dateien mit 306 neuen und 129 gelöschten Zeilen

Datei anzeigen

@ -18,4 +18,4 @@ export default Component.extend({
subscribedTitle(subscribed) { subscribedTitle(subscribed) {
return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`; return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`;
} }
}) });

Datei anzeigen

@ -1,10 +1,10 @@
import Component from "@ember/component"; import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { notEmpty, not } from "@ember/object/computed"; import { not, notEmpty } from "@ember/object/computed";
import I18n from "I18n"; import I18n from "I18n";
export default Component.extend({ export default Component.extend({
classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired'], classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired', 'resolved'],
showFull: false, showFull: false,
resolved: notEmpty('notice.expired_at'), resolved: notEmpty('notice.expired_at'),
dismissed: notEmpty('notice.dismissed_at'), dismissed: notEmpty('notice.dismissed_at'),
@ -18,14 +18,16 @@ export default Component.extend({
@discourseComputed('notice.type') @discourseComputed('notice.type')
icon(type) { icon(type) {
return { return {
warning: 'exclamation-circle', plugin_status_warning: 'exclamation-circle',
plugin_status_connection_error: 'bolt',
subscription_messages_connection_error: 'bolt',
info: 'info-circle' info: 'info-circle'
}[type]; }[type];
}, },
actions: { actions: {
dismiss() { dismiss() {
this.set('dismissing', true) this.set('dismissing', true);
this.notice.dismiss().then(() => { this.notice.dismiss().then(() => {
this.set('dismissing', false); this.set('dismissing', false);
}); });

Datei anzeigen

@ -0,0 +1,3 @@
{{#if importantNotice}}
{{wizard-notice notice=importantNotice importantOnDashboard=true}}
{{/if}}

Datei anzeigen

@ -0,0 +1,16 @@
import { getOwner } from "discourse-common/lib/get-owner";
export default {
shouldRender(attrs, ctx) {
return ctx.siteSettings.wizard_important_notices_on_dashboard;
},
setupComponent() {
const controller = getOwner(this).lookup('controller:admin-dashboard');
const importantNotice = controller.get('customWizardImportantNotice');
if (importantNotice) {
this.set('importantNotice', importantNotice);
}
}
};

Datei anzeigen

@ -1,3 +0,0 @@
{{#if wizardWarningNotice}}
{{wizard-notice notice=wizardWarningNotice}}
{{/if}}

Datei anzeigen

@ -1,12 +0,0 @@
import { getOwner } from "discourse-common/lib/get-owner";
export default {
setupComponent() {
const controller = getOwner(this).lookup('controller:admin-dashboard')
const wizardWarningNotice = controller.get('wizardWarningNotice');
if (wizardWarningNotice) {
this.set('wizardWarningNotice', wizardWarningNotice);
}
}
}

Datei anzeigen

@ -38,7 +38,7 @@ export default Controller.extend({
unauthorize() { unauthorize() {
this.set("unauthorizing", true); this.set("unauthorizing", true);
CustomWizardPro.unauthorize() CustomWizardSubscription.unauthorize()
.then((result) => { .then((result) => {
if (result.success) { if (result.success) {
this.setProperties({ this.setProperties({

Datei anzeigen

@ -1,6 +1,5 @@
import DiscourseURL from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
import { withPluginApi } from "discourse/lib/plugin-api"; import { withPluginApi } from "discourse/lib/plugin-api";
import { ajax } from "discourse/lib/ajax";
import CustomWizardNotice from "../models/custom-wizard-notice"; import CustomWizardNotice from "../models/custom-wizard-notice";
import { A } from "@ember/array"; import { A } from "@ember/array";
@ -31,12 +30,13 @@ export default {
}); });
}, },
setupController(controller, model) { setupController(controller) {
if (this.notices) { if (this.notices) {
let warningNotices = this.notices.filter(n => n.type === 'warning'); let pluginStatusConnectionError = this.notices.filter(n => n.type === 'plugin_status_connection_error')[0];
let pluginStatusWarning = this.notices.filter(n => n.type === 'plugin_status_warning')[0];
if (warningNotices.length) { if (pluginStatusConnectionError || pluginStatusWarning) {
controller.set('wizardWarningNotice', warningNotices[0]); controller.set('customWizardImportantNotice', pluginStatusConnectionError || pluginStatusWarning);
} }
} }

Datei anzeigen

@ -10,13 +10,13 @@ CustomWizardNotice.reopen({
if (result.success) { if (result.success) {
this.set('dismissed_at', result.dismissed_at); this.set('dismissed_at', result.dismissed_at);
} }
}).catch(popupAjaxError) }).catch(popupAjaxError);
} }
}); });
CustomWizardNotice.reopenClass({ CustomWizardNotice.reopenClass({
list() { list() {
return ajax('/admin/wizards/notice').catch(popupAjaxError) return ajax('/admin/wizards/notice').catch(popupAjaxError);
} }
}); });

Datei anzeigen

@ -1,5 +1,5 @@
<div class=subscription-header> <div class="subscription-header">
<h3>{{i18n 'admin.wizard.subscription_container.title'}}</h3> <h3>{{i18n "admin.wizard.subscription_container.title"}}</h3>
<a href="/admin/wizards/subscription" title={{i18n subscribedTitle}}> <a href="/admin/wizards/subscription" title={{i18n subscribedTitle}}>
{{d-icon subscribedIcon}} {{d-icon subscribedIcon}}
@ -9,4 +9,4 @@
<div class="subscription-settings"> <div class="subscription-settings">
{{yield}} {{yield}}
</div> </div>

Datei anzeigen

@ -6,18 +6,26 @@
{{format-date notice.expired_at leaveAgo="true"}} {{format-date notice.expired_at leaveAgo="true"}}
</div> </div>
{{/if}} {{/if}}
<div class="notice-title notice-badge" title={{title}}> <div class="notice-title notice-badge" title={{title}}>
{{d-icon icon}} {{d-icon icon}}
<span>{{title}}</span> <span>{{title}}</span>
</div> </div>
<div class="notice-created-at notice-badge" title={{notice.created_at}}> <div class="notice-created-at notice-badge" title={{notice.created_at}}>
{{d-icon "calendar-alt"}} {{d-icon "far-clock"}}
<span class="notice-issued">{{i18n "admin.wizard.notice.issued"}}</span> <span class="notice-issued">{{i18n "admin.wizard.notice.issued"}}</span>
{{format-date notice.created_at leaveAgo="true"}} {{format-date notice.created_at leaveAgo="true"}}
</div> </div>
{{#if notice.updated_at}}
<div class="notice-updated-at notice-badge" title={{notice.updated_at}}>
{{d-icon "calendar-alt"}}
<span class="notice-updated">{{i18n "admin.wizard.notice.updated"}}</span>
{{format-date notice.updated_at leaveAgo="true"}}
</div>
{{/if}}
<div class="notice-plugin notice-badge" title={{i18n "admin.wizard.notice.plugin"}}> <div class="notice-plugin notice-badge" title={{i18n "admin.wizard.notice.plugin"}}>
{{d-icon "plug"}} {{d-icon "plug"}}
<span>{{i18n "admin.wizard.notice.plugin"}}</span> <span>{{i18n "admin.wizard.notice.plugin"}}</span>
@ -28,10 +36,16 @@
{{{notice.message}}} {{{notice.message}}}
</div> </div>
{{#if importantOnDashboard}}
<a href="/admin/site_settings/category/all_results?filter=wizard_important_notices_on_dashboard" class="disable-important">
{{i18n "admin.wizard.notice.disable_important_on_dashboard"}}
</a>
{{/if}}
{{#if canDismiss}} {{#if canDismiss}}
{{#if dismissing}} {{#if dismissing}}
{{loading-spinner size="small"}} {{loading-spinner size="small"}}
{{else}} {{else}}
<a {{action "dismiss"}} role="button" class="dismiss-notice">{{d-icon "times"}}</a> <a {{action "dismiss"}} role="button" class="dismiss-notice">{{d-icon "times"}}</a>
{{/if}} {{/if}}
{{/if}} {{/if}}

Datei anzeigen

@ -911,13 +911,18 @@
padding: 1em; padding: 1em;
margin-bottom: 1em; margin-bottom: 1em;
border: 1px solid var(--primary); border: 1px solid var(--primary);
border-radius: 4px;
position: relative; position: relative;
&.dismissed { &.dismissed {
display: none; display: none;
} }
&.resolved .notice-badge:not(.notice-expired-at),
&.resolved a,
&.resolved p {
color: var(--primary-medium) !important;
}
.d-icon { .d-icon {
margin-right: .4em; margin-right: .4em;
} }
@ -931,7 +936,6 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
padding: 0 .5em; padding: 0 .5em;
border-radius: 4px;
margin-right: 1em; margin-right: 1em;
font-size: .9em; font-size: .9em;
line-height: 25px; line-height: 25px;
@ -957,7 +961,8 @@
} }
} }
.notice-issued { .notice-issued,
.notice-resolved {
margin-right: .3em; margin-right: .3em;
} }
@ -976,6 +981,13 @@
position: absolute; position: absolute;
top: 1em; top: 1em;
right: 1em; right: 1em;
color: var(--primary); color: var(--primary-medium);
}
.disable-important {
position: absolute;
right: 3em;
top: 1em;
color: var(--primary-medium);
} }
} }

Datei anzeigen

@ -485,9 +485,13 @@ en:
notice: notice:
plugin: Custom Wizard Plugin plugin: Custom Wizard Plugin
issued: Issued issued: Issued
update: Updated
resolved: Resolved resolved: Resolved
title: title:
warning: Warning Notice plugin_status_warning: Warning Notice
plugin_status_connection_error: Connection Notice
subscription_messages_connection_error: Connection Notice
disable_important_on_dashboard: disable
wizard_js: wizard_js:
group: group:

Datei anzeigen

@ -53,16 +53,22 @@ en:
subscription: "%{type} %{property} is subscription only" subscription: "%{type} %{property} is subscription only"
notice: notice:
connection_error: "Failed to connect to [%{server}](http://%{server})" connection_error: "Failed to connect to http://%{domain}"
compatibility_issue: > compatibility_issue: >
The Custom Wizard Plugin may have a compatibility issue with the latest version of Discourse. The Custom Wizard Plugin has a compatibility issue with the latest version of Discourse.
Please check the Custom Wizard Plugin status on [%{server}](http://%{server}) before updating Discourse. Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse.
plugin_status_connection_error_limit: > plugin_status:
We're unable to connect to the plugin status server to determine whether there are any compatibility issues with the latest version of Discourse. connection_error_limit: >
Please contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance. We're unable to connect to the Pavilion Plugin Status Server. Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse or the plugin.
If this connection issue persists please contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
subscription_messages:
connection_error_limit: >
We're unable to connect to the Pavilion Subscription Server. This will not affect the operation of the plugin.
If this connection issue persists please contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
site_settings: site_settings:
custom_wizard_enabled: "Enable custom wizards." custom_wizard_enabled: "Enable custom wizards."
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview" wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview"
wizard_apis_enabled: "Enable API features (experimental)." wizard_apis_enabled: "Enable API features (experimental)."
wizard_important_notices_on_dashboard: "Show important notices about the custom wizard plugin on the admin dashboard."

Datei anzeigen

@ -17,4 +17,7 @@ plugins:
list_type: compact list_type: compact
wizard_apis_enabled: wizard_apis_enabled:
client: true client: true
default: false default: false
wizard_important_notices_on_dashboard:
client: true
default: true

Datei anzeigen

@ -4,7 +4,7 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController
before_action :find_notice, only: [:dismiss] before_action :find_notice, only: [:dismiss]
def index def index
render_serialized(CustomWizard::Notice.list, CustomWizard::NoticeSerializer) render_serialized(CustomWizard::Notice.list(include_recently_expired: true), CustomWizard::NoticeSerializer)
end end
def dismiss def dismiss
@ -19,4 +19,4 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController
@notice = CustomWizard::Notice.find(params[:notice_id]) @notice = CustomWizard::Notice.find(params[:notice_id])
raise Discourse::InvalidParameters.new(:notice_id) unless @notice raise Discourse::InvalidParameters.new(:notice_id) unless @notice
end end
end end

Datei anzeigen

@ -6,4 +6,4 @@ class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled
def execute(args = {}) def execute(args = {})
CustomWizard::Notice.update CustomWizard::Notice.update
end end
end end

Datei anzeigen

@ -3,7 +3,14 @@
class CustomWizard::Notice class CustomWizard::Notice
include ActiveModel::Serialization include ActiveModel::Serialization
PLUGIN_STATUS_DOMAINS = {
"tests-passed" => "try.thepavilion.io",
"stable" => "stable.try.thepavilion.io"
}
SUBSCRIPTION_MESSAGES_DOMAIN = "thepavilion.io"
LOCALHOST_DOMAIN = "localhost:3000"
PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing)
CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable)
attr_reader :id, attr_reader :id,
:message, :message,
@ -11,6 +18,7 @@ class CustomWizard::Notice
:created_at :created_at
attr_accessor :retrieved_at, attr_accessor :retrieved_at,
:updated_at,
:dismissed_at, :dismissed_at,
:expired_at :expired_at
@ -19,6 +27,7 @@ class CustomWizard::Notice
@message = attrs[:message] @message = attrs[:message]
@type = attrs[:type].to_i @type = attrs[:type].to_i
@created_at = attrs[:created_at] @created_at = attrs[:created_at]
@updated_at = attrs[:updated_at]
@retrieved_at = attrs[:retrieved_at] @retrieved_at = attrs[:retrieved_at]
@dismissed_at = attrs[:dismissed_at] @dismissed_at = attrs[:dismissed_at]
@expired_at = attrs[:expired_at] @expired_at = attrs[:expired_at]
@ -52,7 +61,6 @@ class CustomWizard::Notice
attrs = { attrs = {
expired_at: expired_at, expired_at: expired_at,
created_at: created_at, created_at: created_at,
expired_at: expired_at,
message: message, message: message,
type: type type: type
} }
@ -67,14 +75,9 @@ class CustomWizard::Notice
def self.types def self.types
@types ||= Enum.new( @types ||= Enum.new(
info: 0, info: 0,
warning: 1 plugin_status_warning: 1,
) plugin_status_connection_error: 2,
end subscription_messages_connection_error: 3
def self.connection_types
@connection_types ||= Enum.new(
plugin_status: 0,
subscription: 1
) )
end end
@ -82,23 +85,20 @@ class CustomWizard::Notice
notices = [] notices = []
if !skip_subscription if !skip_subscription
subscription_messages = request(subscription_messages_url) subscription_messages = request(:subscription_messages)
if subscription_messages.present? if subscription_messages.present?
subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages])
notices.push(*subscription_notices) notices.push(*subscription_notices)
end end
end end
if !skip_plugin && (Discourse.git_branch === 'tests-passed' || (Rails.env.test? || Rails.env.development?)) if !skip_plugin && request_plugin_status?
plugin_status = request(plugin_status_url) plugin_status = request(:plugin_status)
if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash) if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash)
plugin_notice = convert_plugin_status_to_notice(plugin_status[:status]) plugin_notice = convert_plugin_status_to_notice(plugin_status[:status])
notices.push(plugin_notice) if plugin_notice notices.push(plugin_notice) if plugin_notice
expire_connection_errors(connection_types[:plugin_status])
else
create_connection_error(connection_types[:plugin_status])
end end
end end
@ -107,14 +107,6 @@ class CustomWizard::Notice
notice.retrieved_at = Time.now notice.retrieved_at = Time.now
notice.save notice.save
end end
if reached_connection_error_limit(connection_types[:plugin_status])
new(
message: I18n.t("wizard.notice.plugin_status_connection_error_limit"),
type: types[:warning],
created_at: Time.now
)
end
end end
def self.convert_subscription_messages_to_notices(messages) def self.convert_subscription_messages_to_notices(messages)
@ -133,19 +125,46 @@ class CustomWizard::Notice
if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status])
notice = { notice = {
message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', server: plugin_status_domain)), message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', domain: plugin_status_domain)),
type: types[:warning], type: types[:plugin_status_warning],
created_at: plugin_status[:status_changed_at] created_at: plugin_status[:status_changed_at]
} }
else else
list(types[:warning]).each(&:expire) expire_notices(types[:plugin_status_warning])
end end
notice notice
end end
def self.notify_connection_errors(connection_type_key)
domain = self.send("#{connection_type_key.to_s}_domain")
message = PrettyText.cook(I18n.t("wizard.notice.#{connection_type_key.to_s}.connection_error_limit", domain: domain))
notices = list(type: types[:connection_error], message: message)
if notices.any?
notice = notices.first
notice.updated_at = Time.now
notice.save
else
notice = new(
message: message,
type: types["#{connection_type_key}_connection_error".to_sym],
created_at: Time.now
)
notice.save
end
end
def self.expire_notices(type)
list(type: type).each(&:expire)
end
def self.request_plugin_status?
CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development?
end
def self.subscription_messages_domain def self.subscription_messages_domain
"localhost:3000" (Rails.env.test? || Rails.env.development?) ? LOCALHOST_DOMAIN : SUBSCRIPTION_MESSAGES_DOMAIN
end end
def self.subscription_messages_url def self.subscription_messages_url
@ -153,25 +172,34 @@ class CustomWizard::Notice
end end
def self.plugin_status_domain def self.plugin_status_domain
"localhost:4200" return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?)
PLUGIN_STATUS_DOMAINS[Discourse.git_branch]
end end
def self.plugin_status_url def self.plugin_status_url
"http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" "http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard"
end end
def self.request(url) def self.request(type)
url = self.send("#{type.to_s}_url")
response = Excon.get(url) response = Excon.get(url)
connection_error = CustomWizard::Notice::ConnectionError.new(type)
if response.status == 200 if response.status == 200
connection_error.expire!
expire_notices(types["#{type}_connection_error".to_sym])
begin begin
data = JSON.parse(response.body).deep_symbolize_keys data = JSON.parse(response.body).deep_symbolize_keys
rescue JSON::ParserError rescue JSON::ParserError
return nil return nil
end end
data data
else else
connection_error.create!
notify_connection_errors(type) if connection_error.reached_limit?
nil nil
end end
end end
@ -180,10 +208,6 @@ class CustomWizard::Notice
"#{CustomWizard::PLUGIN_NAME}_notice" "#{CustomWizard::PLUGIN_NAME}_notice"
end end
def self.namespace_connection
"#{CustomWizard::PLUGIN_NAME}_notice_connection"
end
def self.find(id) def self.find(id)
raw = PluginStore.get(namespace, id) raw = PluginStore.get(namespace, id)
new(raw.symbolize_keys) if raw.present? new(raw.symbolize_keys) if raw.present?
@ -193,42 +217,16 @@ class CustomWizard::Notice
PluginStore.set(namespace, id, raw_notice) PluginStore.set(namespace, id, raw_notice)
end end
def self.plugin_status_connection_error_limit def self.list_query(type: nil, message: nil, include_recently_expired: false)
5
end
def self.list_connection_query(type)
query = PluginStoreRow.where(plugin_name: namespace_connection)
query.where("(value::json->>'type')::integer = ?", type)
end
def self.expire_connection_errors(type)
list_connection_query(type).update_all("value = jsonb_set(value::jsonb, '{ expired_at }', (to_char(current_timestamp, 'HH12:MI:SS'))::jsonb)")
end
def self.create_connection_error(type)
id = SecureRandom.hex(16)
attrs = {
message: I18n.t("wizard.notice.connection_error", domain: self.send("#{type}_domain")),
type: type,
created_at: Time.now
}
PluginStore.set(namespace_connection, id, attrs)
end
def self.reached_connection_error_limit(type)
list_connection_query(type).size >= self.send("#{connection_types.key(type)}_connection_error_limit")
end
def self.list_query(type = nil)
query = PluginStoreRow.where(plugin_name: namespace) query = PluginStoreRow.where(plugin_name: namespace)
query = query.where("(value::json->>'expired_at') IS NULL OR (value::json->>'expired_at')::date > now()::date - 1") query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}")
query = query.where("(value::json->>'type')::integer = ?", type) if type query = query.where("(value::json->>'type')::integer = ?", type) if type
query = query.where("(value::json->>'message') = ?", message) if message
query.order("value::json->>'created_at' DESC") query.order("value::json->>'created_at' DESC")
end end
def self.list(type = nil) def self.list(type: nil, message: nil, include_recently_expired: false)
list_query(type) list_query(type: type, message: message, include_recently_expired: include_recently_expired)
.map { |r| self.new(JSON.parse(r.value).symbolize_keys) } .map { |r| self.new(JSON.parse(r.value).symbolize_keys) }
end end
end end

Datei anzeigen

@ -0,0 +1,82 @@
# frozen_string_literal: true
class CustomWizard::Notice::ConnectionError
attr_reader :type_key
def initialize(type_key)
@type_key = type_key
end
def create!
id = "#{type_key.to_s}_error"
if attrs = PluginStore.get(namespace, id)
attrs['updated_at'] = Time.now
attrs['count'] = attrs['count'].to_i + 1
else
domain = CustomWizard::Notice.send("#{type_key.to_s}_domain")
attrs = {
message: I18n.t("wizard.notice.connection_error", domain: domain),
type: self.class.types[type_key],
created_at: Time.now,
count: 1
}
end
PluginStore.set(namespace, id, attrs)
@errors = nil
end
def expire!
if errors.exists?
errors.each do |error_row|
error = JSON.parse(error_row.value)
error['expired_at'] = Time.now
error_row.value = error
error_row.save
end
end
end
def self.types
@types ||= Enum.new(
plugin_status: 0,
subscription_messages: 1
)
end
def plugin_status_limit
5
end
def subscription_messages_limit
10
end
def limit
self.send("#{type_key.to_s}_limit")
end
def reached_limit?
return false unless errors.exists?
current_error['count'].to_i >= limit
end
def current_error
JSON.parse(errors.first.value)
end
def namespace
"#{CustomWizard::PLUGIN_NAME}_notice_connection"
end
def errors
@errors ||= begin
query = PluginStoreRow.where(plugin_name: namespace)
query = query.where("(value::json->>'type')::integer = ?", self.class.types[type_key])
query.where("(value::json->>'expired_at') IS NULL")
end
end
end

Datei anzeigen

@ -40,7 +40,7 @@ if respond_to?(:register_svg_icon)
register_svg_icon "comment-alt" register_svg_icon "comment-alt"
register_svg_icon "far-life-ring" register_svg_icon "far-life-ring"
register_svg_icon "arrow-right" register_svg_icon "arrow-right"
register_svg_icon "shield-virus" register_svg_icon "bolt"
end end
class ::Sprockets::DirectiveProcessor class ::Sprockets::DirectiveProcessor
@ -98,6 +98,7 @@ after_initialize do
../lib/custom_wizard/template.rb ../lib/custom_wizard/template.rb
../lib/custom_wizard/wizard.rb ../lib/custom_wizard/wizard.rb
../lib/custom_wizard/notice.rb ../lib/custom_wizard/notice.rb
../lib/custom_wizard/notice/connection_error.rb
../lib/custom_wizard/subscription.rb ../lib/custom_wizard/subscription.rb
../lib/custom_wizard/subscription/subscription.rb ../lib/custom_wizard/subscription/subscription.rb
../lib/custom_wizard/subscription/authentication.rb ../lib/custom_wizard/subscription/authentication.rb
@ -242,10 +243,9 @@ after_initialize do
end end
AdminDashboardData.add_problem_check do AdminDashboardData.add_problem_check do
warning_notices = CustomWizard::Notice.list(CustomWizard::Notice.types[:warning]) warning_notices = CustomWizard::Notice.list(type: CustomWizard::Notice.types[:plugin_status_warning])
warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil
end end
Jobs.enqueue(:custom_wizard_update_notices)
DiscourseEvent.trigger(:custom_wizard_ready) DiscourseEvent.trigger(:custom_wizard_ready)
end end

Datei anzeigen

@ -6,6 +6,7 @@ class CustomWizard::NoticeSerializer < ApplicationSerializer
:type, :type,
:created_at, :created_at,
:expired_at, :expired_at,
:updated_at,
:dismissed_at, :dismissed_at,
:retrieved_at, :retrieved_at,
:dismissable :dismissable

Datei anzeigen

@ -33,13 +33,13 @@ describe CustomWizard::Notice do
expect(notice.message).to eq(subscription_message[:message]) expect(notice.message).to eq(subscription_message[:message])
expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime) expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime)
end end
it "expires notice if subscription message is expired" do it "expires notice if subscription message is expired" do
subscription_message[:expired_at] = Time.now subscription_message[:expired_at] = Time.now
stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json)
described_class.update(skip_plugin: true) described_class.update(skip_plugin: true)
notice = described_class.list.first notice = described_class.list(include_recently_expired: true).first
expect(notice.expired?).to eq(true) expect(notice.expired?).to eq(true)
end end
end end
@ -51,20 +51,20 @@ describe CustomWizard::Notice do
described_class.update(skip_subscription: true) described_class.update(skip_subscription: true)
end end
it "converts plugin statuses to warn into notices" do it "converts warning into notice" do
notice = described_class.list.first notice = described_class.list.first
expect(notice.type).to eq(described_class.types[:warning]) expect(notice.type).to eq(described_class.types[:plugin_status_warning])
expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain))) expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain)))
expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime)
end end
it "expires unexpired warning notices if status is recommended or compatible" do it "expires warning notices if status is recommended or compatible" do
plugin_status[:status] = 'compatible' plugin_status[:status] = 'compatible'
plugin_status[:status_changed_at] = Time.now plugin_status[:status_changed_at] = Time.now
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update(skip_subscription: true) described_class.update(skip_subscription: true)
notice = described_class.list(described_class.types[:warning]).first notice = described_class.list(type: described_class.types[:plugin_status_warning], include_recently_expired: true).first
expect(notice.expired?).to eq(true) expect(notice.expired?).to eq(true)
end end
end end
@ -75,6 +75,57 @@ describe CustomWizard::Notice do
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update described_class.update
expect(described_class.list.length).to eq(2) expect(described_class.list(include_recently_expired: true).length).to eq(2)
end end
end
context "connection errors" do
before do
freeze_time
end
it "creates an error if connection to notice server fails" do
stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json)
described_class.update(skip_subscription: true)
error = CustomWizard::Notice::ConnectionError.new(:plugin_status)
expect(error.errors.exists?).to eq(true)
end
it "only creates one connection error per type at a time" do
stub_request(:get, described_class.subscription_messages_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json)
stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json)
5.times { described_class.update }
plugin_status_errors = CustomWizard::Notice::ConnectionError.new(:plugin_status)
subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_messages)
expect(plugin_status_errors.errors.length).to eq(1)
expect(subscription_message_errors.errors.length).to eq(1)
end
it "creates a connection error notice if connection errors reach limit" do
stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json)
error = CustomWizard::Notice::ConnectionError.new(:plugin_status)
error.limit.times { described_class.update(skip_subscription: true) }
notice = described_class.list(type: described_class.types[:plugin_status_connection_error]).first
expect(error.current_error['count']).to eq(error.limit)
expect(notice.type).to eq(described_class.types[:plugin_status_connection_error])
end
it "expires a connection error notice if connection succeeds" do
stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json)
error = CustomWizard::Notice::ConnectionError.new(:plugin_status)
error.limit.times { described_class.update(skip_subscription: true) }
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update(skip_subscription: true)
notice = described_class.list(type: described_class.types[:plugin_status_connection_error], include_recently_expired: true).first
expect(notice.type).to eq(described_class.types[:plugin_status_connection_error])
expect(notice.expired_at.present?).to eq(true)
end
end
end

Datei anzeigen

@ -1 +1 @@
//= require_tree_discourse //= require_tree_discourse