management: Support sending custom headers when testing a webhook.

this commit adds an option to specify custom headers when using the
`./manage.py send_webhook_fixture_message` tool.
This commit is contained in:
Hemanth V. Alluri 2019-03-09 15:31:20 +05:30 committed by Tim Abbott
parent e859ab7545
commit 99c3e2ecdc
3 changed files with 73 additions and 4 deletions

View File

@ -217,6 +217,16 @@ Using either method will create a message in Zulip:
<img class="screenshot" src="/static/images/api/helloworld-webhook.png" />
Some webhooks require custom HTTP headers, which can be passed using
`./manage.py send_webhook_fixture_message --custom-headers`. For
example:
--custom-headers='{"X-Custom-Header": "value"}'
The format is a JSON dictionary, so make sure that the header names do
not contain any spaces in them and that you use the precise quoting
approach shown above.
## Step 4: Create tests
Every webhook integration should have a corresponding test file:

View File

@ -1,7 +1,7 @@
import os
import ujson
from typing import Union, Dict
from django.conf import settings
from django.core.management.base import CommandParser
from django.test import Client
@ -18,6 +18,14 @@ Example:
--fixture=zerver/webhooks/integration/fixtures/name.json \
'--url=/api/v1/external/integration?stream=stream_name&api_key=api_key'
To pass custom headers along with the webhook message use the --custom-headers
command line option.
Example:
--custom-headers='{"X-Custom-Header": "value"}'
The format is a JSON dictionary, so make sure that the header names do
not contain any spaces in them and that you use the precise quoting
approach shown above.
"""
def add_arguments(self, parser: CommandParser) -> None:
@ -33,8 +41,30 @@ Example:
help='The url on your Zulip server that you want '
'to post the fixture to')
parser.add_argument('-H', '--custom-headers',
dest='custom-headers',
type=str,
help='The headers you want to provide along with '
'your mock request to Zulip.')
self.add_realm_args(parser, help="Specify which realm/subdomain to connect to; default is zulip")
def parse_headers(self, custom_headers: Union[None, str]) -> Union[None, Dict[str, str]]:
headers = {}
if not custom_headers:
return None
try:
custom_headers_dict = ujson.loads(custom_headers)
for header in custom_headers_dict:
if len(header.split(" ")) > 1:
raise ValueError("custom header '%s' contains a space." % (header))
headers["HTTP_" + header.upper().replace("-", "_")] = str(custom_headers_dict[header])
return headers
except ValueError as ve:
print('Encountered an error while attempting to parse custom headers: %s' % (ve))
print('Note: all strings must be enclosed within "" instead of \'\'')
exit(1)
def handle(self, **options: str) -> None:
if options['fixture'] is None or options['url'] is None:
self.print_help('./manage.py', 'send_webhook_fixture_message')
@ -46,14 +76,19 @@ Example:
print('Fixture {} does not exist'.format(options['fixture']))
exit(1)
headers = self.parse_headers(options['custom-headers'])
json = self._get_fixture_as_json(full_fixture_path)
realm = self.get_realm(options)
if realm is None:
realm = get_realm("zulip")
client = Client()
result = client.post(options['url'], json, content_type="application/json",
HTTP_HOST=realm.host)
if headers:
result = client.post(options['url'], json, content_type="application/json",
HTTP_HOST=realm.host, **headers)
else:
result = client.post(options['url'], json, content_type="application/json",
HTTP_HOST=realm.host)
if result.status_code != 200:
print('Error status %s: %s' % (result.status_code, result.content))
exit(1)

View File

@ -18,6 +18,7 @@ from zerver.lib.test_helpers import stdout_suppressed
from zerver.lib.test_runner import slow
from zerver.models import Recipient, get_user_profile_by_email, get_stream
from zerver.management.commands.send_webhook_fixture_message import Command
from zerver.lib.test_helpers import most_recent_message
from zerver.models import get_realm, UserProfile, Realm
from confirmation.models import RealmCreationKey, generate_realm_creation_url
@ -213,6 +214,29 @@ class TestSendWebhookFixtureMessage(TestCase):
client.post.assert_called_once_with(self.url, {}, content_type="application/json",
HTTP_HOST="zulip.testserver")
@patch('zerver.management.commands.send_webhook_fixture_message.Command._get_fixture_as_json')
@patch('zerver.management.commands.send_webhook_fixture_message.Command._does_fixture_path_exist')
@patch('zerver.management.commands.send_webhook_fixture_message.Command.parse_headers')
def test_check_post_request_with_improper_custom_header(self,
parse_headers_mock: MagicMock,
does_fixture_path_exist_mock: MagicMock,
get_fixture_as_json_mock: MagicMock) -> None:
does_fixture_path_exist_mock.return_value = True
get_fixture_as_json_mock.return_value = "{}"
improper_headers = '{"X-Custom - Headers": "some_val"}'
with self.assertRaises(SystemExit) as se:
call_command(self.COMMAND_NAME, fixture=self.fixture_path, url=self.url, custom_headers=improper_headers)
parse_headers_mock.assert_called_once_with(improper_headers)
def test_parse_headers_method(self) -> None:
command = Command()
self.assertEqual(command.parse_headers(None), None)
self.assertEqual(command.parse_headers('{"X-Custom-Header": "value"}'), {"HTTP_X_CUSTOM_HEADER": "value"})
with self.assertRaises(SystemExit):
command.parse_headers('{"X-Custom - Headers": "some_val"}')
class TestGenerateRealmCreationLink(ZulipTestCase):
COMMAND_NAME = "generate_realm_creation_link"