DEV: Add notice specs and UI updates
Dieser Commit ist enthalten in:
Ursprung
084c6f4a7a
Commit
925c8c009a
23 geänderte Dateien mit 306 neuen und 129 gelöschten Zeilen
|
@ -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`;
|
||||||
}
|
}
|
||||||
})
|
});
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{#if importantNotice}}
|
||||||
|
{{wizard-notice notice=importantNotice importantOnDashboard=true}}
|
||||||
|
{{/if}}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,3 +0,0 @@
|
||||||
{{#if wizardWarningNotice}}
|
|
||||||
{{wizard-notice notice=wizardWarningNotice}}
|
|
||||||
{{/if}}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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({
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -13,11 +13,19 @@
|
||||||
</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,6 +36,12 @@
|
||||||
{{{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"}}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -18,3 +18,6 @@ plugins:
|
||||||
wizard_apis_enabled:
|
wizard_apis_enabled:
|
||||||
client: true
|
client: true
|
||||||
default: false
|
default: false
|
||||||
|
wizard_important_notices_on_dashboard:
|
||||||
|
client: true
|
||||||
|
default: true
|
|
@ -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
|
||||||
|
|
|
@ -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,17 +172,23 @@ 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
|
||||||
|
@ -172,6 +197,9 @@ class CustomWizard::Notice
|
||||||
|
|
||||||
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
|
||||||
|
|
82
lib/custom_wizard/notice/connection_error.rb
Normale Datei
82
lib/custom_wizard/notice/connection_error.rb
Normale Datei
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe CustomWizard::Notice do
|
||||||
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
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
Laden …
In neuem Issue referenzieren