1
|
|
|
""" |
2
|
|
|
byceps.blueprints.admin.webhook.views |
3
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4
|
|
|
|
5
|
|
|
:Copyright: 2006-2021 Jochen Kupperschmidt |
6
|
|
|
:License: Revised BSD (see `LICENSE` file for details) |
7
|
|
|
""" |
8
|
|
|
|
9
|
1 |
|
import json |
10
|
|
|
|
11
|
1 |
|
from flask import abort, request |
12
|
1 |
|
from flask_babel import gettext |
13
|
|
|
|
14
|
1 |
|
from ....announce.events import EVENT_TYPES_TO_NAMES |
15
|
1 |
|
from ....announce.helpers import call_webhook |
16
|
1 |
|
from ....services.webhooks import service as webhook_service |
17
|
1 |
|
from ....services.webhooks.transfer.models import OutgoingWebhook, WebhookID |
18
|
1 |
|
from ....util.authorization import register_permission_enum |
19
|
1 |
|
from ....util.framework.blueprint import create_blueprint |
20
|
1 |
|
from ....util.framework.flash import flash_error, flash_success |
21
|
1 |
|
from ....util.framework.templating import templated |
22
|
1 |
|
from ....util.views import permission_required, redirect_to, respond_no_content |
23
|
|
|
|
24
|
1 |
|
from .authorization import WebhookPermission |
25
|
1 |
|
from .forms import ( |
26
|
|
|
assemble_create_form, |
27
|
|
|
assemble_update_form, |
28
|
|
|
_create_event_field_name, |
29
|
|
|
) |
30
|
|
|
|
31
|
|
|
|
32
|
1 |
|
blueprint = create_blueprint('webhook_admin', __name__) |
33
|
|
|
|
34
|
|
|
|
35
|
1 |
|
register_permission_enum(WebhookPermission) |
36
|
|
|
|
37
|
|
|
|
38
|
1 |
|
WEBHOOK_EVENT_NAMES = frozenset(EVENT_TYPES_TO_NAMES.values()) |
39
|
|
|
|
40
|
|
|
|
41
|
1 |
|
@blueprint.get('/') |
42
|
1 |
|
@permission_required(WebhookPermission.view) |
43
|
1 |
|
@templated |
44
|
|
|
def index(): |
45
|
|
|
"""Show webhooks.""" |
46
|
1 |
|
webhooks = webhook_service.get_all_webhooks() |
47
|
|
|
|
48
|
1 |
|
webhooks.sort(key=lambda w: (w.description is None, w.description or '')) |
49
|
|
|
|
50
|
1 |
|
return { |
51
|
|
|
'webhooks': webhooks, |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
|
55
|
1 |
|
@blueprint.get('/create') |
56
|
1 |
|
@permission_required(WebhookPermission.administrate) |
57
|
1 |
|
@templated |
58
|
1 |
|
def create_form(erroneous_form=None): |
59
|
|
|
"""Show form to create a webhook.""" |
60
|
|
|
event_names = WEBHOOK_EVENT_NAMES |
61
|
|
|
CreateForm = assemble_create_form(event_names) |
62
|
|
|
|
63
|
|
|
form = erroneous_form if erroneous_form else CreateForm() |
64
|
|
|
|
65
|
|
|
return { |
66
|
|
|
'form': form, |
67
|
|
|
'event_names': event_names, |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
|
71
|
1 |
|
@blueprint.post('/') |
72
|
1 |
|
@permission_required(WebhookPermission.administrate) |
73
|
|
|
def create(): |
74
|
|
|
"""Create a webhook.""" |
75
|
|
|
event_names = WEBHOOK_EVENT_NAMES |
76
|
|
|
CreateForm = assemble_create_form(event_names) |
77
|
|
|
|
78
|
|
|
form = CreateForm(request.form) |
79
|
|
|
|
80
|
|
|
if not form.validate(): |
81
|
|
|
return create_form(form) |
82
|
|
|
|
83
|
|
|
event_selectors = {} |
84
|
|
|
for event_name in event_names: |
85
|
|
|
if form.get_field_for_event_name(event_name).data: |
86
|
|
|
event_selectors[event_name] = None |
87
|
|
|
|
88
|
|
|
format = form.format.data.strip() |
89
|
|
|
url = form.url.data.strip() |
90
|
|
|
description = form.description.data.strip() |
91
|
|
|
enabled = False |
92
|
|
|
|
93
|
|
|
webhook = webhook_service.create_outgoing_webhook( |
94
|
|
|
event_selectors, format, url, enabled, description=description |
95
|
|
|
) |
96
|
|
|
|
97
|
|
|
flash_success(gettext('Webhook has been created.')) |
98
|
|
|
|
99
|
|
|
return redirect_to('.index') |
100
|
|
|
|
101
|
|
|
|
102
|
1 |
|
@blueprint.get('/webhooks/<uuid:webhook_id>/update') |
103
|
1 |
|
@permission_required(WebhookPermission.administrate) |
104
|
1 |
|
@templated |
105
|
1 |
|
def update_form(webhook_id, erroneous_form=None): |
106
|
|
|
"""Show form to update a webhook.""" |
107
|
|
|
webhook = _get_webhook_or_404(webhook_id) |
108
|
|
|
|
109
|
|
|
event_names = WEBHOOK_EVENT_NAMES |
110
|
|
|
UpdateForm = assemble_update_form(event_names) |
111
|
|
|
|
112
|
|
|
# Pre-fill event type checkboxes. |
113
|
|
|
event_type_fields = {} |
114
|
|
|
for event_type_name in webhook.event_selectors: |
115
|
|
|
field_name = _create_event_field_name(event_type_name) |
116
|
|
|
event_type_fields[field_name] = True |
117
|
|
|
|
118
|
|
|
form = ( |
119
|
|
|
erroneous_form |
120
|
|
|
if erroneous_form |
121
|
|
|
else UpdateForm(obj=webhook, **event_type_fields) |
122
|
|
|
) |
123
|
|
|
|
124
|
|
|
return { |
125
|
|
|
'webhook': webhook, |
126
|
|
|
'form': form, |
127
|
|
|
'event_names': event_names, |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
|
131
|
1 |
|
@blueprint.post('/webhooks/<uuid:webhook_id>') |
132
|
1 |
|
@permission_required(WebhookPermission.administrate) |
133
|
|
|
def update(webhook_id): |
134
|
|
|
"""Update the webhook.""" |
135
|
|
|
webhook = _get_webhook_or_404(webhook_id) |
136
|
|
|
|
137
|
|
|
event_names = WEBHOOK_EVENT_NAMES |
138
|
|
|
UpdateForm = assemble_update_form(event_names) |
139
|
|
|
|
140
|
|
|
form = UpdateForm(request.form) |
141
|
|
|
|
142
|
|
|
if not form.validate(): |
143
|
|
|
return update_form(webhook.id, form) |
144
|
|
|
|
145
|
|
|
event_selectors = {} |
146
|
|
|
for event_name in event_names: |
147
|
|
|
if form.get_field_for_event_name(event_name).data: |
148
|
|
|
event_selectors[event_name] = None |
149
|
|
|
|
150
|
|
|
format = form.format.data.strip() |
151
|
|
|
url = form.url.data.strip() |
152
|
|
|
text_prefix = form.text_prefix.data.lstrip() # Allow trailing whitespace. |
153
|
|
|
extra_fields_str = form.extra_fields.data.strip() |
154
|
|
|
extra_fields = json.loads(extra_fields_str) if extra_fields_str else None |
155
|
|
|
description = form.description.data.strip() |
156
|
|
|
enabled = form.enabled.data |
157
|
|
|
|
158
|
|
|
webhook = webhook_service.update_outgoing_webhook( |
159
|
|
|
webhook.id, |
160
|
|
|
event_selectors, |
161
|
|
|
format, |
162
|
|
|
text_prefix, |
163
|
|
|
extra_fields, |
164
|
|
|
url, |
165
|
|
|
description, |
166
|
|
|
enabled, |
167
|
|
|
) |
168
|
|
|
|
169
|
|
|
flash_success(gettext('Webhook has been updated.')) |
170
|
|
|
|
171
|
|
|
return redirect_to('.index') |
172
|
|
|
|
173
|
|
|
|
174
|
1 |
|
@blueprint.post('/webhooks/<uuid:webhook_id>/test') |
175
|
1 |
|
@permission_required(WebhookPermission.administrate) |
176
|
1 |
|
@respond_no_content |
177
|
|
|
def test(webhook_id): |
178
|
|
|
"""Call the webhook (synchronously).""" |
179
|
|
|
webhook = _get_webhook_or_404(webhook_id) |
180
|
|
|
|
181
|
|
|
text = 'Test, test … is this thing on?!' |
182
|
|
|
try: |
183
|
|
|
call_webhook(webhook, text) |
184
|
|
|
flash_success( |
185
|
|
|
gettext('Webhook call has been successful.'), icon='success' |
186
|
|
|
) |
187
|
|
|
except Exception as e: |
188
|
|
|
flash_error( |
189
|
|
|
gettext('Webhook call failed:') |
190
|
|
|
+ f'<br><pre style="white-space: pre-wrap;">{e}</pre>', |
191
|
|
|
icon='warning', |
192
|
|
|
text_is_safe=True, |
193
|
|
|
) |
194
|
|
|
|
195
|
|
|
|
196
|
|
|
# helpers |
197
|
|
|
|
198
|
|
|
|
199
|
1 |
|
def _get_webhook_or_404(webhook_id: WebhookID) -> OutgoingWebhook: |
200
|
|
|
webhook = webhook_service.find_webhook(webhook_id) |
201
|
|
|
|
202
|
|
|
if webhook is None: |
203
|
|
|
abort(404) |
204
|
|
|
|
205
|
|
|
return webhook |
206
|
|
|
|