AWS S3
Description #
Plugin that provides a SimpleAPI for managing AWS S3 objects, including listing, uploading, downloading, deleting files, and generating presigned URLs. It also includes a chart application that renders a form interface for interacting with S3 directly from the chart.
Configuration #
This example plugin defines the following “secrets” in the manifest file:
"secrets": [
"S3Key",
"S3Secret",
"S3Region",
"S3Bucket"
],
Once defined in the MANIFEST.json, set the secrets for your plugin in the Admin UI of your Canvas EMR. Read more
S3Key #
Your AWS Access Key ID.
S3Secret #
Your AWS Secret Access Key.
S3Region #
The AWS region where your S3 bucket is located (e.g., us-east-1).
S3Bucket #
The name of your S3 bucket.
CANVAS_MANIFEST.json #
{
"sdk_version": "0.81.0",
"plugin_version": "0.0.1",
"name": "aws_manip",
"description": "use AWS S3 to store, retrieve and delete documents",
"components": {
"protocols": [
{
"class": "aws_manip.handlers.aws_manip:AwsManip",
"description": "AWS extractor based on AWS S3"
}
],
"applications": [
{
"class": "aws_manip.handlers.aws_form_app:AwsFormApp",
"name": "AWS S3 Document Management",
"description": "AWS S3 manip",
"icon": "static/aws_manip.png",
"scope": "patient_specific",
"show_in_panel": false
}
],
"commands": [],
"content": [],
"effects": [],
"views": []
},
"secrets": [
"S3Key",
"S3Secret",
"S3Region",
"S3Bucket"
],
"tags": {},
"references": [],
"license": "",
"diagram": false,
"readme": "./README.md"
}
handlers/ #
aws_manip.py #
Purpose
This code defines a SimpleAPI handler that exposes REST endpoints for managing objects in an Amazon S3 bucket using the Canvas SDK’s AWS S3 client.
Class Overview
- The main class,
AwsManip, extendsSimpleAPI. - It creates an S3 client using credentials stored in plugin secrets.
Main Workflow
GET /list_items— Lists all objects in the configured S3 bucket.GET /get_item/<item_key>— Retrieves an object’s content by its key.GET /presigned_url/<item_key>— Generates a presigned URL for temporary (1-hour) access to an object.POST /upload_item/<item_key>— Uploads content to S3, handling both text and binary content types.DELETE /delete_item/<item_key>— Deletes an object from S3 by key.
S3 Client Integration
- The
_s3_clientmethod creates anS3client instance fromcanvas_sdk.clients.aws.libraries, configured withS3Credentialsfrom plugin secrets. - Each endpoint checks
client.is_ready()before performing operations.
from http import HTTPStatus
from aws_manip.constants.secrets import Secrets
from canvas_sdk.clients.aws.libraries import S3
from canvas_sdk.clients.aws.structures import Credentials as S3Credentials
from canvas_sdk.effects import Effect
from canvas_sdk.effects.simple_api import JSONResponse, PlainTextResponse, Response
from canvas_sdk.handlers.simple_api import Credentials, SimpleAPI, api
class AwsManip(SimpleAPI):
"""Simple API handler for AWS S3 object management operations."""
PREFIX = None
USER_TYPE_STAFF = "Staff"
def authenticate(self, credentials: Credentials) -> bool:
"""Authenticate API requests.
Args:
credentials: The credentials provided with the request.
Returns:
True to allow all requests (authentication bypassed).
"""
return True
def _s3_client(self) -> S3:
"""Create and configure an S3 client with credentials from secrets.
Returns:
Configured S3 client instance.
"""
return S3(
S3Credentials(
key=self.secrets[Secrets.s3_key],
secret=self.secrets[Secrets.s3_secret],
region=self.secrets[Secrets.s3_region],
bucket=self.secrets[Secrets.s3_bucket],
)
)
@api.get("/list_items")
def list_items(self) -> list[Response | Effect]:
"""List all objects in the S3 bucket."""
client = self._s3_client()
if client.is_ready():
content = [p.key for p in client.list_s3_objects("")]
status_code = HTTPStatus(HTTPStatus.OK)
return [JSONResponse(content, status_code=status_code)]
return []
@api.get("/get_item/<item_key>")
def get_item(self) -> list[Response | Effect]:
"""Retrieve an object's content from S3 by key."""
item_key = self.request.path_params["item_key"]
client = self._s3_client()
if client.is_ready() and item_key:
content = client.access_s3_object(item_key).content
status_code = HTTPStatus(HTTPStatus.OK)
return [Response(content, status_code=status_code)]
return []
@api.get("/presigned_url/<item_key>")
def presigned_url(self) -> list[Response | Effect]:
"""Generate a presigned URL for temporary access to an S3 object."""
item_key = self.request.path_params["item_key"]
client = self._s3_client()
if client.is_ready() and item_key:
content = client.generate_presigned_url(item_key, 3600)
status_code = HTTPStatus(HTTPStatus.OK)
return [PlainTextResponse(content, status_code=status_code)]
return []
@api.post("/upload_item/<item_key>")
def upload_item(self) -> list[Response | Effect]:
"""Upload content to S3 with the specified key."""
item_key = self.request.path_params["item_key"]
client = self._s3_client()
content = self.request.body
content_type = self.request.content_type
if client.is_ready() and item_key:
if content_type == "text/plain":
response = client.upload_text_to_s3(item_key, content.decode("utf-8"))
else:
response = client.upload_binary_to_s3(item_key, content, content_type)
return [Response(response.content, status_code=response.status_code)]
return []
@api.delete("/delete_item/<item_key>")
def delete_item(self) -> list[Response | Effect]:
"""Delete an object from S3 by key."""
item_key = self.request.path_params["item_key"]
client = self._s3_client()
if client.is_ready() and item_key:
content = client.delete_object(item_key).content
status_code = HTTPStatus(HTTPStatus.OK)
return [Response(content, status_code=status_code)]
return []
aws_form_app.py #
Purpose
This code defines an Application handler that launches a modal form in the right chart pane for interacting with the S3 management API endpoints.
from canvas_sdk.effects import Effect
from canvas_sdk.effects.launch_modal import LaunchModalEffect
from canvas_sdk.handlers.application import Application
from canvas_sdk.templates import render_to_string
class AwsFormApp(Application):
"""Application handler for launching the AWS S3 management form interface."""
PLUGIN_API_BASE_ROUTE = "/plugin-io/api/aws_manip"
def on_open(self) -> Effect:
"""Render and launch the AWS S3 management modal form."""
content = render_to_string(
"templates/aws_form.html",
{
"listItemsURL": f"{self.PLUGIN_API_BASE_ROUTE}/list_items",
"getItemURL": f"{self.PLUGIN_API_BASE_ROUTE}/get_item",
"presignedUrlURL": f"{self.PLUGIN_API_BASE_ROUTE}/presigned_url",
"uploadItemURL": f"{self.PLUGIN_API_BASE_ROUTE}/upload_item",
"deleteItemURL": f"{self.PLUGIN_API_BASE_ROUTE}/delete_item",
},
)
return LaunchModalEffect(
content=content,
target=LaunchModalEffect.TargetType.RIGHT_CHART_PANE,
).apply()