mirror of
https://github.com/zulip/zulip.git
synced 2025-11-16 11:52:01 +00:00
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:
committed by
Tim Abbott
parent
e859ab7545
commit
99c3e2ecdc
@@ -217,6 +217,16 @@ Using either method will create a message in Zulip:
|
|||||||
|
|
||||||
<img class="screenshot" src="/static/images/api/helloworld-webhook.png" />
|
<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
|
## Step 4: Create tests
|
||||||
|
|
||||||
Every webhook integration should have a corresponding test file:
|
Every webhook integration should have a corresponding test file:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import ujson
|
import ujson
|
||||||
|
from typing import Union, Dict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import CommandParser
|
from django.core.management.base import CommandParser
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
@@ -18,6 +18,14 @@ Example:
|
|||||||
--fixture=zerver/webhooks/integration/fixtures/name.json \
|
--fixture=zerver/webhooks/integration/fixtures/name.json \
|
||||||
'--url=/api/v1/external/integration?stream=stream_name&api_key=api_key'
|
'--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:
|
def add_arguments(self, parser: CommandParser) -> None:
|
||||||
@@ -33,8 +41,30 @@ Example:
|
|||||||
help='The url on your Zulip server that you want '
|
help='The url on your Zulip server that you want '
|
||||||
'to post the fixture to')
|
'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")
|
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:
|
def handle(self, **options: str) -> None:
|
||||||
if options['fixture'] is None or options['url'] is None:
|
if options['fixture'] is None or options['url'] is None:
|
||||||
self.print_help('./manage.py', 'send_webhook_fixture_message')
|
self.print_help('./manage.py', 'send_webhook_fixture_message')
|
||||||
@@ -46,14 +76,19 @@ Example:
|
|||||||
print('Fixture {} does not exist'.format(options['fixture']))
|
print('Fixture {} does not exist'.format(options['fixture']))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
headers = self.parse_headers(options['custom-headers'])
|
||||||
json = self._get_fixture_as_json(full_fixture_path)
|
json = self._get_fixture_as_json(full_fixture_path)
|
||||||
realm = self.get_realm(options)
|
realm = self.get_realm(options)
|
||||||
if realm is None:
|
if realm is None:
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
|
|
||||||
client = Client()
|
client = Client()
|
||||||
result = client.post(options['url'], json, content_type="application/json",
|
if headers:
|
||||||
HTTP_HOST=realm.host)
|
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:
|
if result.status_code != 200:
|
||||||
print('Error status %s: %s' % (result.status_code, result.content))
|
print('Error status %s: %s' % (result.status_code, result.content))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from zerver.lib.test_helpers import stdout_suppressed
|
|||||||
from zerver.lib.test_runner import slow
|
from zerver.lib.test_runner import slow
|
||||||
from zerver.models import Recipient, get_user_profile_by_email, get_stream
|
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.lib.test_helpers import most_recent_message
|
||||||
from zerver.models import get_realm, UserProfile, Realm
|
from zerver.models import get_realm, UserProfile, Realm
|
||||||
from confirmation.models import RealmCreationKey, generate_realm_creation_url
|
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",
|
client.post.assert_called_once_with(self.url, {}, content_type="application/json",
|
||||||
HTTP_HOST="zulip.testserver")
|
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):
|
class TestGenerateRealmCreationLink(ZulipTestCase):
|
||||||
COMMAND_NAME = "generate_realm_creation_link"
|
COMMAND_NAME = "generate_realm_creation_link"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user