zulip/tools/generate-integration-docs-s...

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)