FEATURE: Add Filter for Webhook Events by Status (#27332)
* FEATURE: Add Filter for Webhook Events by Status * Fixing multiple issues * Lint * Fixing multiple issues * Change the range of the status for webhook events
This commit is contained in:
parent
970d7e9cd9
commit
c13f64d35b
|
@ -4,4 +4,22 @@ export default class WebHookEvent extends RestAdapter {
|
|||
basePath() {
|
||||
return "/admin/api/";
|
||||
}
|
||||
|
||||
appendQueryParams(path, findArgs, extension) {
|
||||
const urlSearchParams = new URLSearchParams();
|
||||
|
||||
for (const [key, value] of Object.entries(findArgs)) {
|
||||
if (value && key !== "webhookId") {
|
||||
urlSearchParams.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
const queryString = urlSearchParams.toString();
|
||||
let url = `${path}/${findArgs.webhookId}${extension || ""}`;
|
||||
|
||||
if (queryString) {
|
||||
url = `${url}?${queryString}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
<div
|
||||
class="web-hook-events-listing"
|
||||
{{did-insert this.subscribe}}
|
||||
{{did-update this.reloadEvents @status}}
|
||||
{{will-destroy this.unsubscribe}}
|
||||
>
|
||||
<DButton
|
||||
@icon="paper-plane"
|
||||
@label="admin.web_hooks.events.ping"
|
||||
@action={{this.ping}}
|
||||
@disabled={{not this.pingEnabled}}
|
||||
class="webhook-events__ping-button"
|
||||
/>
|
||||
<div class="web-hook-events-actions">
|
||||
<ComboBox
|
||||
@value={{@status}}
|
||||
@content={{this.statuses}}
|
||||
@onChange={{fn (mut @status)}}
|
||||
@options={{hash none="admin.web_hooks.events.filter_status.all"}}
|
||||
class="delivery-status-filters"
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@icon="paper-plane"
|
||||
@label="admin.web_hooks.events.ping"
|
||||
@action={{this.ping}}
|
||||
@disabled={{not this.pingEnabled}}
|
||||
class="webhook-events__ping-button"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.events}}
|
||||
<LoadMore @selector=".web-hook-events li" @action={{this.loadMore}}>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { service } from "@ember/service";
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
export default class WebhookEvents extends Component {
|
||||
@service messageBus;
|
||||
|
@ -24,10 +25,40 @@ export default class WebhookEvents extends Component {
|
|||
}
|
||||
|
||||
async loadEvents() {
|
||||
this.events = await this.store.findAll(
|
||||
"web-hook-event",
|
||||
this.args.webhookId
|
||||
);
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
this.events = await this.store.findAll("web-hook-event", {
|
||||
webhookId: this.args.webhookId,
|
||||
status: this.args.status,
|
||||
});
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
get statuses() {
|
||||
return [
|
||||
{
|
||||
id: "successful",
|
||||
name: I18n.t("admin.web_hooks.events.filter_status.successful"),
|
||||
},
|
||||
{
|
||||
id: "failed",
|
||||
name: I18n.t("admin.web_hooks.events.filter_status.failed"),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@bind
|
||||
reloadEvents() {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadEvents();
|
||||
}
|
||||
|
||||
@bind
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
|
@ -8,6 +9,9 @@ export default class AdminWebHooksShowController extends Controller {
|
|||
@service dialog;
|
||||
@service router;
|
||||
@controller adminWebHooks;
|
||||
@tracked status;
|
||||
|
||||
queryParams = ["status"];
|
||||
|
||||
@action
|
||||
edit() {
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<WebhookEvents @webhookId={{this.model.id}} />
|
||||
<WebhookEvents @webhookId={{this.model.id}} @status={{this.status}} />
|
|
@ -338,7 +338,10 @@ table.api-keys {
|
|||
}
|
||||
}
|
||||
|
||||
.webhook-events__ping-button {
|
||||
.web-hook-events-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,14 +87,19 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
def list_events
|
||||
limit = 50
|
||||
offset = params[:offset].to_i
|
||||
events = @web_hook.web_hook_events
|
||||
if params[:status] == "successful"
|
||||
events = events.successful
|
||||
elsif params[:status] == "failed"
|
||||
events = events.failed
|
||||
end
|
||||
|
||||
total = events.count
|
||||
events = events.limit(limit).offset(offset)
|
||||
|
||||
json = {
|
||||
web_hook_events:
|
||||
serialize_data(
|
||||
@web_hook.web_hook_events.limit(limit).offset(offset),
|
||||
AdminWebHookEventSerializer,
|
||||
),
|
||||
total_rows_web_hook_events: @web_hook.web_hook_events.count,
|
||||
web_hook_events: serialize_data(events, AdminWebHookEventSerializer),
|
||||
total_rows_web_hook_events: total,
|
||||
load_more_web_hook_events:
|
||||
web_hook_events_admin_api_index_path(limit: limit, offset: offset + limit, format: :json),
|
||||
extras: {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class WebHookEvent < ActiveRecord::Base
|
||||
scope :successful, -> { where("status >= 200 AND status <= 299") }
|
||||
scope :failed, -> { where("status < 200 OR status > 299") }
|
||||
belongs_to :web_hook
|
||||
|
||||
after_save :update_web_hook_delivery_status
|
||||
|
|
|
@ -5357,6 +5357,10 @@ en:
|
|||
timestamp: "Created"
|
||||
completion: "Completion Time"
|
||||
actions: "Actions"
|
||||
filter_status:
|
||||
all: "All Events"
|
||||
successful: "Delivered"
|
||||
failed: "Failed"
|
||||
home:
|
||||
title: "Home"
|
||||
account:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:web_hook_event) do
|
||||
web_hook { Fabricate(:web_hook) }
|
||||
payload { { some_key: "some_value" }.to_json }
|
||||
status 200
|
||||
end
|
|
@ -197,6 +197,40 @@ RSpec.describe Admin::WebHooksController do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#list_events" do
|
||||
fab!(:web_hook_event1) { Fabricate(:web_hook_event, web_hook: web_hook, id: 1, status: 200) }
|
||||
fab!(:web_hook_event2) { Fabricate(:web_hook_event, web_hook: web_hook, id: 2, status: 404) }
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
context "when status is 'successful'" do
|
||||
it "lists the successfully delivered webhook events" do
|
||||
get "/admin/api/web_hook_events/#{web_hook.id}.json", params: { status: "successful" }
|
||||
expect(response.parsed_body["web_hook_events"].map { |c| c["id"] }).to eq(
|
||||
[web_hook_event1.id],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when status is 'failed'" do
|
||||
it "lists the failed webhook events" do
|
||||
get "/admin/api/web_hook_events/#{web_hook.id}.json", params: { status: "failed" }
|
||||
expect(response.parsed_body["web_hook_events"].map { |c| c["id"] }).to eq(
|
||||
[web_hook_event2.id],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there is no status param" do
|
||||
it "lists all webhook events" do
|
||||
get "/admin/api/web_hook_events/#{web_hook.id}.json"
|
||||
expect(response.parsed_body["web_hook_events"].map { |c| c["id"] }).to match_array(
|
||||
[web_hook_event1.id, web_hook_event2.id],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ping" do
|
||||
context "when logged in as admin" do
|
||||
before { sign_in(admin) }
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe "Admin WebHook Events", type: :system do
|
||||
fab!(:web_hook)
|
||||
fab!(:admin)
|
||||
fab!(:web_hook_event1) { Fabricate(:web_hook_event, web_hook: web_hook, status: 200) }
|
||||
fab!(:web_hook_event2) { Fabricate(:web_hook_event, web_hook: web_hook, status: 404) }
|
||||
|
||||
let(:admin_web_hooks_page) { PageObjects::Pages::AdminWebHookEvents.new }
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
it "shows all webhook events when filter is on 'All Events'" do
|
||||
admin_web_hooks_page.visit(web_hook.id)
|
||||
|
||||
expect(admin_web_hooks_page).to have_web_hook_event(web_hook_event1.id)
|
||||
expect(admin_web_hooks_page).to have_web_hook_event(web_hook_event2.id)
|
||||
end
|
||||
|
||||
it "shows only successfully delivered webhook events when filter is on 'Delivered'" do
|
||||
admin_web_hooks_page.visit(web_hook.id)
|
||||
admin_web_hooks_page.click_filter_all
|
||||
admin_web_hooks_page.click_filter_delivered
|
||||
|
||||
expect(admin_web_hooks_page).to have_web_hook_event(web_hook_event1.id)
|
||||
expect(admin_web_hooks_page).to have_no_web_hook_event(web_hook_event2.id)
|
||||
end
|
||||
|
||||
it "shows only webhook events that are failed to deliver when filter is on 'Failed'" do
|
||||
admin_web_hooks_page.visit(web_hook.id)
|
||||
admin_web_hooks_page.click_filter_all
|
||||
admin_web_hooks_page.click_filter_failed
|
||||
|
||||
expect(admin_web_hooks_page).to have_no_web_hook_event(web_hook_event1.id)
|
||||
expect(admin_web_hooks_page).to have_web_hook_event(web_hook_event2.id)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Pages
|
||||
class AdminWebHookEvents < PageObjects::Pages::Base
|
||||
def visit(id)
|
||||
page.visit("/admin/api/web_hooks/#{id}")
|
||||
self
|
||||
end
|
||||
|
||||
def click_filter_all
|
||||
find(".select-kit-header", text: "All Events").click
|
||||
end
|
||||
|
||||
def click_filter_delivered
|
||||
find(".select-kit-row", text: "Delivered").click
|
||||
end
|
||||
|
||||
def click_filter_failed
|
||||
find(".select-kit-row", text: "Failed").click
|
||||
end
|
||||
|
||||
def has_web_hook_event?(id)
|
||||
page.has_css?("li .event-id", text: id)
|
||||
end
|
||||
|
||||
def has_no_web_hook_event?(id)
|
||||
page.has_no_css?("li .event-id", text: id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue