Source code for feretui.helper

# This file is a part of the FeretUI project
#
#    Copyright (C) 2024 Jean-Sebastien SUZANNE <js.suzanne@gmail.com>
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file,You can
# obtain one at http://mozilla.org/MPL/2.0/.
"""Module feretui.helper.

The helper is function to help the integration of the client in an another
project.

* :func:`.action_validator`: Decorator to validate the call of the action
  callback
* :func:`.action_for_authenticated_user`: The action can be call only
  by the authenticated user
* :func:`.action_for_unauthenticated_user`: The action can be call only
  by the unauthenticated user
* :func:`.page_for_authenticated_user_or_goto`: Decorator to protect the
  accebility of one page, and allow this page only for a user.
* :func:`.page_for_unauthenticated_user_or_goto`: Decorator to protect the
  accebility of one page, and allow this page only if the user is not
  authenticated.
* :func:`.menu_for_authenticated_user`: Callback to return
  if the user is authenticated.
* :func:`.menu_for_unauthenticated_user`: Callback to return
  if the user is authenticated.
* :func:`.menu_for_all_users`: No restriction
  if the user is authenticated.
"""

from collections.abc import Callable
from functools import wraps
from typing import TYPE_CHECKING

from feretui.exceptions import (
    ActionUserIsAuthenticatedError,
    ActionUserIsNotAuthenticatedError,
    ActionValidatorError,
)
from feretui.request import RequestMethod
from feretui.response import Response
from feretui.session import Session

if TYPE_CHECKING:
    from feretui.feretui import FeretUI


[docs] def action_validator( methods: list[RequestMethod] = None, ) -> Callable: """Validate the action callback. :: @myferet.register_action @action_validator(methods=[RequestMethod.POST]) def my_action(feretui, request): return Response(...) .. note:: The response of the callback must be a :class:`feretui.response.Response` :param methods: The request methods of the action, if None the action can be called with any request method, else allow only the defined request methods. :type methods: list[:class:`feretui.request.RequestMethod`] :return: a wrapper function: :rtype: Callable """ if methods is not None and not isinstance(methods, list): methods = [methods] def wrapper_function(func: Callable) -> Callable: @wraps(func) def wrapper_call( feret: "FeretUI", request: Response, ) -> Response: if methods is not None and request.method not in methods: raise ActionValidatorError( f"The received method is {request.method} " f"but waiting method {methods}", ) response = func(feret, request) if not isinstance(response, Response): raise ActionValidatorError( f"The response '{response}' is not an instance of Response", ) return response return wrapper_call return wrapper_function
[docs] def action_for_authenticated_user(func: Callable) -> Callable: """Raise an exception if the user is not authenticated. It is a decorator :: @myferet.register_action @action_for_authenticated_user def my_action(feretui, request): return Response(...) :return: a wrapper function: :rtype: Callable :exception: ActionUserIsNotAuthenticatedError """ @wraps(func) def wrapper_call( feret: "FeretUI", request: Response, ) -> Response: if request.session.user is None: raise ActionUserIsNotAuthenticatedError( "The action is called by an unauthenticated user", ) return func(feret, request) return wrapper_call
[docs] def action_for_unauthenticated_user(func: Callable) -> Callable: """Raise an exception if the user is authenticated. It is a decorator :: @myferet.register_action @action_for_unauthenticated_user def my_action(feretui, request): return Response(...) :return: a wrapper function: :rtype: Callable :exception: ActionUserIsAuthenticatedError """ @wraps(func) def wrapper_call( feret: "FeretUI", request: Response, ) -> Response: if request.session.user: raise ActionUserIsAuthenticatedError( "The action is called by an authenticated user", ) return func(feret, request) return wrapper_call
[docs] def page_for_authenticated_user_or_goto( fallback_page: str | Callable, ) -> Callable: """Display the decorated page if the user is authenticated. In the case or the user is not autenticated. The returned page is the fallback page. :: @page_for_authenticated_user_or_goto('404') def my_page(myferet, session, options): ... :param fallback_page: The page if the user is not authenticated :type fallback_page: str or the callable page :return: a decorator :rtype: Callable """ def wrap_func(func: Callable) -> Callable: @wraps(func) def wrap_call( feretui: "FeretUI", session: Session, options: dict, ) -> str: if session.user: return func(feretui, session, options) page = fallback_page if isinstance(fallback_page, str): page = feretui.get_page(fallback_page) return page(feretui, session, options) return wrap_call return wrap_func
[docs] def page_for_unauthenticated_user_or_goto( fallback_page: str | Callable, ) -> Callable: """Display the decorated page if the user is not authenticated. In the case or the user is autenticated. The returned page is the fallback page. :: @page_for_unauthenticated_user_or_goto('forbidden') def my_page(myferet, session, options): ... :param fallback_page: The page if the user is authenticated :type fallback_page: str or the callable page :return: a decorator :rtype: Callable """ def wrap_func(func: Callable) -> Callable: @wraps(func) def wrap_call( feretui: "FeretUI", session: Session, options: dict, ) -> str: if session.user is None: return func(feretui, session, options) page = fallback_page if isinstance(fallback_page, str): page = feretui.get_page(fallback_page) return page(feretui, session, options) return wrap_call return wrap_func