Skip to content

Commit

Permalink
Merge pull request #60 from DorskFR/dependencies_routes
Browse files Browse the repository at this point in the history
Dependencies can be added to each separate route (list of dependencies)
  • Loading branch information
awtkns authored Apr 18, 2021
2 parents 2869887 + f81a759 commit 48e5541
Show file tree
Hide file tree
Showing 24 changed files with 392 additions and 186 deletions.
5 changes: 0 additions & 5 deletions .flake8

This file was deleted.

46 changes: 42 additions & 4 deletions docs/en/docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ All the CRUDRouters included with `fastapi_crudrouter` support FastAPI dependenc
Since all CRUDRouter's subclass the [FastAPI APIRouter](https://fastapi.tiangolo.com/tutorial/bigger-applications/?h=+router#apirouter),
you can use any features APIRouter features.

## Example
Below is a simple example of how you could use OAuth2 in conjunction with a CRUDRouter to secure your routes.

```python
from fastapi import FastAPI, testclient, Depends
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from fastapi_crudrouter import MemoryCRUDRouter

Expand All @@ -19,6 +18,45 @@ def token_auth(token: str):
if not token:
raise HTTPException(401, "Invalid token")

router = MemoryCRUDRouter(schema=Potato, dependencies=[Depends(token_auth)])
router = MemoryCRUDRouter(schema=MySchema, dependencies=[Depends(token_auth)])
app.include_router(router)
```
```

## Custom Dependencies Per Route
All CRUDRouters allow you to add a sequence of dependencies on a per-route basis. The dependencies can be set when
initializing any CRUDRouter using the key word arguments below.

```python
CRUDRouter(
# ...
get_all_route=[Depends(get_all_route_dep), ...],
get_one_route=[Depends(get_one_route_dep), ...],
create_route=[Depends(create_route_dep), ...],
update_route=[Depends(update_route_dep), ...],
delete_one_route=[Depends(user), ...],
delete_all_route=[Depends(user), ...],
)

```

!!! tip "Multiple Dependencies Per Route"
As they are passed as a sequence, you are able to set multiple dependencies individually per route.

!!! attention "Disabling Routes Entirely"
Setting the key word arguments shown above to `False`, disables the route entirely.


### Example
In the example below, we are adding a fictitious dependency to the "create route" (POST) which requires the user to be
logged in to create an object. At the same time, we are also independently adding an admin dependency to only the "delete all
route" which limits the route's usage to only admin users.

```python
MemoryCRUDRouter(
schema=MySchema,
create_route=[Depends(user)],
delete_all_route=[Depends(admin)]
)
```


5 changes: 5 additions & 0 deletions docs/en/docs/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ As an example, the *delete all* route can be disabled by doing the following:
router = MemmoryCRUDRouter(schema=MyModel, deleta_all_route=False)
```

!!! tip "Custom Dependencies"
Instead to passing a bool to the arguments listed about, you can also pass a sequence of custom dependencies to be
applied to each route. See the docs on [dependencies](dependencies.md) for more details.


## Overriding Routes
Should you need to add custom functionality to any of your routes any of the included routers allows you to do so.
Should you wish to disable a route from being generated, it can be done [here](../routing/#disabling-routes).
Expand Down
4 changes: 2 additions & 2 deletions docs/en/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ theme:
nav:
- Overview: index.md
- Schemas: schemas.md
- Pagination: pagination.md
- Routing: routing.md
- Backends:
- In Memory: backends/memory.md
- SQLAlchemy: backends/sqlalchemy.md
- Databases (async): backends/async.md
- Ormar (async): backends/ormar.md
- Tortoise (async): backends/tortoise.md
- Routing: routing.md
- Pagination: pagination.md
- Dependencies: dependencies.md
- Contributing: contributing.md
- Releases: releases.md
Expand Down
44 changes: 30 additions & 14 deletions fastapi_crudrouter/core/_base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Any, Callable, Generic, List, Optional, Type
from typing import Any, Callable, Generic, List, Optional, Type, Union

from fastapi import APIRouter, HTTPException
from fastapi.types import DecoratedCallable

from ._types import T
from ._types import T, DEPENDENCIES
from ._utils import pagination_factory, schema_factory

NOT_FOUND = HTTPException(404, "Item not found")
Expand All @@ -23,12 +23,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any,
) -> None:

Expand All @@ -53,59 +53,75 @@ def __init__(
super().__init__(prefix=prefix, tags=tags, **kwargs)

if get_all_route:
super().add_api_route(
self._add_api_route(
"",
self._get_all(),
methods=["GET"],
response_model=Optional[List[self.schema]], # type: ignore
summary="Get All",
dependencies=get_all_route,
)

if create_route:
super().add_api_route(
self._add_api_route(
"",
self._create(),
methods=["POST"],
response_model=self.schema,
summary="Create One",
dependencies=create_route,
)

if delete_all_route:
super().add_api_route(
self._add_api_route(
"",
self._delete_all(),
methods=["DELETE"],
response_model=Optional[List[self.schema]], # type: ignore
summary="Delete All",
dependencies=delete_all_route,
)

if get_one_route:
super().add_api_route(
self._add_api_route(
"/{item_id}",
self._get_one(),
methods=["GET"],
response_model=self.schema,
summary="Get One",
dependencies=get_one_route,
)

if update_route:
super().add_api_route(
self._add_api_route(
"/{item_id}",
self._update(),
methods=["PUT"],
response_model=self.schema,
summary="Update One",
dependencies=update_route,
)

if delete_one_route:
super().add_api_route(
self._add_api_route(
"/{item_id}",
self._delete_one(),
methods=["DELETE"],
response_model=self.schema,
summary="Delete One",
dependencies=delete_one_route,
)

def _add_api_route(
self,
path: str,
endpoint: Callable[..., Any],
dependencies: Union[bool, DEPENDENCIES],
**kwargs: Any,
) -> None:
dependencies = [] if isinstance(dependencies, bool) else dependencies
super().add_api_route(path, endpoint, dependencies=dependencies, **kwargs)

def api_route(
self, path: str, *args: Any, **kwargs: Any
) -> Callable[[DecoratedCallable], DecoratedCallable]:
Expand Down
4 changes: 3 additions & 1 deletion fastapi_crudrouter/core/_types.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import Dict, Optional, TypeVar
from typing import Dict, TypeVar, Optional, Sequence

from fastapi.params import Depends
from pydantic import BaseModel

PAGINATION = Dict[str, Optional[int]]
PYDANTIC_SCHEMA = BaseModel

T = TypeVar("T", bound=BaseModel)
DEPENDENCIES = Optional[Sequence[Depends]]
25 changes: 17 additions & 8 deletions fastapi_crudrouter/core/databases.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
from typing import Any, Callable, List, Mapping, Type, Coroutine, Optional
from typing import (
Any,
Callable,
List,
Mapping,
Type,
Coroutine,
Optional,
Union,
)

from fastapi import HTTPException

from . import CRUDGenerator, NOT_FOUND, _utils
from ._types import PAGINATION, PYDANTIC_SCHEMA
from ._types import PAGINATION, PYDANTIC_SCHEMA, DEPENDENCIES

try:
from sqlalchemy.sql.schema import Table
Expand All @@ -29,12 +38,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any
) -> None:
assert (
Expand Down
16 changes: 8 additions & 8 deletions fastapi_crudrouter/core/mem.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Callable, List, Type, cast, Optional
from typing import Any, Callable, List, Type, cast, Optional, Union

from . import CRUDGenerator, NOT_FOUND
from ._types import PAGINATION, PYDANTIC_SCHEMA as SCHEMA
from ._types import DEPENDENCIES, PAGINATION, PYDANTIC_SCHEMA as SCHEMA

CALLABLE = Callable[..., SCHEMA]
CALLABLE_LIST = Callable[..., List[SCHEMA]]
Expand All @@ -16,12 +16,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any
) -> None:
super().__init__(
Expand Down
15 changes: 8 additions & 7 deletions fastapi_crudrouter/core/ormar.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
Type,
cast,
Coroutine,
Union,
)

from fastapi import HTTPException

from . import CRUDGenerator, NOT_FOUND, _utils
from ._types import PAGINATION
from ._types import DEPENDENCIES, PAGINATION

try:
from ormar import Model, NoMatch
Expand All @@ -34,12 +35,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any
) -> None:
assert ormar_installed, "Ormar must be installed to use the OrmarCRUDRouter."
Expand Down
16 changes: 8 additions & 8 deletions fastapi_crudrouter/core/sqlalchemy.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Any, Callable, List, Type, Generator, Optional
from typing import Any, Callable, List, Type, Generator, Optional, Union

from fastapi import Depends, HTTPException

from . import CRUDGenerator, NOT_FOUND, _utils
from ._types import PAGINATION, PYDANTIC_SCHEMA as SCHEMA
from ._types import DEPENDENCIES, PAGINATION, PYDANTIC_SCHEMA as SCHEMA

try:
from sqlalchemy.orm import Session
Expand All @@ -28,12 +28,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any
) -> None:
assert (
Expand Down
17 changes: 8 additions & 9 deletions fastapi_crudrouter/core/tortoise.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Callable, List, Type, cast, Coroutine, Optional
from typing import Any, Callable, List, Type, cast, Coroutine, Optional, Union

from . import CRUDGenerator, NOT_FOUND
from ._types import PAGINATION, PYDANTIC_SCHEMA as SCHEMA
from ._types import DEPENDENCIES, PAGINATION, PYDANTIC_SCHEMA as SCHEMA

try:
from tortoise.models import Model
Expand All @@ -26,13 +26,12 @@ def __init__(
prefix: Optional[str] = None,
tags: Optional[List[str]] = None,
paginate: Optional[int] = None,
get_all_route: bool = True,
get_one_route: bool = True,
create_route: bool = True,
update_route: bool = True,
delete_one_route: bool = True,
delete_all_route: bool = True,
*args: Any,
get_all_route: Union[bool, DEPENDENCIES] = True,
get_one_route: Union[bool, DEPENDENCIES] = True,
create_route: Union[bool, DEPENDENCIES] = True,
update_route: Union[bool, DEPENDENCIES] = True,
delete_one_route: Union[bool, DEPENDENCIES] = True,
delete_all_route: Union[bool, DEPENDENCIES] = True,
**kwargs: Any
) -> None:
assert (
Expand Down
Loading

1 comment on commit 48e5541

@vercel
Copy link

@vercel vercel bot commented on 48e5541 Apr 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.