diff --git a/app/assets/javascripts/admin/addon/adapters/web-hook-event.js b/app/assets/javascripts/admin/addon/adapters/web-hook-event.js
index 8a6fa2b082d..a9b8c5093ef 100644
--- a/app/assets/javascripts/admin/addon/adapters/web-hook-event.js
+++ b/app/assets/javascripts/admin/addon/adapters/web-hook-event.js
@@ -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;
+ }
}
diff --git a/app/assets/javascripts/admin/addon/components/webhook-events.hbs b/app/assets/javascripts/admin/addon/components/webhook-events.hbs
index c163126cd8a..7bfdeb1111d 100644
--- a/app/assets/javascripts/admin/addon/components/webhook-events.hbs
+++ b/app/assets/javascripts/admin/addon/components/webhook-events.hbs
@@ -1,15 +1,26 @@
-
+
+
+
+
+
{{#if this.events}}
diff --git a/app/assets/javascripts/admin/addon/components/webhook-events.js b/app/assets/javascripts/admin/addon/components/webhook-events.js
index 8d9e1f4d45f..fede8d838a7 100644
--- a/app/assets/javascripts/admin/addon/components/webhook-events.js
+++ b/app/assets/javascripts/admin/addon/components/webhook-events.js
@@ -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
diff --git a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js
index 6df416ab127..41fc6d26add 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js
@@ -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() {
diff --git a/app/assets/javascripts/admin/addon/templates/web-hooks-show.hbs b/app/assets/javascripts/admin/addon/templates/web-hooks-show.hbs
index a4f35ad4521..80e5461e7c3 100644
--- a/app/assets/javascripts/admin/addon/templates/web-hooks-show.hbs
+++ b/app/assets/javascripts/admin/addon/templates/web-hooks-show.hbs
@@ -31,4 +31,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/common/admin/api.scss b/app/assets/stylesheets/common/admin/api.scss
index 29c3ded7413..607daf52c35 100644
--- a/app/assets/stylesheets/common/admin/api.scss
+++ b/app/assets/stylesheets/common/admin/api.scss
@@ -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;
}
diff --git a/app/controllers/admin/web_hooks_controller.rb b/app/controllers/admin/web_hooks_controller.rb
index eeb489d663f..d471132363f 100644
--- a/app/controllers/admin/web_hooks_controller.rb
+++ b/app/controllers/admin/web_hooks_controller.rb
@@ -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: {
diff --git a/app/models/web_hook_event.rb b/app/models/web_hook_event.rb
index d9a53041f29..ca048bece45 100644
--- a/app/models/web_hook_event.rb
+++ b/app/models/web_hook_event.rb
@@ -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
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index fbb6036110d..84841597601 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -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:
diff --git a/spec/fabricators/web_hook_event_fabricator.rb b/spec/fabricators/web_hook_event_fabricator.rb
new file mode 100644
index 00000000000..93b451f6627
--- /dev/null
+++ b/spec/fabricators/web_hook_event_fabricator.rb
@@ -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
diff --git a/spec/requests/admin/web_hooks_controller_spec.rb b/spec/requests/admin/web_hooks_controller_spec.rb
index b5e3de66975..1f0d7d978db 100644
--- a/spec/requests/admin/web_hooks_controller_spec.rb
+++ b/spec/requests/admin/web_hooks_controller_spec.rb
@@ -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) }
diff --git a/spec/system/admin_web_hook_events_spec.rb b/spec/system/admin_web_hook_events_spec.rb
new file mode 100644
index 00000000000..3c6c2897f14
--- /dev/null
+++ b/spec/system/admin_web_hook_events_spec.rb
@@ -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
diff --git a/spec/system/page_objects/pages/admin_web_hook_events.rb b/spec/system/page_objects/pages/admin_web_hook_events.rb
new file mode 100644
index 00000000000..8ccd6ba6f78
--- /dev/null
+++ b/spec/system/page_objects/pages/admin_web_hook_events.rb
@@ -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