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() {
|
basePath() {
|
||||||
return "/admin/api/";
|
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
|
<div
|
||||||
class="web-hook-events-listing"
|
class="web-hook-events-listing"
|
||||||
{{did-insert this.subscribe}}
|
{{did-insert this.subscribe}}
|
||||||
|
{{did-update this.reloadEvents @status}}
|
||||||
{{will-destroy this.unsubscribe}}
|
{{will-destroy this.unsubscribe}}
|
||||||
>
|
>
|
||||||
<DButton
|
<div class="web-hook-events-actions">
|
||||||
@icon="paper-plane"
|
<ComboBox
|
||||||
@label="admin.web_hooks.events.ping"
|
@value={{@status}}
|
||||||
@action={{this.ping}}
|
@content={{this.statuses}}
|
||||||
@disabled={{not this.pingEnabled}}
|
@onChange={{fn (mut @status)}}
|
||||||
class="webhook-events__ping-button"
|
@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}}
|
{{#if this.events}}
|
||||||
<LoadMore @selector=".web-hook-events li" @action={{this.loadMore}}>
|
<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 { ajax } from "discourse/lib/ajax";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
export default class WebhookEvents extends Component {
|
export default class WebhookEvents extends Component {
|
||||||
@service messageBus;
|
@service messageBus;
|
||||||
|
@ -24,10 +25,40 @@ export default class WebhookEvents extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadEvents() {
|
async loadEvents() {
|
||||||
this.events = await this.store.findAll(
|
this.loading = true;
|
||||||
"web-hook-event",
|
|
||||||
this.args.webhookId
|
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
|
@bind
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
import Controller, { inject as controller } from "@ember/controller";
|
import Controller, { inject as controller } from "@ember/controller";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
|
@ -8,6 +9,9 @@ export default class AdminWebHooksShowController extends Controller {
|
||||||
@service dialog;
|
@service dialog;
|
||||||
@service router;
|
@service router;
|
||||||
@controller adminWebHooks;
|
@controller adminWebHooks;
|
||||||
|
@tracked status;
|
||||||
|
|
||||||
|
queryParams = ["status"];
|
||||||
|
|
||||||
@action
|
@action
|
||||||
edit() {
|
edit() {
|
||||||
|
|
|
@ -31,4 +31,4 @@
|
||||||
</div>
|
</div>
|
||||||
</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;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,14 +87,19 @@ class Admin::WebHooksController < Admin::AdminController
|
||||||
def list_events
|
def list_events
|
||||||
limit = 50
|
limit = 50
|
||||||
offset = params[:offset].to_i
|
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 = {
|
json = {
|
||||||
web_hook_events:
|
web_hook_events: serialize_data(events, AdminWebHookEventSerializer),
|
||||||
serialize_data(
|
total_rows_web_hook_events: total,
|
||||||
@web_hook.web_hook_events.limit(limit).offset(offset),
|
|
||||||
AdminWebHookEventSerializer,
|
|
||||||
),
|
|
||||||
total_rows_web_hook_events: @web_hook.web_hook_events.count,
|
|
||||||
load_more_web_hook_events:
|
load_more_web_hook_events:
|
||||||
web_hook_events_admin_api_index_path(limit: limit, offset: offset + limit, format: :json),
|
web_hook_events_admin_api_index_path(limit: limit, offset: offset + limit, format: :json),
|
||||||
extras: {
|
extras: {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class WebHookEvent < ActiveRecord::Base
|
class WebHookEvent < ActiveRecord::Base
|
||||||
|
scope :successful, -> { where("status >= 200 AND status <= 299") }
|
||||||
|
scope :failed, -> { where("status < 200 OR status > 299") }
|
||||||
belongs_to :web_hook
|
belongs_to :web_hook
|
||||||
|
|
||||||
after_save :update_web_hook_delivery_status
|
after_save :update_web_hook_delivery_status
|
||||||
|
|
|
@ -5357,6 +5357,10 @@ en:
|
||||||
timestamp: "Created"
|
timestamp: "Created"
|
||||||
completion: "Completion Time"
|
completion: "Completion Time"
|
||||||
actions: "Actions"
|
actions: "Actions"
|
||||||
|
filter_status:
|
||||||
|
all: "All Events"
|
||||||
|
successful: "Delivered"
|
||||||
|
failed: "Failed"
|
||||||
home:
|
home:
|
||||||
title: "Home"
|
title: "Home"
|
||||||
account:
|
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
|
||||||
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
|
describe "#ping" do
|
||||||
context "when logged in as admin" do
|
context "when logged in as admin" do
|
||||||
before { sign_in(admin) }
|
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