Skip to content

feat: Test webhook from backend #5354

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
wants to merge 32 commits into
base: main
Choose a base branch
from
Open

Conversation

Zaimwa9
Copy link
Contributor

@Zaimwa9 Zaimwa9 commented Apr 17, 2025

Thanks for submitting a PR! Please check the boxes below:

  • I have added information to docs/ if required so people know about the feature!
  • I have filled in the "Changes" section below?
  • I have filled in the "How did you test this code" section below?
  • I have used a Conventional Commit title for this Pull Request

Changes

The goal of this PR is to move the webhook testing logic from the frontend (with custom method to mimic backend signature) to the backend
Frontend payload exemple (audit log)

{
    "payload": {
        "created_date": "2020-02-23T17:30:57.006318Z",
        "log": "New Flag / Remote Config created: my_feature",
        "author": {
            "id": 3,
            "email": "[email protected]",
            "first_name": "john",
            "last_name": "doe"
        },
        "environment": null,
        "project": {
            "id": 6,
            "name": "Project name",
            "organisation": 1
        },
        "related_object_id": 6,
        "related_object_type": "FEATURE"
    },
    "scope": {
        "id": 2,
        "type": "organisation"
    },
    "secret": "my-secret",
    "webhookUrl": "http://localhost:3001/webhook"
}
  • New WebhookViewSet with test endpoint
  • Re-uses Organisation/Environment permissions based on scope payload object (on top of existing org/env pk from url)
  • Reviewed errors handling: Success = 200 (unchanged), any error from end-user will return status 502 bad gateway and a text message with status and raw error (e.g Webhook returned error status: 400)
  • Couple of UI fixes (see screenshots)

How did you test this code?

  • Added unit tests
  • Spawned 2 webservers (python / node) testing against them
  1. Go to environment => webhooks or organisation settings => webhooks
  2. Prepare a webhook endpoint
  3. Test with / without different signatures
  4. Try different error responses

example code:
pip install flask flask-cors
python server.py

# server.py
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
from functools import wraps
import os
import datetime
from flask_cors import CORS

app = Flask(__name__)

WEBHOOK_SECRET = "your-secret"
CORS(app, resources={
    r"/*": {
        "origins": "*", 
        "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
        "allow_headers": ["Content-Type", "Authorization", "x-flagsmith-signature"]
    }
})

@app.route('/webhook', methods=['POST'])
def webhook():
    received_signature = request.headers.get('x-flagsmith-signature')
    
    if not received_signature and WEBHOOK_SECRET:
        return jsonify({
            'status': 'error',
            'message': 'Missing signature header'
        }), 401

    if WEBHOOK_SECRET:
        request_body = json.dumps(request.json)     
        expected_signature = hmac.new(
            key=WEBHOOK_SECRET.encode(),
            msg=request_body.encode(),
            digestmod=hashlib.sha256
        ).hexdigest()

        print(f"Expected signature: {expected_signature}")
        print(f"Received signature: {received_signature}")
        if not hmac.compare_digest(expected_signature, received_signature):
            return jsonify({
                'status': 'error',
                'message': 'Invalid signature'
            }), 401

    print("Webhook received and verified:", {
        'headers': dict(request.headers),
        'body': request.json,
        'timestamp': datetime.datetime.utcnow().isoformat()
    })

    return jsonify({
        'status': 'success',
        'message': 'Webhook received and verified successfully'
    })

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 3001))
    app.run(host='0.0.0.0', port=port)

@Zaimwa9 Zaimwa9 requested review from a team as code owners April 17, 2025 17:07
@Zaimwa9 Zaimwa9 requested review from tiagoapolo and khvn26 and removed request for a team April 17, 2025 17:07
Copy link

vercel bot commented Apr 17, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs ⬜️ Ignored (Inspect) Visit Preview Apr 30, 2025 9:43am
flagsmith-frontend-preview ⬜️ Ignored (Inspect) Visit Preview Apr 30, 2025 9:43am
flagsmith-frontend-staging ⬜️ Ignored (Inspect) Visit Preview Apr 30, 2025 9:43am

@Zaimwa9 Zaimwa9 marked this pull request as draft April 17, 2025 17:07
@github-actions github-actions bot added front-end Issue related to the React Front End Dashboard api Issue related to the REST API labels Apr 17, 2025
Comment on lines 62 to 67
T = TypeVar('T', bound=Model)

@runtime_checkable
class HasObjects(Protocol[T]):
objects: Manager[T]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious to have your opinion on this typing and cast @khvn26 before using it more (it's to tackle this environments/views.py:291: error: "type[T]" has no attribute "objects" [attr-defined])

Copy link
Member

@khvn26 khvn26 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why it's done here, but I don't think this is necessarily a good solution.

I'd try following the official way to deal with this first by using the ._default_manager attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah perfect thanks I will look into it

Copy link
Member

@khvn26 khvn26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Came up with some comments/questions.

@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 18, 2025
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 21, 2025
@Zaimwa9 Zaimwa9 requested a review from khvn26 April 25, 2025 08:22
Copy link
Member

@khvn26 khvn26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the recent changes are a massive improvement, good job 👍

There's still a couple comments I'd like you to address, though:

  1. I'm not particularly fond of using static sample data, though, as it can get stale easily. Ideally, we should be able to reuse existing webhook tasks in the check.

  2. Skimming through the code, I've found (currently unused?) trigger_sample_webhook utility function and TriggerSampleWebhookMixin that we need to either refactor and integrate into your work, or get rid of.

@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 29, 2025
@Zaimwa9
Copy link
Contributor Author

Zaimwa9 commented Apr 30, 2025

@khvn26 made the changes. To answer your questions:

  1. I went half-way by using the models to generate the sample data so they are closer to reality while remaining predictable (for testing). They are using the existing methods so not much more maintainance, thanks for raising it
  2. Indeed, it seems the purpose was similar, I could have caught it. I removed it as it was unused. Anticipating the question. I voluntarily didn't use _call_webhook there because as of it doesn't pass the error through which is imo helpful for users setting up the webhook and modifying could be a breaking change

@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 30, 2025
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 30, 2025
@Zaimwa9 Zaimwa9 requested a review from khvn26 April 30, 2025 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Issue related to the REST API feature New feature or request front-end Issue related to the React Front End Dashboard
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants