This release is a pre-release and may not be stable for production use.
fhir-tbs-py
Topic-based subscription extension for python aiohttp web applications.
Features:
- Automatically created webhook aiohttp handlers based on definitions
- Unified R4B/R5 API for automatic registration (managed subscriptions)
- Optional managed subscriptions
- Optional authentication using
X-Api-Key id-only/full-resourcesupport
Install
Install fhir-tbs[r4b] or fhir-tbs[r5] using poetry/pipenv/pip.
Usage
- Instantiate R4BTBS/R5TBS class with optionally passing predefined subscriptions using
subscriptionsarg andsubscription_defaultswith default subscription parameters (e.g.payload_content,timeoutorheartbeat_period):tbs = R4BTBS(subscription_defaults={"payload_content": "full-resource"})
- Define subscriptions using decorator
tbs.define:@tbs.define( topic="https://example.com/SubscriptionTopic/new-appointment-event", filter_by=[ { "resource_type": "Appointment", "filter_parameter": "status", "value": "booked" } ], webhook_id="new-appointment" ) async def new_appointment_handler( app: web.Application, reference: str, _included_resources: list[r4b.AnyResource], _timestamp: str | None, ) -> None: logging.info("New appointment %s", reference)
- Invoke
setup_tbson app initialization passing needed parameters (see specification below):setup_tbs(app, tbs, webhook_path_prefix="webhook")
Specification
fhir_tbs.r4b.R4BTBS/fhir_tbs.r5.R5TBS
- subscriptions (list[fhir_tbs.SubscriptionDefinitionWithHandler], optional) - predefined list of subscriptions.
- subscription_defaults (optional) - default parameters for all subscription definitions.
- payload_content (str, optional):
id-only/full-resource(default isid-only) - timeout (int, optional): default is
60 - heartbeat_period (int, optional): default is
20
- payload_content (str, optional):
tbs_instance.define
- topic (str): URL of SubscriptionTopic to subscribe.
- webhook_id (optional): Optional webhook id that will be part of webhook URL.
- filter_by (list[FilterBy], optional): Optional list of filters applied to topic.
- payload_content (str, optional):
id-only/full-resource(default isid-only) - timeout (int, optional): default is
60 - heartbeat_period (int, optional): default is
20
setup_tbs
- app (web.Application): aiohttp application.
- tbs (R4BTBS/R5TBS): TBS class instance.
- webhook_path_prefix (str): Prefix for the generated aiohttp routes.
- webhook_token (str, optional): The authorization token that is checked in X-Api-Token header.
- manage_subscriptions (bool, optional): The flag that indicates whether subscription registration/population should be enabled.
- handle_delivery_errors (bool, optional): WIP The flag that indicated whether subscription delivery errors (e.g. broken connection or missing events) should be handled.
- app_url (str, optional): Application url that is used when
manage_subscriptions/handle_delivery_errorsare set. - get_fhir_client (Callable[[web.Application], AsyncFHIRClient], optional): Getter for web.Application that returns AsyncFHIRClient that further used when
manage_subscriptions/handle_delivery_errorsare set.
Examples
General example
Create subscriptions.py with the following content:
import logging
from fhirpy import AsyncFHIRClient
import fhirpy_types_r4b as r4b
from fhir_tbs import SubscriptionDefinitionWithHandler
from fhir_tbs.r4b import R4BTBS
from aiohttp import web
# Make sure that app has fhir_client_key
fhir_client_key = web.AppKey("fhir_client_key", AsyncFHIRClient)
async def new_appointment_sub(
app: web.Application,
appointment_ref: str,
included_resources: list[r4b.AnyResource],
_timestamp: str
) -> None:
fhir_client = app[fhir_client_key]
# For id-only use fhir_client to fetch the resource
appointment = r4b.Appointment(**(await fhir_client.get(appointment_ref)))
# For full-resource find in in included resources by reference (straightforward example)
appointment = [
resource for resource in included_resources
if appointment_ref == f"{resource.resourceType}/{resource.id}"
][0]
logging.info("New appointment %s", appointment.model_dump())
tbs = R4BTBS()
@tbs.define(
topic="https://example.com/SubscriptionTopic/new-appointment-event",
filter_by=[
{
"resource_type": "Appointment",
"filter_parameter": "status",
"value": "booked"
}
],
webhook_id="new-appointment"
)
async def new_appointment_handler(
app: web.Application,
reference: str,
_included_resources: list[r4b.AnyResource],
_timestamp: str | None,
) -> None:
logging.info("New appointment %s", reference)
def create_app() -> web.Application:
app = web.Application()
app[fhir_client_key] = AsyncFHIRClient(...)
setup_tbs(
app,
tbs,
webhook_path_prefix="webhook",
app_url="http://app:8080",
get_fhir_client=lambda app: app[fhir_client_key],
manage_subscriptions=True,
handle_delivery_errors=True
)
Using in aidbox-python-sdk for external subscriptions
external_webhook_path_prefix_parts = ["external-webhook"]
external_tbs = R4BTBS(subscriptions=subscriptions)
def setup_external_tbs(app: web.Application) -> None:
setup_tbs(
app,
external_tbs,
webhook_prefix_path="/".join(external_webhook_path_prefix_parts),
app_url="http://aidbox.example.com",
get_fhir_client=lambda app: app[fhir_client_key],
manage_subscriptions=True,
handle_delivery_errors=True
)
@sdk.operation(
methods=["POST"],
path=[*external_webhook_path_prefix_parts, {"name": "webhook-name"}],
public=True,
)
async def external_webhook_proxy_op(
_operation: SDKOperation, request: SDKOperationRequest
) -> web.Response:
session = request["app"][ak.session]
app_url = str(request["app"][ak.settings].APP_URL).rstrip("/")
webhook_name = request["route-params"]["webhook-name"]
path = "/".join([*external_webhook_path_prefix_parts, webhook_name])
token = request["headers"].get("x-api-key")
async with session.post(
f"{app_url}/{path}",
headers={"X-Api-Key": token} if token else {},
json=request["resource"],
) as response:
return web.Response(
body=await response.text(),
status=response.status,
content_type=response.content_type,
)
def create_app() -> web.Application:
app = web.Application()
app[fhir_client_key] = AsyncFHIRClient(...)
setup_external_tbs(app)
Metadata
Release files for fhir-tbs 1.0.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fhir_tbs-1.0.0a1.tar.gz | 11.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fhir_tbs-1.0.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.4 kB
Release files / fhir_tbs-1.0.0a1.tar.gz
| Download URL | fhir_tbs-1.0.0a1.tar.gz |
|---|---|
| Size | 11.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
894ae6e64cc46ca96532132ed048cd0c5e1a406dfb937092752448629dad935f
|
|
BLAKE2b-256 checksum How to use checksums |
8aaea6b0819ead7fbb9613cb2f615e71efef492bc63f6c6ad088266499e9d493
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/5.1.1 CPython/3.12.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 28, 2024.
Transparency logRelease files / fhir_tbs-1.0.0a1-py3-none-any.whl
| Download URL | fhir_tbs-1.0.0a1-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fb5c7f2e43d8626063151be3a26e963a17443ec6c617490a376cdfa81fdd4d17
|
|
BLAKE2b-256 checksum How to use checksums |
8ff2f6e6b63baf7877413037e90b6522e26cd40fd32125481bf5252cf78e2fae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/5.1.1 CPython/3.12.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 28, 2024.
Transparency log