Skip to content

Commit 53875e0

Browse files
add support for embeddable canvas in notebook (#18)
* notebook canvas updates * notebook canvas readme updates * notebook canvas missing update * switch to CanvasCreatedWebhookV2Beta webhook * fix unit test * Update examples/chem-sync-local-flask/local_app/benchling_app/handler.py Co-authored-by: damola-benchling <damola.shomoye@benchling.com> --------- Co-authored-by: damola-benchling <damola.shomoye@benchling.com>
1 parent 2a46c32 commit 53875e0

File tree

8 files changed

+45
-17
lines changed

8 files changed

+45
-17
lines changed

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ https://brave-wombats-poke.loca.lt
7979
### Benchling Prerequisites
8080
1. Access to a Benchling tenant, like `https://my-tenant.benchling.com`
8181
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).
82-
3. This example also requires a [Lab Automation](https://www.benchling.com/resources/benchling-lab-automation) license.
82+
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.
8383
4. [Molecule entities](https://help.benchling.com/hc/en-us/articles/9684254682893-Molecule-entity-overview) will need to be enabled on your tenant.
8484
5. [Global Apps](https://docs.benchling.com/docs/global-apps-faq) will need to be enabled on your tenant.
8585

@@ -196,8 +196,8 @@ expects a few configuration items:
196196
1. A folder
197197
2. A molecule entity schema with two decimal fields
198198

199-
The `features` section of `manifest.yaml` also states that our App will render
200-
its UI on an `ASSAY_RUN`. So we'll also need:
199+
We declare two `features` in the `manifest.yaml` so that our App can render
200+
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:
201201
1. An Lab Automation run schema
202202

203203
#### Folder
@@ -221,9 +221,9 @@ The created molecule schema should look something like this:
221221
_Note: The names can be different, and the schema is allowed to have additional fields.
222222
As long as it's for a `Molecule` entity, and has at least two `Decimal` fields._
223223

224-
#### Lab Automation Run Schema
224+
#### [Optional] Lab Automation Run Schema
225225

226-
Create a new lab automation run schema in the registry.
226+
If using a Run, create a new lab automation run schema in the registry.
227227

228228
![image info](./docs/create-run-schema.gif)
229229

@@ -235,7 +235,7 @@ The values of the data in Benchling can then be changed without updating App cod
235235
Let's update our configuration to:
236236
1. Specify a folder for syncing sequences
237237
2. Link a molecule schema and fields for the synced chemicals
238-
3. Select an assay run schema to associate with our Benchling App
238+
3. [Optional] If using a Run, select an assay run schema to associate with our Benchling App
239239

240240
![image info](./docs/update-app-config.gif)
241241

@@ -249,12 +249,19 @@ Let's grant some access by adding the Benchling App to an organization.
249249
## Running the App - Syncing a Chemical
250250

251251
1. Create a new notebook entry
252-
2. Insert a run of the schema linked in App Config
253-
3. Create the Run
254-
4. Enter a valid chemical name to search for, such as `acetaminophen`
255-
5. Click "Search Chemicals"
256-
6. After reviewing the preview, click "Create Molecule"
257-
7. Click the linked entity to view it in Benchling
252+
2. Insert a Canvas
253+
3. Enter a valid chemical name to search for, such as `acetaminophen`
254+
4. Click "Search Chemicals"
255+
5. After reviewing the preview, click "Create Molecule"
256+
6. Click the linked entity to view it in Benchling
257+
258+
![image info](./docs/demo-notebook.gif)
259+
260+
## [Optional] Running the App - via a Run
261+
262+
1. Insert a Run of the schema linked in App Config
263+
2. Create the Run
264+
3. Continue with steps 3-6 above
258265

259266
![image info](./docs/demo.gif)
260267

22.8 MB
Loading

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def receive_webhooks(target: str) -> tuple[str, int]: # noqa: ARG001
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+
verify(app_def_id, request.data.decode("utf-8"), request.headers) # type: ignore[arg-type]
2929

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

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
from benchling_sdk.apps.status.errors import AppUserFacingError
44
from benchling_sdk.models.webhooks.v0 import (
5+
CanvasCreatedWebhookV2Beta,
56
CanvasInitializeWebhookV2,
67
CanvasInteractionWebhookV2,
78
WebhookEnvelopeV0,
89
)
910

1011
from local_app.benchling_app.canvas_interaction import route_interaction_webhook
1112
from local_app.benchling_app.setup import init_app_from_webhook
12-
from local_app.benchling_app.views.canvas_initialize import render_search_canvas
13+
from local_app.benchling_app.views.canvas_initialize import (
14+
render_search_canvas,
15+
render_search_canvas_for_created_canvas,
16+
)
1317
from local_app.lib.logger import get_logger
1418

1519
logger = get_logger()
@@ -31,6 +35,8 @@ def handle_webhook(webhook_dict: dict[str, Any]) -> None:
3135
render_search_canvas(app, webhook.message)
3236
elif isinstance(webhook.message, CanvasInteractionWebhookV2):
3337
route_interaction_webhook(app, webhook.message)
38+
elif isinstance(webhook.message, CanvasCreatedWebhookV2Beta):
39+
render_search_canvas_for_created_canvas(app, webhook.message.canvas_id)
3440
else:
3541
# Should only happen if the app's manifest requests webhooks that aren't handled in its code paths
3642
raise UnsupportedWebhookError(f"Received an unsupported webhook type: {webhook}")

examples/chem-sync-local-flask/local_app/benchling_app/views/canvas_initialize.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from local_app.benchling_app.views.constants import SEARCH_BUTTON_ID, SEARCH_TEXT_ID
1515

1616

17+
def _canvas_builder_from_canvas_id(app: App, canvas_id: str) -> CanvasBuilder:
18+
current_canvas = app.benchling.apps.get_canvas_by_id(canvas_id)
19+
return CanvasBuilder.from_canvas(current_canvas)
20+
1721
def render_search_canvas(app: App, canvas_initialized: CanvasInitializeWebhookV2) -> None:
1822
with app.create_session_context("Show Sync Search", timeout_seconds=20):
1923
canvas_builder = CanvasBuilder(
@@ -25,6 +29,13 @@ def render_search_canvas(app: App, canvas_initialized: CanvasInitializeWebhookV2
2529
app.benchling.apps.create_canvas(canvas_builder.to_create())
2630

2731

32+
def render_search_canvas_for_created_canvas(app: App, canvas_id: str) -> None:
33+
with app.create_session_context("Show Sync Search", timeout_seconds=20):
34+
canvas_builder = _canvas_builder_from_canvas_id(app, canvas_id)
35+
canvas_builder.blocks.append(input_blocks())
36+
app.benchling.apps.update_canvas(canvas_id, canvas_builder.to_update())
37+
38+
2839
def input_blocks() -> list[UiBlock]:
2940
return [
3041
MarkdownUiBlock(

examples/chem-sync-local-flask/manifest.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ features:
77
- name: Sync Step
88
id: sync_step
99
type: ASSAY_RUN
10+
- name: Sync Step
11+
id: canvas_sync_step
12+
type: CANVAS
1013
subscriptions:
1114
deliveryMethod: WEBHOOK
1215
messages:
1316
- type: v2.canvas.initialized
1417
- type: v2.canvas.userInteracted
18+
- type: v2-beta.canvas.created
1519
configuration:
1620
- name: Sync Folder
1721
type: folder
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
flask~=3.0.2
22
# Cryptography extra needed for webhook verification
3-
benchling-sdk[cryptography]==1.13.0
3+
benchling-sdk[cryptography]==1.19.0

examples/chem-sync-local-flask/tests/unit/local_app/benchling_app/views/test_canvas_initialize.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
from benchling_sdk.apps.canvas.framework import CanvasBuilder
44
from benchling_sdk.apps.framework import App
5-
from benchling_sdk.models.webhooks.v0 import CanvasInitializeWebhookV0
5+
from benchling_sdk.models.webhooks.v0 import CanvasInitializeWebhookV2
66

77
from local_app.benchling_app.views.canvas_initialize import input_blocks, render_search_canvas
88

99

1010
class TestCanvasInitialize:
1111

1212
def test_render_search_canvas(self) -> None:
13-
initialize_webhook = MagicMock(CanvasInitializeWebhookV0)
13+
initialize_webhook = MagicMock(CanvasInitializeWebhookV2)
1414
initialize_webhook.feature_id = "feature_id"
1515
initialize_webhook.resource_id = "resource_id"
1616
app = MagicMock(App)

0 commit comments

Comments
 (0)