Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 52e5c8d

Browse files
authored
Merge pull request #1825 from vitoravelino/webhooks-deliveries-vue
js: migrated webhook deliveries to vue components
2 parents 43b2ff8 + 3299aef commit 52e5c8d

File tree

15 files changed

+277
-97
lines changed

15 files changed

+277
-97
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<panel>
3+
<h5 slot="heading-left">
4+
<a data-placement="right"
5+
data-toggle="popover"
6+
data-container=".panel-heading"
7+
data-content="A delivery is created once a webhook has been triggered. They are not re-created but updated after retriggering."
8+
data-original-title="What's this?"
9+
tabindex="0">
10+
<i class="fa fa-info-circle"></i>
11+
</a>
12+
Deliveries
13+
</h5>
14+
15+
<div slot="body">
16+
<webhook-deliveries-table :webhook="webhook" :deliveries="deliveries" sort-by="name" prefix="wd_"></webhook-deliveries-table>
17+
</div>
18+
</panel>
19+
</template>
20+
21+
<script>
22+
import Panel from '~/shared/components/panel';
23+
import ToggleLink from '~/shared/components/toggle-link';
24+
25+
import WebhookDeliveriesTable from './table';
26+
27+
export default {
28+
props: {
29+
webhook: {
30+
type: Object,
31+
},
32+
deliveries: {
33+
type: Array,
34+
},
35+
},
36+
37+
components: {
38+
Panel,
39+
ToggleLink,
40+
WebhookDeliveriesTable,
41+
},
42+
};
43+
</script>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<template>
2+
<tr :class="scopeClass">
3+
<td>
4+
<i :class="statusIcon"></i>
5+
{{ delivery.uuid }}
6+
</td>
7+
<td>{{ updatedAt }}</td>
8+
<td v-if="webhook.updatable">
9+
<button class="btn btn-default delete-webhook-btn"
10+
@click="retrigger"
11+
v-if="webhook.enabled">
12+
<i class="fa fa-refresh" :class="{'fa-spin': ongoingRequest}"></i>
13+
</button>
14+
<button class="btn btn-default delete-webhook-btn"
15+
data-placement="left"
16+
data-toggle="popover"
17+
data-title="Please confirm"
18+
data-content="<p>This webhook is disabled. Are you sure you want to retrigger it?</p>
19+
<a class='btn btn-default'>No</a> <a class='btn btn-primary yes'>Yes</a>"
20+
data-template="<div class='popover popover-webhook-delete' role='tooltip'><div class='arrow'></div><h3 class='popover-title'></h3><div class='popover-content'></div></div>'"
21+
data-html="true"
22+
role="button"
23+
:disabled="ongoingRequest"
24+
v-else>
25+
<i class="fa fa-refresh" :class="{'fa-spin': ongoingRequest}"></i>
26+
</button>
27+
</td>
28+
</tr>
29+
</template>
30+
31+
<script>
32+
import Vue from 'vue';
33+
import moment from 'moment';
34+
35+
import { handleHttpResponseError } from '~/utils/http';
36+
37+
import WebhookDeliveriesService from '../../services/deliveries';
38+
39+
const { set } = Vue;
40+
41+
export default {
42+
props: ['delivery', 'webhook'],
43+
44+
data() {
45+
return {
46+
ongoingRequest: false,
47+
};
48+
},
49+
50+
computed: {
51+
scopeClass() {
52+
return `webhook_delivery_${this.delivery.id}`;
53+
},
54+
55+
statusIcon() {
56+
if (this.delivery.status === 200) {
57+
return 'fa fa-check fa-lg text-success';
58+
}
59+
60+
return 'fa fa-close fa-lg text-danger';
61+
},
62+
63+
updatedAt() {
64+
return moment(this.delivery.updated_at).fromNow();
65+
},
66+
},
67+
68+
methods: {
69+
retrigger() {
70+
const namespaceId = this.webhook.namespace_id;
71+
const webhookId = this.webhook.id;
72+
const id = this.delivery.id;
73+
74+
set(this, 'ongoingRequest', true);
75+
WebhookDeliveriesService.retrigger(namespaceId, webhookId, id).then((response) => {
76+
const delivery = response.data;
77+
78+
this.$bus.$emit('deliveryRetriggered', delivery);
79+
this.$alert.$show(`Delivery '${this.delivery.uuid}' was retriggered successfully`);
80+
}).catch(handleHttpResponseError)
81+
.finally(() => set(this, 'ongoingRequest', false));
82+
},
83+
},
84+
85+
mounted() {
86+
const REMOVE_BTN = '.delete-webhook-delivery-btn';
87+
const POPOVER_DELETE = '.popover-webhook-delivery-delete';
88+
89+
// TODO: refactor bootstrap popover to a component
90+
$(this.$el).on('inserted.bs.popover', REMOVE_BTN, () => {
91+
const $yes = $(POPOVER_DELETE).find('.yes');
92+
$yes.click(this.retrigger.bind(this));
93+
});
94+
},
95+
};
96+
</script>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<template>
2+
<div>
3+
<table class="table table-striped table-hover">
4+
<colgroup>
5+
<col class="col-70">
6+
<col class="col-20">
7+
<col class="col-10" v-if="webhook.updatable">
8+
</colgroup>
9+
<thead>
10+
<tr>
11+
<th>UUID</th>
12+
<th>Last attempt</th>
13+
<th v-if="webhook.updatable">Retrigger</th>
14+
</tr>
15+
</thead>
16+
<tbody>
17+
<webhook-delivery-table-row v-for="delivery in deliveries" :key="delivery.id" :delivery="delivery" :webhook="webhook"></webhook-delivery-table-row>
18+
</tbody>
19+
</table>
20+
21+
<table-pagination :total.sync="deliveries.length" :current-page="currentPage" :itens-per-page.sync="limit" @update="updateCurrentPage"></table-pagination>
22+
</div>
23+
</template>
24+
25+
<script>
26+
import TableSortableMixin from '~/shared/mixins/table-sortable';
27+
import TablePaginatedMixin from '~/shared/mixins/table-paginated';
28+
29+
import WebhookDeliveryTableRow from './table-row';
30+
31+
export default {
32+
props: ['deliveries', 'webhook'],
33+
34+
mixins: [TableSortableMixin, TablePaginatedMixin],
35+
36+
components: {
37+
WebhookDeliveryTableRow,
38+
},
39+
};
40+
</script>

app/assets/javascripts/modules/webhooks/components/headers/panel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</div>
1818

1919
<div slot="body">
20-
<webhook-headers-table :webhook="webhook" :headers="headers" sort-by="name"></webhook-headers-table>
20+
<webhook-headers-table :webhook="webhook" :headers="headers" sort-by="name" prefix="wb_"></webhook-headers-table>
2121
</div>
2222
</panel>
2323
</template>

app/assets/javascripts/modules/webhooks/pages/show.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<webhook-details-panel :webhook="webhook" :state="state"></webhook-details-panel>
44
<new-webhook-header-form :webhook="webhook" :state="state" form-state="newHeaderFormVisible"></new-webhook-header-form>
55
<webhook-headers-panel :webhook="webhook" :headers="headers" :state="state"></webhook-headers-panel>
6+
<webhook-deliveries-panel :webhook="webhook" :deliveries="deliveries"></webhook-deliveries-panel>
67
</div>
78
</template>
89

910
<script>
1011
import Vue from 'vue';
1112
1213
import NewWebhookHeaderForm from '../components/headers/form';
14+
import WebhookDeliveriesPanel from '../components/deliveries/panel';
1315
import WebhookDetailsPanel from '../components/details';
1416
import WebhookHeadersPanel from '../components/headers/panel';
1517
@@ -25,10 +27,14 @@
2527
headersRef: {
2628
type: Array,
2729
},
30+
deliveriesRef: {
31+
type: Array,
32+
},
2833
},
2934
3035
components: {
3136
NewWebhookHeaderForm,
37+
WebhookDeliveriesPanel,
3238
WebhookDetailsPanel,
3339
WebhookHeadersPanel,
3440
},
@@ -37,6 +43,7 @@
3743
return {
3844
webhook: { ...this.webhookRef },
3945
headers: [...this.headersRef],
46+
deliveries: [...this.deliveriesRef],
4047
state: WebhooksStore.state,
4148
};
4249
},
@@ -68,12 +75,26 @@
6875
6976
set(this, 'headers', headers);
7077
},
78+
79+
onDeliveryRetrigger(delivery) {
80+
const currentDeliveries = this.deliveries;
81+
const index = currentDeliveries.findIndex(d => d.id === delivery.id);
82+
83+
const deliveries = [
84+
...currentDeliveries.slice(0, index),
85+
delivery,
86+
...currentDeliveries.slice(index + 1),
87+
];
88+
89+
set(this, 'deliveries', deliveries);
90+
},
7191
},
7292
7393
created() {
7494
this.$bus.$on('webhookUpdated', webhook => this.onUpdate(webhook));
7595
this.$bus.$on('webhookHeaderCreated', header => this.onHeaderCreate(header));
7696
this.$bus.$on('webhookHeaderDestroyed', header => this.onHeaderDestroy(header));
97+
this.$bus.$on('webhookDeliveryRetriggered', delivery => this.onDeliveryRetrigger(delivery));
7798
},
7899
};
79100
</script>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Vue from 'vue';
2+
import VueResource from 'vue-resource';
3+
4+
Vue.use(VueResource);
5+
6+
const oldResource = Vue.resource('namespaces/{namespaceId}/webhooks/{webhookId}/deliveries{/id}.json');
7+
8+
function retrigger(namespaceId, webhookId, id) {
9+
return oldResource.update({ namespaceId, webhookId, id }, {});
10+
}
11+
12+
export default {
13+
retrigger,
14+
};

app/controllers/webhook_deliveries_controller.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ def update
1515
authorize webhook_delivery
1616

1717
webhook_delivery.retrigger
18-
render template: "webhooks/retrigger", locals: { webhook_delivery: webhook_delivery }
18+
19+
respond_to do |format|
20+
@webhook_delivery_serialized = API::Entities::WebhookDeliveries.represent(
21+
webhook_delivery,
22+
current_user: current_user,
23+
type: :internal
24+
).to_json
25+
26+
format.json { render json: @webhook_delivery_serialized }
27+
end
1928
end
2029
end

app/controllers/webhooks_controller.rb

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Also, it manages their state, i.e. enabled/disabled.
55
class WebhooksController < ApplicationController
66
before_action :set_namespace
7-
before_action :set_webhook, only: %i[update show destroy toggle_enabled]
7+
before_action :set_webhook, except: %i[index create]
88

99
after_action :verify_authorized, except: [:index]
1010
after_action :verify_policy_scoped, only: :index
@@ -13,7 +13,13 @@ class WebhooksController < ApplicationController
1313
# GET /namespaces/1/webhooks.json
1414
def index
1515
authorize @namespace
16-
@webhooks = policy_scope(Webhook).where(namespace: @namespace).page(params[:page])
16+
17+
@namespace_serialized = API::Entities::Namespaces.represent(
18+
@namespace,
19+
current_user: current_user,
20+
type: :internal
21+
).to_json
22+
@webhooks = policy_scope(Webhook).where(namespace: @namespace)
1723
@webhooks_serialized = API::Entities::Webhooks.represent(
1824
@webhooks,
1925
current_user: current_user,
@@ -70,7 +76,16 @@ def update
7076
def show
7177
authorize @webhook
7278

73-
@deliveries = @webhook.deliveries.page(params[:page])
79+
@webhook_serialized = API::Entities::Webhooks.represent(
80+
@webhook,
81+
current_user: current_user,
82+
type: :internal
83+
).to_json
84+
@webhook_headers = @webhook.headers
85+
@webhook_headers_serialized = API::Entities::WebhookHeaders.represent(@webhook_headers).to_json
86+
@deliveries = @webhook.deliveries
87+
@deliveries_serialized = API::Entities::WebhookDeliveries.represent(@deliveries).to_json
88+
7489
respond_with(@namespace, @webhook)
7590
end
7691

@@ -107,22 +122,10 @@ def toggle_enabled
107122

108123
def set_namespace
109124
@namespace = Namespace.find(params[:namespace_id])
110-
@namespace_serialized = API::Entities::Namespaces.represent(
111-
@namespace,
112-
current_user: current_user,
113-
type: :internal
114-
).to_json
115125
end
116126

117127
def set_webhook
118128
@webhook = @namespace.webhooks.find(params[:id])
119-
@webhook_serialized = API::Entities::Webhooks.represent(
120-
@webhook,
121-
current_user: current_user,
122-
type: :internal
123-
).to_json
124-
@webhook_headers = @webhook.headers
125-
@webhook_headers_serialized = API::Entities::WebhookHeaders.represent(@webhook_headers).to_json
126129
end
127130

128131
def webhook_params

app/models/webhook.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def process_headers
111111

112112
# process_auth returns a basic auth string if username and password are provided.
113113
def process_auth
114-
return if username.empty? || password.empty?
114+
return if username.blank? || password.blank?
115115
"#{username}:#{password}"
116116
end
117117

app/views/webhooks/retrigger.html.erb

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)