Skip to content

Commit

Permalink
webhook to SQS lambda trigger
Browse files Browse the repository at this point in the history
  • Loading branch information
voynow committed Oct 11, 2024
1 parent 2c3ecb3 commit 042718c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 16 deletions.
18 changes: 6 additions & 12 deletions src/lambda_function.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import logging
import os
import traceback
Expand All @@ -12,22 +13,22 @@

def strategy_router(event: dict, invocation_id: str) -> dict:
"""
Route the event to the appropriate handler based on the event type.
Route the event to the appropriate handler based on the event type
This API is public but each method is protected in some way
:param event: The event dictionary containing event data
:param invocation_id: The unique identifier for the invocation
:return: {"success": bool, other_metadata: dict} where the error key is
only present if success is False
"""

# Will fail on bad authenticate_with_code
if event.get("email") and event.get("code"):
return auth_manager.signup(
email=event["email"],
code=event["code"],
)

# Will fail on bad authenticate_with_code
elif event.get("code"):
user_auth = auth_manager.authenticate_with_code(event["code"])
return {"success": True, "jwt_token": user_auth.jwt_token}
Expand All @@ -39,16 +40,9 @@ def strategy_router(event: dict, invocation_id: str) -> dict:
payload=event.get("payload"),
)

elif (
event.get("subscription_id")
and event.get("aspect_type")
and event.get("object_type")
and event.get("object_id")
and event.get("owner_id")
):
elif webhook_router.is_strava_webhook_event(event):
return webhook_router.handle_request(event, invocation_id)

# This will only run if triggered by NIGHTLY_EMAIL_TRIGGER_ARN
elif (
event.get("resources")
and event.get("resources")[0] == os.environ["NIGHTLY_EMAIL_TRIGGER_ARN"]
Expand All @@ -58,7 +52,7 @@ def strategy_router(event: dict, invocation_id: str) -> dict:
elif event.get("trigger_test_key") == os.environ["TRIGGER_TEST_KEY"]:
return update_pipeline.integration_test_executor(invocation_id)
else:
return {"success": False, "error": f"Unknown event type: {event}"}
return {"success": False, "error": f"Could not route event: {event}"}


def lambda_handler(event, context):
Expand Down
55 changes: 52 additions & 3 deletions src/webhook_router.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os

from src import auth_manager
Expand All @@ -9,6 +10,30 @@
from src.update_pipeline import training_week_update_executor


def is_strava_webhook_event(event: dict) -> bool:
"""
Check if the event is a valid SQS event from Strava webhook
:param event: The event dictionary containing event data
:return: True if the event is a valid Strava webhook event, False otherwise
"""
if not event.get("Records") or len(event.get("Records")) < 1:
return False

try:
webhook_event = json.loads(event.get("Records")[0].get("body"))
except json.JSONDecodeError:
return False

return (
webhook_event.get("subscription_id")
and webhook_event.get("aspect_type")
and webhook_event.get("object_type")
and webhook_event.get("object_id")
and webhook_event.get("owner_id")
)


def handle_activity_create(user: UserRow, event: dict, invocation_id: str) -> dict:
strava_client = auth_manager.get_strava_client(user.athlete_id)
activity = strava_client.get_activity(event.get("object_id"))
Expand All @@ -26,11 +51,12 @@ def handle_activity_create(user: UserRow, event: dict, invocation_id: str) -> di
}


def handle_request(event: dict, invocation_id: str) -> dict:
def _handle_request(event: dict, invocation_id: str) -> dict:
"""
Handle Strava webhook events for activities and athletes.
Handle a single Strava webhook event
:param event: Webhook event payload from Strava
:param event: The event dictionary containing event data
:param invocation_id: The invocation ID for logging
:return: dict with {"success": bool, "message": str (optional)}
"""
if int(event.get("subscription_id")) != int(
Expand All @@ -52,3 +78,26 @@ def handle_request(event: dict, invocation_id: str) -> dict:
}

return {"success": False, "error": f"Unknown event type: {event_type}"}


def handle_request(event: dict, invocation_id: str) -> dict:
"""
Handle Strava webhook events sent from SQS
:param event: Webhook event payload from SQS
:param invocation_id: The invocation ID for logging
:return: dict with {"success": bool, "responses": list of dicts
with {"success": bool, "message": str (optional)}}
"""
responses = []
for record in event.get("Records"):
try:
responses.append(
_handle_request(
event=json.loads(record.get("body")),
invocation_id=invocation_id,
)
)
except Exception as e:
responses.append({"success": False, "error": str(e)})
return {"success": True, "responses": responses}
6 changes: 5 additions & 1 deletion web/src/app/strava_webhook/route.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import AWS from 'aws-sdk';
import { NextRequest, NextResponse } from 'next/server';

const sqs = new AWS.SQS({ region: 'us-east-1' });
const sqs = new AWS.SQS({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'us-east-1',
});

export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
Expand Down

0 comments on commit 042718c

Please sign in to comment.