From 7224af964124af9cecc804940782d8f9dae74593 Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:17:14 -0800 Subject: [PATCH 1/7] Update README.md --- examples/chem-sync-local-flask/README.md | 31 ++++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/chem-sync-local-flask/README.md b/examples/chem-sync-local-flask/README.md index ccd806a..4bfd896 100644 --- a/examples/chem-sync-local-flask/README.md +++ b/examples/chem-sync-local-flask/README.md @@ -1,12 +1,13 @@ # Benchling App Example: Chemical Sync for Local Development -An example Benchling App written in Python which allows users to search for chemicals +An example Benchling App written in Python which allows users to search for chemicals via [PubChem](https://pubchem.ncbi.nlm.nih.gov/) and create them in Benchling. ![image info](./docs/demo-full.gif) _The App features branching flows and will also validate user inputs._ **Appendices**: + * [Architecture Diagram](#architecture-diagram) ## Technical Prerequisites @@ -16,7 +17,8 @@ This app is optimized as a minimal local development experience using [Docker](h > ⚠️ **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. It relies on a few other tools that will be installed for you within Docker containers: -* [Localtunnel](https://localtunnel.me/) - expose a public webhook URL and forward the results locally. ⚠️ *Not for production or real data!* + +* [Localtunnel](https://localtunnel.me/) - expose a public webhook URL and forward the results locally. ⚠️ _Not for production or real data!_ * [Flask](https://flask.palletsprojects.com/) - A simple Python web application framework ## Getting Started @@ -42,10 +44,9 @@ docker compose up --build -d Tip: You can omit the `-d` option if you want to run in the foreground. Otherwise, use `docker compose logs -f` to tail logs. > ℹ️ **Windows Note 1:** "Use ContainerD for pulling and storing images" may need to be enabled in `Docker > Settings > Features in development > Beta Features` - +> > ℹ️ **Windows Note 2**: If running into an error like "ERROR: request returned Bad Gateway for API route and version", [this solution](https://github.com/docker/for-mac/issues/6956#issuecomment-1876444658) may fix the problem. - You can verify that Flask is up and running: ```bash @@ -56,19 +57,19 @@ If Flask is running, you should see `OK` printed. Be sure to note the URL created for you by `localtunnel`. The log line should look something like this: -``` +```bash app-workshop-local-tunnel-1 | your url is: https://brave-wombats-poke.loca.lt ``` On *nix systems, you can easily obtain _just_ the URL via: -``` +```bash docker compose logs local-tunnel | grep -o https://.* | tail -n 1 ``` Example Output: -``` +```bash https://brave-wombats-poke.loca.lt ``` @@ -77,6 +78,7 @@ https://brave-wombats-poke.loca.lt ## Setting Up Your App in Benchling ### 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. [Optional] If you'd like to render the App's UI in a Run, you'll need a [Benchling Connect](https://www.benchling.com/connect) license. @@ -104,7 +106,7 @@ append the path our Flask route expects (see `local_app/app.py`). For example, if our `localtunnel` generated URL is `https://hot-ideas-doubt.loca.lt`, the webhook URL in Benchling should be: -``` +```string https://hot-ideas-doubt.loca.lt/1/webhooks ``` @@ -140,7 +142,7 @@ If you restart both containers, be sure to update your App in Benchling with the ### Setting Client ID -Our App needs a Client ID to pair with the Client Secret for authentication to Benchling. In this case, we've created our +Our App needs a Client ID to pair with the Client Secret for authentication to Benchling. In this case, we've created our App to accept `CLIENT_ID` as an environment variable. One easy way to set an environment variables for Docker is to add a `.env` file. @@ -155,10 +157,10 @@ Windows example: echo.> .env ``` -Open it in an editor of your choice and set the values with the plaintext client ID +Open it in an editor of your choice and set the values with the plaintext client ID for your App. For example: -``` +```bash CLIENT_ID=Ts7jtwPohM ``` @@ -172,7 +174,7 @@ The App definition ID is available from the Developer Console by selecting the A Add it to your `.env` file with a variable name `APP_DEFINITION_ID`. The contents of your `.env` file should now look something like: -``` +```bash CLIENT_ID=Ts7jtwPohM APP_DEFINITION_ID=appdef_Trow4zbR3o ``` @@ -193,11 +195,13 @@ docker-compose up -d If you examine the `configuration` section of `manifest.yaml`, you'll see our App expects a few configuration items: + 1. A folder 2. A molecule entity schema with two decimal fields We declare two `features` in the `manifest.yaml` so that our App can render its UI as a `CANVAS` (e.g. within the Notebook) or on an `ASSAY_RUN`. If you'd like to use a Run, we'll also need: + 1. An Lab Automation run schema #### Folder @@ -233,6 +237,7 @@ App Configuration gives us a stable code contract for referencing data mapped in The values of the data in Benchling can then be changed without updating App code. Let's update our configuration to: + 1. Specify a folder for syncing sequences 2. Link a molecule schema and fields for the synced chemicals 3. [Optional] If using a Run, select an assay run schema to associate with our Benchling App @@ -269,4 +274,4 @@ Let's grant some access by adding the Benchling App to an organization. ### Architecture Diagram -![image info](./docs/architecture-diagram.png) \ No newline at end of file +![image info](./docs/architecture-diagram.png) From fd76f40bb2bb3dea2814d4bb837993ff3c07868d Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:01:09 -0800 Subject: [PATCH 2/7] version is obsolete --- examples/chem-sync-local-flask/docker-compose.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/chem-sync-local-flask/docker-compose.yaml b/examples/chem-sync-local-flask/docker-compose.yaml index 048b3d3..52cd510 100644 --- a/examples/chem-sync-local-flask/docker-compose.yaml +++ b/examples/chem-sync-local-flask/docker-compose.yaml @@ -1,5 +1,3 @@ -version: "3.8" - services: benchling-app: build: . From e7bc1db6880f2e4a5b306f39e769b320337d03fe Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Sat, 8 Mar 2025 10:19:53 -0800 Subject: [PATCH 3/7] Cannot use Molecule entity schema --- examples/chem-sync-local-flask/README.md | 15 +++++++++++---- examples/chem-sync-local-flask/manifest.yaml | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/chem-sync-local-flask/README.md b/examples/chem-sync-local-flask/README.md index 4bfd896..2098077 100644 --- a/examples/chem-sync-local-flask/README.md +++ b/examples/chem-sync-local-flask/README.md @@ -1,7 +1,8 @@ # Benchling App Example: Chemical Sync for Local Development -An example Benchling App written in Python which allows users to search for chemicals -via [PubChem](https://pubchem.ncbi.nlm.nih.gov/) and create them in Benchling. +An example Benchling App written in Python which allows users to search for +chemicals via [PubChem](https://pubchem.ncbi.nlm.nih.gov/) and create them in +Benchling. ![image info](./docs/demo-full.gif) _The App features branching flows and will also validate user inputs._ @@ -12,7 +13,8 @@ _The App features branching flows and will also validate user inputs._ ## Technical Prerequisites -This app is optimized as a minimal local development experience using [Docker](https://www.docker.com/) for reproducibility. +This app is optimized as a minimal local development experience using +[Docker](https://www.docker.com/) for reproducibility. > ⚠️ **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. @@ -21,8 +23,13 @@ It relies on a few other tools that will be installed for you within Docker cont * [Localtunnel](https://localtunnel.me/) - expose a public webhook URL and forward the results locally. ⚠️ _Not for production or real data!_ * [Flask](https://flask.palletsprojects.com/) - A simple Python web application framework + ## Getting Started +TODO: Create the app and set the envars first +TODO: Cannot use Molecule entity schema in default install. +Use 'mixture' instead. + Create an empty placeholder file for Docker secrets. *nix example: ```bash @@ -64,7 +71,7 @@ app-workshop-local-tunnel-1 | your url is: https://brave-wombats-poke.loca.lt On *nix systems, you can easily obtain _just_ the URL via: ```bash -docker compose logs local-tunnel | grep -o https://.* | tail -n 1 +docker compose logs local-tunnel | grep -o "https://.*" ``` Example Output: diff --git a/examples/chem-sync-local-flask/manifest.yaml b/examples/chem-sync-local-flask/manifest.yaml index f7953e3..4308d37 100644 --- a/examples/chem-sync-local-flask/manifest.yaml +++ b/examples/chem-sync-local-flask/manifest.yaml @@ -2,7 +2,7 @@ manifestVersion: 1 info: name: Sample Sync App - version: 0.1.0 + version: 0.1.1 features: - name: Sync Step id: sync_step @@ -23,7 +23,7 @@ configuration: requiredConfig: true - name: Molecule Schema type: entity_schema - subtype: molecule + subtype: mixture description: A chemical schema for molecules synced from PubChem. requiredConfig: true fieldDefinitions: From 208f5bfcd4472c09ba3e1b322f88c5f54aaac4b1 Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:53:17 -0700 Subject: [PATCH 4/7] molecule now works --- examples/chem-sync-local-flask/README.md | 2 -- examples/chem-sync-local-flask/manifest.yaml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/chem-sync-local-flask/README.md b/examples/chem-sync-local-flask/README.md index 2098077..b338ce6 100644 --- a/examples/chem-sync-local-flask/README.md +++ b/examples/chem-sync-local-flask/README.md @@ -27,8 +27,6 @@ It relies on a few other tools that will be installed for you within Docker cont ## Getting Started TODO: Create the app and set the envars first -TODO: Cannot use Molecule entity schema in default install. -Use 'mixture' instead. Create an empty placeholder file for Docker secrets. *nix example: diff --git a/examples/chem-sync-local-flask/manifest.yaml b/examples/chem-sync-local-flask/manifest.yaml index 4308d37..ae67b8f 100644 --- a/examples/chem-sync-local-flask/manifest.yaml +++ b/examples/chem-sync-local-flask/manifest.yaml @@ -23,7 +23,7 @@ configuration: requiredConfig: true - name: Molecule Schema type: entity_schema - subtype: mixture + subtype: molecule description: A chemical schema for molecules synced from PubChem. requiredConfig: true fieldDefinitions: From 50594994737b2e086fd1e7d98caf267699e48aa2 Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:54:49 -0700 Subject: [PATCH 5/7] bump version --- examples/chem-sync-local-flask/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/chem-sync-local-flask/manifest.yaml b/examples/chem-sync-local-flask/manifest.yaml index ae67b8f..d39b0e8 100644 --- a/examples/chem-sync-local-flask/manifest.yaml +++ b/examples/chem-sync-local-flask/manifest.yaml @@ -2,7 +2,7 @@ manifestVersion: 1 info: name: Sample Sync App - version: 0.1.1 + version: 0.1.2 features: - name: Sync Step id: sync_step From 4040aafd77d187544069bcef6388fcafee7f419f Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar" <19791+drernie@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:01:26 -0700 Subject: [PATCH 6/7] wrap README --- examples/chem-sync-local-flask/README.md | 118 +++++++++++++++-------- 1 file changed, 76 insertions(+), 42 deletions(-) diff --git a/examples/chem-sync-local-flask/README.md b/examples/chem-sync-local-flask/README.md index b338ce6..d04d15d 100644 --- a/examples/chem-sync-local-flask/README.md +++ b/examples/chem-sync-local-flask/README.md @@ -16,13 +16,18 @@ _The App features branching flows and will also validate user inputs._ This app is optimized as a minimal local development experience using [Docker](https://www.docker.com/) for reproducibility. -> ⚠️ **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. +> ⚠️ **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. -It relies on a few other tools that will be installed for you within Docker containers: - -* [Localtunnel](https://localtunnel.me/) - expose a public webhook URL and forward the results locally. ⚠️ _Not for production or real data!_ -* [Flask](https://flask.palletsprojects.com/) - A simple Python web application framework +It relies on a few other tools that will be installed for you within Docker +containers: +* [Localtunnel](https://localtunnel.me/) - expose a public webhook URL and + forward the results locally. ⚠️ _Not for production or real data!_ +* [Flask](https://flask.palletsprojects.com/) - A simple Python web application + framework ## Getting Started @@ -46,11 +51,17 @@ Start Docker: docker compose up --build -d ``` -Tip: You can omit the `-d` option if you want to run in the foreground. Otherwise, use `docker compose logs -f` to tail logs. +Tip: You can omit the `-d` option if you want to run in the foreground. +Otherwise, use `docker compose logs -f` to tail logs. + +> ℹ️ **Windows Note 1:** "Use ContainerD for pulling and storing images" may +> need to be enabled in `Docker > Settings > Features in development > Beta +> Features` -> ℹ️ **Windows Note 1:** "Use ContainerD for pulling and storing images" may need to be enabled in `Docker > Settings > Features in development > Beta Features` -> -> ℹ️ **Windows Note 2**: If running into an error like "ERROR: request returned Bad Gateway for API route and version", [this solution](https://github.com/docker/for-mac/issues/6956#issuecomment-1876444658) may fix the problem. +> ℹ️ **Windows Note 2**: If running into an error like "ERROR: request returned +> Bad Gateway for API route and version", [this +> solution](https://github.com/docker/for-mac/issues/6956#issuecomment-1876444658) +> may fix the problem. You can verify that Flask is up and running: @@ -60,7 +71,8 @@ curl localhost:8000/health If Flask is running, you should see `OK` printed. -Be sure to note the URL created for you by `localtunnel`. The log line should look something like this: +Be sure to note the URL created for you by `localtunnel`. The log line should +look something like this: ```bash app-workshop-local-tunnel-1 | your url is: https://brave-wombats-poke.loca.lt @@ -78,21 +90,28 @@ Example Output: https://brave-wombats-poke.loca.lt ``` -> 💡 Don't forget to append `/1/webhooks`, making the full URL given to Benchling `https://brave-wombats-poke.loca.lt/1/webhooks` +> 💡 Don't forget to append `/1/webhooks`, making the full URL given to +> Benchling `https://brave-wombats-poke.loca.lt/1/webhooks` ## Setting Up Your App in Benchling ### 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. [Optional] If you'd like to render the App's UI in a Run, you'll need a [Benchling Connect](https://www.benchling.com/connect) license. -4. [Molecule entities](https://help.benchling.com/hc/en-us/articles/9684254682893-Molecule-entity-overview) will need to be enabled on your tenant. -5. [Global Apps](https://docs.benchling.com/docs/global-apps-faq) will need to be enabled on your tenant. +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. [Optional] If you'd like to render the App's UI in a Run, you'll need a + [Benchling Connect](https://www.benchling.com/connect) license. +4. [Molecule + entities](https://help.benchling.com/hc/en-us/articles/9684254682893-Molecule-entity-overview) + will need to be enabled on your tenant. +5. [Global Apps](https://docs.benchling.com/docs/global-apps-faq) will need to + be enabled on your tenant. ### Upload the App Manifest -Click the user icon in the bottom left corner to bring up the main menu. Select "Feature Settings" > "Developer Console" +Click the user icon in the bottom left corner to bring up the main menu. Select +"Feature Settings" > "Developer Console" Next, click the "Create app" button and choose "From manifest." @@ -108,8 +127,8 @@ a new public webhook URL. Update the Benchling App's Webhook URL in the UI with the new server and append the path our Flask route expects (see `local_app/app.py`). -For example, if our `localtunnel` generated URL is `https://hot-ideas-doubt.loca.lt`, -the webhook URL in Benchling should be: +For example, if our `localtunnel` generated URL is +`https://hot-ideas-doubt.loca.lt`, the webhook URL in Benchling should be: ```string https://hot-ideas-doubt.loca.lt/1/webhooks @@ -124,31 +143,35 @@ Generate a client secret in Benchling and be sure to copy the secret. ![image info](./docs/generate-secret.gif) Since the client secret is sensitive, it's handled a bit differently. It's -registered as a `secret` in our `docker-compose.yaml` file, which will be looking -for a file `./client_secret`. +registered as a `secret` in our `docker-compose.yaml` file, which will be +looking for a file `./client_secret`. -We can create this file and paste in the secret plaintext value if we have the secret in our clipboard. -On *nix: +We can create this file and paste in the secret plaintext value if we have the +secret in our clipboard. On *nix: ```bash touch .client_secret pbpaste > .client_secret ``` -> ⚠️ **Security Note:** Be sure to avoid committing `.client_secret` to a source code repository. +> ⚠️ **Security Note:** Be sure to avoid committing `.client_secret` to a source +> code repository. -You'll then need to restart _just_ the `benchling-app` Docker service to pick up the changes: +You'll then need to restart _just_ the `benchling-app` Docker service to pick up +the changes: ```bash docker-compose up -d ``` -If you restart both containers, be sure to update your App in Benchling with the new webhook URL from localtunnel. +If you restart both containers, be sure to update your App in Benchling with the +new webhook URL from localtunnel. ### Setting Client ID -Our App needs a Client ID to pair with the Client Secret for authentication to Benchling. In this case, we've created our -App to accept `CLIENT_ID` as an environment variable. +Our App needs a Client ID to pair with the Client Secret for authentication to +Benchling. In this case, we've created our App to accept `CLIENT_ID` as an +environment variable. One easy way to set an environment variables for Docker is to add a `.env` file. @@ -162,8 +185,8 @@ Windows example: echo.> .env ``` -Open it in an editor of your choice and set the values with the plaintext client ID -for your App. For example: +Open it in an editor of your choice and set the values with the plaintext client +ID for your App. For example: ```bash CLIENT_ID=Ts7jtwPohM @@ -171,13 +194,17 @@ CLIENT_ID=Ts7jtwPohM ### Setting App Definition ID -The App definition ID is available from the Developer Console by selecting the App to view. +The App definition ID is available from the Developer Console by selecting the +App to view. ![image info](./docs/global-app-definition-id.png) -> ℹ️ **Note:** If you do NOT see this ID, please ensure [Global Apps](https://docs.benchling.com/docs/global-apps-faq) are enabled for your tenant. +> ℹ️ **Note:** If you do NOT see this ID, please ensure [Global +> Apps](https://docs.benchling.com/docs/global-apps-faq) are enabled for your +> tenant. -Add it to your `.env` file with a variable name `APP_DEFINITION_ID`. The contents of your `.env` file should now look something like: +Add it to your `.env` file with a variable name `APP_DEFINITION_ID`. The +contents of your `.env` file should now look something like: ```bash CLIENT_ID=Ts7jtwPohM @@ -194,18 +221,22 @@ docker-compose up -d ### Security Note: Storing App Secrets in Production -> ⚠️ **Security Note:** In production, store the secret with a secure solution such as a secrets store (AWS Secrets Manager, as an example) or, if storing programmatically, encrypted using app-layer encryption. Avoid placing it in plaintext anywhere in code or configuration. +> ⚠️ **Security Note:** In production, store the secret with a secure solution +> such as a secrets store (AWS Secrets Manager, as an example) or, if storing +> programmatically, encrypted using app-layer encryption. Avoid placing it in +> plaintext anywhere in code or configuration. ### Create App Registry Dependencies -If you examine the `configuration` section of `manifest.yaml`, you'll see our App -expects a few configuration items: +If you examine the `configuration` section of `manifest.yaml`, you'll see our +App expects a few configuration items: 1. A folder 2. A molecule entity schema with two decimal fields -We declare two `features` in the `manifest.yaml` so that our App can render -its UI as a `CANVAS` (e.g. within the Notebook) or on an `ASSAY_RUN`. If you'd like to use a Run, we'll also need: +We declare two `features` in the `manifest.yaml` so that our App can render its +UI as a `CANVAS` (e.g. within the Notebook) or on an `ASSAY_RUN`. If you'd like +to use a Run, we'll also need: 1. An Lab Automation run schema @@ -227,8 +258,9 @@ The created molecule schema should look something like this: ![image info](./docs/schema-example.png) -_Note: The names can be different, and the schema is allowed to have additional fields. -As long as it's for a `Molecule` entity, and has at least two `Decimal` fields._ +_Note: The names can be different, and the schema is allowed to have additional +fields. As long as it's for a `Molecule` entity, and has at least two `Decimal` +fields._ #### [Optional] Lab Automation Run Schema @@ -238,14 +270,16 @@ If using a Run, create a new lab automation run schema in the registry. ### Updating the App's Configuration -App Configuration gives us a stable code contract for referencing data mapped in a Benchling tenant. -The values of the data in Benchling can then be changed without updating App code. +App Configuration gives us a stable code contract for referencing data mapped in +a Benchling tenant. The values of the data in Benchling can then be changed +without updating App code. Let's update our configuration to: 1. Specify a folder for syncing sequences 2. Link a molecule schema and fields for the synced chemicals -3. [Optional] If using a Run, select an assay run schema to associate with our Benchling App +3. [Optional] If using a Run, select an assay run schema to associate with our + Benchling App ![image info](./docs/update-app-config.gif) From e120f2db11f4222a741ca7e066c842fc5cdde7f4 Mon Sep 17 00:00:00 2001 From: "Dr. Ernie Prabhakar (aider)" <19791+drernie@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:45:33 -0700 Subject: [PATCH 7/7] feat: add Flask app startup code with port 8000 --- examples/chem-sync-local-flask/local_app/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/chem-sync-local-flask/local_app/app.py b/examples/chem-sync-local-flask/local_app/app.py index 767decc..527efa7 100644 --- a/examples/chem-sync-local-flask/local_app/app.py +++ b/examples/chem-sync-local-flask/local_app/app.py @@ -44,3 +44,8 @@ def _enqueue_work() -> None: args=(request.json,), ) thread.start() + + +if __name__ == "__main__": + app = create_app() + app.run(host="0.0.0.0", port=8000)