Skip to content

Commit c02dc3a

Browse files
awolfdenAdam Wolfman
andauthored
Add view to visualize test webhooks with websockets (#4)
* Add view for webhooks visualization * Remove socket log on receipt * Update webhooks javascript and README Co-authored-by: Adam Wolfman <[email protected]>
1 parent 9c4f356 commit c02dc3a

File tree

6 files changed

+154
-17
lines changed

6 files changed

+154
-17
lines changed

python-flask-directory-sync-example/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,23 @@ If you get stuck, please reach out to us at [email protected] so we can help.
8484

8585
## Start the server
8686

87-
1. Use this command to run the app:
87+
10. Use this command to run the app:
8888
```bash
8989
flask run
9090
```
9191

92-
2. Once the server is running, navigate to http://localhost:5000 to view the home page of the app where you can then select the view for users or groups.
92+
11. Once the server is running, navigate to http://localhost:5000 to view the home page of the app where you can then select the view for users or groups.
9393

9494
- The `/users` URL corresponds to the WorkOS API's [List Directory Users endpoint](https://workos.com/docs/reference/directory-sync/user/list)
9595
- The `/groups` URL corresponds to the WorkOS API's [List Directory Groups endpoint](https://workos.com/docs/reference/directory-sync/group/list)
9696
- You can extend this Django example app by adding views to `directory_sync/views.py` for the other available [Directory Sync API endpoints](https://workos.com/docs/reference/directory-sync).
9797

98+
99+
## Test Webhooks
100+
101+
12. WorkOS sends Webhooks as a way of managing updates to Directory Sync connections. The Webhooks section of the WorkOS Dashboard allows you to send test webhooks to your application. The Test Webhooks section of this application allows you to visualize the validated webhooks directly in this application in real-time. [Please review the tutorial here](https://workos.com/blog/test-workos-webhooks-locally-ngrok) for details on how this can be done locally.
102+
103+
98104
## Need help?
99105

100106
When you clone this repo, the `DEBUG` setting is `False` by default in `app.py`. You can set `DEBUG = True` if you need to troubleshoot something during the tutorial, but you must use `DEBUG = False` in order to successfully connect to the WorkOS API.

python-flask-directory-sync-example/app.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
import os
2-
from flask import (Flask, render_template, request, Response)
2+
from flask import (Flask, render_template, request)
33
import workos
44
from workos import client as workos_client
5+
from flask_socketio import (SocketIO, emit)
6+
import json
7+
58

69
DEBUG = False
710
app = Flask(__name__)
811

12+
13+
app.config['SECRET_KEY'] = 'secret!'
14+
socketio = SocketIO(app)
15+
16+
if __name__ == '__main__':
17+
socketio.run(app)
18+
919
workos.api_key = os.getenv('WORKOS_API_KEY')
1020
workos.base_api_url = 'http://localhost:5000/' if DEBUG else workos.base_api_url
1121
directory_id = os.getenv('DIRECTORY_ID')
@@ -42,19 +52,20 @@ def directory_groups():
4252

4353
return render_template('groups.html', groups=groups)
4454

45-
@app.route('/webhooks', methods=['POST'])
55+
@app.route('/webhooks', methods=['GET', 'POST'])
4656
def webhooks():
4757
print(request)
48-
payload = request.get_data()
49-
sig_header = request.headers['WorkOS-Signature']
50-
51-
response = workos_client.webhooks.verify_event(
52-
payload = payload,
53-
sig_header = sig_header,
54-
secret = os.getenv('WEBHOOKS_SECRET')
55-
)
56-
# Validate the response is successful
57-
print(response)
58+
if request.data:
59+
payload = request.get_data()
60+
sig_header = request.headers['WorkOS-Signature']
61+
response = workos_client.webhooks.verify_event(
62+
payload = payload,
63+
sig_header = sig_header,
64+
secret = os.getenv('WEBHOOKS_SECRET')
65+
)
66+
67+
message = json.dumps(response)
68+
socketio.emit('webhook_received', message)
5869

5970
# Return a 200 to prevent retries based on validation
60-
return Response(status=200)
71+
return render_template('webhooks.html')
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

22
Flask>=1.1.2
33
workos>=0.3.2
4-
python-dotenv
4+
python-dotenv
5+
flask_socketio

python-flask-directory-sync-example/static/home.css

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ body {
3434
align-items: center;
3535
}
3636

37+
.flex_right {
38+
justify-content: flex-end;
39+
}
40+
41+
.flex_space_between {
42+
justify-content: space-between;
43+
}
44+
45+
.hidden {
46+
display: none;
47+
}
48+
49+
.margin_top {
50+
margin-top: 15px;
51+
}
52+
3753
.container_success {
3854
display: flex;
3955
flex-direction: column;
@@ -73,6 +89,12 @@ body {
7389
transition-duration: 0.4s;
7490
cursor: pointer;
7591
}
92+
93+
.button_outline {
94+
background-color: #ffffff;
95+
color: #6363f1;
96+
border-color: #6363f1;
97+
}
7698

7799
.button:hover {
78100
background-color: #555555;
@@ -84,11 +106,19 @@ body {
84106
width: 95%;
85107
}
86108

109+
.half_width {
110+
width: 50%;
111+
}
112+
87113
.directory_button {
88114
width: 40%;
89115
margin-top: 20px;
90116
}
91117

118+
.directory_container {
119+
width: 48vw;
120+
}
121+
92122
h2, h1 {
93123
text-align: center;
94124
color: #555555;
@@ -175,4 +205,13 @@ div.text_box {
175205

176206
.logged_in_nav img:hover {
177207
border: 2px solid #555555;
208+
}
209+
210+
.webhooks_container {
211+
width: 45vw;
212+
background-color: #dad8d8;
213+
padding: 25px;
214+
max-height: 700px;
215+
overflow-y: scroll;
216+
178217
}

python-flask-directory-sync-example/templates/home.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ <h1>WorkOS</h1>
1313
</div>
1414

1515
<h2>Python Flask Directory Sync Example App</h2>
16-
16+
<hr style="width:100%">
17+
<a href="/webhooks" class="button login_button button_outline">Test Webhooks</a>
18+
<hr style="width:100%">
1719
{% for i in directories.data %}
1820
<a class="button login_button" href="/directory?id={{i['id']}}">{{ i['name'] }}</a>
1921
{% endfor %}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" href="../static/home.css">
4+
</head>
5+
<body class="container_success">
6+
<div class="logged_in_nav">
7+
<div class="flex">
8+
<p>Webhooks</p>
9+
</div>
10+
<div>
11+
<img src="../static/images/workos_logo_new.png" alt="workos logo">
12+
</div>
13+
</div>
14+
<div class='flex'>
15+
<div class="logged_in_div_left">
16+
<div>
17+
<h1>Your app,</h1>
18+
<h2>Enterprise Ready</h2>
19+
</div>
20+
<div>
21+
<a href="https://workos.com/" target="_blank"><button class='button'>WorkOS</button></a>
22+
<a href="https://workos.com/docs" target="_blank"><button class='button'>Documentation</button></a>
23+
<a href="https://workos.com/docs/reference" target="_blank"><button class='button'>API Reference</button></a>
24+
<a href="https://workos.com/blog" target="_blank"><button class='button'>Blog</button></a>
25+
<a href="{{ url_for('home') }}"><button class='button'>Home</button></a>
26+
</div>
27+
</div>
28+
<div class="logged_in_div_right">
29+
<div class="flex_column">
30+
<h2>Webhooks</h2>
31+
<div id="webhooks-view">
32+
33+
</div>
34+
</div>
35+
<div id='clear_button_div' class='flex directory_container'>
36+
<div id="tutorial_button" class="flex half_width">
37+
<a href="https://workos.com/blog/test-workos-webhooks-locally-ngrok" target="_blank" class="button login_button">Tutorial</a>
38+
</div>
39+
<div id="clear_button" class="hidden">
40+
<a href="/webhooks" class="button button_outline">Clear</a>
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
46+
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
47+
<script type="text/javascript" charset="utf-8">
48+
var counter = 0
49+
var webhooksView = document.getElementById('webhooks-view')
50+
var clearButtonDiv = document.getElementById('clear_button_div')
51+
var clearButton = document.getElementById('clear_button')
52+
var tutorialButton = document.getElementById('tutorial_button')
53+
54+
var socket = io("http://localhost:5000")
55+
socket.on('connect', function() {
56+
console.log('socket connection successful')
57+
})
58+
59+
socket.on('webhook_received', (data) => {
60+
webhooksView.classList.add("webhooks_container")
61+
webhooksView.insertAdjacentHTML("afterbegin",
62+
63+
"<div class='margin_top'> Webhook received at: " + new Date().toISOString() + "</div>" +
64+
"<br/>" +
65+
"<code>" + data + "</code>" +
66+
"<br/>"
67+
);
68+
if (counter < 1) {
69+
clearButtonDiv.classList.remove('login_button')
70+
clearButtonDiv.classList.add('flex_right')
71+
clearButton.classList.remove('hidden')
72+
tutorialButton.classList.add('hidden')
73+
counter ++
74+
}
75+
})
76+
</script>
77+
</body>
78+
</html>

0 commit comments

Comments
 (0)