-
Notifications
You must be signed in to change notification settings - Fork 11
Events over webhooks example app #12
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
base: main
Are you sure you want to change the base?
Conversation
|
||
 | ||
|
||
> ℹ️ Events delivered by webhooks is in early access and unavailable by default. To use this example, please reach out to Benchling support to enable webhook delivery for events on your tenant. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Psterne @dgervais-benchling I didn't see any public docs on getting setup with Events over Webhooks. I also used "Events delivered by webhooks" as I feel like "events over webhooks" is our internal language and not necessarily intuitive to users.
Please suggest any alternative copy you'd like here or elsewhere in the README
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bowenwr Updated docs for Events (via Webhooks and Eventbridge) are WIP. We can link to it from here once those are live.
I agree that "Events delivered by webhooks" is better. At some point I'm hoping we can just call it "Webhooks"!
A database password can be created now. It can be any valid PostgreSQL password. One way to generate a random password for local development purposes on *nix systems might be: | ||
|
||
```bash | ||
date +%s | sha256sum | base64 | head -c 16 | cat > .database_password |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasonbenchling or @stash-benchling for sec eng (Jason previously reviewed the last example App). Wanted to be sure this was reasonable guidance.
I'd actually almost prefer to disable passwords for postgres for this local example only, but I figured it was better to:
- Have the user set some password, even if it's weak
- Store it securely (e.g., via Docker secrets)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not use the current time for a source of entropy on it's own. Stakes are low here, this is a test/sample app, but we don't want people YOLOing that pattern into production 😅
Since we assume the user has python
available (after all, it's a flask app), we can use a python one liner to simplify getting secure randomness. There's some bash gymnastics that need to be done to read directly from /dev/urandom
and get usable output, so better to take advantage of python's wrapper IMO.
Something like:
python -c "import secrets; print(secrets.token_hex(64))" > .database_password
would be preferable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasonbenchling Thanks for the quick response. The Flask App runs its own container where Python is installed. I tried this in my own local terminal (where python
is not installed globally) and it failed within my virtual environment 😅
I'm hoping to reduce the number of steps / requirements for developers, so I think the safest thing to do is just remove this "random" password generation and tell the user to edit in their own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bowenwr That seems reasonable -- tell the end user to "generate and store a secure random password" or something along those lines.
openssl
is another probably pretty common option to generate randomness, but that also might not always be present. Some of the internet suggested /dev/urandom
based methods that work on linux boxes don't work on my mac, so that's probably not a great portable option either.
### Benchling Prerequisites | ||
1. Access to a Benchling tenant, like `https://my-tenant.benchling.com` | ||
2. Ensure you've been granted access to the [Benchling Developer Platform Capability](https://help.benchling.com/hc/en-us/articles/9714802977805-Access-the-Benchling-Developer-Platform). | ||
3. This example also requires Events delivered by Webhooks to be enabled on your tenant. Reach out to Benchling support to find out more about participating in early access. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Psterne @dgervais-benchling similar to above, let me know if you want different copy to express this requirement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Is there a step 4 that's missing? Should Global Apps be step 4?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The number skipped 4 and went to 5. Fixed now, thanks for catching!
if isinstance(webhook.message, EventCreatedWebhookV0Beta): | ||
# Since we'll be receiving ALL entity registrations, add a filter to only | ||
# work with events that meet our criteria (e.g., specific schema) | ||
if _is_target_event(app, webhook.message): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@edwin-benchling This filtering logic is one of the main differences from the other example App that would merit closer review.
from local_app.lib.postgresql import write_data | ||
|
||
|
||
def sync_event_data(app: App, entity_registered: V2EntityRegisteredEventBeta) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@edwin-benchling This is the second file that's "new" and could use closer review.
logger = get_logger() | ||
|
||
|
||
def write_data(entity_name: str, benchling_api_id: str, field_value: str | None) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@edwin-benchling This is the third and final file that is new and merits closer review.
Although I don't wanna focus too hard on teaching users how to connect to PostgreSQL in Python and intentionally kept this light.
FYI @benchling/app-ecosystem for broad awareness. I tagged @edwin-benchling as DRI for engineering review, but feedback is welcome from everyone. |
echo.> .database_password | ||
``` | ||
|
||
A database password can be created now. It can be any valid PostgreSQL password. Generate and store a secure password in `.database_password`. For local development, you could open the file and manually enter any valid password. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasonbenchling Removed the "random generation" bash example and converted to general guidance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit re: pbpaste
, but other than that, looks good
|
||
```bash | ||
touch .client_secret | ||
pbpaste > .client_secret |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pbpaste
is just a MacOS thing, afaik
Ugh, we also have it in |
FYI @benchling/solutions-teams for an App code example using the new beta Events over Webhooks functionality. |
|
||
|
||
def _connection() -> psycopg2.extensions.connection: | ||
return psycopg2.connect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it'd be easier to build a Repository pattern and implement it with an in-memory database and leave it as an exercise to the reader to provide their own implementation of a durable database, especially if the point of the example code is not really about persistence...
but since this is already done, I don't feel as strongly.
|
||
|
||
class TestPostgreSql: | ||
# postgresql fixture provided by pytest-postgresql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I'm not sure why, but this fixture did not kick in when running tests locally. I'd just get errors like
> raise ExecutableMissingException(
f"Could not found {self.executable}. Is PostgreSQL server installed? "
f"Alternatively pg_config installed might be from different "
f"version that postgresql-server."
) from ex
E pytest_postgresql.exceptions.ExecutableMissingException: Could not found /usr/lib/postgresql/14/bin/pg_ctl. Is PostgreSQL server installed? Alternatively pg_config installed might be from different version that postgresql-server.
Adds an example app demonstrating receiving a data event from Benchling and writing the results to an external PostgreSQL database.
Copies many of the files and patterns (e.g.,
localtunnel
) fromexamples/chem-sync-local-flask
.