mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 13:33:24 +00:00
This commit makes it convenient to specify additional headers to send in the integration webhook request from the command line.
153 lines
5.8 KiB
Python
Executable File
153 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Create or update a webhook integration screenshot using a test fixture."""
|
|
|
|
# check for the venv
|
|
from lib import sanity_check
|
|
|
|
sanity_check.check_venv(__file__)
|
|
|
|
import os
|
|
import sys
|
|
|
|
TOOLS_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
ROOT_DIR = os.path.dirname(TOOLS_DIR)
|
|
|
|
sys.path.insert(0, ROOT_DIR)
|
|
from scripts.lib.setup_path import setup_path
|
|
|
|
setup_path()
|
|
|
|
os.environ["DJANGO_SETTINGS_MODULE"] = "zproject.settings"
|
|
import django
|
|
|
|
django.setup()
|
|
|
|
import argparse
|
|
import subprocess
|
|
from typing import Any, Dict, Optional
|
|
|
|
import requests
|
|
import ujson
|
|
|
|
from zerver.models import UserProfile, Message, get_user_by_delivery_email, get_realm
|
|
from zerver.lib.actions import do_create_user, notify_created_bot
|
|
from zerver.lib.upload import upload_avatar_image
|
|
from zerver.lib.actions import do_change_avatar_fields
|
|
from zerver.lib.integrations import WebhookIntegration, INTEGRATIONS, split_fixture_path
|
|
from zerver.lib.webhooks.common import get_fixture_http_headers
|
|
from setup.generate_zulip_bots_static_files import create_png_from_svg
|
|
from tools.lib.test_script import prepare_puppeteer_run
|
|
|
|
def create_integration_bot(integration: WebhookIntegration) -> UserProfile:
|
|
realm = get_realm('zulip')
|
|
owner = get_user_by_delivery_email("iago@zulip.com", realm)
|
|
bot_email = "{}-bot@example.com".format(integration.name)
|
|
bot_name = "{} Bot".format(integration.name.capitalize())
|
|
try:
|
|
bot = UserProfile.objects.get(email=bot_email)
|
|
except UserProfile.DoesNotExist:
|
|
bot = do_create_user(
|
|
email=bot_email,
|
|
password="123",
|
|
realm=owner.realm,
|
|
full_name=bot_name,
|
|
short_name=bot_name,
|
|
bot_type=UserProfile.INCOMING_WEBHOOK_BOT,
|
|
bot_owner=owner,
|
|
)
|
|
notify_created_bot(bot)
|
|
|
|
if integration.logo_url is None:
|
|
return bot
|
|
logo_relative_path = integration.logo_url[len(realm.uri) + 1:]
|
|
logo_path = os.path.join(ROOT_DIR, logo_relative_path)
|
|
if logo_path.endswith(".svg"):
|
|
logo_path = create_png_from_svg(logo_path)
|
|
|
|
with open(logo_path, "rb") as f:
|
|
upload_avatar_image(f, owner, bot)
|
|
do_change_avatar_fields(bot, UserProfile.AVATAR_FROM_USER)
|
|
|
|
return bot
|
|
|
|
def get_integration(integration_name: str) -> WebhookIntegration:
|
|
integration = INTEGRATIONS[integration_name]
|
|
assert isinstance(integration, WebhookIntegration), "Not a WebhookIntegration"
|
|
return integration
|
|
|
|
def get_requests_headers(integration_name: str, fixture_name: str) -> Dict[str, Any]:
|
|
headers = get_fixture_http_headers(integration_name, fixture_name)
|
|
|
|
def fix_name(header: str) -> str:
|
|
header = header if not header.startswith('HTTP_') else header[len('HTTP_'):]
|
|
return header.replace('_', '-')
|
|
|
|
return {fix_name(k): v for k, v in headers.items()}
|
|
|
|
def webhook_json_fixture(path: str) -> str:
|
|
path = os.path.abspath(path)
|
|
if not (os.path.exists(path) and path.endswith('.json') and 'webhooks' in path):
|
|
raise argparse.ArgumentTypeError('Not a valid webhook JSON fixture')
|
|
return path
|
|
|
|
def custom_headers(headers_json: str) -> Dict[str, str]:
|
|
if not headers_json:
|
|
return {}
|
|
try:
|
|
return ujson.loads(headers_json)
|
|
except ValueError as ve:
|
|
raise argparse.ArgumentTypeError(
|
|
'Encountered an error while attempting to parse custom headers: {}\n'
|
|
'Note: all strings must be enclosed within "" instead of \'\''.format(ve))
|
|
|
|
def send_bot_payload_message(bot: UserProfile, integration: WebhookIntegration, fixture_path: str,
|
|
extra_headers: Optional[Dict[str, str]]) -> None:
|
|
# Delete all messages, so new message is the only one it's message group
|
|
Message.objects.filter(sender=bot).delete()
|
|
|
|
assert isinstance(bot.bot_owner, UserProfile)
|
|
url = "{}/{}?api_key={}&stream=devel".format(
|
|
bot.bot_owner.realm.uri, integration.url, bot.api_key
|
|
)
|
|
with open(fixture_path) as f:
|
|
data = ujson.load(f)
|
|
_, fixture_name = split_fixture_path(fixture_path)
|
|
headers = get_requests_headers(integration.name, fixture_name)
|
|
if extra_headers:
|
|
headers.update(extra_headers)
|
|
try:
|
|
response = requests.post(url, json=data, headers=headers)
|
|
except requests.exceptions.ConnectionError:
|
|
print('This tool needs the local dev server to be running. '
|
|
'Please start it using tools/run-dev.py before running this tool.')
|
|
sys.exit(1)
|
|
if response.status_code != 200:
|
|
print(response.json())
|
|
print('Failed to trigger webhook')
|
|
sys.exit(1)
|
|
print('Triggered {} webhook'.format(integration.name))
|
|
|
|
def capture_last_message_screenshot(bot: UserProfile, integration: WebhookIntegration,
|
|
fixture_name: str) -> None:
|
|
message = Message.objects.filter(sender=bot).last()
|
|
if message is None:
|
|
print('No message found for {} integration for {}'.format(integration.name, fixture_name))
|
|
return
|
|
message_id = str(message.id)
|
|
screenshot_script = os.path.join(TOOLS_DIR, 'message-screenshot.js')
|
|
subprocess.check_call(['node', screenshot_script, integration.name, message_id])
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('fixture', type=webhook_json_fixture, help='Path to the fixture to use')
|
|
parser.add_argument('-H', '--custom-headers',
|
|
type=custom_headers,
|
|
help='Any additional headers to be sent with the request.')
|
|
options = parser.parse_args()
|
|
|
|
prepare_puppeteer_run()
|
|
integration_name, fixture_name = split_fixture_path(options.fixture)
|
|
integration = get_integration(integration_name)
|
|
bot = create_integration_bot(integration)
|
|
send_bot_payload_message(bot, integration, options.fixture, options.custom_headers)
|
|
capture_last_message_screenshot(bot, integration, fixture_name)
|