-
Notifications
You must be signed in to change notification settings - Fork 125
Webhook API to receive and process Workflow payload #673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Kamlesh72
wants to merge
11
commits into
middlewarehq:deployments-webhook
Choose a base branch
from
Kamlesh72:feat/webhook-apis
base: deployments-webhook
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
0d401c1
Initialize and start procrastinate worker
Kamlesh72 5de4b24
Procrastinate queue logs for cli
Kamlesh72 f090676
Procrastinate queue ui system logs
Kamlesh72 8415365
Error handling for webhook-related exceptions
Kamlesh72 88b7a3a
Logger format change and add required dataclass, enums
Kamlesh72 3f3e4b8
Added repo url map, webhook abstract class and WebhookEvent model
Kamlesh72 f7401a8
API key validator and abstract class methods fix
Kamlesh72 4cf6dd5
Added queue tasks, webhook api and webhook workflow class implementation
Kamlesh72 beed13d
Merge branch 'deployments-webhook' into feat/webhook-apis
Kamlesh72 7427a30
Add SQL migration for webhook event table
Kamlesh72 296191e
Strict checks, detail error log and code refactor
Kamlesh72 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from flask import Blueprint, request | ||
from typing import Any, Dict, List | ||
from mhq.service.query_validator import get_query_validator | ||
from mhq.store.models.webhooks.enums import WebhookEventRequestType | ||
from mhq.service.webhooks.factory import WebhookEventFactory | ||
from mhq.service.queue.tasks import WebhookQueue | ||
|
||
app = Blueprint("webhooks", __name__) | ||
|
||
|
||
@app.route("/public/webhook/<event_type>", methods={"POST"}) | ||
def receive_webhook_workflows(event_type: str): | ||
webhook_event_type = WebhookEventRequestType(event_type) | ||
secret_key = request.headers.get("X-API-KEY") | ||
|
||
query_validator = get_query_validator() | ||
default_org = query_validator.get_default_org() | ||
org_id = str(default_org.id) | ||
query_validator.api_key_validator(secret_key, org_id) | ||
|
||
webhook_event_factory = WebhookEventFactory() | ||
webhook_service = webhook_event_factory(webhook_event_type) | ||
|
||
payload: Dict[str, List[Any]] = request.get_json() | ||
webhook_service.validate_payload(payload) | ||
webhook_event_id = webhook_service.save_webhook_event(org_id, payload) | ||
|
||
job_id = WebhookQueue.enqueue_webhook.defer(webhook_event_id=str(webhook_event_id)) | ||
|
||
return {"message": "Job enqueued successfully", "job_id": job_id}, 200 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Base exception class for webhook-related errors | ||
class WebhookException(Exception): | ||
def __init__(self, message: str, resolution: str): | ||
self.message = message | ||
self.resolution = resolution | ||
super().__init__(self.message) | ||
|
||
|
||
class InvalidApiKeyError(WebhookException): | ||
def __init__(self): | ||
super().__init__( | ||
message="Invalid or missing API key", | ||
resolution=( | ||
"Ensure you are passing the correct API key in the `X-API-KEY` header. " | ||
"To generate a new key, navigate to Manage Integrations → Webhook → Setup." | ||
), | ||
) | ||
|
||
|
||
class PayloadLimitExceededError(WebhookException): | ||
def __init__(self): | ||
super().__init__( | ||
message="Payload exceeds the allowed size limit.", | ||
resolution="Only a maximum of 500 records is allowed per request.", | ||
) | ||
|
||
|
||
class InvalidPayloadError(WebhookException): | ||
def __init__(self, message: str = "Invalid JSON payload received."): | ||
super().__init__( | ||
message=message, | ||
resolution=( | ||
"Please ensure your payload follows the correct format. " | ||
"You can review the expected structure under Manage Integrations → Webhook → Setup." | ||
), | ||
) | ||
|
||
|
||
class InvalidEventTypeError(WebhookException): | ||
def __init__(self, event_type: str): | ||
super().__init__( | ||
message=f"Invalid webhook event type received: '{event_type}'", | ||
resolution="Ensure the URL path ends with either `/workflow` or `/incident`", | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
backend/analytics_server/mhq/service/queue/task_handlers/webhook_queue_handler.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from mhq.service.webhooks.factory import WebhookEventFactory | ||
from mhq.service.webhooks.webhook_event_service import ( | ||
get_webhook_service, | ||
WebhookEventService, | ||
) | ||
import traceback | ||
|
||
|
||
class WebhookQueueHandler: | ||
def __init__(self, webhooks_service: WebhookEventService): | ||
self.webhooks_service = webhooks_service | ||
|
||
def webhook_receiver_handler(self, webhook_event_id: str): | ||
webhook_event = None | ||
try: | ||
webhook_event = self.webhooks_service.get_webhook_event(webhook_event_id) | ||
if not webhook_event: | ||
raise Exception("Webhook payload not found in database.") | ||
webhook_event_factory = WebhookEventFactory() | ||
webhook_event_handler = webhook_event_factory(webhook_event.request_type) | ||
webhook_event_handler.process_webhook_event(webhook_event) | ||
|
||
webhook_event.error = None | ||
self.webhooks_service.update_webhook_event(webhook_event) | ||
except Exception as e: | ||
if not webhook_event: | ||
raise e | ||
webhook_event.error = { | ||
"type": e.__class__.__name__, | ||
"message": str(e), | ||
"args": e.args, | ||
"traceback": traceback.format_exc(), | ||
} | ||
self.webhooks_service.update_webhook_event(webhook_event) | ||
raise e | ||
|
||
|
||
def get_webhook_queue_handler(): | ||
return WebhookQueueHandler(webhooks_service=get_webhook_service()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from procrastinate_worker import app, flask_app | ||
from mhq.service.queue.task_handlers.webhook_queue_handler import ( | ||
get_webhook_queue_handler, | ||
) | ||
|
||
|
||
class WebhookQueue: | ||
@staticmethod | ||
@app.task(queue="webhookQueue", name="WebhookQueue.enqueue_webhook") | ||
def enqueue_webhook(webhook_event_id: str): | ||
with flask_app.app_context(): | ||
webhook_queue_handler = get_webhook_queue_handler() | ||
webhook_queue_handler.webhook_receiver_handler(webhook_event_id) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from mhq.service.webhooks.factory_abstract import WebhookEventHandler | ||
from mhq.store.models.webhooks.enums import WebhookEventRequestType | ||
from mhq.service.webhooks.webhook_workflow_handler import get_webhook_workflow_handler | ||
|
||
|
||
class WebhookEventFactory: | ||
def __call__(self, request_type: WebhookEventRequestType) -> WebhookEventHandler: | ||
if request_type == WebhookEventRequestType.WORKFLOW: | ||
return get_webhook_workflow_handler() | ||
|
||
raise NotImplementedError(f"Unknown request type - {request_type}") |
29 changes: 29 additions & 0 deletions
29
backend/analytics_server/mhq/service/webhooks/factory_abstract.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import Any, Dict | ||
from mhq.store.models.webhooks.webhooks import WebhookEvent | ||
|
||
|
||
class WebhookEventHandler(ABC): | ||
@abstractmethod | ||
def validate_payload(self, payload: Dict[str, Any]): | ||
""" | ||
Validates the incoming webhook event data before processing. | ||
""" | ||
|
||
@abstractmethod | ||
def save_webhook_event(self, org_id: str, payload: Dict[str, Any]) -> str: | ||
""" | ||
Saves the webhook event in database. | ||
""" | ||
|
||
@abstractmethod | ||
def process_webhook_event(self, webhook_event: WebhookEvent): | ||
""" | ||
Executes the main business logic for processing the webhook event. | ||
""" | ||
|
||
@abstractmethod | ||
def prune_synced_data(self, webhook_event: WebhookEvent): | ||
""" | ||
Prunes the synced data based on Interval. | ||
""" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice