Skip to content

Commit b0079eb

Browse files
Add webhook example
1 parent 66a9436 commit b0079eb

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed
3.75 MB
Loading
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# HTTP Callbacks (a.k.a. Webhooks)
2+
![demo.gif](.github/demo.gif)
3+
4+
```bash
5+
pip install -r requirements.txt
6+
./main.py
7+
```
8+
9+
In this example we are creating a HTTP server that has two routes:
10+
1. `GET /` - triggers the creation of submission on Judge0
11+
2. `PUT /callback` - called by Judge0 once the submission is done
12+
13+
When you run the HTTP server you should visit `http://localhost:8000` and that will create a new submission on Judge0. This submission will have a `callback_url` attribute set which Judge0 will call `PUT` HTTP verb once the submission is done.
14+
15+
For this to work the `callback_url` must be publicly accessible, so in this example we are using a free service https://localhost.run that allows us to expose your local HTTP server and make it available on the internet and accessible by Judge0.
16+
17+
Once the submission is done Judge0 will call our second route `PUT /callback` and in your terminal you should see the result.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python3
2+
from fastapi import FastAPI, Depends
3+
from pydantic import BaseModel
4+
5+
import uvicorn
6+
import asyncio
7+
import judge0
8+
9+
10+
class CallbackResponse(BaseModel):
11+
created_at: str
12+
finished_at: str
13+
language: dict
14+
status: dict
15+
stdout: str
16+
17+
18+
class AppContext:
19+
def __init__(self):
20+
self.public_url = ""
21+
22+
23+
LOCAL_SERVER_PORT = 8000
24+
25+
app = FastAPI()
26+
app_context = AppContext()
27+
28+
29+
def get_app_context():
30+
return app_context
31+
32+
33+
@app.get("/")
34+
async def root(app_context=Depends(get_app_context)):
35+
if not app_context.public_url:
36+
return {
37+
"message": "Public URL is not available yet. Try again after a few seconds."
38+
}
39+
40+
submission = judge0.Submission(
41+
source_code="print('Hello, World!')",
42+
language_id=judge0.PYTHON,
43+
callback_url=f"{app_context.public_url}/callback",
44+
)
45+
46+
return judge0.async_execute(submissions=submission)
47+
48+
49+
@app.put("/callback")
50+
async def callback(response: CallbackResponse):
51+
print(f"Received: {response}")
52+
53+
54+
# We are using free service from https://localhost.run to get a public URL for our local server.
55+
# This approach is not recommended for production use. It is only for demonstration purposes
56+
# since domain names change regularly and there is a speed limit for the free service.
57+
async def run_ssh_tunnel():
58+
app_context = get_app_context()
59+
60+
command = [
61+
"ssh",
62+
"-o",
63+
"StrictHostKeyChecking=no",
64+
"-o",
65+
"ServerAliveInterval=30",
66+
"-R",
67+
f"80:localhost:{LOCAL_SERVER_PORT}",
68+
"localhost.run",
69+
]
70+
71+
process = await asyncio.create_subprocess_exec(
72+
*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT
73+
)
74+
75+
while True:
76+
line = await process.stdout.readline()
77+
if not line:
78+
break
79+
80+
decoded_line = line.decode().strip()
81+
if decoded_line.endswith(".lhr.life"):
82+
app_context.public_url = decoded_line.split()[-1].strip()
83+
84+
await process.wait()
85+
86+
87+
async def run_server():
88+
config = uvicorn.Config(
89+
app, host="127.0.0.1", port=LOCAL_SERVER_PORT, workers=5, loop="asyncio"
90+
)
91+
server = uvicorn.Server(config)
92+
await server.serve()
93+
94+
95+
async def main():
96+
await asyncio.gather(run_ssh_tunnel(), run_server())
97+
98+
99+
if __name__ == "__main__":
100+
asyncio.run(main())
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fastapi==0.115.4
2+
uvicorn==0.32.0

0 commit comments

Comments
 (0)