webhooks-rails 0.1.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +20 -0
- data/app/assets/config/webhooks_manifest.js +1 -0
- data/app/assets/stylesheets/webhooks/application.css +15 -0
- data/app/controllers/webhooks/application_controller.rb +6 -0
- data/app/controllers/webhooks/attempts_controller.rb +25 -0
- data/app/controllers/webhooks/endpoints_controller.rb +65 -0
- data/app/controllers/webhooks/events_controller.rb +37 -0
- data/app/helpers/webhooks/application_helper.rb +6 -0
- data/app/jobs/webhooks/application_job.rb +6 -0
- data/app/jobs/webhooks/deliver_job.rb +11 -0
- data/app/mailers/webhooks/application_mailer.rb +8 -0
- data/app/models/webhooks/application_record.rb +7 -0
- data/app/models/webhooks/attempt.rb +71 -0
- data/app/models/webhooks/endpoint.rb +74 -0
- data/app/models/webhooks/event.rb +50 -0
- data/app/models/webhooks/event_serializer.rb +30 -0
- data/app/models/webhooks/request.rb +37 -0
- data/app/models/webhooks/response.rb +29 -0
- data/app/views/layouts/webhooks/application.html.erb +29 -0
- data/app/views/webhooks/attempts/_attempt.html.erb +32 -0
- data/app/views/webhooks/attempts/show.html.erb +33 -0
- data/app/views/webhooks/endpoints/_endpoint.html.erb +31 -0
- data/app/views/webhooks/endpoints/_form.html.erb +42 -0
- data/app/views/webhooks/endpoints/edit.html.erb +16 -0
- data/app/views/webhooks/endpoints/index.html.erb +41 -0
- data/app/views/webhooks/endpoints/new.html.erb +16 -0
- data/app/views/webhooks/endpoints/show.html.erb +102 -0
- data/app/views/webhooks/events/_event.html.erb +11 -0
- data/app/views/webhooks/events/index.html.erb +38 -0
- data/app/views/webhooks/events/new.html.erb +35 -0
- data/app/views/webhooks/events/show.html.erb +49 -0
- data/app/views/webhooks/requests/_request.html.erb +47 -0
- data/app/views/webhooks/responses/_response.html.erb +55 -0
- data/app/views/webhooks/shared/_navigation.html.erb +100 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20210518022959_create_webhooks_endpoints.rb +17 -0
- data/db/migrate/20210518043350_create_webhooks_events.rb +12 -0
- data/db/migrate/20210518050123_create_webhooks_attempts.rb +36 -0
- data/db/migrate/20210518054916_create_webhooks_requests.rb +14 -0
- data/db/migrate/20210518060614_create_webhooks_responses.rb +15 -0
- data/lib/tasks/webhooks_tasks.rake +5 -0
- data/lib/webhooks-rails.rb +3 -0
- data/lib/webhooks.rb +21 -0
- data/lib/webhooks/engine.rb +31 -0
- data/lib/webhooks/version.rb +5 -0
- metadata +183 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
<tr>
|
2
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
3
|
+
<%= event %>
|
4
|
+
</td>
|
5
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
6
|
+
<%= event.event %>
|
7
|
+
</td>
|
8
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right">
|
9
|
+
<%= link_to event.created_at, event %>
|
10
|
+
</td>
|
11
|
+
</tr>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<div class="py-6">
|
2
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 md:flex md:justify-between md:space-x-5">
|
3
|
+
<h1 class="text-2xl font-semibold text-gray-900">Events</h1>
|
4
|
+
<div class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
|
5
|
+
<%= link_to 'New Event', new_event_path, class: 'inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blue-500' %>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
9
|
+
<div class="py-4">
|
10
|
+
<div class="flex flex-col">
|
11
|
+
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
12
|
+
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
13
|
+
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
14
|
+
<table class="min-w-full divide-y divide-gray-200">
|
15
|
+
<thead class="bg-gray-50">
|
16
|
+
<tr>
|
17
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
18
|
+
Event Type
|
19
|
+
</th>
|
20
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
21
|
+
Event
|
22
|
+
</th>
|
23
|
+
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
24
|
+
Timestamp
|
25
|
+
</th>
|
26
|
+
</tr>
|
27
|
+
</thead>
|
28
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
29
|
+
<%= render @events %>
|
30
|
+
</tbody>
|
31
|
+
</table>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div class="py-6">
|
2
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
3
|
+
<h1 class="text-2xl font-semibold text-gray-900">New Event</h1>
|
4
|
+
</div>
|
5
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
6
|
+
<div class="py-4">
|
7
|
+
<div class="flex flex-col">
|
8
|
+
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
9
|
+
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
10
|
+
<%= form_with model: @event do |form| %>
|
11
|
+
<div class="space-y-6">
|
12
|
+
<div>
|
13
|
+
<%= form.label :event_type, class: 'block text-sm font-medium text-gray-700' %>
|
14
|
+
<%= form.collection_select :event_type, Rails.application.config.webhooks.events.types, :to_s, :to_s, prompt: true, class: "select" %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div>
|
18
|
+
<%= form.label :event, class: 'block text-sm font-medium text-gray-700' %>
|
19
|
+
<div class="mt-1">
|
20
|
+
<%= form.text_area :event, value: '{"action":"user.created"}', rows: 25, class: 'block w-full shadow-sm focus:ring-light-blue-500 focus:border-light-blue-500 sm:text-sm border-gray-300 rounded-md py-2 px-4 font-mono', placeholder: 'https://example.com/webhook' %>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="flex justify-end">
|
25
|
+
<%= link_to 'Cancel', events_path, class: 'bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500' %>
|
26
|
+
<%= form.submit 'Create Event', class: 'cursor-pointer ml-3 inline-flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500' %>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<div class="py-6">
|
2
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 md:flex md:justify-between md:space-x-5">
|
3
|
+
<h1 class="text-2xl font-semibold text-gray-900"><%= @event %></h1>
|
4
|
+
</div>
|
5
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
6
|
+
<div class="py-4">
|
7
|
+
<div class="flex flex-col">
|
8
|
+
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
9
|
+
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8 space-y-6">
|
10
|
+
<pre><%= @event.event %></pre>
|
11
|
+
|
12
|
+
<div>
|
13
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
14
|
+
Attempts
|
15
|
+
</h3>
|
16
|
+
<p class="mt-2 max-w-4xl text-sm text-gray-500">
|
17
|
+
All webhook delivery attempts and their status.
|
18
|
+
</p>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
22
|
+
<table class="min-w-full divide-y divide-gray-200">
|
23
|
+
<thead class="bg-gray-50">
|
24
|
+
<tr>
|
25
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
26
|
+
Status
|
27
|
+
</th>
|
28
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
29
|
+
Event Type
|
30
|
+
</th>
|
31
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
32
|
+
Endpoint
|
33
|
+
</th>
|
34
|
+
<th scope="col" class="relative px-6 py-3">
|
35
|
+
<span class="sr-only">Edit</span>
|
36
|
+
</th>
|
37
|
+
</tr>
|
38
|
+
</thead>
|
39
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
40
|
+
<%= render @event.attempts.order(created_at: :desc) %>
|
41
|
+
</tbody>
|
42
|
+
</table>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
</div>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<div class="space-y-6">
|
2
|
+
<div>
|
3
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
4
|
+
Request
|
5
|
+
</h3>
|
6
|
+
<p class="mt-2 max-w-4xl text-sm text-gray-500">
|
7
|
+
The request delivered to <%= @attempt.endpoint %>
|
8
|
+
</p>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
12
|
+
<table class="min-w-full divide-y divide-gray-200">
|
13
|
+
<thead class="bg-gray-50">
|
14
|
+
<tr>
|
15
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
16
|
+
Header
|
17
|
+
</th>
|
18
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
19
|
+
Value
|
20
|
+
</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
24
|
+
<% request.headers.each do |key, value| %>
|
25
|
+
<tr>
|
26
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
27
|
+
<%= key %>
|
28
|
+
</td>
|
29
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
30
|
+
<%= value %>
|
31
|
+
</td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</tbody>
|
35
|
+
</table>
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<div>
|
39
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
40
|
+
Request Body
|
41
|
+
</h3>
|
42
|
+
<p class="mt-2 max-w-4xl text-sm text-gray-500">
|
43
|
+
The request delivered to <%= @attempt.endpoint %>
|
44
|
+
</p>
|
45
|
+
</div>
|
46
|
+
<div class="bg-white shadow overflow-hidden border-b border-gray-200 sm:rounded-lg px-6 py-3"><pre><%= request.body %></pre></div>
|
47
|
+
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<div class="space-y-6">
|
2
|
+
<div>
|
3
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
4
|
+
Response
|
5
|
+
</h3>
|
6
|
+
<p class="mt-2 max-w-4xl text-sm text-gray-500">
|
7
|
+
The response headers received from <%= response.endpoint %>
|
8
|
+
</p>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
12
|
+
<table class="min-w-full divide-y divide-gray-200">
|
13
|
+
<thead class="bg-gray-50">
|
14
|
+
<tr>
|
15
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
16
|
+
Header
|
17
|
+
</th>
|
18
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
19
|
+
Value
|
20
|
+
</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
24
|
+
<tr>
|
25
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
26
|
+
Status
|
27
|
+
</td>
|
28
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
29
|
+
<%= response.status_code %>
|
30
|
+
</td>
|
31
|
+
</tr>
|
32
|
+
<% response.headers.each do |key, value| %>
|
33
|
+
<tr>
|
34
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
35
|
+
<%= key %>
|
36
|
+
</td>
|
37
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
38
|
+
<%= value %>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<% end %>
|
42
|
+
</tbody>
|
43
|
+
</table>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div>
|
47
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
48
|
+
Response Body
|
49
|
+
</h3>
|
50
|
+
<p class="mt-2 max-w-4xl text-sm text-gray-500">
|
51
|
+
The response body received from <%= response.endpoint %>
|
52
|
+
</p>
|
53
|
+
</div>
|
54
|
+
<div class="bg-white shadow overflow-hidden border-b border-gray-200 sm:rounded-lg px-6 py-3"><pre><%= response.body %></pre></div>
|
55
|
+
</div>
|
@@ -0,0 +1,100 @@
|
|
1
|
+
<!-- Off-canvas menu for mobile, show/hide based on off-canvas menu state. -->
|
2
|
+
<div class="fixed inset-0 flex z-40 md:hidden" role="dialog" aria-modal="true">
|
3
|
+
<!--
|
4
|
+
Off-canvas menu overlay, show/hide based on off-canvas menu state.
|
5
|
+
|
6
|
+
Entering: "transition-opacity ease-linear duration-300"
|
7
|
+
From: "opacity-0"
|
8
|
+
To: "opacity-100"
|
9
|
+
Leaving: "transition-opacity ease-linear duration-300"
|
10
|
+
From: "opacity-100"
|
11
|
+
To: "opacity-0"
|
12
|
+
-->
|
13
|
+
<div class="fixed inset-0 bg-gray-600 bg-opacity-75" aria-hidden="true"></div>
|
14
|
+
|
15
|
+
<!--
|
16
|
+
Off-canvas menu, show/hide based on off-canvas menu state.
|
17
|
+
|
18
|
+
Entering: "transition ease-in-out duration-300 transform"
|
19
|
+
From: "-translate-x-full"
|
20
|
+
To: "translate-x-0"
|
21
|
+
Leaving: "transition ease-in-out duration-300 transform"
|
22
|
+
From: "translate-x-0"
|
23
|
+
To: "-translate-x-full"
|
24
|
+
-->
|
25
|
+
<div class="relative flex-1 flex flex-col max-w-xs w-full bg-white">
|
26
|
+
<!--
|
27
|
+
Close button, show/hide based on off-canvas menu state.
|
28
|
+
|
29
|
+
Entering: "ease-in-out duration-300"
|
30
|
+
From: "opacity-0"
|
31
|
+
To: "opacity-100"
|
32
|
+
Leaving: "ease-in-out duration-300"
|
33
|
+
From: "opacity-100"
|
34
|
+
To: "opacity-0"
|
35
|
+
-->
|
36
|
+
<div class="absolute top-0 right-0 -mr-12 pt-2">
|
37
|
+
<button class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
|
38
|
+
<span class="sr-only">Close sidebar</span>
|
39
|
+
<!-- Heroicon name: outline/x -->
|
40
|
+
<svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
41
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
42
|
+
</svg>
|
43
|
+
</button>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="flex-1 h-0 pt-5 pb-4 overflow-y-auto">
|
47
|
+
<div class="flex-shrink-0 flex items-center px-4">
|
48
|
+
Webhooks
|
49
|
+
</div>
|
50
|
+
<nav class="mt-5 px-2 space-y-1">
|
51
|
+
<%= link_to endpoints_path, class: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-base font-medium rounded-md' do %>
|
52
|
+
<svg class="text-gray-500 mr-4 flex-shrink-0 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
53
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
54
|
+
</svg>
|
55
|
+
Endpoints
|
56
|
+
<% end %>
|
57
|
+
|
58
|
+
<%= link_to events_path, class: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-base font-medium rounded-md' do %>
|
59
|
+
<svg class="text-gray-500 mr-4 flex-shrink-0 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
60
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
61
|
+
</svg>
|
62
|
+
Events
|
63
|
+
<% end %>
|
64
|
+
</nav>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
|
68
|
+
<div class="flex-shrink-0 w-14">
|
69
|
+
<!-- Force sidebar to shrink to fit close icon -->
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<!-- Static sidebar for desktop -->
|
74
|
+
<div class="hidden md:flex md:flex-shrink-0">
|
75
|
+
<div class="flex flex-col w-64">
|
76
|
+
<!-- Sidebar component, swap this element with another sidebar if you like -->
|
77
|
+
<div class="flex flex-col h-0 flex-1 border-r border-gray-200 bg-white">
|
78
|
+
<div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
79
|
+
<div class="flex items-center flex-shrink-0 px-4">
|
80
|
+
Webhooks
|
81
|
+
</div>
|
82
|
+
<nav class="mt-5 flex-1 px-2 bg-white space-y-1">
|
83
|
+
<%= link_to endpoints_path, class: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-sm font-medium rounded-md' do %>
|
84
|
+
<svg class="text-gray-500 mr-3 flex-shrink-0 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
85
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
86
|
+
</svg>
|
87
|
+
Endpoints
|
88
|
+
<% end %>
|
89
|
+
|
90
|
+
<%= link_to events_path, class: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-2 py-2 text-sm font-medium rounded-md' do %>
|
91
|
+
<svg class="text-gray-500 mr-3 flex-shrink-0 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
92
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
93
|
+
</svg>
|
94
|
+
Events
|
95
|
+
<% end %>
|
96
|
+
</nav>
|
97
|
+
</div>
|
98
|
+
</div>
|
99
|
+
</div>
|
100
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Webhooks::Engine.routes.draw do
|
4
|
+
resources :endpoints
|
5
|
+
resources :events, only: %i[index new show create]
|
6
|
+
resources :attempts, only: :show do
|
7
|
+
member do
|
8
|
+
post :reattempt
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
get '/' => redirect('/endpoints')
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateWebhooksEndpoints < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :webhooks_endpoints do |t|
|
6
|
+
t.string :url, null: false, limit: 256
|
7
|
+
t.string :event_types, array: true, null: true
|
8
|
+
t.integer :state, null: false, default: 0
|
9
|
+
t.string :secret, null: false
|
10
|
+
|
11
|
+
t.timestamps null: false, precision: 6
|
12
|
+
|
13
|
+
t.index :url, unique: true
|
14
|
+
t.index :secret, unique: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateWebhooksEvents < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :webhooks_events do |t|
|
6
|
+
t.string :event_type, null: false
|
7
|
+
t.json :event, null: false, default: {}
|
8
|
+
|
9
|
+
t.timestamps null: false, precision: 6
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateWebhooksAttempts < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
reversible do |direction|
|
6
|
+
direction.up do
|
7
|
+
execute <<-SQL.squish
|
8
|
+
DROP INDEX IF EXISTS index_webhooks_attempts_on_endpoint_id_and_event_id_and_attempt;
|
9
|
+
SQL
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :webhooks_attempts do |t|
|
14
|
+
t.belongs_to :webhooks_endpoint, null: false, foreign_key: true
|
15
|
+
t.belongs_to :webhooks_event, null: false, foreign_key: true
|
16
|
+
|
17
|
+
# Was this attempt triggered manually?
|
18
|
+
t.boolean :manual, null: false, default: false
|
19
|
+
t.integer :state, null: false, default: 0
|
20
|
+
t.integer :attempt, null: false, default: 1
|
21
|
+
|
22
|
+
t.datetime :retry_at, null: true, precision: 6
|
23
|
+
t.timestamps null: false, precision: 6
|
24
|
+
|
25
|
+
t.index %i[webhooks_endpoint_id webhooks_event_id attempt], unique: true, name: 'index_webhooks_attempts_on_endpoint_id_and_event_id_and_attempt'
|
26
|
+
end
|
27
|
+
|
28
|
+
reversible do |direction|
|
29
|
+
direction.up do
|
30
|
+
execute <<-SQL.squish
|
31
|
+
CREATE UNIQUE INDEX index_webhooks_attempts_on_endpoint_id_and_event_id_and_attempt ON webhooks_attempts ("webhooks_endpoint_id", "webhooks_event_id", "attempt") WHERE "manual" = 'f';
|
32
|
+
SQL
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateWebhooksRequests < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :webhooks_requests do |t|
|
6
|
+
t.belongs_to :webhooks_attempt, null: false, foreign_key: true, index: { unique: true }
|
7
|
+
|
8
|
+
t.json :headers, null: false
|
9
|
+
t.json :body, null: false
|
10
|
+
|
11
|
+
t.datetime :created_at, null: false, precision: 6
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|