Skip to content

Commit

Permalink
use middleware to intercept event handlers (for database session mana…
Browse files Browse the repository at this point in the history
…gement, ...)

* feat: use pattern to handle exceptions
* feat: allow middleware without yield
* docs: describe the implementation of middlewares
  • Loading branch information
FabienArcellier committed Jun 24, 2024
1 parent 01712b7 commit b7caf3d
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
49 changes: 47 additions & 2 deletions docs/framework/event-handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,41 @@ def evaluate(state, payload):

Take into account that globals apply to all users. If you need to store data that's only relevant to a particular user, use application state.

## Middlewares

Middlewares are functions that run before and after every event handler.
They can be used to perform tasks such as logging, error handling, session management, or modifying the state.

```py
import writer as wf

@wf.middleware()
def middleware_before(state, payload, context):
print("Middleware before event handler")
state['running'] += 1
yield
print("Middleware after event handler")
state['running'] -= 1
```

A middleware receives the same parameters as an event handler.

A middleware can be used to handle exceptions that happens in event handlers.

```py
import writer as wf

@wf.middleware()
def middleware_before(state):
try:
yield
except Exception as e:
state['error_counter'] += 1
state['last_error'] = str()
finally:
pass
```

## Standard output

The standard output of an app is captured and shown in the code editor's log. You can use the standard `print` function to output results.
Expand Down Expand Up @@ -232,15 +267,25 @@ You can use any awaitable object within an async event handler. This includes th

## Context

The `context` argument provides additional information about the event. For example, if the event
was triggered by a _Button_, the `context` will include target field that contains the id of the button.
The `context` argument provides additional information about the event.

The context provide the id of component that trigger the event in `target` field.

```py
def handle_click(state, context: dict):
last_source_of_click = context['target']
state["last_source_of_click"] = last_source_of_click
```

The context provides the event triggered in the `event` field.

```py
def handle_click(state, context: dict):
event_type = context['event']
if event_type == 'click':
state["last_event"] = 'Click'
```

The repeater components have additional fields in the context, such as defined in `keyVariable` and `valueVariable`.

```py
Expand Down
4 changes: 2 additions & 2 deletions src/writer/app_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def _handle_state_enquiry(self, session: WriterSession) -> StateEnquiryResponseP

return res_payload

def _handle_state_full(self, session: WriterSession) -> StateContentResponsePayload:
def _handle_state_content(self, session: WriterSession) -> StateContentResponsePayload:
serialized_state = {}
try:
serialized_state = session.session_state.user_state.to_raw_state()
Expand Down Expand Up @@ -275,7 +275,7 @@ def _handle_message(self, session_id: str, request: AppProcessServerRequest) ->
return AppProcessServerResponse(
status="ok",
status_message=None,
payload=self._handle_state_full(session)
payload=self._handle_state_content(session)
)

if type == "setUserinfo":
Expand Down
6 changes: 6 additions & 0 deletions src/writer/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,12 @@ def build_writer_func_arguments(func: Callable, writer_args: dict) -> List[Any]:
Constructs the list of arguments based on the signature of the function
which can be a handler or middleware.
>>> def my_event_handler(state, context):
>>> yield
>>> args = build_writer_func_arguments(my_event_handler, {'state': {}, 'payload': {}, 'context': {"target": '11'}, 'session': None, 'ui': None})
>>> [{}, {"target": '11'}]
:param func: the function that will be called
:param writer_args: the possible arguments in writer (state, payload, ...)
"""
Expand Down

0 comments on commit b7caf3d

Please sign in to comment.