Skip to content

Commit

Permalink
Version4 Improvement #87
Browse files Browse the repository at this point in the history
  • Loading branch information
AliRn76 committed Mar 24, 2024
2 parents 7650f94 + 7467a40 commit f709aa0
Show file tree
Hide file tree
Showing 75 changed files with 3,003 additions and 1,100 deletions.
9 changes: 8 additions & 1 deletion README.md
Expand Up @@ -20,10 +20,11 @@
- Built-in API **Caching** System (In Memory, **Redis**)
- Built-in **Authentication** Classes
- Built-in **Permission** Classes
- Built-in Visual API **Monitoring** (In Terminal)
- Support Custom **Background Tasks**
- Support Custom **Middlewares**
- Support Custom **Throttling**
- Visual API **Monitoring** (In Terminal)
- Support **Function-Base** and **Class-Base** APIs
- It's One Of The **Fastest Python Frameworks**
---

Expand Down Expand Up @@ -176,6 +177,12 @@ $ pip install panther
---

### How Panther Works!

![diagram](https://raw.githubusercontent.com/AliRn76/panther/master/docs/docs/images/diagram.png)

---

### Roadmap

![roadmap](https://raw.githubusercontent.com/AliRn76/panther/master/docs/docs/images/roadmap.jpg)
Expand Down
28 changes: 18 additions & 10 deletions docs/docs/authentications.md
Expand Up @@ -3,12 +3,20 @@
> <b>Type:</b> `str`
>
> <b>Default:</b> `None`
You can set your Authentication class in `configs`, now, if you set `auth=True` in `@API()`, Panther will use this class for authentication of every `API`, and put the `user` in `request.user` or `raise HTTP_401_UNAUTHORIZED`
We implemented a built-in authentication class which used `JWT` for authentication.
You can set Authentication class in your `configs`

Panther use it, to authenticate every API/ WS if `auth=True` and give you the user or `raise HTTP_401_UNAUTHORIZED`

The `user` will be in `request.user` in APIs and in `self.user` in WSs

---

We implemented 2 built-in authentication classes which use `JWT` for authentication.

But, You can use your own custom authentication class too.

---

### JWTAuthentication

Expand All @@ -17,7 +25,7 @@ This class will
- Get the `token` from `Authorization` header of request.
- Check the `Bearer`
- `decode` the `token`
- Find the matched `user` (It uses the `USER_MODEL`)
- Find the matched `user`

> `JWTAuthentication` is going to use `panther.db.models.BaseUser` if you didn't set the `USER_MODEL` in your `configs`
Expand Down Expand Up @@ -59,9 +67,7 @@ JWTConfig = {

#### Websocket Authentication
This class is very useful when you are trying to authenticate the user in websocket

Add this into your `configs`:
The `QueryParamJWTAuthentication` is very useful when you are trying to authenticate the user in websocket, you just have to add this into your `configs`:
```python
WS_AUTHENTICATION = 'panther.authentications.QueryParamJWTAuthentication'
```
Expand All @@ -77,7 +83,9 @@ WS_AUTHENTICATION = 'panther.authentications.QueryParamJWTAuthentication'
- Or raise `panther.exceptions.AuthenticationAPIError`


- Address it in `configs`
- `AUTHENTICATION = 'project_name.core.authentications.CustomAuthentication'`
- Add it into your `configs`
```python
AUTHENTICATION = 'project_name.core.authentications.CustomAuthentication'
```

> You can see the source code of JWTAuthentication [[here]](https://github.com/AliRn76/panther/blob/da2654ccdd83ebcacda91a1aaf51d5aeb539eff5/panther/authentications.py#L38)
> You can see the source code of JWTAuthentication [[here]](https://github.com/AliRn76/panther/blob/da2654ccdd83ebcacda91a1aaf51d5aeb539eff5/panther/authentications.py)
2 changes: 1 addition & 1 deletion docs/docs/background_tasks.md
Expand Up @@ -110,7 +110,7 @@ Panther is going to run the `background tasks` as a thread in the background on
task = BackgroundTask(do_something, 'Ali', 26)
```

- > Default interval is 1.
- > Default of interval() is 1.
- > The -1 interval means infinite,
Expand Down
61 changes: 30 additions & 31 deletions docs/docs/class_first_crud.md
@@ -1,6 +1,6 @@
We assume you could run the project with [Introduction](https://pantherpy.github.io/#installation)

Now let's write custom API `Create`, `Retrieve`, `Update` and `Delete` for a `Book`:
Now let's write custom APIs for `Create`, `Retrieve`, `Update` and `Delete` a `Book`:

## Structure & Requirements
### Create Model
Expand Down Expand Up @@ -82,7 +82,7 @@ Now we are going to create a book on `post` request, We need to:

class BookAPI(GenericAPI):

def post(self):
async def post(self):
...
```

Expand All @@ -95,7 +95,7 @@ Now we are going to create a book on `post` request, We need to:

class BookAPI(GenericAPI):

def post(self, request: Request):
async def post(self, request: Request):
...
```

Expand All @@ -122,7 +122,7 @@ Now we are going to create a book on `post` request, We need to:
class BookAPI(GenericAPI):
input_model = BookSerializer

def post(self, request: Request):
async def post(self, request: Request):
...

```
Expand All @@ -138,7 +138,7 @@ Now we are going to create a book on `post` request, We need to:
class BookAPI(GenericAPI):
input_model = BookSerializer

def post(self, request: Request):
async def post(self, request: Request):
body: BookSerializer = request.validated_data
...
```
Expand All @@ -156,9 +156,9 @@ Now we are going to create a book on `post` request, We need to:
class BookAPI(GenericAPI):
input_model = BookSerializer

def post(self, request: Request):
async def post(self, request: Request):
body: BookSerializer = request.validated_data
Book.insert_one(
await Book.insert_one(
name=body.name,
author=body.author,
pages_count=body.pages_count,
Expand All @@ -180,15 +180,14 @@ Now we are going to create a book on `post` request, We need to:
class BookAPI(GenericAPI):
input_model = BookSerializer

def post(self, request: Request):
async def post(self, request: Request):
body: BookSerializer = request.validated_data
book = Book.insert_one(
book = await Book.insert_one(
name=body.name,
author=body.author,
pages_count=body.pages_count,
)
return Response(data=book, status_code=status.HTTP_201_CREATED)

```

> The response.data can be `Instance of Models`, `dict`, `str`, `tuple`, `list`, `str` or `None`
Expand All @@ -213,11 +212,11 @@ from app.models import Book
class BookAPI(GenericAPI):
input_model = BookSerializer

def post(self, request: Request):
async def post(self, request: Request):
...

def get(self):
books: list[Book] = Book.find()
async def get(self):
books = await Book.find()
return Response(data=books, status_code=status.HTTP_200_OK)

```
Expand Down Expand Up @@ -256,11 +255,11 @@ Assume we don't want to return field `author` in response:
input_model = BookSerializer
output_model = BookOutputSerializer

def post(self, request: Request):
async def post(self, request: Request):
...

def get(self):
books: list[Book] = Book.find()
async def get(self):
books = await Book.find()
return Response(data=books, status_code=status.HTTP_200_OK)
```

Expand Down Expand Up @@ -292,11 +291,11 @@ class BookAPI(GenericAPI):
cache = True
cache_exp_time = timedelta(seconds=10)

def post(self, request: Request):
async def post(self, request: Request):
...

def get(self):
books: list[Book] = Book.find()
async def get(self):
books = await Book.find()
return Response(data=books, status_code=status.HTTP_200_OK)

```
Expand Down Expand Up @@ -329,11 +328,11 @@ class BookAPI(GenericAPI):
cache_exp_time = timedelta(seconds=10)
throttling = Throttling(rate=10, duration=timedelta(minutes=1))

def post(self, request: Request):
async def post(self, request: Request):
...

def get(self):
books: list[Book] = Book.find()
async def get(self):
books = await Book.find()
return Response(data=books, status_code=status.HTTP_200_OK)

```
Expand Down Expand Up @@ -383,8 +382,8 @@ For `retrieve`, `update` and `delete` API, we are going to

class SingleBookAPI(GenericAPI):

def get(self, book_id: int):
if book := Book.find_one(id=book_id):
async def get(self, book_id: int):
if book := await Book.find_one(id=book_id):
return Response(data=book, status_code=status.HTTP_200_OK)
else:
return Response(status_code=status.HTTP_404_NOT_FOUND)
Expand All @@ -406,11 +405,11 @@ from app.serializers import BookSerializer
class SingleBookAPI(GenericAPI):
input_model = BookSerializer

def get(self, book_id: int):
async def get(self, book_id: int):
...

def put(self, request: Request, book_id: int):
is_updated: bool = Book.update_one({'id': book_id}, request.validated_data.model_dump())
async def put(self, request: Request, book_id: int):
is_updated: bool = await Book.update_one({'id': book_id}, request.validated_data.model_dump())
data = {'is_updated': is_updated}
return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
```
Expand All @@ -431,14 +430,14 @@ from app.models import Book
class SingleBookAPI(GenericAPI):
input_model = BookSerializer

def get(self, book_id: int):
async def get(self, book_id: int):
...

def put(self, request: Request, book_id: int):
async def put(self, request: Request, book_id: int):
...

def delete(self, book_id: int):
is_deleted: bool = Book.delete_one(id=book_id)
async def delete(self, book_id: int):
is_deleted: bool = await Book.delete_one(id=book_id)
if is_deleted:
return Response(status_code=status.HTTP_204_NO_CONTENT)
else:
Expand Down
20 changes: 19 additions & 1 deletion docs/docs/configs.md
Expand Up @@ -7,12 +7,14 @@ Panther collect all the configs from your `core/configs.py` or the module you pa
> <b>Type:</b> `bool` (<b>Default:</b> `False`)
It should be `True` if you want to use `panther monitor` command
and see the monitoring logs
and watch the monitoring

If `True`:

- Log every request in `logs/monitoring.log`

_Requires [watchfiles](https://watchfiles.helpmanual.io) package._

---
### [LOG_QUERIES](https://pantherpy.github.io/log_queries)
> <b>Type:</b> `bool` (<b>Default:</b> `False`)
Expand All @@ -33,6 +35,8 @@ List of middlewares you want to use
Every request goes through `authentication()` method of this `class` (if `auth = True`)

_Requires [python-jose](https://python-jose.readthedocs.io/en/latest/) package._

_Example:_ `AUTHENTICATION = 'panther.authentications.JWTAuthentication'`

---
Expand Down Expand Up @@ -119,6 +123,8 @@ It will reformat your code on every reload (on every change if you run the proje

You may want to write your custom `ruff.toml` in root of your project.

_Requires [ruff](https://docs.astral.sh/ruff/) package._

Reference: [https://docs.astral.sh/ruff/formatter/](https://docs.astral.sh/ruff/formatter/)

_Example:_ `AUTO_REFORMAT = True`
Expand All @@ -133,4 +139,16 @@ We use it to create `database` connection
### [REDIS](https://pantherpy.github.io/redis)
> <b>Type:</b> `dict` (<b>Default:</b> `{}`)
_Requires [redis](https://redis-py.readthedocs.io/en/stable/) package._

We use it to create `redis` connection


---
### [TIMEZONE](https://pantherpy.github.io/timezone)
> <b>Type:</b> `str` (<b>Default:</b> `'UTC'`)
Used in `panther.utils.timezone_now()` which returns a `datetime` based on your `timezone`

And `panther.utils.timezone_now()` used in `BaseUser.date_created` and `BaseUser.last_login`

29 changes: 29 additions & 0 deletions docs/docs/events.md
@@ -0,0 +1,29 @@
## Startup Event

Use `Event.startup` decorator

```python
from panther.events import Event


@Event.startup
def do_something_on_startup():
print('Hello, I am at startup')
```

## Shutdown Event

```python
from panther.events import Event


@Event.shutdown
def do_something_on_shutdown():
print('Good Bye, I am at shutdown')
```


## Notice

- You can have **multiple** events on `startup` and `shutdown`
- Events can be `sync` or `async`

0 comments on commit f709aa0

Please sign in to comment.