Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 - Mongoz support #177

Merged
merged 7 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Checklist

- [ ] The code has 100% test coverage
- [ ] The documentation was properly created or updated (if applicable) following the correct guidelines and language.
- [ ] You branch out from the V2 tag and the V2 tag was properly rebased from main.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
name: deploy

steps:
- uses: "actions/checkout@v3"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v4"
with:
python-version: 3.8
Expand Down
17 changes: 15 additions & 2 deletions .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,21 @@ jobs:
--health-timeout 5s
--health-retries 5

mongodb:
image: mongo:latest
env:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: mongoadmin
MONGO_INITDB_DATABASE: mongodb
ports:
- 27017:27017

steps:
- uses: "actions/checkout@v3"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v4"
with:
python-version: "${{ matrix.python-version }}"
# allow-prereleases: true
allow-prereleases: true
- uses: actions/cache@v3
id: cache
with:
Expand All @@ -48,5 +57,9 @@ jobs:
- name: "Install dependencies"
if: steps.cache.outputs.cache-hit != 'true'
run: "scripts/install"
- name: "Run linting"
run: "scripts/lint"
- name: "Run tests"
env:
DATABASE_URI: "mongodb://root:mongoadmin@localhost:27017"
run: "scripts/test"
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ use it.
* **Pluggables**: Create plugins for Esmerald and hook them into any application and/or
distribute them.
* **DAO and AsyncDAO**: Avoid database calls directly from the APIs. Use business objects instead.
* **Saffier ORM**: Native support for [Saffier ORM](https://esmerald.dev/databases/saffier/motivation.md).
* **Edgy**: Native support for [Edgy](https://esmerald.dev/databases/edgy/motivation.md).
* **ORM Support**: Native support for [Saffier][saffier_orm] and [Edgy][edgy_orm].
* **ODM Support**: Native support for [Mongoz][mongoz_odm].
* **APIView**: Class Based endpoints for your beloved OOP design.
* **JSON serialization/deserialization**: Both UJSON and ORJON support.
* **Lifespan**: Support for the newly lifespan and on_start/on_shutdown events.
Expand Down Expand Up @@ -533,3 +533,7 @@ There are plenty more things you can do with Esmerald. Enjoy! 😊

Currently there are no sponsors of Esmerald but you can financially help and support the author though
[GitHub sponsors](https://github.com/sponsors/tarsil) and become a **Special one** or a **Legend**.

[saffier_orm]: https://esmerald.dev/databases/saffier/motivation
[edgy_orm]: https://esmerald.dev/databases/saffier/motivation
[mongoz_odm]: https://esmerald.dev/databases/mongoz/motivation
30 changes: 30 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,36 @@ services:
expose:
- "6379"

mongodb:
restart: always
image: mongo:latest
container_name: mongo
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: mongoadmin
MONGO_INITDB_DATABASE: mongodb
volumes:
- "mongo_db_data:/data/db"
expose:
- 27017
ports:
- 27017:27017

mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ENABLE_ADMIN: 'true'
ME_CONFIG_MONGODB_SERVER: mongodb
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: mongoadmin
ME_CONFIG_BASICAUTH_USERNAME: admin
ME_CONFIG_BASICAUTH_PASSWORD: password

volumes:
esmerald:
external: true
mongo_db_data:
external: true
16 changes: 12 additions & 4 deletions docs/databases/edgy/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Since [Edgy](https://edgy.tarsild.io) if from the same author of Esmerald, it gives some
extra motivation for its use and therefore an example in how to use the
[JWTAuthMiddleware](./middleware.md), even if in a very simplistic, within your Esmerald application.
[JWTAuthMiddleware](./middleware.md), even if in a very simplistic way, within your Esmerald application.


Let us build a simple integration and application where we will be creating:
Expand Down Expand Up @@ -36,10 +36,10 @@ defaulting to the one model provided by Esmerald out-of-the-box.

## Create user API

Now that the [user model](#create-user-model) is defined and created, time to create an api
Now that the [user model](#create-user-model) is defined and created, it is time to create an api
that allows the creation of users in the system.

This example won't cover corner cases like integrity in ase of duplicates and so on as this is
This example won't cover corner cases like integrity in case of duplicates and so on as this is
something that you can easily manage.

```python title="accounts/views.py"
Expand All @@ -51,7 +51,7 @@ something that you can easily manage.
Now the [create user](#create-user-api) is available to us to be used later on, we need a view
that also allow us to login and return the JWT access token.

For this API to work, we need to garantee the data being sent is valid, authenticate and then
For this API to work, we need to guarantee the data being sent is valid, authenticate and then
return the JWT token.

```python title="accounts/views.py"
Expand Down Expand Up @@ -122,6 +122,14 @@ For this will be using `httpx` but you are free to use whatever client you prefe
{!> ../docs_src/databases/edgy/example/access.py !}
```

Did you notice the `X_API_TOKEN` in the `headers`? Well that is because the default `api_key_header`
from the [JWTConfig](../../configurations/jwt.md#parameters) is called `X_API_TOKEN` and the
contrib middleware from Esmerald to provide integration with Edgy uses it to validate if is passed
in the header or not.

Like everything in Esmerald, that is also configurable. If you change the `header` to something else
in that config, it will automatically reflect across the contib middlewares.

## Conclusions

This is just a simple example how you could use Edgy with the provided `JWTAuthMiddleware`
Expand Down
13 changes: 2 additions & 11 deletions docs/databases/edgy/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ There are different ways of calling this middleware in any Esmerald application.
### Via settings

```python
{!> ../docs_src/configurations/jwt/settings.py!}
{!> ../docs_src/databases/edgy/jwt/settings.py!}
```

### Via application instantiation
Expand All @@ -48,13 +48,4 @@ There are different ways of calling this middleware in any Esmerald application.
{!> ../docs_src/databases/edgy/middleware/example3.py !}
```

### Important note

In the examples you could see sometimes the `StarletteMiddleware` being used and in other you didn't. The reason behind
is very simple and also explained in the [middleware section](../../middleware/middleware.md#important).

If you need to specify parameters in your middleware then you will need to wrap it in a `starlette.middleware.Middleware`
object to do it so.

If no parameters are needed, then you can simply pass the middleware class directly and Esmerald will take care of the
rest.
{!> ../docs_src/_shared/databases_important_note.md !}
13 changes: 6 additions & 7 deletions docs/databases/edgy/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ why this is like the way it is.

### Meta class

There are wayf of making the models and the registry cleaner, after all, you might want to use the
There are way of making the models and the registry cleaner, after all, you might want to use the
same registry in different models across multiple applications in your codebase.

One way and a way Esmerald always recommend, is by leveraging the [settings](../../application/settings.md)
One way and a way Esmerald always recommend, is by leveraging the [settings](../../application/settings.md).

### Leveraging the settings for your models

Expand Down Expand Up @@ -70,9 +70,8 @@ have the fields declared. Esmerald has a similar approach and provides the follo

### The functions available

Using simply this model it does not bring too much benefits as it si something you can do easily and fast but the
functionality applied to this model is already something that can be something that requires some extra time to
assemble.
Using simply this model it does not bring too much benefits as it is something you can do easily and fast but the
functionality applied to it is already something that would require some extra time to assemble.

!!! Warning
The following examples assume that you are taking advantage of the settings as
Expand Down Expand Up @@ -116,7 +115,7 @@ on behind the scenes.

When using the `create_user` and `create_superuser` behind the scenes it is not only creating that same record and
storing in the database but is also <a href='https://nordpass.com/blog/password-hash/' target='_blank'>hashing</a>
the password for you using the built-in Esmerald [password hashers](#password-hashers) and this is a life saving
the password for you, using the built-in Esmerald [password hashers](#password-hashers) and this is a life saving
time and implementation.

Esmerald also provides the `set_password` and `check_password` functions to make it easier to
Expand Down Expand Up @@ -159,4 +158,4 @@ Edgy also provides some insights in
## General example

More examples and more thorough explanations how to use [Edgy](https://edgy.tarsild.io)
can be consulted in their [documentation](https://edgy.tarsild.io).
can be consulted in its own [documentation](https://edgy.tarsild.io).
2 changes: 1 addition & 1 deletion docs/databases/edgy/motivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ $ pip install edgy[mssql]
## Documentation and more details

Since Esmerald supports Edgy, the best place to understand how to use all the powerful features given by it you can simply
use their [documentation](https://edgy.tarsild.io/) and learn more how to leverage it.
use its [documentation](https://edgy.tarsild.io/) and learn more how to leverage it.
153 changes: 153 additions & 0 deletions docs/databases/mongoz/documents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Documents

In simple terms, documents are a representation of a database table in the format of an object declared by the language
implementing.

## User documents

Integrating with Mongoz, Esmerald already provides some of the documents that helps you with the
initial configuration.

1. `AbstractUser` - The base user class containing all the fields required a user.
2. `User` - A subsclass of `AbstractUser`

## User

Extenting the existing `User` document is as simple as this:

```python
{!> ../docs_src/databases/mongoz/models.py !}
```

This is a clean way of declaring the documents and using the Mongoz docs, you can easily understand
why this is like the way it is.

### Meta class

There are way of making the documents and the registry cleaner, after all, you might want to use the
same registry in different documents across multiple applications in your codebase.

One way and a way Esmerald always recommend, is by leveraging the [settings](../../application/settings.md).

### Leveraging the settings for your documents

Let us use the same example but this time, we will be using the settings.
Since **you can access the settings anywhere in the codebase**.

Check it out the example below and how by using the settings, you can literally leverage Esmerald
with Mongoz.

=== "settings.py"

```python hl_lines="10-12"
{!> ../docs_src/databases/mongoz/settings/settings.py !}
```

=== "documents.py"

```python hl_lines="9 32"
{!> ../docs_src/databases/mongoz/settings/models.py !}
```

You simply isolated your common database connection and registry inside the globally accessible
settings and with that you can import in any Esmerald application, ChildEsmerald or whatever you
prefer without the need of repeating yourself.

### User document fields

If you are familiar with Django then you are also aware of the way they have their users table and the way they
have the fields declared. Esmerald has a similar approach and provides the following.

* `first_name`
* `last_name`
* `username`
* `email`
* `password`
* `last_login`
* `is_active`
* `is_staff`
* `is_superuser`

### The functions available

Using simply this document it does not bring too much benefits as it is something you can do easily and fast but the
functionality applied to it is already something that would require some extra time to assemble.

!!! Warning
The following examples assume that you are taking advantage of the settings as
[decribed before](#leveraging-the-settings-for-your-documents).

**create_user**

```python
{!> ../docs_src/databases/mongoz/create_user.py !}
```

**create_superuser**

```python
{!> ../docs_src/databases/mongoz/create_superuser.py !}
```

**check_password**

```python hl_lines="28"
{!> ../docs_src/databases/mongoz/check_password.py !}
```

Because you are using the `User` provided by Esmerald, the same object is also prepared to validate
the password against the system. If you are familiar with Django, this was based on it and has the
same principle.

**set_password**

```python hl_lines="28"
{!> ../docs_src/databases/mongoz/set_password.py !}
```

The same for setting passwords. The `User` already contains the functionality to set a password of
a given `User` instance.

### What happened

Although the way of using the `User` table was intentionally designed to be simple there is in fact a lot going
on behind the scenes.

When using the `create_user` and `create_superuser` behind the scenes it is not only creating that same record and
storing in the database but is also <a href='https://nordpass.com/blog/password-hash/' target='_blank'>hashing</a>
the password for you, using the built-in Esmerald [password hashers](#password-hashers) and this is a life saving
time and implementation.

Esmerald also provides the `set_password` and `check_password` functions to make it easier to
validate and change a user's password using the `User` instance.

## Password Hashers

Esmerald already brings some pre-defined password hashers that are available in the
[Esmerald settings](../../application/settings.md) and ready to be used.

```python

@property
def password_hashers(self) -> List[str]:
return [
"esmerald.contrib.auth.hashers.PBKDF2PasswordHasher",
"esmerald.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
]

```

Esmerald uses <a href='https://passlib.readthedocs.io/en/stable/' target='_blank'>passlib</a> under the hood
in order to facilitate the process of hashing passwords.

You can always override the property `password_hashers` in your
[custom settings](../../application/settings.md#custom-settings) and use your own.

```python
{!> ../docs_src/databases/mongoz/hashers.py !}
```

## General example

More examples and more thorough explanations how to use [Mongoz](https://mongoz.tarsild.io)
can be consulted in its own [documentation](https://mongoz.tarsild.io).
Loading