Skip to content

Commit 95091e1

Browse files
awolfdenAdam Wolfman
andauthored
Add webhooks visualization feature (#7)
* Wire UI for webhooks view template * Add webhooks visualization feature * Update home template with correct framework Co-authored-by: Adam Wolfman <[email protected]>
1 parent eaaf490 commit 95091e1

File tree

8 files changed

+162
-16
lines changed

8 files changed

+162
-16
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ If you get stuck, please reach out to us at [email protected] so we can help.
126126
- The `/groups` URL corresponds to the WorkOS API's [List Directory Groups endpoint](https://workos.com/docs/reference/directory-sync/group/list)
127127
- 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).
128128

129+
130+
13. 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.
131+
129132
## Need help?
130133

131134
When you clone this repo, the `DEBUG` setting is `False` by default in `workos_django/settings.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. You may also leave the `DEBUG` setting as `False` and connect with WorkOS if you include the `--insecure` flag as referenced in step 11.

python-django-directory-sync-example/directory_sync/static/css/main.css

Lines changed: 30 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;
@@ -80,6 +96,12 @@ body {
8096
color: white;
8197
}
8298

99+
.button_outline {
100+
background-color: #ffffff;
101+
color: #6363f1;
102+
border-color: #6363f1;
103+
}
104+
83105
.login_button {
84106
width: 95%;
85107
}
@@ -170,4 +192,12 @@ div.text_box {
170192

171193
.logged_in_nav img:hover {
172194
border: 2px solid #555555;
195+
}
196+
197+
.webhooks_container {
198+
width: 45vw;
199+
background-color: #dad8d8;
200+
padding: 25px;
201+
max-height: 700px;
202+
overflow-y: scroll;
173203
}

python-django-directory-sync-example/directory_sync/templates/directory_sync/home.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ <h1>WorkOS</h1>
1414
</div>
1515
</div>
1616

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

python-django-directory-sync-example/directory_sync/views.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
from django.conf import settings
66
from django.shortcuts import render
77
from django.views.decorators.csrf import csrf_exempt
8+
import socketio
9+
10+
from workos_django.settings import BASE_DIR
11+
12+
basedir = BASE_DIR
13+
sio = socketio.Server()
14+
thread = None
815

916
workos.api_key = os.getenv('WORKOS_API_KEY')
1017
workos.base_api_url = 'http://localhost:8000/' if settings.DEBUG else workos.base_api_url
11-
directory_id = os.getenv('DIRECTORY_ID') # Follow the WorkOS guide to get this
18+
19+
1220

1321
def get_home(request):
14-
print('request', request)
1522
directories = workos.client.directory_sync.list_directories()
1623
directories = directories['data']
1724
return render(request, 'directory_sync/home.html', {"directories": directories})
@@ -34,16 +41,18 @@ def get_directory_groups(request):
3441

3542
@csrf_exempt
3643
def webhooks(request):
37-
dict_payload = json.loads(request.body)
38-
payload = json.dumps(dict_payload)
39-
sig_header = request.headers.get('WorkOS-Signature')
44+
if request.body:
45+
dict_payload = json.loads(request.body)
46+
payload = json.dumps(dict_payload)
47+
sig_header = request.headers.get('WorkOS-Signature')
4048

41-
response = workos.client.webhooks.verify_event(
42-
payload = payload,
43-
sig_header = sig_header,
44-
secret = os.getenv('WEBHOOKS_SECRET')
45-
)
49+
response = workos.client.webhooks.verify_event(
50+
payload = payload,
51+
sig_header = sig_header,
52+
secret = os.getenv('WEBHOOKS_SECRET')
53+
)
4654

47-
print(response)
55+
message = json.dumps(response)
56+
sio.emit('webhook_received', message)
4857

49-
return HttpResponse(200)
58+
return render(request, 'directory_sync/webhooks.html')

python-django-directory-sync-example/requirements.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ pytz==2021.1
77
requests==2.25.1
88
sqlparse==0.4.2
99
urllib3==1.26.5
10-
workos==1.5.1
11-
python-dotenv
10+
workos==1.6.0
11+
python-dotenv
12+
enum-compat==0.0.3
13+
eventlet==0.30.0
14+
greenlet==0.4.17
15+
python-engineio
16+
python-socketio
17+
six==1.10.0

python-django-directory-sync-example/workos_django/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
# Build paths inside the project like this: BASE_DIR / 'subdir'.
1616
BASE_DIR = Path(__file__).resolve().parent.parent
1717

18+
import mimetypes
19+
mimetypes.add_type("text/css", ".css", True)
1820

1921
# Quick-start development settings - unsuitable for production
2022
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
@@ -119,4 +121,6 @@
119121
# https://docs.djangoproject.com/en/3.1/howto/static-files/
120122

121123
STATIC_URL = '/static/'
124+
122125
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
126+

python-django-directory-sync-example/workos_django/wsgi.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,18 @@
1010
import os
1111

1212
from django.core.wsgi import get_wsgi_application
13+
from django.contrib.staticfiles.handlers import StaticFilesHandler
1314

1415
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'workos_django.settings')
1516

16-
application = get_wsgi_application()
17+
from socketio import WSGIApp
18+
from directory_sync.views import sio
19+
20+
21+
directory_sync = StaticFilesHandler(get_wsgi_application())
22+
23+
application = WSGIApp(sio, directory_sync)
24+
25+
import eventlet
26+
import eventlet.wsgi
27+
eventlet.wsgi.server(eventlet.listen(('', 8000)), application)

0 commit comments

Comments
 (0)