Skip to content

Commit

Permalink
Add admin UI to edit webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
homeworkprod committed Feb 20, 2021
1 parent 21f6a2a commit f1f5305
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 3 deletions.
15 changes: 14 additions & 1 deletion byceps/blueprints/admin/webhook/forms.py
Expand Up @@ -15,16 +15,29 @@
from ....util.l10n import LocalizedForm


class CreateForm(LocalizedForm):
class _BaseForm(LocalizedForm):
description = StringField(lazy_gettext('Description'), [InputRequired()])
format = StringField(lazy_gettext('Format'), [InputRequired()])
url = StringField(lazy_gettext('URL'), [InputRequired()])


class CreateForm(_BaseForm):
pass


class UpdateForm(_BaseForm):
text_prefix = StringField(lazy_gettext('Text prefix'), [InputRequired()])
enabled = BooleanField(lazy_gettext('Enabled'))


def assemble_create_form(event_names: Set[str]) -> Type[LocalizedForm]:
return _extend_form_for_event_types(CreateForm, event_names)


def assemble_update_form(event_names: Set[str]) -> Type[LocalizedForm]:
return _extend_form_for_event_types(UpdateForm, event_names)


def _extend_form_for_event_types(
form_class,
event_names: Set[str],
Expand Down
Expand Up @@ -43,6 +43,7 @@ <h1>{{ title }} {{ render_extra_in_heading(webhooks|length) }}</h1>
</div>

<div class="button-row button-row--compact button-row--right">
<a class="button" href="{{ url_for('.update_form', webhook_id=webhook.id) }}">{{ render_icon('edit') }} <span>{{ _('Update') }}</span></a>
<a class="button" data-action="test-webhook" href="{{ url_for('.test', webhook_id=webhook.id) }}">{{ render_icon('webhook') }} <span>{{ _('Test') }}</span></a>
</div>

Expand Down
@@ -0,0 +1,40 @@
{% extends 'layout/admin/base.html' %}
{% from 'macros/admin.html' import render_backlink %}
{% from 'macros/forms.html' import form_buttons, form_field, form_field_checkbox, form_fieldset, form_supplement %}
{% from 'macros/icons.html' import render_icon %}
{% set current_page = 'webhook_admin' %}
{% set title = _('Update Webhook') %}

{% block before_body %}
{{ render_backlink(url_for('.index'), _('Webhooks')) }}
{%- endblock %}

{% block body %}

<h1>{{ render_icon('edit') }} {{ title }}</h1>

<form action="{{ url_for('.update', webhook_id=webhook.id) }}" method="post">
{% call form_fieldset() %}
{{ form_field(form.description, autofocus='autofocus') }}
{{ form_field(form.format) }}
{{ form_field(form.url) }}
{{ form_field(form.text_prefix) }}
{{ form_field(form.enabled) }}
<div class="form-row">
<label>{{ _('Selectors') }}</label>
<fieldset>
{%- for event_name in event_names|sort %}
<div class="checkbox">
{%- with field = form.get_field_for_event_name(event_name) %}
{{- field() }} {{ field.label(event_name) }}
{%- endwith %}
</div>
{%- endfor %}
<fieldset>
</div>
{% endcall %}

{{ form_buttons(_('Update')) }}
</form>

{%- endblock %}
75 changes: 74 additions & 1 deletion byceps/blueprints/admin/webhook/views.py
Expand Up @@ -20,7 +20,11 @@
from ....util.views import permission_required, redirect_to, respond_no_content

from .authorization import WebhookPermission
from .forms import assemble_create_form
from .forms import (
assemble_create_form,
assemble_update_form,
_create_event_field_name,
)


blueprint = create_blueprint('webhook_admin', __name__)
Expand Down Expand Up @@ -93,6 +97,75 @@ def create():
return redirect_to('.index')


@blueprint.route('/webhooks/<uuid:webhook_id>/update')
@permission_required(WebhookPermission.administrate)
@templated
def update_form(webhook_id, erroneous_form=None):
"""Show form to update a webhook."""
webhook = _get_webhook_or_404(webhook_id)

event_names = WEBHOOK_EVENT_NAMES
UpdateForm = assemble_update_form(event_names)

# Pre-fill event type checkboxes.
event_type_fields = {}
for event_type_name in webhook.event_selectors:
field_name = _create_event_field_name(event_type_name)
event_type_fields[field_name] = True

form = (
erroneous_form
if erroneous_form
else UpdateForm(obj=webhook, **event_type_fields)
)

return {
'webhook': webhook,
'form': form,
'event_names': event_names,
}


@blueprint.route('/webhooks/<uuid:webhook_id>', methods=['POST'])
@permission_required(WebhookPermission.administrate)
def update(webhook_id):
"""Update the webhook."""
webhook = _get_webhook_or_404(webhook_id)

event_names = WEBHOOK_EVENT_NAMES
UpdateForm = assemble_update_form(event_names)

form = UpdateForm(request.form)

if not form.validate():
return update_form(webhook.id, form)

event_selectors = {}
for event_name in event_names:
if form.get_field_for_event_name(event_name).data:
event_selectors[event_name] = None

format = form.format.data.strip()
url = form.url.data.strip()
text_prefix = form.text_prefix.data.lstrip() # Allow trailing whitespace.
description = form.description.data.strip()
enabled = form.enabled.data

webhook = webhook_service.update_outgoing_webhook(
webhook.id,
event_selectors,
format,
url,
enabled,
text_prefix=text_prefix,
description=description,
)

flash_success(gettext('Webhook has been updated.'))

return redirect_to('.index')


@blueprint.route('/webhooks/<uuid:webhook_id>/test', methods=['POST'])
@permission_required(WebhookPermission.administrate)
@respond_no_content
Expand Down
36 changes: 35 additions & 1 deletion byceps/services/webhooks/service.py
Expand Up @@ -41,6 +41,35 @@ def create_outgoing_webhook(
return _db_entity_to_outgoing_webhook(webhook)


def update_outgoing_webhook(
webhook_id: WebhookID,
event_selectors: EventSelectors,
format: str,
url: str,
enabled: bool,
*,
text_prefix: Optional[str] = None,
extra_fields: Optional[Dict[str, Any]] = None,
description: Optional[str] = None,
) -> OutgoingWebhook:
"""Update an outgoing webhook."""
webhook = _find_db_webhook(webhook_id)
if webhook is None:
raise ValueError(f'Unknown webhook ID "{webhook_id}"')

webhook.event_selectors = event_selectors
webhook.format = format
webhook.text_prefix = text_prefix
webhook.extra_fields = extra_fields
webhook.url = url
webhook.description = description
webhook.enabled = enabled

db.session.commit()

return _db_entity_to_outgoing_webhook(webhook)


def delete_outgoing_webhook(webhook_id: WebhookID) -> None:
"""Delete the outgoing webhook."""
db.session.query(DbOutgoingWebhook) \
Expand All @@ -51,14 +80,19 @@ def delete_outgoing_webhook(webhook_id: WebhookID) -> None:

def find_webhook(webhook_id: WebhookID) -> Optional[OutgoingWebhook]:
"""Return the webhook with that ID, if found."""
webhook = db.session.query(DbOutgoingWebhook).get(webhook_id)
webhook = _find_db_webhook(webhook_id)

if webhook is None:
return None

return _db_entity_to_outgoing_webhook(webhook)


def _find_db_webhook(webhook_id: WebhookID) -> Optional[DbOutgoingWebhook]:
"""Return the webhook database entity with that ID, if found."""
return db.session.query(DbOutgoingWebhook).get(webhook_id)


def get_all_webhooks() -> List[OutgoingWebhook]:
"""Return all webhooks."""
webhooks = db.session.query(DbOutgoingWebhook).all()
Expand Down

0 comments on commit f1f5305

Please sign in to comment.