webhukhs 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cae5927b7426085a8e140f6bdb4565423d43d1b72c65b65c9602b72bd81a1338
4
- data.tar.gz: e77b5321fbd8b814b3d6265da104ec9b4e77ffc36a2d0b0c62e67ae1c1f3ff05
3
+ metadata.gz: 99b94333e5ec2959d8096f909a5370fccc5b68b9388e0a7028584a5cb334d660
4
+ data.tar.gz: 85c0dfd6138967c9c9f0fca36666ed07f7a9b714a608c34b13ed725d73dff67f
5
5
  SHA512:
6
- metadata.gz: 40519e7b2ca3b8c8e4bbf924f78d86269f6f4b68b1a72f3a7f963ec87fde656f49db69c6268de76f19b22ec762e3e14078c9839f44e080b41bc1e99131ecb449
7
- data.tar.gz: 2a8e469254e9f76b46068a9b06001157f570f33a6cab931c2ee23cada3c13eadbff765c368cba58e6a62a1d657db8d142a2f89d2b8fe94b4034eb76f45cb4e6c
6
+ metadata.gz: 1ee87b2fb85e85a69493977d30cc21bbba0831d5b69973d560d4229260fdd7357a1a343c7da3634b503817827632e7f6ab8c1f5b92cd081030f11c68a8086053
7
+ data.tar.gz: e2820a7337b24bf51cc85e400889969f4744102c3ddc3948df9ae051d2baa96af6a8ae6ffad329cac90952f9d32362232ee2cd2a812dd7059fb04b9fabcb5ed1
data/AGENTS.md CHANGED
@@ -32,12 +32,6 @@ Decide which bucket it falls into:
32
32
  Decide between A) and B) before changing anything. If unsure, ask
33
33
  the user.
34
34
 
35
- ## What you may change
36
-
37
- - `lib/age.rb` -- the implementation. Multiple designs are valid.
38
- - `spec/age_spec.rb` -- the tests. Some existing tests are bad and
39
- may be rewritten or replaced.
40
-
41
35
  ## Constraints
42
36
 
43
37
  - You may not skip mutants by configuring mutant to ignore them.
data/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.8.1
9
+
10
+ ### Fixed
11
+ - Sort the admin listing by ID by default to avoid a full-table sort.
12
+
8
13
  ## 0.8.0
9
14
  ## Added
10
15
  - An optional, read-only admin engine (`require "webhukhs/admin"`, mount `Webhukhs::Admin::Engine`) to list,
data/README.md CHANGED
@@ -121,6 +121,14 @@ no build step.
121
121
  To try it out, run `bin/dev` from a checkout of this repository. It starts the app in `example`
122
122
  with some webhooks seeded, and the admin at http://localhost:3000/admin/webhukhs.
123
123
 
124
+ The list defaults to descending ID order using the existing primary-key index; no extra migration is
125
+ needed. Click **Received** to sort by timestamp instead.
126
+
127
+ **We strongly recommend against UUIDv4 primary keys.** Use UUIDv7 or auto-incrementing integer IDs
128
+ instead. UUIDv4 IDs are random, so sorting by ID does not reflect creation time, and inserts have
129
+ poorer index locality. Webhukhs generates UUIDv7 when Ruby supports it, but falls back to UUIDv4
130
+ otherwise; use a Ruby with UUIDv7 support if you need UUID primary keys.
131
+
124
132
  ## More Examples
125
133
  - `example` folder contains a demo app with the engine and the admin fully configured - run it with `bin/dev`.
126
134
  - We provide a number of webhook handlers which demonstrate certain features of Webhukhs. You will find them in `handler-examples`.
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Public namespace for Webhukhs runtime and configuration.
3
4
  module Webhukhs
5
+ # Read-only administration for received webhooks.
4
6
  module Admin
5
7
  # Lists and shows stored webhooks. Read-only: the lifecycle belongs to Webhukhs::ProcessingJob.
6
8
  class ReceivedWebhooksController < ApplicationController
7
9
  # Webhooks per index page
8
10
  PAGE_SIZE = 50
9
- # Columns the index can be sorted by
11
+ # Additional sortable columns beyond the default id order
10
12
  SORTABLE_COLUMNS = %w[created_at updated_at handler_module_name handler_event_id status].freeze
11
13
 
12
14
  helper_method :handler_options, :status_options
@@ -15,7 +17,7 @@ module Webhukhs
15
17
  #
16
18
  # @return [void]
17
19
  def index
18
- @sort = params[:sort].presence_in(SORTABLE_COLUMNS) || "created_at"
20
+ @sort = params[:sort].presence_in(SORTABLE_COLUMNS) || "id"
19
21
  @direction = (params[:direction] == "asc") ? "asc" : "desc"
20
22
  @page = [params[:page].to_i, 1].max
21
23
 
@@ -25,7 +27,7 @@ module Webhukhs
25
27
  # to return them in a different order for every OFFSET - rows then repeat or vanish between pages
26
28
  rows = ReceivedWebhook
27
29
  .where(filters)
28
- .order(@sort => @direction, :id => @direction)
30
+ .order(@sort => @direction, "id" => @direction)
29
31
  .offset((@page - 1) * PAGE_SIZE)
30
32
  .limit(PAGE_SIZE + 1)
31
33
  @next_page = @page + 1 if rows.length > PAGE_SIZE
@@ -19,6 +19,7 @@
19
19
 
20
20
  <%
21
21
  columns = [
22
+ ["Id", "id"],
22
23
  ["Received", "created_at"],
23
24
  ["Handler", "handler_module_name"],
24
25
  ["Status", "status"],
@@ -28,7 +29,7 @@
28
29
  %>
29
30
  <div id="received-webhooks">
30
31
  <%# The id cell carries the link, and the CSS stretches it over the whole row %>
31
- <%= admin_table(["Id"] + columns, @webhooks) do |webhook|
32
+ <%= admin_table(columns, @webhooks) do |webhook|
32
33
  [
33
34
  link_to(tag.code(webhook.id, class: "cell-ellipsis cell-id", title: webhook.id), received_webhook_path(webhook), class: "row-link"),
34
35
  time_tag(webhook.created_at, webhook.created_at.iso8601),
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Webhukhs
4
- VERSION = "0.8.0"
4
+ VERSION = "0.8.1"
5
5
  end
data/test/admin_test.rb CHANGED
@@ -23,21 +23,41 @@ class AdminTest < ActionDispatch::IntegrationTest
23
23
  css_select("table.admin-table tbody tr td:nth-child(5) code").map(&:text)
24
24
  end
25
25
 
26
- test "lists webhooks newest first by default" do
27
- create_webhook(handler_event_id: "old", created_at: 2.days.ago)
28
- create_webhook(handler_event_id: "new", created_at: 1.day.ago)
26
+ test "lists webhooks by descending id regardless of received time by default" do
27
+ create_webhook(handler_event_id: "first", created_at: 1.day.ago)
28
+ create_webhook(handler_event_id: "last", created_at: 2.days.ago)
29
29
 
30
30
  get "/admin/received"
31
31
 
32
32
  assert_response :ok
33
- assert_equal ["new", "old"], listed_event_ids
33
+ assert_equal ["last", "first"], listed_event_ids
34
34
  # The whole row is the link, so it has to be there in the first cell
35
35
  assert_select "tbody tr td:first-child a.row-link[href^='/admin/received/']", count: 2
36
- assert_select "th a.sort-link[href=?]", "/admin/received?direction=asc&sort=created_at", text: "Received ▼"
36
+ assert_select "th a.sort-link[href=?]", "/admin/received?direction=asc&sort=id", text: "Id ▼"
37
+ assert_select "th a.sort-link[href=?]", "/admin/received?direction=desc&sort=created_at", text: "Received"
37
38
  assert_select "th a.sort-link span.sort-arrow", text: "▼", count: 1
38
39
  assert_select "th a.sort-link[href=?]", "/admin/received?direction=desc&sort=status", text: "Status"
39
40
  end
40
41
 
42
+ test "sorts by id in ascending order when requested" do
43
+ create_webhook(handler_event_id: "first", created_at: 1.day.ago)
44
+ create_webhook(handler_event_id: "last", created_at: 2.days.ago)
45
+
46
+ get "/admin/received", params: {sort: "id", direction: "asc"}
47
+
48
+ assert_equal ["first", "last"], listed_event_ids
49
+ assert_select "th a.sort-link[href=?]", "/admin/received?direction=desc&sort=id", text: "Id ▲"
50
+ end
51
+
52
+ test "still sorts by received time when explicitly requested" do
53
+ create_webhook(handler_event_id: "new", created_at: 1.day.ago)
54
+ create_webhook(handler_event_id: "old", created_at: 2.days.ago)
55
+
56
+ get "/admin/received", params: {sort: "created_at"}
57
+
58
+ assert_equal ["new", "old"], listed_event_ids
59
+ end
60
+
41
61
  test "offers an auto-refresh switch, off by default, for the listing it refetches" do
42
62
  create_webhook
43
63
 
@@ -85,15 +105,15 @@ class AdminTest < ActionDispatch::IntegrationTest
85
105
  assert_equal ["z", "a"], listed_event_ids
86
106
  end
87
107
 
88
- test "ignores sort columns and directions it does not know" do
89
- # Created out of order, so that falling back to anything but created_at shows
90
- create_webhook(handler_event_id: "new", created_at: 1.day.ago, status: "skipped")
91
- create_webhook(handler_event_id: "old", created_at: 2.days.ago, status: "error")
108
+ test "ignores unsupported sort columns and directions" do
109
+ # Creation order differs from received time, so the fallback must use id.
110
+ create_webhook(handler_event_id: "new", created_at: 1.day.ago, status: "skipped", body: "z")
111
+ create_webhook(handler_event_id: "old", created_at: 2.days.ago, status: "error", body: "a")
92
112
 
93
- get "/admin/received", params: {sort: "no_such_column", direction: "sideways"}
113
+ get "/admin/received", params: {sort: "body", direction: "sideways"}
94
114
 
95
115
  assert_response :ok
96
- assert_equal ["new", "old"], listed_event_ids
116
+ assert_equal ["old", "new"], listed_event_ids
97
117
  end
98
118
 
99
119
  test "filters by handler and status, and treats blank filters as no filter" do
@@ -119,7 +139,7 @@ class AdminTest < ActionDispatch::IntegrationTest
119
139
  assert_select "select[name=handler] option[value='']", text: "Any handler"
120
140
  assert_select "select[name=status] option[value='']", text: "Any status"
121
141
  # Clearing the filters keeps the sort
122
- assert_select "form.filters a[href=?]", "/admin/received?direction=desc&sort=created_at", text: "Clear"
142
+ assert_select "form.filters a[href=?]", "/admin/received?direction=desc&sort=id", text: "Clear"
123
143
  assert_select "select[name=handler] option[selected]", text: "PrivateHandler"
124
144
 
125
145
  statuses = css_select("select[name=status] option").map { |option| option["value"] }
@@ -94,6 +94,10 @@ class AdminClickThrough < ActiveSupport::TestCase
94
94
  # The stylesheet must have made it through the CSP - unstyled, the header would not be a flexbox
95
95
  assert_equal "flex", @browser.evaluate("getComputedStyle(document.querySelector('.admin-header')).display")
96
96
  assert_equal PAGE_SIZE, @browser.css("tbody tr").length
97
+ assert_equal "the-failed-one", texts("tbody tr td:nth-child(5)").first
98
+
99
+ click_and_wait_for_page(@browser.at_xpath("//th/a[contains(., 'Received')]"))
100
+ assert_includes @browser.current_url, "direction=desc"
97
101
  assert_equal "evt-0", texts("tbody tr td:nth-child(5)").first
98
102
 
99
103
  click_and_wait_for_page(@browser.at_xpath("//th/a[contains(., 'Received')]"))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webhukhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Katkov
@@ -343,7 +343,6 @@ files:
343
343
  - handler-examples/revolut_business_v1_handler.rb
344
344
  - handler-examples/revolut_business_v2_handler.rb
345
345
  - handler-examples/starling_payments_handler.rb
346
- - lib/tasks/webhukhs_tasks.rake
347
346
  - lib/webhukhs.rb
348
347
  - lib/webhukhs/admin.rb
349
348
  - lib/webhukhs/admin/application_controller.rb
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :munster do
3
- # # Task goes here
4
- # end