Custom Decorators
Austial is built around a single mechanism — a metadata bag attached to
every decorated class or function (austial.core.metadata.set_metadata /
get_metadata). Every framework decorator (@Module,
@Controller, the route decorators, @UseGuards, @Injectable, …) is
built on top of it, and it’s directly available to you for your own
decorators, guards, and interceptors.
set_metadata / get_metadata
from austial.core.metadata import get_metadata, set_metadata
ROLES_KEY = "roles"
def Roles(*roles: str):
def decorator(target):
set_metadata(ROLES_KEY, list(roles), target)
return target
return decorator@Controller("cats")
class CatsController:
@Get()
@Roles("admin", "moderator")
async def find_all(self):
...This is a decorator factory that stamps metadata onto whatever it’s applied to (class or method).
Reading metadata with Reflector
austial.core.reflector.Reflector is the read-side counterpart, injectable
anywhere the container resolves dependencies — most commonly inside a
guard:
from austial import CanActivate, ExecutionContext
from austial.core.reflector import Reflector
from .roles_decorator import ROLES_KEY
class RolesGuard(CanActivate):
def __init__(self, reflector: Reflector):
self._reflector = reflector
async def can_activate(self, context: ExecutionContext) -> bool:
required_roles = self._reflector.get(ROLES_KEY, context.get_handler())
if not required_roles:
return True # no @Roles(...) on this handler -> no restriction
request = context.get_request()
user_roles = request.headers.get("x-roles", "").split(",")
return any(role in user_roles for role in required_roles)@Get()
@Roles("admin")
@UseGuards(RolesGuard)
async def find_all(self):
...Reflector also exposes get_all_and_merge/get_all_and_override for
reading the same metadata key stamped at both the controller and handler
level and combining them.
Where this is used internally
Every @Use* decorator (@UseGuards, @UsePipes, @UseInterceptors,
@UseFilters), @HttpCode, @Header, and @Catch are themselves thin
wrappers around set_metadata/get_metadata with a dedicated key
(GUARDS_METADATA, PIPES_METADATA, INTERCEPTORS_METADATA,
FILTERS_METADATA, CATCH_METADATA in austial/core/metadata.py) — so a
custom decorator you write with the same primitives is a first-class
citizen, not a workaround.