DEV: Admin webhooks interface issues (#19360)

1. The events table had broken styling, making each row overflow
2. It had confusing routes: `/:id` for "edit" and `/:id/events` for "show" (now it's `/:id/edit` and `/:id` respectively)
3. There previously was an unused backend action (`#edit`) - now it is used (and `web_hooks/:id/events` route has been removed)
4. There was outdated/misplaced/duplicated CSS
5. And more
This commit is contained in:
Jarek Radosz
2022-12-13 01:53:08 +01:00
committed by GitHub
parent 4001e6f174
commit f9bdda84ca
23 changed files with 611 additions and 443 deletions
@@ -0,0 +1,101 @@
import Controller, { inject as controller } from "@ember/controller";
import EmberObject, { action } from "@ember/object";
import I18n from "I18n";
import { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default Controller.extend({
adminWebHooks: controller(),
dialog: service(),
eventTypes: alias("adminWebHooks.eventTypes"),
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
contentTypes: alias("adminWebHooks.contentTypes"),
@discourseComputed
showTagsFilter() {
return this.siteSettings.tagging_enabled;
},
@discourseComputed("model.isSaving", "saved", "saveButtonDisabled")
savingStatus(isSaving, saved, saveButtonDisabled) {
if (isSaving) {
return I18n.t("saving");
} else if (!saveButtonDisabled && saved) {
return I18n.t("saved");
}
// Use side effect of validation to clear saved text
this.set("saved", false);
return "";
},
@discourseComputed("model.isNew")
saveButtonText(isNew) {
return isNew
? I18n.t("admin.web_hooks.create")
: I18n.t("admin.web_hooks.save");
},
@discourseComputed("model.secret")
secretValidation(secret) {
if (!isEmpty(secret)) {
if (secret.includes(" ")) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.secret_invalid"),
});
}
if (secret.length < 12) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.secret_too_short"),
});
}
}
},
@discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]")
eventTypeValidation(isWildcard, eventTypes) {
if (!isWildcard && isEmpty(eventTypes)) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.event_type_missing"),
});
}
},
@discourseComputed(
"model.isSaving",
"secretValidation",
"eventTypeValidation",
"model.payload_url"
)
saveButtonDisabled(
isSaving,
secretValidation,
eventTypeValidation,
payloadUrl
) {
return isSaving
? false
: secretValidation || eventTypeValidation || isEmpty(payloadUrl);
},
@action
async save() {
this.set("saved", false);
try {
await this.model.save();
this.set("saved", true);
this.adminWebHooks.model.addObject(this.model);
this.transitionToRoute("adminWebHooks.show", this.model);
} catch (e) {
popupAjaxError(e);
}
},
});
@@ -0,0 +1,36 @@
import Controller, { inject as controller } from "@ember/controller";
import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import { alias } from "@ember/object/computed";
export default Controller.extend({
adminWebHooks: controller(),
dialog: service(),
contentTypes: alias("adminWebHooks.contentTypes"),
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
deliveryStatuses: alias("adminWebHooks.deliveryStatuses"),
eventTypes: alias("adminWebHooks.eventTypes"),
model: alias("adminWebHooks.model"),
@action
destroy(webhook) {
return this.dialog.deleteConfirm({
message: I18n.t("admin.web_hooks.delete_confirm"),
didConfirm: async () => {
try {
await webhook.destroyRecord();
this.model.removeObject(webhook);
} catch (e) {
popupAjaxError(e);
}
},
});
},
@action
loadMore() {
this.model.loadMore();
},
});
@@ -1,81 +0,0 @@
import Controller from "@ember/controller";
import { ajax } from "discourse/lib/ajax";
import { action } from "@ember/object";
import { alias } from "@ember/object/computed";
import discourseComputed, { bind } from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend({
pingDisabled: false,
incomingCount: alias("incomingEventIds.length"),
init() {
this._super(...arguments);
this.incomingEventIds = [];
},
@discourseComputed("incomingCount")
hasIncoming(incomingCount) {
return incomingCount > 0;
},
subscribe() {
this.messageBus.subscribe(
`/web_hook_events/${this.get("model.extras.web_hook_id")}`,
this._addIncoming
);
},
unsubscribe() {
this.messageBus.unsubscribe("/web_hook_events/*", this._addIncoming);
},
@bind
_addIncoming(data) {
if (data.event_type === "ping") {
this.set("pingDisabled", false);
}
if (!this.incomingEventIds.includes(data.web_hook_event_id)) {
this.incomingEventIds.pushObject(data.web_hook_event_id);
}
},
@action
showInserted(event) {
event?.preventDefault();
const webHookId = this.get("model.extras.web_hook_id");
ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, {
type: "GET",
data: { ids: this.incomingEventIds },
}).then((data) => {
const objects = data.map((webHookEvent) =>
this.store.createRecord("web-hook-event", webHookEvent)
);
this.model.unshiftObjects(objects);
this.set("incomingEventIds", []);
});
},
actions: {
loadMore() {
this.model.loadMore();
},
ping() {
this.set("pingDisabled", true);
ajax(
`/admin/api/web_hooks/${this.get("model.extras.web_hook_id")}/ping`,
{
type: "POST",
}
).catch((error) => {
this.set("pingDisabled", false);
popupAjaxError(error);
});
},
},
});
@@ -1,121 +1,32 @@
import Controller, { inject as controller } from "@ember/controller";
import EmberObject from "@ember/object";
import { action } from "@ember/object";
import I18n from "I18n";
import { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default Controller.extend({
adminWebHooks: controller(),
dialog: service(),
eventTypes: alias("adminWebHooks.eventTypes"),
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
contentTypes: alias("adminWebHooks.contentTypes"),
router: service(),
@discourseComputed
showTagsFilter() {
return this.siteSettings.tagging_enabled;
@action
edit() {
return this.router.transitionTo("adminWebHooks.edit", this.model);
},
@discourseComputed("model.isSaving", "saved", "saveButtonDisabled")
savingStatus(isSaving, saved, saveButtonDisabled) {
if (isSaving) {
return I18n.t("saving");
} else if (!saveButtonDisabled && saved) {
return I18n.t("saved");
}
// Use side effect of validation to clear saved text
this.set("saved", false);
return "";
},
@discourseComputed("model.isNew")
saveButtonText(isNew) {
return isNew
? I18n.t("admin.web_hooks.create")
: I18n.t("admin.web_hooks.save");
},
@discourseComputed("model.secret")
secretValidation(secret) {
if (!isEmpty(secret)) {
if (secret.includes(" ")) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.secret_invalid"),
});
}
if (secret.length < 12) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.secret_too_short"),
});
}
}
},
@discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]")
eventTypeValidation(isWildcard, eventTypes) {
if (!isWildcard && isEmpty(eventTypes)) {
return EmberObject.create({
failed: true,
reason: I18n.t("admin.web_hooks.event_type_missing"),
});
}
},
@discourseComputed(
"model.isSaving",
"secretValidation",
"eventTypeValidation",
"model.payload_url"
)
saveButtonDisabled(
isSaving,
secretValidation,
eventTypeValidation,
payloadUrl
) {
return isSaving
? false
: secretValidation || eventTypeValidation || isEmpty(payloadUrl);
},
actions: {
save() {
this.set("saved", false);
const model = this.model;
const isNew = model.get("isNew");
return model
.save()
.then(() => {
this.set("saved", true);
this.adminWebHooks.get("model").addObject(model);
if (isNew) {
this.transitionToRoute("adminWebHooks.show", model.get("id"));
}
})
.catch(popupAjaxError);
},
destroy() {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.web_hooks.delete_confirm"),
didConfirm: () => {
this.model
.destroyRecord()
.then(() => {
this.adminWebHooks.get("model").removeObject(this.model);
this.transitionToRoute("adminWebHooks");
})
.catch(popupAjaxError);
},
});
},
@action
destroy() {
return this.dialog.deleteConfirm({
message: I18n.t("admin.web_hooks.delete_confirm"),
didConfirm: async () => {
try {
await this.model.destroyRecord();
this.adminWebHooks.model.removeObject(this.model);
this.transitionToRoute("adminWebHooks");
} catch (e) {
popupAjaxError(e);
}
},
});
},
});
@@ -1,29 +1,3 @@
import Controller from "@ember/controller";
import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
export default Controller.extend({
dialog: service(),
@action
destroy(webhook) {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.web_hooks.delete_confirm"),
didConfirm: () => {
webhook
.destroyRecord()
.then(() => {
this.model.removeObject(webhook);
})
.catch(popupAjaxError);
},
});
},
@action
loadMore() {
this.model.loadMore();
},
});
export default Controller.extend({});