Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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