Skip to content

Commit 65744dd

Browse files
Danny ShinDanny Shin
authored andcommitted
completed localhost compatability
1 parent 62f1df1 commit 65744dd

File tree

6 files changed

+61
-56
lines changed

6 files changed

+61
-56
lines changed

examples/chem-sync-local-flask/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ COPY requirements.txt ./
77
RUN pip install -r requirements.txt
88
COPY ./local_app /src/local_app
99
WORKDIR /src/local_app
10-
CMD ["flask", "run", "--host", "0.0.0.0"]
10+
CMD ["flask", "run", "--host", "0.0.0.0", "--port", "5004"]

examples/chem-sync-local-flask/README.md

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This app is optimized as a minimal local development experience using [Docker](h
1616
> ⚠️ **Development Only**: This example is not meant to be copied into production as-is. There are additional deployment, scale, and security concerns that should be addressed before deploying an app based on this example to production.
1717
1818
It relies on a few other tools that will be installed for you within Docker containers:
19-
* [Cloudflare-tunnel](https://www.cloudflare.com/products/tunnel/) - expose a public webhook URL and forward the results locally. ⚠️ *Not for production or real data!*
19+
* [ngrok](https://ngrok.com/) - expose a public webhook URL and forward the results locally. ⚠️ *Not for production or real data!*
2020
* [Flask](https://flask.palletsprojects.com/) - A simple Python web application framework
2121

2222
## Getting Started
@@ -33,6 +33,23 @@ Windows example:
3333
echo.> .client_secret
3434
```
3535

36+
Create an `.env` file for Docker environment variables. *nix example:
37+
38+
```bash
39+
touch .env
40+
```
41+
42+
Windows example:
43+
44+
```cmd
45+
echo.> .env
46+
```
47+
48+
Then, add your `ngrok` auth token under the alias `NGROK_AUTHTOKEN` which can be found on [ngrok your-authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) like so:
49+
````
50+
NGROK_AUTHTOKEN=<YOUR_AUTH_TOKEN_HERE>
51+
````
52+
3653
Start Docker:
3754

3855
```bash
@@ -54,28 +71,18 @@ curl localhost:8000/health
5471

5572
If Flask is running, you should see `OK` printed.
5673

57-
Be sure to note the URL created for you by `cloudflare-tunnel`. The log line should look something like this:
58-
59-
```
60-
cloudflare-tunnel-1 | 2025-03-11T18:45:24Z INF +--------------------------------------------------------------------------------------------+
61-
cloudflare-tunnel-1 | 2025-03-11T18:45:24Z INF | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
62-
cloudflare-tunnel-1 | 2025-03-11T18:45:24Z INF | https://pioneer-rights-hardly-disorder.trycloudflare.com |
63-
cloudflare-tunnel-1 | 2025-03-11T18:45:24Z INF +--------------------------------------------------------------------------------------------+
64-
```
65-
66-
On *nix systems, you can easily obtain _just_ the URL via:
67-
68-
```
69-
docker compose logs cloudflare-tunnel | grep -o 'https://[^ ]*trycloudflare.com[^ ]*' | tail -n 1
74+
You can obtain the public facing url generated by `ngrok` with:
75+
```bash
76+
curl localhost:4040/api/tunnels
7077
```
7178

7279
Example Output:
80+
````
81+
{"tunnels":[{"name":"command_line","ID":<SOME_ID>>,"uri":"/api/tunnels/command_line","public_url":"https://your-public-url.ngrok-free.dev"
82+
...
83+
````
7384

74-
```
75-
https://pioneer-rights-hardly-disorder.trycloudflare.com
76-
```
77-
78-
> 💡 Don't forget to append `/1/webhooks`, making the full URL given to Benchling `https://pioneer-rights-hardly-disorder.trycloudflare.com/1/webhooks`
85+
> 💡 Don't forget to append `/1/webhooks`, making the full URL given to Benchling `https://your-public-url.ngrok-free.dev/1/webhooks`
7986
8087
## Setting Up Your App in Benchling
8188

@@ -98,17 +105,17 @@ When prompted to upload a file, select `manifest.yaml` and click "Create."
98105

99106
### Update the Webhook URL
100107

101-
Every time we restart the `cloudflare-tunnel` Docker container, it will provision
108+
Every time we restart the `ngrok` Docker container, it will provision
102109
a new public webhook URL.
103110

104111
Update the Benchling App's Webhook URL in the UI with the new server and
105112
append the path our Flask route expects (see `local_app/app.py`).
106113

107-
For example, if our `cloudflare-tunnel` generated URL is `https://pioneer-rights-hardly-disorder.trycloudflare.com`,
114+
For example, if our `ngrok` generated URL is `https://your-public-url.ngrok-free.dev`,
108115
the webhook URL in Benchling should be:
109116

110117
```
111-
https://https://pioneer-rights-hardly-disorder.trycloudflare.com/1/webhooks
118+
https://your-public-url.ngrok-free.dev/1/webhooks
112119
```
113120

114121
![image info](./docs/update-webhook-url.gif)
@@ -139,26 +146,15 @@ You'll then need to restart _just_ the `benchling-app` Docker service to pick up
139146
docker-compose up -d
140147
```
141148

142-
If you restart both containers, be sure to update your App in Benchling with the new webhook URL from cloudflare-tunnel.
149+
If you restart both containers, be sure to update your App in Benchling with the new webhook URL from ngrok.
143150

144151
### Setting Client ID
145152

146153
Our App needs a Client ID to pair with the Client Secret for authentication to Benchling. In this case, we've created our
147154
App to accept `CLIENT_ID` as an environment variable.
148155

149-
One easy way to set an environment variables for Docker is to add a `.env` file.
150-
151-
```bash
152-
touch .env
153-
```
154-
155-
Windows example:
156-
157-
```cmd
158-
echo.> .env
159-
```
160156

161-
Open it in an editor of your choice and set the values with the plaintext client ID
157+
Open the `.env` file in an editor of your choice and set the values with the plaintext client ID
162158
for your App. For example:
163159

164160
```

examples/chem-sync-local-flask/docker-compose.yaml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
services:
22
benchling-app:
33
build: .
4-
ports:
5-
- "8000:5000"
64
volumes:
75
- type: bind
86
source: .
@@ -21,16 +19,29 @@ services:
2119
- CLIENT_SECRET_FILE=/run/secrets/app_client_secret
2220
secrets:
2321
- app_client_secret
22+
network_mode: host
23+
2424

2525
# FOR LOCAL DEVELOPMENT ONLY!
2626
# Free tool for providing a public URL to forward webhooks to our Benchling App running locally
2727
# Do not do this in production or use with any sensitive data.
2828
# Benchling has not vetted this tool for use in production or in sensitive systems.
2929
# Conduct your own due diligence before choosing a tool for production use.
30-
cloudflare-tunnel:
31-
image: cloudflare/cloudflared
30+
tunnel:
31+
image: ngrok/ngrok:3.7.0-alpine
3232
restart: unless-stopped
33-
command: tunnel --url http://benchling-app:5000
33+
command: "http localhost:5004"
34+
volumes:
35+
# For Mac and Linux
36+
- type: bind
37+
source: /run/host-services/ssh-auth.sock
38+
target: /run/host-services/ssh-auth.sock
39+
environment:
40+
# For Mac and Linux
41+
- SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock
42+
# Note: You'll need to set NGROK_AUTHTOKEN as an environment variable, for example in .env
43+
- NGROK_AUTHTOKEN
44+
network_mode: host
3445

3546
secrets:
3647
app_client_secret:

examples/chem-sync-local-flask/local_app/app.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from threading import Thread
22

3-
from benchling_sdk.apps.helpers.webhook_helpers import verify
43
from flask import Flask, request
54

65
from local_app.benchling_app.handler import handle_webhook
7-
from local_app.benchling_app.setup import app_definition_id
6+
7+
# from local_app.benchling_app.setup import app_definition_id # noqa: ERA001
88
from local_app.lib.logger import get_logger
99

1010
logger = get_logger()
@@ -21,11 +21,12 @@ def health_check() -> tuple[str, int]:
2121
@app.route("/1/webhooks/<path:target>", methods=["POST"])
2222
def receive_webhooks(target: str) -> tuple[str, int]: # noqa: ARG001
2323
# For security, don't do anything else without first verifying the webhook
24-
app_def_id = app_definition_id()
24+
# app_def_id = app_definition_id() # noqa: ERA001
2525

2626
# Important! To verify webhooks, we need to pass the body as an unmodified string
2727
# Flask's request.data is bytes, so decode to string. Passing bytes or JSON won't work
28-
verify(app_def_id, request.data.decode("utf-8"), request.headers)
28+
# Disable when using local instance of Benchling Monolith
29+
# verify(app_def_id, request.data.decode("utf-8"), request.headers) # noqa: ERA001
2930

3031
logger.debug("Received webhook message: %s", request.json)
3132
# Dispatch work and ACK webhook as quickly as possible

examples/chem-sync-local-flask/local_app/benchling_app/setup.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from benchling_sdk.auth.client_credentials_oauth2 import ClientCredentialsOAuth2
77
from benchling_sdk.benchling import Benchling
88
from benchling_sdk.models.webhooks.v0 import WebhookEnvelopeV0
9+
from httpx import Client
910

1011

1112
def init_app_from_webhook(webhook: WebhookEnvelopeV0) -> App:
@@ -27,19 +28,21 @@ def app_definition_id() -> str:
2728

2829

2930
def _benchling_from_webhook(webhook: WebhookEnvelopeV0) -> Benchling:
30-
return Benchling(webhook.base_url, _auth_method())
31+
client = Client(verify=False) # noqa: S501
32+
return Benchling(webhook.base_url, _auth_method(), httpx_client=client)
3133

3234

3335
@cache
3436
def _auth_method() -> ClientCredentialsOAuth2:
3537
client_id = os.environ.get("CLIENT_ID")
3638
assert client_id is not None, "Missing CLIENT_ID from environment"
3739
client_secret = _client_secret_from_file()
38-
return ClientCredentialsOAuth2(client_id, client_secret)
40+
client = Client(verify=False) # noqa: S501
41+
return ClientCredentialsOAuth2(client_id, client_secret, httpx_client=client)
3942

4043

4144
def _client_secret_from_file() -> str:
4245
file_path = os.environ.get("CLIENT_SECRET_FILE")
4346
assert file_path is not None, "Missing CLIENT_SECRET_FILE from environment"
4447
with Path(file_path).open() as f:
45-
return f.read()
48+
return f.read().strip()

examples/chem-sync-local-flask/tests/unit/local_app/test_app.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,8 @@ def client(app: Flask) -> FlaskClient:
2828
class TestApp:
2929

3030
@patch("local_app.app._enqueue_work")
31-
@patch("local_app.app.app_definition_id")
32-
@patch("local_app.app.verify")
33-
def test_app_receive_webhook(
34-
self, mock_verify, mock_app_definition_id, mock_enqueue_work, client,
35-
) -> None:
31+
def test_app_receive_webhook(self, mock_verify_app_installation, client) -> None:
3632
webhook = load_webhook_json(_TEST_FILES_PATH / "canvas_initialize_webhook.json")
3733
response = client.post("1/webhooks/canvas", json=webhook.to_dict())
3834
assert response.status_code == 200
39-
mock_verify.assert_called_once()
40-
mock_app_definition_id.assert_called_once()
41-
mock_enqueue_work.assert_called_once()
35+
mock_verify_app_installation.assert_called_once()

0 commit comments

Comments
 (0)