def _middleware(self): # Get class level middleware middlewares = [] if self.auth: middlewares.append(self.auth) if self.middleware: middlewares.extend(self.middleware) if self.scopes: middlewares.append(Guard(self.scopes)) return middlewares
def add( self, path: str, endpoint: Optional[Callable] = None, methods: List[str] = ['GET'], *, name: Optional[str] = None, autoprefix: bool = True, response_model: Optional[Any] = None, tags: Optional[List[str]] = None, middleware: Optional[List] = None, auth: Optional[Guard] = None, scopes: Optional[List] = None, summary: Optional[str] = None, description: Optional[str] = None, ) -> Callable[[Decorator], Decorator]: """Generic add method and decorator""" # Convert auth and scope helper param to middleware if middleware is None: middleware = [] if auth: middleware.append(auth) if scopes: middleware.append(Guard(scopes)) # Clean path and name (name, full_path, name, full_name) = self._clean_add(path, name, autoprefix) def handle(endpoint): # Create route SuperDict route = ApiRoute({ 'path': full_path or '/', 'name': full_name, 'endpoint': endpoint, 'methods': methods, 'response_model': response_model, 'tags': tags, 'middleware': middleware, 'summary': summary, 'description': description, 'original_path': path, 'original_name': name, }) key = '-'.join(sorted(methods)) + '-' + full_name #self.routes[full_name] = route self.routes[key] = route return route # Method access if endpoint: return handle(endpoint) # Decorator access def decorator(func: Decorator) -> Decorator: handle(func) return func return decorator
def add( self, path: str, endpoint: Optional[Callable] = None, methods: List[str] = ['GET'], *, name: Optional[str] = None, autoprefix: bool = True, middleware: Optional[List] = None, auth: Optional[Guard] = None, scopes: Optional[List] = None, ) -> Callable[[Decorator], Decorator]: """Generic add method and decorator""" # Convert auth and scope helper param to middleware if middleware is None: middleware = [] if auth: middleware.append(auth) if scopes: middleware.append(Guard(scopes)) # Clean path and name (name, full_path, name, full_name) = self._clean_add(path, name, autoprefix) def handle(endpoint): # Create route SuperDict route = WebRoute({ 'path': full_path, 'name': full_name, 'endpoint': endpoint, 'methods': methods, 'middleware': middleware, 'original_path': path, 'original_name': name, }) self.routes[full_name] = route return route # Method access if endpoint: return handle(endpoint) # Decorator access def decorator(func: Decorator) -> Decorator: handle(func) return func return decorator
def group( self, prefix: str = '', *, routes: Optional[List] = None, name: str = '', tags: Optional[List[str]] = None, autoprefix: bool = True, middleware: Optional[List] = None, auth: Optional[Guard] = None, scopes: Optional[List] = None, ) -> Callable[[Decorator], Decorator]: """Route groups method and decorator""" # Convert auth helper param to middleware if middleware is None: middleware = [] if auth: middleware.append(auth) # Convert scopes to Guard route middleware if scopes: middleware.append(Guard(scopes)) # Get name if not name: name = prefix # Clean name if name: name = name.replace('/', '.') if name[-1] == '.': name = name[0:-1] # Remove trailing . if name[0] == '.': name = name[1:] # Remove beginning . def handle(routes): # Controllers return multiple routes as a List, so flatten everything into one List all_routes = [] for route in routes: if type(route) == list: all_routes.extend(route) else: all_routes.append(route) # New routes with updated prefixes new_routes = [] # Loop group routes and update prefixes for route in all_routes: # Remove old route from self.routes if route.name in self.routes: self.routes.pop(route.name) # Strip global prefix if exists path = route.path if len(path) >= len( self.prefix) and path[0:len(self.prefix )] == self.prefix: path = path[len(self.prefix):] rname = route.name if len(rname) >= len( self.name) and rname[0:len(self.name)] == self.name: rname = rname[len(self.name) + 1:] full_path = prefix + path full_name = name + '.' + rname # Get route middleware based on parent child overrides (route_middleware, route.endpoint) = self._merge_route_middleware( middleware, route.middleware, route.endpoint) #dump(route, middleware, route.middleware) # Add new route with new group prefix and name # Because this is a polymorphic router (works for Web and API router) # The self.add methods will be different. The actual route should mimic the self.add # parameters, so modify the route when calculated values and pass in as # self.add **kwargs if tags: if route.tags is None: route.tags = [] route.tags.extend(tags) route.path = full_path route.name = full_name # Override group level autoprefix only if False to disable all, else use what the inside route used if autoprefix == False: route.autoprefix = autoprefix route.middleware = route_middleware del route.original_path del route.original_name # NO, this didn't handle polymorphic router. self.add is different for each router. # new_route = self.add( # path=full_path, # endpoint=route.endpoint, # methods=route.methods, # name=full_name, # autoprefix=autoprefix, # #middleware=route.middleware or middleware # Closest one to the route wins # middleware=route_middleware # ) new_route = self.add(**route) new_routes.append(new_route) # Return new routes for recursive nested groups return new_routes # Method access if routes: return handle(routes) # Decorator access def decorator(func: Decorator) -> Decorator: # Backup and clear existing routes all_routes = self._routes.clone() self._routes = Dict() # Get routes from the group method func() # Build routes list from group method routes = [] for route in self._routes.values(): routes.append(route) # Restore all routes besides the ones in the gruop method self._routes = all_routes.clone() # Add these routes to the proper group handle(routes) return func return decorator
def add( self, # Common to both ApiRouter and WebRouter path: str, endpoint: Optional[Callable] = None, methods: List[str] = ['GET'], *, name: Optional[str] = None, autoprefix: bool = True, middleware: Optional[List] = None, auth: Optional[Guard] = None, scopes: Optional[List] = None, inherits: Optional[Callable] = None, ) -> Callable[[Decorator], Decorator]: """Add a new HTTP method route to the WebRouter :param path: URL path, beginning with / :param endpoint: None if decorator, else method to call when route is fired. :param name: Name of this route to consistently access with url('name') helper. :param autoprefix: Disable the name autoprefixer (autoprefix adds the appname. prefix). Useful when overriding another packages route. :param middleware: List of route level middleware. :param auth: Shorthand for the middleware=[Guard(['scope'])]. Usage auth=Guard(['scope']). :param scopes: Shorthand for middleware=[Guard(['scope'])]. Usage scopes=['scope1', 'scope1']. """ # Convert auth and scope helper param to middleware if middleware is None: middleware = [] if auth: middleware.append(auth) if scopes: middleware.append(Guard(scopes)) # Clean path and name (path, full_path, name, full_name) = self._format_path_name(path, name, autoprefix, methods) def handle(endpoint): # Merge function arguments if inheriting if inherits: endpoint = merge_args(inherits, endpoint) # Create route SuperDict route = WebRoute({ # Common to both ApiRouter and WebRouter 'path': full_path, 'name': full_name, 'methods': methods, 'endpoint': endpoint, 'middleware': middleware, 'autoprefix': autoprefix, 'original_path': path, 'original_name': name, }) # Full name already contains -POST, -PUT...for uniqueness across methods self.routes[full_name] = route return route # Method access if endpoint: return handle(endpoint) # Decorator access def decorator(func: Decorator) -> Decorator: handle(func) return func return decorator
def add( self, # Common to both ApiRouter and WebRouter path: str, endpoint: Optional[Callable] = None, methods: List[str] = ['GET'], *, name: Optional[str] = None, autoprefix: bool = True, middleware: Optional[List] = None, auth: Optional[Guard] = None, scopes: Optional[List] = None, inherits: Optional[Callable] = None, # ApiRouter specific responses: Optional[Dict] = None, response_model: Optional[Any] = None, tags: Optional[List[str]] = None, summary: Optional[str] = None, description: Optional[str] = None, ) -> Callable[[Decorator], Decorator]: """Add a new HTTP method route to the ApiRouter :param path: URL path, beginning with / :param endpoint: None if decorator, else method to call when route is fired. :param name: Name of this route to consistently access with url('name') helper. :param autoprefix: Disable the name autoprefixer (autoprefix adds the appname. prefix). Useful when overriding another packages route. :param middleware: List of route level middleware. :param auth: Shorthand for the middleware=[Guard(['scope'])]. Usage auth=Guard(['scope']). :param scopes: Shorthand for middleware=[Guard(['scope'])]. Usage scopes=['scope1', 'scope1']. :param response_model: Response ORM Model. Can also use -> Model on function return type. :param tags: List of tags to group the endpoint in the OpenAPI docs. :param summary: Summary of this endpoint for OpenAPI docs. :param description: Description of this endpoint for OpenAPI docs. :param inherits: Endpoint function can inhert the parameters of another function. Useful if you have tons of parameters you want shared on multiple endpoints. Usage inherits=AuthApi.getsig. """ # Convert auth and scope helper param to middleware if middleware is None: middleware = [] if auth: middleware.append(auth) if scopes: middleware.append(Guard(scopes)) # Clean path and name (path, full_path, name, full_name) = self._format_path_name(path, name, autoprefix, methods) def handle(endpoint): nonlocal response_model nonlocal description # Merge function arguments if inheriting if inherits: endpoint = merge_args(inherits, endpoint) # If endpoint is partial, grab inside func for type hint and docstrings endpoint_func = endpoint if isinstance(endpoint, partial): # Endpoint is a partial (was overwritten to default some higher order middleware) # A partial overwrites the original docstring. Functools update_wrapper will copy it back # as well as handle merging of other important properties #update_wrapper(route.endpoint, route.endpoint.func) endpoint_func = endpoint.func # Blank out the __doc__ on actual Partial itself, not actual endpoint inside partial. # If not, OpenAPI doc description will be partial(func, *args, **keywords) - new function with partial application etc... endpoint.__doc__ = None # Get response_model from typehint if not explicitly defined as a parameter response_model = response_model or get_type_hints( endpoint_func).get('return') # Get openapi description from route param or endpoint docstring description = description or endpoint_func.__doc__ # Create route SuperDict route = ApiRoute({ # Common to both ApiRouter and WebRouter 'path': full_path, 'name': full_name, 'methods': methods, 'endpoint': endpoint, 'middleware': middleware, 'autoprefix': autoprefix, 'original_path': path, 'original_name': name, 'responses': responses, # ApiRouter specific 'response_model': response_model, 'tags': tags, 'summary': summary, 'description': description, }) # Full name already contains -POST, -PUT...for uniqueness across methods self.routes[full_name] = route return route # Method access if endpoint: return handle(endpoint) # Decorator access def decorator(func: Decorator) -> Decorator: handle(func) return func return decorator