webhook templates
This commit is contained in:
@@ -11,6 +11,7 @@ from flask import Blueprint, jsonify, request, current_app
|
||||
from web.auth.decorators import api_auth_required
|
||||
from web.models import Webhook, WebhookDeliveryLog, Alert
|
||||
from web.services.webhook_service import WebhookService
|
||||
from web.services.template_service import get_template_service
|
||||
|
||||
bp = Blueprint('webhooks_api', __name__)
|
||||
|
||||
@@ -144,6 +145,9 @@ def create_webhook():
|
||||
severity_filter: Array of severities to filter
|
||||
timeout: Request timeout in seconds (default: 10)
|
||||
retry_count: Number of retry attempts (default: 3)
|
||||
template: Jinja2 template for custom payload (optional)
|
||||
template_format: Template format - 'json' or 'text' (default: json)
|
||||
content_type_override: Custom Content-Type header (optional)
|
||||
|
||||
Returns:
|
||||
JSON response with created webhook
|
||||
@@ -172,6 +176,26 @@ def create_webhook():
|
||||
'message': f'Invalid auth_type. Must be one of: {", ".join(valid_auth_types)}'
|
||||
}), 400
|
||||
|
||||
# Validate template_format
|
||||
valid_template_formats = ['json', 'text']
|
||||
template_format = data.get('template_format', 'json')
|
||||
if template_format not in valid_template_formats:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Invalid template_format. Must be one of: {", ".join(valid_template_formats)}'
|
||||
}), 400
|
||||
|
||||
# Validate template if provided
|
||||
template = data.get('template')
|
||||
if template:
|
||||
template_service = get_template_service()
|
||||
is_valid, error_msg = template_service.validate_template(template, template_format)
|
||||
if not is_valid:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Invalid template: {error_msg}'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
webhook_service = WebhookService(current_app.db_session)
|
||||
|
||||
@@ -197,6 +221,9 @@ def create_webhook():
|
||||
severity_filter=severity_filter,
|
||||
timeout=data.get('timeout', 10),
|
||||
retry_count=data.get('retry_count', 3),
|
||||
template=template,
|
||||
template_format=template_format,
|
||||
content_type_override=data.get('content_type_override'),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
updated_at=datetime.now(timezone.utc)
|
||||
)
|
||||
@@ -223,6 +250,9 @@ def create_webhook():
|
||||
'custom_headers': custom_headers_parsed,
|
||||
'timeout': webhook.timeout,
|
||||
'retry_count': webhook.retry_count,
|
||||
'template': webhook.template,
|
||||
'template_format': webhook.template_format,
|
||||
'content_type_override': webhook.content_type_override,
|
||||
'created_at': webhook.created_at.isoformat()
|
||||
}
|
||||
}), 201
|
||||
@@ -255,6 +285,9 @@ def update_webhook(webhook_id):
|
||||
severity_filter: Array of severities
|
||||
timeout: Request timeout
|
||||
retry_count: Retry attempts
|
||||
template: Jinja2 template for custom payload
|
||||
template_format: Template format - 'json' or 'text'
|
||||
content_type_override: Custom Content-Type header
|
||||
|
||||
Returns:
|
||||
JSON response with update status
|
||||
@@ -278,6 +311,26 @@ def update_webhook(webhook_id):
|
||||
'message': f'Invalid auth_type. Must be one of: {", ".join(valid_auth_types)}'
|
||||
}), 400
|
||||
|
||||
# Validate template_format if provided
|
||||
if 'template_format' in data:
|
||||
valid_template_formats = ['json', 'text']
|
||||
if data['template_format'] not in valid_template_formats:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Invalid template_format. Must be one of: {", ".join(valid_template_formats)}'
|
||||
}), 400
|
||||
|
||||
# Validate template if provided
|
||||
if 'template' in data and data['template']:
|
||||
template_format = data.get('template_format', webhook.template_format or 'json')
|
||||
template_service = get_template_service()
|
||||
is_valid, error_msg = template_service.validate_template(data['template'], template_format)
|
||||
if not is_valid:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Invalid template: {error_msg}'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
webhook_service = WebhookService(current_app.db_session)
|
||||
|
||||
@@ -303,6 +356,12 @@ def update_webhook(webhook_id):
|
||||
webhook.timeout = data['timeout']
|
||||
if 'retry_count' in data:
|
||||
webhook.retry_count = data['retry_count']
|
||||
if 'template' in data:
|
||||
webhook.template = data['template']
|
||||
if 'template_format' in data:
|
||||
webhook.template_format = data['template_format']
|
||||
if 'content_type_override' in data:
|
||||
webhook.content_type_override = data['content_type_override']
|
||||
|
||||
webhook.updated_at = datetime.now(timezone.utc)
|
||||
current_app.db_session.commit()
|
||||
@@ -326,6 +385,9 @@ def update_webhook(webhook_id):
|
||||
'custom_headers': custom_headers,
|
||||
'timeout': webhook.timeout,
|
||||
'retry_count': webhook.retry_count,
|
||||
'template': webhook.template,
|
||||
'template_format': webhook.template_format,
|
||||
'content_type_override': webhook.content_type_override,
|
||||
'updated_at': webhook.updated_at.isoformat()
|
||||
}
|
||||
})
|
||||
@@ -484,6 +546,121 @@ def get_webhook_logs(webhook_id):
|
||||
})
|
||||
|
||||
|
||||
@bp.route('/preview-template', methods=['POST'])
|
||||
@api_auth_required
|
||||
def preview_template():
|
||||
"""
|
||||
Preview a webhook template with sample data.
|
||||
|
||||
Request body:
|
||||
template: Jinja2 template string (required)
|
||||
template_format: Template format - 'json' or 'text' (default: json)
|
||||
|
||||
Returns:
|
||||
JSON response with rendered template preview
|
||||
"""
|
||||
data = request.get_json() or {}
|
||||
|
||||
if not data.get('template'):
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'template is required'
|
||||
}), 400
|
||||
|
||||
template = data['template']
|
||||
template_format = data.get('template_format', 'json')
|
||||
|
||||
# Validate template format
|
||||
if template_format not in ['json', 'text']:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Invalid template_format. Must be json or text'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
template_service = get_template_service()
|
||||
|
||||
# Validate template
|
||||
is_valid, error_msg = template_service.validate_template(template, template_format)
|
||||
if not is_valid:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Template validation error: {error_msg}'
|
||||
}), 400
|
||||
|
||||
# Render with sample data
|
||||
rendered, error = template_service.render_test_payload(template, template_format)
|
||||
|
||||
if error:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Template rendering error: {error}'
|
||||
}), 400
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'rendered': rendered,
|
||||
'format': template_format
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Failed to preview template: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
@bp.route('/template-presets', methods=['GET'])
|
||||
@api_auth_required
|
||||
def get_template_presets():
|
||||
"""
|
||||
Get list of available webhook template presets.
|
||||
|
||||
Returns:
|
||||
JSON response with template presets
|
||||
"""
|
||||
import os
|
||||
|
||||
try:
|
||||
# Load presets manifest
|
||||
presets_file = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'../templates/webhook_presets/presets.json'
|
||||
)
|
||||
|
||||
with open(presets_file, 'r') as f:
|
||||
presets_manifest = json.load(f)
|
||||
|
||||
# Load template contents for each preset
|
||||
presets_dir = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'../templates/webhook_presets'
|
||||
)
|
||||
|
||||
for preset in presets_manifest:
|
||||
template_file = os.path.join(presets_dir, preset['file'])
|
||||
with open(template_file, 'r') as f:
|
||||
preset['template'] = f.read()
|
||||
# Remove file reference from response
|
||||
del preset['file']
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'presets': presets_manifest
|
||||
})
|
||||
|
||||
except FileNotFoundError as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Template presets not found: {str(e)}'
|
||||
}), 500
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Failed to load template presets: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
@bp.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
|
||||
Reference in New Issue
Block a user