From 8d3ad248126c15195d0f21b436ca05b3718bc255 Mon Sep 17 00:00:00 2001 From: Shibani Mahapatra Date: Tue, 31 Dec 2019 09:00:59 +0000 Subject: [PATCH] Add support for 'application/x-www-form-urlencoded' Add support for Content-Type: application/x-www-form-urlencoded, so that `python-github-webhook` supports both Content-Types supported by GitHub webhooks. Signed-Off-By: Shibani Mahapatra --- github_webhook/webhook.py | 9 +++++++-- setup.py | 2 +- tests/test_webhook.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/github_webhook/webhook.py b/github_webhook/webhook.py index d6addf9..85787d3 100644 --- a/github_webhook/webhook.py +++ b/github_webhook/webhook.py @@ -2,7 +2,7 @@ import hashlib import hmac import logging - +import json import six from flask import abort, request @@ -58,7 +58,12 @@ def _postreceive(self): abort(400, "Invalid signature") event_type = _get_header("X-Github-Event") - data = request.get_json() + content_type = _get_header("content-type") + data = ( + json.loads(request.form.to_dict(flat=True)["payload"]) + if content_type == "application/x-www-form-urlencoded" + else request.get_json() + ) if data is None: abort(400, "Request body must contain json") diff --git a/setup.py b/setup.py index cea1aa4..ebd03dc 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="github-webhook", - version="1.0.2", + version="1.0.3", description="Very simple, but powerful, microframework for writing Github webhooks in Python", url="https://github.com/bloomberg/python-github-webhook", author="Alex Chamberlain, Fred Phillips, Daniel Kiss, Daniel Beer", diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 6575632..b6e64a2 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -4,6 +4,7 @@ import pytest import werkzeug +import json try: from unittest import mock @@ -23,6 +24,14 @@ def mock_request(): @pytest.fixture def push_request(mock_request): mock_request.headers["X-Github-Event"] = "push" + mock_request.headers["content-type"] = "application/json" + yield mock_request + + +@pytest.fixture +def push_request_encoded(mock_request): + mock_request.headers["X-Github-Event"] = "push" + mock_request.headers["content-type"] = "application/x-www-form-urlencoded" yield mock_request @@ -64,9 +73,35 @@ def test_run_push_hook(webhook, handler, push_request): handler.assert_called_once_with(push_request.get_json.return_value) +def test_run_push_hook_urlencoded(webhook, handler, push_request_encoded): + github_mock_payload = {"payload": '{"key": "value"}'} + push_request_encoded.form.to_dict.return_value = github_mock_payload + payload = json.loads(github_mock_payload["payload"]) + + # WHEN + webhook._postreceive() + + # THEN + handler.assert_called_once_with(payload) + + def test_do_not_run_push_hook_on_ping(webhook, handler, mock_request): # GIVEN mock_request.headers["X-Github-Event"] = "ping" + mock_request.headers["content-type"] = "application/json" + + # WHEN + webhook._postreceive() + + # THEN + handler.assert_not_called() + + +def test_do_not_run_push_hook_on_ping_urlencoded(webhook, handler, mock_request): + # GIVEN + mock_request.headers["X-Github-Event"] = "ping" + mock_request.headers["content-type"] = "application/x-www-form-urlencoded" + mock_request.form.to_dict.return_value = {"payload": '{"key": "value"}'} # WHEN webhook._postreceive()