diff --git a/app/controllers/webhook_events_controller.rb b/app/controllers/webhook_events_controller.rb
index d8844ce1..fbb81b9a 100644
--- a/app/controllers/webhook_events_controller.rb
+++ b/app/controllers/webhook_events_controller.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
class WebhookEventsController < ApplicationController
- load_and_authorize_resource :webhook_url, parent: false, only: %i[show resend], id_param: :webhook_id
+ load_and_authorize_resource :webhook_url, parent: false, id_param: :webhook_id
+ before_action :load_webhook_event
def show
- @webhook_event = @webhook_url.webhook_events.find_by!(uuid: params[:id])
- @webhook_attempts = @webhook_event.webhook_attempts.order(created_at: :desc)
-
return unless current_ability.can?(:read, @webhook_event.record)
@data =
@@ -23,10 +21,10 @@ class WebhookEventsController < ApplicationController
end
def resend
- @webhook_event = @webhook_url.webhook_events.find_by!(uuid: params[:id])
-
id_key = WebhookUrls::EVENT_TYPE_ID_KEYS.fetch(@webhook_event.event_type.split('.').first)
+ last_attempt_id = @webhook_event.webhook_attempts.maximum(:id)
+
WebhookUrls::EVENT_TYPE_TO_JOB_CLASS[@webhook_event.event_type].perform_async(
id_key => @webhook_event.record_id,
'webhook_url_id' => @webhook_event.webhook_url_id,
@@ -35,6 +33,34 @@ class WebhookEventsController < ApplicationController
'last_status' => 0
)
- head :ok
+ render turbo_stream: [
+ turbo_stream.after(
+ params[:button_id],
+ helpers.tag.submit_form(
+ helpers.button_to('', refresh_settings_webhook_event_path(@webhook_url.id, @webhook_event.uuid),
+ params: { last_attempt_id: }),
+ class: 'hidden', data: { interval: 3_000 }
+ )
+ )
+ ]
+ end
+
+ def refresh
+ return head :ok if @webhook_event.webhook_attempts.maximum(:id) == params[:last_attempt_id].to_i
+
+ render turbo_stream: [
+ turbo_stream.replace(helpers.dom_id(@webhook_event),
+ partial: 'event_row',
+ locals: { with_status: true, webhook_url: @webhook_url, webhook_event: @webhook_event }),
+ turbo_stream.replace("drawer_events_#{helpers.dom_id(@webhook_event)}",
+ partial: 'drawer_events',
+ locals: { webhook_url: @webhook_url, webhook_event: @webhook_event })
+ ]
+ end
+
+ private
+
+ def load_webhook_event
+ @webhook_event = @webhook_url.webhook_events.find_by!(uuid: params[:id])
end
end
diff --git a/app/controllers/webhook_settings_controller.rb b/app/controllers/webhook_settings_controller.rb
index 852b5314..482116ef 100644
--- a/app/controllers/webhook_settings_controller.rb
+++ b/app/controllers/webhook_settings_controller.rb
@@ -53,6 +53,8 @@ class WebhookSettingsController < ApplicationController
def resend
submitter = current_account.submitters.where.not(completed_at: nil).order(:id).last
+ authorize!(:read, submitter)
+
if submitter.blank? || @webhook_url.blank?
return redirect_back(fallback_location: settings_webhooks_path,
alert: I18n.t('unable_to_resend_webhook_request'))
diff --git a/app/javascript/elements/submit_form.js b/app/javascript/elements/submit_form.js
index 4b5969e5..7a27ed5f 100644
--- a/app/javascript/elements/submit_form.js
+++ b/app/javascript/elements/submit_form.js
@@ -1,5 +1,17 @@
export default class extends HTMLElement {
connectedCallback () {
- this.querySelector('form').requestSubmit()
+ if (this.dataset.interval) {
+ this.interval = setInterval(() => {
+ this.querySelector('form').requestSubmit()
+ }, parseInt(this.dataset.interval))
+ } else {
+ this.querySelector('form').requestSubmit()
+ }
+ }
+
+ disconnectedCallback () {
+ if (this.interval) {
+ clearInterval(this.interval)
+ }
}
}
diff --git a/app/models/webhook_attempt.rb b/app/models/webhook_attempt.rb
index 845c2dfa..a3067546 100644
--- a/app/models/webhook_attempt.rb
+++ b/app/models/webhook_attempt.rb
@@ -20,6 +20,6 @@ class WebhookAttempt < ApplicationRecord
belongs_to :webhook_event
def success?
- response_status_code.to_i / 100 == 2
+ [2, 3].include?(response_status_code.to_i / 100)
end
end
diff --git a/app/views/webhook_events/_drawer_events.html.erb b/app/views/webhook_events/_drawer_events.html.erb
new file mode 100644
index 00000000..c75f54c8
--- /dev/null
+++ b/app/views/webhook_events/_drawer_events.html.erb
@@ -0,0 +1,57 @@
+
+
+ <% webhook_attempts = webhook_event.webhook_attempts.sort_by { |e| -e.id } %>
+ <% if webhook_event.status == 'error' %>
+ <% last_attempt = webhook_attempts.select { |e| e.attempt < SendWebhookRequest::MANUAL_ATTEMPT }.max_by(&:attempt) %>
+ <% if webhook_event.webhook_attempts.none?(&:success?) && last_attempt.attempt <= 10 %>
+ -
+
+ <%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %>
+
+
+ <%= t('next_attempt_in_time_in_words', time_in_words: distance_of_time_in_words(Time.current, last_attempt.created_at + (2**last_attempt.attempt).minutes)) %>
+
+
+ <% end %>
+ <% end %>
+ <% if webhook_attempts.present? %>
+ <% webhook_attempts.each do |webhook_attempt| %>
+ -
+
+ <%= svg_icon(webhook_attempt.success? ? 'check' : 'x', class: 'w-4 h-4 shrink-0') %>
+
+
+ <%= l(webhook_attempt.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
+
+
+
+ <%= Rack::Utils::HTTP_STATUS_CODES[webhook_attempt.response_status_code] %>
+ <% if webhook_attempt.response_status_code.positive? %>
+ (<%= webhook_attempt.response_status_code %>)
+ <% end %>
+
+ <% unless webhook_attempt.success? %>
+
+ <%= webhook_attempt.response_body.presence || Rack::Utils::HTTP_STATUS_CODES[webhook_attempt.response_status_code] %>
+
+ <% end %>
+
+
+ <% end %>
+ <% else %>
+ -
+
+ <%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %>
+
+
+ <%= l(webhook_event.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
+
+
+ <% end %>
+
+ <% unless webhook_event.status == 'pending' %>
+
+ <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-sm text-white', method: :post, onclick: '[this.form.requestSubmit(), this.disabled = true]' %>
+
+ <% end %>
+
diff --git a/app/views/webhook_events/_event_row.html.erb b/app/views/webhook_events/_event_row.html.erb
new file mode 100644
index 00000000..a6af3020
--- /dev/null
+++ b/app/views/webhook_events/_event_row.html.erb
@@ -0,0 +1,28 @@
+
+
+
+
+ <% if webhook_event.status == 'success' %>
+
+ <%= svg_icon('check', class: 'w-4 h-4 shrink-0 stroke-2') %>
+ <%= webhook_event.webhook_attempts.max_by(&:id)&.response_status_code if local_assigns[:with_status] %>
+
+ <% elsif webhook_event.status == 'pending' %>
+
+ <%= svg_icon('clock', class: 'w-4 h-4 shrink-0 stroke-2') %>
+ <%= webhook_event.webhook_attempts.max_by(&:id)&.response_status_code if local_assigns[:with_status] %>
+
+ <% elsif webhook_event.status == 'error' %>
+
+ <%= svg_icon('x', class: 'w-4 h-4 shrink-0') %>
+ <%= webhook_event.webhook_attempts.max_by(&:id)&.response_status_code if local_assigns[:with_status] %>
+
+ <% end %>
+
<%= webhook_event.event_type %>
+
+
+ <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-xs h-2 text-white relative z-[1] hidden md:group-hover:inline-block', data: { turbo_frame: :drawer }, method: :post, onclick: "[this.form.requestSubmit(), this.disabled = true, this.classList.remove('hidden')]" %>
+ <%= l(webhook_event.created_at, locale: current_account.locale, format: :short) %>
+
+
+
diff --git a/app/views/webhook_events/show.html.erb b/app/views/webhook_events/show.html.erb
index d59c9745..12df5de0 100644
--- a/app/views/webhook_events/show.html.erb
+++ b/app/views/webhook_events/show.html.erb
@@ -1,57 +1,6 @@
<%= render 'shared/turbo_drawer', title: @webhook_event.event_type, close_after_submit: false do %>
-
- <% if @webhook_event.status == 'error' %>
- <% last_attempt = @webhook_attempts.select { |e| SendWebhookRequest::AUTOMATED_RETRY_RANGE.cover?(e.attempt) }.max_by(&:attempt) %>
- <% if SendWebhookRequest::AUTOMATED_RETRY_RANGE.cover?(last_attempt&.attempt) %>
- -
-
- <%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %>
-
-
- <%= t('next_attempt_in_time_in_words', time_in_words: distance_of_time_in_words(Time.current, last_attempt.created_at + (2**last_attempt.attempt).minutes)) %>
-
-
- <% end %>
- <% end %>
- <% if @webhook_attempts.present? %>
- <% @webhook_attempts.each do |webhook_attempt| %>
- -
-
- <%= svg_icon(webhook_attempt.success? ? 'check' : 'x', class: 'w-4 h-4 shrink-0') %>
-
-
- <%= l(webhook_attempt.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
-
-
-
- <%= Rack::Utils::HTTP_STATUS_CODES[webhook_attempt.response_status_code] %>
- <% if webhook_attempt.response_status_code.positive? %>
- (<%= webhook_attempt.response_status_code %>)
- <% end %>
-
- <% unless webhook_attempt.success? %>
-
- <%= webhook_attempt.response_body.presence || Rack::Utils::HTTP_STATUS_CODES[webhook_attempt.response_status_code] %>
-
- <% end %>
-
-
- <% end %>
- <% else %>
- -
-
- <%= svg_icon('clock', class: 'w-4 h-4 shrink-0') %>
-
-
- <%= l(@webhook_event.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %>
-
-
- <% end %>
-
- <% unless @webhook_event.status == 'pending' %>
- <%= button_to button_title(title: t('resend'), disabled_with: 'sending', icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(@webhook_url.id, @webhook_event.uuid), class: 'absolute right-4 top-3 btn btn-neutral btn-sm text-white', method: :post %>
- <% end %>
+ <%= render 'drawer_events', webhook_url: @webhook_url, webhook_event: @webhook_event %>
<% if @data %>
<% response = JSON.pretty_generate({ event_type: @webhook_event.event_type, timestamp: @webhook_event.created_at.as_json, data: @data }) %>
diff --git a/app/views/webhook_settings/show.html.erb b/app/views/webhook_settings/show.html.erb
index 0e9fbf3f..89c27570 100644
--- a/app/views/webhook_settings/show.html.erb
+++ b/app/views/webhook_settings/show.html.erb
@@ -89,33 +89,7 @@
<% if @webhook_events.present? %>
- <% @webhook_events.each do |event| %>
-
-
-
-
- <% if event.status == 'success' %>
-
- <%= svg_icon('check', class: 'w-4 h-4 shrink-0 stroke-2') %>
-
- <% elsif event.status == 'pending' %>
-
- <%= svg_icon('clock', class: 'w-4 h-4 shrink-0 stroke-2') %>
-
- <% elsif event.status == 'error' %>
-
- <%= svg_icon('x', class: 'w-4 h-4 shrink-0') %>
-
- <% end %>
-
<%= event.event_type %>
-
-
- <%= button_to button_title(title: t('resend'), disabled_with: t('sending'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(@webhook_url.id, event.uuid), class: 'btn btn-neutral btn-xs h-2 text-white relative z-[1] hidden md:group-hover:inline-block', data: { turbo_frame: :drawer }, method: :post %>
- <%= l(event.created_at, locale: current_account.locale, format: :short) %>
-
-
-
- <% end %>
+ <%= render partial: 'webhook_events/event_row', collection: @webhook_events, as: :webhook_event, locals: { webhook_url: @webhook_url } %>
<% else %>
@@ -147,7 +121,7 @@
<% end %>
- <% elsif submitter = current_account.submitters.where.not(completed_at: nil).order(:id).last %>
+ <% elsif (submitter = current_account.submitters.where.not(completed_at: nil).order(:id).last) && can?(:read, submitter) %>
diff --git a/config/routes.rb b/config/routes.rb
index ad4ebb8a..dd962782 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -182,6 +182,7 @@ Rails.application.routes.draw do
resources :events, only: %i[show], controller: 'webhook_events' do
post :resend, on: :member
+ post :refresh, on: :member
end
end
resource :account, only: %i[show update destroy]
diff --git a/lib/send_webhook_request.rb b/lib/send_webhook_request.rb
index c39b685e..e9dd9fcb 100644
--- a/lib/send_webhook_request.rb
+++ b/lib/send_webhook_request.rb
@@ -76,7 +76,7 @@ module SendWebhookRequest
attempt:
)
- webhook_event.update!(status: response.success? ? 'success' : 'error')
+ webhook_event.update!(status: response.status.to_i >= 400 ? 'error' : 'success')
response
rescue StandardError