DEV: Modernize the remaining admin-webhooks parts (#19438)
This commit is contained in:
parent
f19d687f01
commit
fd405179a7
|
@ -1,47 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["hook-event"],
|
||||
typeName: alias("type.name"),
|
||||
|
||||
@discourseComputed("typeName")
|
||||
name(typeName) {
|
||||
return I18n.t(`admin.web_hooks.${typeName}_event.name`);
|
||||
},
|
||||
|
||||
@discourseComputed("typeName")
|
||||
details(typeName) {
|
||||
return I18n.t(`admin.web_hooks.${typeName}_event.details`);
|
||||
},
|
||||
|
||||
@discourseComputed("model.[]", "typeName")
|
||||
eventTypeExists(eventTypes, typeName) {
|
||||
return eventTypes.any((event) => event.name === typeName);
|
||||
},
|
||||
|
||||
@discourseComputed("eventTypeExists")
|
||||
enabled: {
|
||||
get(eventTypeExists) {
|
||||
return eventTypeExists;
|
||||
},
|
||||
set(value, eventTypeExists) {
|
||||
const type = this.type;
|
||||
const model = this.model;
|
||||
// add an association when not exists
|
||||
if (value !== eventTypeExists) {
|
||||
if (value) {
|
||||
model.addObject(type);
|
||||
} else {
|
||||
model.removeObjects(
|
||||
model.filter((eventType) => eventType.name === type.name)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,110 +0,0 @@
|
|||
import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "li",
|
||||
expandDetails: null,
|
||||
expandDetailsRequestKey: "request",
|
||||
expandDetailsResponseKey: "response",
|
||||
dialog: service(),
|
||||
|
||||
@discourseComputed("model.status")
|
||||
statusColorClasses(status) {
|
||||
if (!status) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (status >= 200 && status <= 299) {
|
||||
return "text-successful";
|
||||
} else {
|
||||
return "text-danger";
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("model.created_at")
|
||||
createdAt(createdAt) {
|
||||
return moment(createdAt).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
|
||||
@discourseComputed("model.duration")
|
||||
completion(duration) {
|
||||
const seconds = Math.floor(duration / 10.0) / 100.0;
|
||||
return I18n.t("admin.web_hooks.events.completed_in", { count: seconds });
|
||||
},
|
||||
|
||||
@discourseComputed("expandDetails")
|
||||
expandRequestIcon(expandDetails) {
|
||||
return expandDetails === this.expandDetailsRequestKey
|
||||
? "ellipsis-h"
|
||||
: "ellipsis-v";
|
||||
},
|
||||
|
||||
@discourseComputed("expandDetails")
|
||||
expandResponseIcon(expandDetails) {
|
||||
return expandDetails === this.expandDetailsResponseKey
|
||||
? "ellipsis-h"
|
||||
: "ellipsis-v";
|
||||
},
|
||||
|
||||
actions: {
|
||||
redeliver() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.web_hooks.events.redeliver_confirm"),
|
||||
didConfirm: () => {
|
||||
return ajax(
|
||||
`/admin/api/web_hooks/${this.get(
|
||||
"model.web_hook_id"
|
||||
)}/events/${this.get("model.id")}/redeliver`,
|
||||
{ type: "POST" }
|
||||
)
|
||||
.then((json) => {
|
||||
this.set("model", json.web_hook_event);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
toggleRequest() {
|
||||
const expandDetailsKey = this.expandDetailsRequestKey;
|
||||
|
||||
if (this.expandDetails !== expandDetailsKey) {
|
||||
let headers = Object.assign(
|
||||
{
|
||||
"Request URL": this.get("model.request_url"),
|
||||
"Request method": "POST",
|
||||
},
|
||||
ensureJSON(this.get("model.headers"))
|
||||
);
|
||||
this.setProperties({
|
||||
headers: plainJSON(headers),
|
||||
body: prettyJSON(this.get("model.payload")),
|
||||
expandDetails: expandDetailsKey,
|
||||
bodyLabel: I18n.t("admin.web_hooks.events.payload"),
|
||||
});
|
||||
} else {
|
||||
this.set("expandDetails", null);
|
||||
}
|
||||
},
|
||||
|
||||
toggleResponse() {
|
||||
const expandDetailsKey = this.expandDetailsResponseKey;
|
||||
|
||||
if (this.expandDetails !== expandDetailsKey) {
|
||||
this.setProperties({
|
||||
headers: plainJSON(this.get("model.response_headers")),
|
||||
body: this.get("model.response_body"),
|
||||
expandDetails: expandDetailsKey,
|
||||
bodyLabel: I18n.t("admin.web_hooks.events.body"),
|
||||
});
|
||||
} else {
|
||||
this.set("expandDetails", null);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default Component.extend({
|
||||
classes: ["text-muted", "text-danger", "text-successful", "text-muted"],
|
||||
icons: ["far-circle", "times-circle", "circle", "circle"],
|
||||
circleIcon: null,
|
||||
deliveryStatus: null,
|
||||
|
||||
@discourseComputed("deliveryStatuses", "model.last_delivery_status")
|
||||
status(deliveryStatuses, lastDeliveryStatus) {
|
||||
return deliveryStatuses.find((s) => s.id === lastDeliveryStatus);
|
||||
},
|
||||
|
||||
@discourseComputed("status.id", "icons")
|
||||
icon(statusId, icons) {
|
||||
return icons[statusId - 1];
|
||||
},
|
||||
|
||||
@discourseComputed("status.id", "classes")
|
||||
class(statusId, classes) {
|
||||
return classes[statusId - 1];
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
this.set(
|
||||
"circleIcon",
|
||||
htmlSafe(iconHTML(this.icon, { class: this.class }))
|
||||
);
|
||||
this.set(
|
||||
"deliveryStatus",
|
||||
I18n.t(`admin.web_hooks.delivery_status.${this.get("status.name")}`)
|
||||
);
|
||||
},
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
<label>
|
||||
<label class="hook-event">
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{this.enabled}}
|
|
@ -0,0 +1,41 @@
|
|||
import Component from "@glimmer/component";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class WebhookEventChooser extends Component {
|
||||
get name() {
|
||||
return I18n.t(`admin.web_hooks.${this.args.type.name}_event.name`);
|
||||
}
|
||||
|
||||
get details() {
|
||||
return I18n.t(`admin.web_hooks.${this.args.type.name}_event.details`);
|
||||
}
|
||||
|
||||
get eventTypeExists() {
|
||||
return this.args.eventTypes.any(
|
||||
(event) => event.name === this.args.type.name
|
||||
);
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
return this.eventTypeExists;
|
||||
}
|
||||
|
||||
set enabled(value) {
|
||||
const eventTypes = this.args.eventTypes;
|
||||
|
||||
// add an association when not exists
|
||||
if (value === this.eventTypeExists) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
eventTypes.addObject(this.args.type);
|
||||
} else {
|
||||
eventTypes.removeObjects(
|
||||
eventTypes.filter((eventType) => eventType.name === this.args.type.name)
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<li>
|
||||
<div class="col first status">
|
||||
<span class={{this.statusColorClasses}}>{{@event.status}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col event-id">{{@event.id}}</div>
|
||||
|
||||
<div class="col timestamp">{{this.createdAt}}</div>
|
||||
|
||||
<div class="col completion">{{this.completion}}</div>
|
||||
|
||||
<div class="col actions">
|
||||
<DButton
|
||||
@icon={{this.expandRequestIcon}}
|
||||
@action={{this.toggleRequest}}
|
||||
@label="admin.web_hooks.events.request"
|
||||
/>
|
||||
<DButton
|
||||
@icon={{this.expandResponseIcon}}
|
||||
@action={{this.toggleResponse}}
|
||||
@label="admin.web_hooks.events.response"
|
||||
/>
|
||||
<DButton
|
||||
@icon="sync"
|
||||
@action={{this.redeliver}}
|
||||
@label="admin.web_hooks.events.redeliver"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.expandDetails}}
|
||||
<div class="details">
|
||||
<h3>{{i18n "admin.web_hooks.events.headers"}}</h3>
|
||||
<pre><code>{{this.headers}}</code></pre>
|
||||
|
||||
<h3>{{this.bodyLabel}}</h3>
|
||||
<pre><code>{{this.body}}</code></pre>
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
|
@ -0,0 +1,102 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class WebhookEvent extends Component {
|
||||
@service dialog;
|
||||
|
||||
@tracked body = "";
|
||||
@tracked bodyLabel = "";
|
||||
@tracked expandDetails = null;
|
||||
@tracked headers = "";
|
||||
expandDetailsRequestKey = "request";
|
||||
expandDetailsResponseKey = "response";
|
||||
|
||||
get statusColorClasses() {
|
||||
const { status } = this.args.event;
|
||||
|
||||
if (!status) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (status >= 200 && status <= 299) {
|
||||
return "text-successful";
|
||||
} else {
|
||||
return "text-danger";
|
||||
}
|
||||
}
|
||||
|
||||
get createdAt() {
|
||||
return moment(this.args.event.created_at).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
get completion() {
|
||||
const seconds = Math.floor(this.args.event.duration / 10.0) / 100.0;
|
||||
return I18n.t("admin.web_hooks.events.completed_in", { count: seconds });
|
||||
}
|
||||
|
||||
get expandRequestIcon() {
|
||||
return this.expandDetails === this.expandDetailsRequestKey
|
||||
? "ellipsis-h"
|
||||
: "ellipsis-v";
|
||||
}
|
||||
|
||||
get expandResponseIcon() {
|
||||
return this.expandDetails === this.expandDetailsResponseKey
|
||||
? "ellipsis-h"
|
||||
: "ellipsis-v";
|
||||
}
|
||||
|
||||
@action
|
||||
redeliver() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.web_hooks.events.redeliver_confirm"),
|
||||
didConfirm: async () => {
|
||||
try {
|
||||
const json = await ajax(
|
||||
`/admin/api/web_hooks/${this.args.event.web_hook_id}/events/${this.args.event.id}/redeliver`,
|
||||
{ type: "POST" }
|
||||
);
|
||||
this.args.event.setProperties(json.web_hook_event);
|
||||
} catch (e) {
|
||||
popupAjaxError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
toggleRequest() {
|
||||
if (this.expandDetails !== this.expandDetailsRequestKey) {
|
||||
const headers = {
|
||||
"Request URL": this.args.event.request_url,
|
||||
"Request method": "POST",
|
||||
...ensureJSON(this.args.event.headers),
|
||||
};
|
||||
|
||||
this.headers = plainJSON(headers);
|
||||
this.body = prettyJSON(this.args.event.payload);
|
||||
this.expandDetails = this.expandDetailsRequestKey;
|
||||
this.bodyLabel = I18n.t("admin.web_hooks.events.payload");
|
||||
} else {
|
||||
this.expandDetails = null;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleResponse() {
|
||||
if (this.expandDetails !== this.expandDetailsResponseKey) {
|
||||
this.headers = plainJSON(this.args.event.response_headers);
|
||||
this.body = this.args.event.response_body;
|
||||
this.expandDetails = this.expandDetailsResponseKey;
|
||||
this.bodyLabel = I18n.t("admin.web_hooks.events.body");
|
||||
} else {
|
||||
this.expandDetails = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,8 +28,8 @@
|
|||
{{/if}}
|
||||
|
||||
<ul>
|
||||
{{#each this.events as |webHookEvent|}}
|
||||
<AdminWebHookEvent @model={{webHookEvent}} />
|
||||
{{#each this.events as |event|}}
|
||||
<WebhookEvent @event={{event}} />
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -61,8 +61,8 @@ export default class WebhookEvents extends Component {
|
|||
data: { ids: this.incomingEventIds },
|
||||
});
|
||||
|
||||
const objects = data.map((webHookEvent) =>
|
||||
this.store.createRecord("web-hook-event", webHookEvent)
|
||||
const objects = data.map((webhookEvent) =>
|
||||
this.store.createRecord("web-hook-event", webhookEvent)
|
||||
);
|
||||
this.events.unshiftObjects(objects);
|
||||
this.incomingEventIds = [];
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
{{d-icon this.iconName (hash class=this.iconClass)}}
|
||||
{{this.deliveryStatus}}
|
|
@ -0,0 +1,24 @@
|
|||
import Component from "@glimmer/component";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class WebhookStatus extends Component {
|
||||
iconNames = ["far-circle", "times-circle", "circle", "circle"];
|
||||
iconClasses = ["text-muted", "text-danger", "text-successful", "text-muted"];
|
||||
|
||||
get status() {
|
||||
const lastStatus = this.args.webhook.last_delivery_status;
|
||||
return this.args.deliveryStatuses.find((s) => s.id === lastStatus);
|
||||
}
|
||||
|
||||
get deliveryStatus() {
|
||||
return I18n.t(`admin.web_hooks.delivery_status.${this.status.name}`);
|
||||
}
|
||||
|
||||
get iconName() {
|
||||
return this.iconNames[this.status.id - 1];
|
||||
}
|
||||
|
||||
get iconClass() {
|
||||
return this.iconClasses[this.status.id - 1];
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ export default RestModel.extend({
|
|||
groupsFilterInName: null,
|
||||
|
||||
@discourseComputed("wildcard_web_hook")
|
||||
webHookType: {
|
||||
webhookType: {
|
||||
get(wildcard) {
|
||||
return wildcard ? "wildcard" : "individual";
|
||||
},
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<div class="col first status">
|
||||
<span class={{this.statusColorClasses}}>{{this.model.status}}</span>
|
||||
</div>
|
||||
<div class="col event-id">{{this.model.id}}</div>
|
||||
<div class="col timestamp">{{this.createdAt}}</div>
|
||||
<div class="col completion">{{this.completion}}</div>
|
||||
<div class="col actions">
|
||||
<DButton @icon={{this.expandRequestIcon}} @action={{action "toggleRequest"}} @label="admin.web_hooks.events.request" />
|
||||
<DButton @icon={{this.expandResponseIcon}} @action={{action "toggleResponse"}} @label="admin.web_hooks.events.response" />
|
||||
<DButton @icon="sync" @action={{action "redeliver"}} @label="admin.web_hooks.events.redeliver" />
|
||||
</div>
|
||||
{{#if this.expandDetails}}
|
||||
<div class="details">
|
||||
<h3>{{i18n "admin.web_hooks.events.headers"}}</h3>
|
||||
<pre><code>{{this.headers}}</code></pre>
|
||||
<h3>{{this.bodyLabel}}</h3>
|
||||
<pre><code>{{this.body}}</code></pre>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1 +0,0 @@
|
|||
{{this.circleIcon}} {{this.deliveryStatus}}
|
|
@ -26,8 +26,13 @@
|
|||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "admin.web_hooks.event_chooser"}}</label>
|
||||
<label>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="individual" @selection={{this.model.webHookType}} />
|
||||
|
||||
<label class="subscription-choice">
|
||||
<RadioButton
|
||||
@name="subscription-choice"
|
||||
@value="individual"
|
||||
@selection={{this.model.webhookType}}
|
||||
/>
|
||||
{{i18n "admin.web_hooks.individual_event"}}
|
||||
<InputTip @validation={{this.eventTypeValidation}} />
|
||||
</label>
|
||||
|
@ -35,13 +40,20 @@
|
|||
{{#unless this.model.wildcard_web_hook}}
|
||||
<div class="event-selector">
|
||||
{{#each this.eventTypes as |type|}}
|
||||
<AdminWebHookEventChooser @type={{type}} @model={{this.model.web_hook_event_types}} />
|
||||
<WebhookEventChooser
|
||||
@type={{type}}
|
||||
@eventTypes={{this.model.web_hook_event_types}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<label>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="wildcard" @selection={{this.model.webHookType}} />
|
||||
<label class="subscription-choice">
|
||||
<RadioButton
|
||||
@name="subscription-choice"
|
||||
@value="wildcard"
|
||||
@selection={{this.model.webhookType}}
|
||||
/>
|
||||
{{i18n "admin.web_hooks.wildcard_event"}}
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -24,24 +24,26 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.model as |webHook|}}
|
||||
{{#each this.model as |webhook|}}
|
||||
<tr>
|
||||
<td class="delivery-status">
|
||||
<LinkTo @route="adminWebHooks.show" @model={{webHook}}>
|
||||
<AdminWebHookStatus
|
||||
<LinkTo @route="adminWebHooks.show" @model={{webhook}}>
|
||||
<WebhookStatus
|
||||
@deliveryStatuses={{this.deliveryStatuses}}
|
||||
@model={{webHook}}
|
||||
@webhook={{webhook}}
|
||||
/>
|
||||
</LinkTo>
|
||||
</td>
|
||||
<td class="payload-url">
|
||||
<LinkTo @route="adminWebHooks.edit" @model={{webHook}}>{{webHook.payload_url}}</LinkTo>
|
||||
<LinkTo @route="adminWebHooks.edit" @model={{webhook}}>
|
||||
{{webhook.payload_url}}
|
||||
</LinkTo>
|
||||
</td>
|
||||
<td class="description">{{webHook.description}}</td>
|
||||
<td class="description">{{webhook.description}}</td>
|
||||
<td class="controls">
|
||||
<LinkTo
|
||||
@route="adminWebHooks.edit"
|
||||
@model={{webHook}}
|
||||
@model={{webhook}}
|
||||
class="btn btn-default no-text"
|
||||
title={{i18n "admin.web_hooks.edit"}}
|
||||
>
|
||||
|
@ -51,7 +53,7 @@
|
|||
<DButton
|
||||
@class="destroy btn-danger"
|
||||
@action={{this.destroy}}
|
||||
@actionParam={{webHook}}
|
||||
@actionParam={{webhook}}
|
||||
@icon="times"
|
||||
@title="delete"
|
||||
/>
|
||||
|
|
|
@ -65,16 +65,19 @@ export function enableMissingIconWarning() {
|
|||
}
|
||||
|
||||
export function renderIcon(renderType, id, params) {
|
||||
for (let i = 0; i < _renderers.length; i++) {
|
||||
let renderer = _renderers[i];
|
||||
let rendererForType = renderer[renderType];
|
||||
params ||= {};
|
||||
|
||||
if (rendererForType) {
|
||||
const icon = { id, replacementId: REPLACEMENTS[id] };
|
||||
let result = rendererForType(icon, params || {});
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
for (const renderer of _renderers) {
|
||||
const rendererForType = renderer[renderType];
|
||||
if (!rendererForType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const icon = { id, replacementId: REPLACEMENTS[id] };
|
||||
const result = rendererForType(icon, params);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ export default Component.extend({
|
|||
: this._reset("invisible");
|
||||
},
|
||||
|
||||
_set(name, icon, key, iconArgs = null) {
|
||||
_set(name, icon, key, iconArgs) {
|
||||
this.set(`${name}Icon`, htmlSafe(iconHTML(`${icon}`, iconArgs)));
|
||||
this.set(`${name}Title`, I18n.t(`topic_statuses.${key}.help`));
|
||||
return true;
|
||||
|
|
|
@ -15,7 +15,7 @@ export default createWidget("topic-status", {
|
|||
const result = [];
|
||||
|
||||
TopicStatusIcons.render(topic, function (name, key) {
|
||||
const iconArgs = key === "unpinned" ? { class: "unpinned" } : null;
|
||||
const iconArgs = { class: key === "unpinned" ? "unpinned" : null };
|
||||
const icon = iconNode(name, iconArgs);
|
||||
|
||||
const attributes = {
|
||||
|
|
|
@ -260,8 +260,13 @@ table.api-keys {
|
|||
.instructions {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.subscription-choice {
|
||||
margin-bottom: 10px;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue