diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index e03ef9b..171a5c6 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -1,38 +1,65 @@ name: Integration Test -on: - workflow_dispatch: - permissions: - users: - - ItayTheDar +on: [push, workflow_call] + jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + app_type: ["Blank", "SyncORM", "AsyncORM"] + steps: - - name: Check out repository code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.8' # or any version you need - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install . - - - name: Start the application - run: | - pynest create-nest-app -n NestApp -db sqlite - cd NestApp - pynest generate-module -n user - sudo python main.py & - sleep 10 # Wait for the server to start - - - name: Test the application - run: | - curl http://localhost:8000/docs - curl get http://localhost:8000/get_user - curl post http://localhost:8000/add_user -d '{"name": "test"}' + - name: Check out repository code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install . + + - name: Start Application + run: | + if [ "${{ matrix.app_type }}" == "Blank" ]; then + app_name="NestApp" + is_async="" + elif [ "${{ matrix.app_type }}" == "SyncORM" ]; then + app_name="ORMNestApp" + is_async="" + elif [ "${{ matrix.app_type }}" == "AsyncORM" ]; then + app_name="AsyncORMNestApp" + is_async="--is-async" + fi + + if [ "${{ matrix.app_type }}" == "Blank" ]; then + pynest create-nest-app -n "$app_name" + else + pynest create-nest-app -n "$app_name" -db sqlite $is_async + pip install aiosqlite + fi + + cd "$app_name" + pynest g module -n user + uvicorn "app:app" --host "0.0.0.0" --port 8000 --reload & + + - name: Wait for the server to start + run: sleep 10 + + - name: Test the application + run: | + curl -f http://localhost:8000/docs + curl -f -X 'POST' \ + "http://localhost:8000/user/" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{"name": "Example Name"}' + curl -f http://localhost:8000/user/ + + - name: Kill the server + run: kill $(jobs -p) || true diff --git a/README.md b/README.md index 73a52f5..444619f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-
+
PyNest is a Python framework built on top of FastAPI that follows the modular architecture of NestJS
@@ -26,7 +26,7 @@ PyNest is designed to help structure your APIs in an intuitive, easy to understa
With PyNest, you can build scalable and maintainable APIs with ease. The framework supports dependency injection, type annotations, decorators, and code generation, making it easy to write clean and testable code.
-This framework is not a direct port of NestJS to Python but rather a re-imagining of the framework specifically for Python developers, including data scientists, data analysts, and data engineers. It aims to assist them in building better and faster APIs for their data applications.
+This framework is not a direct port of NestJS to Python but rather a re-imagining of the framework specifically for Python developers, including backend engineers and ML engineers. It aims to assist them in building better and faster APIs for their data applications.
## Getting Started
To get started with PyNest, you'll need to install it using pip:
@@ -44,17 +44,12 @@ this command will create a new project with the following structure:
```text
├── app.py
-├── orm_config.py
├── main.py
+├── requirements.txt
+├── .gitignore
+├── README.md
├── src
│ ├── __init__.py
-│ ├── examples
-│ │ ├── __init__.py
-│ │ ├── examples_controller.py
-│ │ ├── examples_service.py
-│ │ ├── examples_model.py
-│ ├── ├── examples_entity.py
-│ ├── ├── examples_module.py
```
once you have created your app, get into the folder and run the following command:
@@ -66,20 +61,20 @@ cd my_app_name
run the server with the following command:
```bash
-uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
+uvicorn "app:app" --host "0.0.0.0" --port "8000" --reload
```
-Now you can visit [OpenAPI](http://localhost:80/docs) in your browser to see the default API documentation.
+Now you can visit [OpenAPI](http://localhost:8000/docs) in your browser to see the default API documentation.
### Adding modules
To add a new module to your application, you can use the pynest generate module command:
-
+]()
```bash
-pynest generate-module -n users
+pynest g module -n users
```
-This will create a new module called ```users``` in your application with the following structure:
+This will create a new module called ```users``` in your application with the following structure under the ```src``` folder:
```text
├── users
@@ -97,6 +92,42 @@ You can then start defining routes and other application components using decora
For more information on how to use PyNest, check out the official documentation at https://pythonnest.github.io/PyNest/.
+## PyNest CLI Usage Guide
+
+This document provides a guide on how to use the PyNest Command Line Interface (CLI). Below are the available commands and their descriptions:
+
+### `pynest` Command
+
+- **Description**: The main command group for PyNest CLI.
+
+#### `create-nest-app` Subcommand
+
+- **Description**: Create a new nest app.
+- **Options**:
+ - `--app-name`/`-n`: The name of the nest app (required).
+ - `--db-type`/`-db`: The type of the database (optional). You can specify PostgreSQL, MySQL, SQLite, or MongoDB.
+ - `--is-async`: Whether the project should be asynchronous (optional, default is False).
+
+### `g` command group
+
+- **Description**: Group command for generating boilerplate code.
+
+#### `module` Subcommand
+
+- **Description**: Generate a new module (controller, service, entity, model, module).
+- **Options**:
+ - `--name`/`-n`: The name of the module (required).
+
+
+#### CLI Examples
+* create a blank nest application -
+`pynest create-nest-app -n my_app_name`
+* create a nest application with postgres database and async connection -
+
+`pynest create-nest-app -n my_app_name -db postgresql --is-async`
+* create new module -
+`pynest g module -n users`
+
## Key Features
### Modular Architecture
diff --git a/docs/async_orm.md b/docs/async_orm.md
index 6539ead..0925058 100644
--- a/docs/async_orm.md
+++ b/docs/async_orm.md
@@ -10,7 +10,8 @@ environment.
- Python 3.9+
- PyNest (latest version)
-- SQLAlchemy 2.0
+- SQLAlchemy < 2.0
+- async driver for your database (e.g. asyncpg for PostgreSQL, aiomysql for MySQL, or aiosqlite for SQLite)
## Setting Up
@@ -27,14 +28,20 @@ pip install pynest-api
#### Create a new project
```bash
-pynest create-nest-app -n my_app_name -db postgresql --async
+pynest create-nest-app -n my_app_name -db postgresql --is-async
+```
+
+Note: you need to install the async driver for your database, for example, if you are using PostgreSQL, you need to install asyncpg:
+
+```bash
+pip install asyncpg
```
this command will create a new project with the following structure:
```text
├── app.py
-├── orm_config.py
+├── config.py
├── main.py
├── src
│ ├── __init__.py
@@ -49,7 +56,7 @@ this command will create a new project with the following structure:
once you have created your app, this is the code that support the asynchronous feature:
-`orm_config.py`
+`config.py`
```python
from nest.core.database.orm_provider import AsyncOrmProvider
@@ -77,9 +84,9 @@ Now we need to declare the App object and register the module in
`app.py`
```python
-from orm_config import config
+from config import config
from nest.core.app import App
-from src.examples.examples_module import ExamplesModule
+from .examples_module import ExamplesModule
app = App(
description="PyNest service",
@@ -101,24 +108,14 @@ AsyncOrmProvider is a key component in managing asynchronous database connection
### AsyncSession
AsyncSession from sqlalchemy.ext.asyncio is used for executing asynchronous database operations. It is essential for leveraging the full capabilities of SQLAlchemy 2.0 in an async environment.
-## Implementing Async Features
-### Creating Models
-Define your models using SQLAlchemy's declarative base. For example, the Examples model:
-
-### AsyncSession
-
-AsyncSession, from sqlalchemy.ext.asyncio is used
-for executing asynchronous database operations.It is essential for leveraging the full capabilities of SQLAlchemy 2.0 in
-an async environment.
-
## Implementing Async Features
-### Creating Models
+### Creating Entities
Define your models using SQLAlchemy's declarative base. For example, the Examples model:
```python
-from orm_config import config
+from config import config
from sqlalchemy import Integer, String
from sqlalchemy.orm import Mapped, mapped_column
@@ -136,11 +133,11 @@ Implement services to handle business logic.
There are two ways of creating service.
1. In that way, the service does not init any parameter, and that each function that depends on the database is getting
- the async session fron the controller
+ the async session from the controller
```python
-from src.examples.examples_model import Examples
-from src.examples.examples_entity import Examples as ExamplesEntity
+from .examples_model import Examples
+from .examples_entity import Examples as ExamplesEntity
from nest.core.decorators.database import async_db_request_handler
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -168,9 +165,9 @@ class ExamplesService:
using the session that was init in the constructor
```python
-from src.examples.examples_model import Examples
-from src.examples.examples_entity import Examples as ExamplesEntity
-from orm_config import config
+from .examples_model import Examples
+from .examples_entity import Examples as ExamplesEntity
+from config import config
from nest.core.decorators.database import async_db_request_handler
from functools import lru_cache
from sqlalchemy import select, text
@@ -181,7 +178,7 @@ class ExamplesService:
def __init__(self):
self.orm_config = config
- self.session = self.orm_config.get_self_db
+ self.session = self.orm_config.session
@async_db_request_handler
async def add_examples(self, examples: Examples):
@@ -206,18 +203,18 @@ logic.
Here we have also two ways of creating the controller.
-1. In that way, the controller's functions are getting the async session from the orm_config
+1. In that way, the controller's functions are getting the async session from the config
```python
from nest.core import Controller, Get, Post, Depends
-from src.examples.examples_service import ExamplesService
-from src.examples.examples_model import Examples
-from orm_config import config
+from .examples_service import ExamplesService
+from .examples_model import Examples
+from config import config
from sqlalchemy.ext.asyncio import AsyncSession
-@Controller("examples", prefix="examples")
+@Controller("examples")
class ExamplesController:
service: ExamplesService = Depends(ExamplesService)
@@ -236,11 +233,11 @@ class ExamplesController:
```python
from nest.core import Controller, Get, Post, Depends
-from src.examples.examples_service import ExamplesService
-from src.examples.examples_model import Examples
+from .examples_service import ExamplesService
+from .examples_model import Examples
-@Controller("examples", prefix="examples")
+@Controller("examples")
class ExamplesController:
service: ExamplesService = Depends(ExamplesService)
@@ -256,6 +253,22 @@ class ExamplesController:
> **Hint:** Keep in mind that there are no difference between the two methods, the only difference is the way of getting
> the async session object, and how to use it. Choose you favorite syntax and use it.
+### Creating Module
+
+Create a module to register the controller and the service.
+
+```python
+from .examples_controller import ExamplesController
+from .examples_service import ExamplesService
+
+
+class ExamplesModule:
+ controllers = [ExamplesController]
+ services = [ExamplesService]
+```
+
+
+
## async_db_request_handler decorator
The async_db_request_handler decorator is used to handle the async session object. It is used in the service layer to
diff --git a/docs/blank.md b/docs/blank.md
index e104c94..c022b37 100644
--- a/docs/blank.md
+++ b/docs/blank.md
@@ -79,7 +79,7 @@ Implement services to handle business logic.
`examples_service.py`
```python
-from src.examples.examples_model import Examples
+from .examples_model import Examples
from functools import lru_cache
@@ -105,11 +105,11 @@ logic.
```python
from nest.core import Controller, Get, Post, Depends
-from src.examples.examples_service import ExamplesService
-from src.examples.examples_model import Examples
+from .examples_service import ExamplesService
+from .examples_model import Examples
-@Controller("examples", prefix="examples")
+@Controller("examples")
class ExamplesController:
service: ExamplesService = Depends(ExamplesService)
@@ -121,3 +121,19 @@ class ExamplesController:
async def add_examples(self, examples: Examples):
return await self.service.add_examples(examples)
```
+
+## Creating Module
+
+create the module file to register the controller and the service
+
+`examples_module.py`
+
+```python
+from .examples_controller import ExamplesController
+from .examples_service import ExamplesService
+
+
+class ExamplesModule:
+ controllers = [ExamplesController]
+ services = [ExamplesService]
+```
diff --git a/docs/imgs/pynest-logo.png b/docs/imgs/pynest-logo.png
new file mode 100644
index 0000000..13a7cbe
Binary files /dev/null and b/docs/imgs/pynest-logo.png differ
diff --git a/docs/imgs/pynest_logo-modified.png b/docs/imgs/pynest_logo-modified.png
deleted file mode 100644
index f143b5a..0000000
Binary files a/docs/imgs/pynest_logo-modified.png and /dev/null differ
diff --git a/docs/mongodb.md b/docs/mongodb.md
index 1789214..0f48ec3 100644
--- a/docs/mongodb.md
+++ b/docs/mongodb.md
@@ -49,7 +49,7 @@ this command will create a new project with the following structure:
```text
├── app.py
-├── orm_config.py
+├── config.py
├── main.py
├── src
│ ├── __init__.py
@@ -64,7 +64,7 @@ this command will create a new project with the following structure:
once you have created your app, this is the code that support the mongo integration:
-`orm_config.py`
+`config.py`
```python
from nest.core.database.odm_provider import OdmProvider
@@ -92,7 +92,7 @@ Now we need to declare the App object and register the module in
`app.py`
```python
-from orm_config import config
+from config import config
from nest.core.app import App
from src.examples.examples_module import ExamplesModule
@@ -145,8 +145,8 @@ class Examples(BaseModel):
Implement services to handle business logic.
```python
-from src.examples.examples_model import Examples
-from src.examples.examples_entity import Examples as ExamplesEntity
+from .examples_model import Examples
+from .examples_entity import Examples as ExamplesEntity
from nest.core.decorators import db_request_handler
from functools import lru_cache
@@ -173,11 +173,11 @@ logic.
```python
from nest.core import Controller, Get, Post, Depends
-from src.examples.examples_service import ExamplesService
-from src.examples.examples_model import Examples
+from .examples_service import ExamplesService
+from .examples_model import Examples
-@Controller("examples", prefix="/examples")
+@Controller("examples")
class ExamplesController:
service: ExamplesService = Depends(ExamplesService)
@@ -196,8 +196,8 @@ class ExamplesController:
Create a module to register the controller and service.
```python
-from src.examples.examples_controller import ExamplesController
-from src.examples.examples_service import ExamplesService
+from .examples_controller import ExamplesController
+from .examples_service import ExamplesService
class ExamplesModule:
diff --git a/docs/sync_orm.md b/docs/sync_orm.md
index 17cb83a..2dcf1cf 100644
--- a/docs/sync_orm.md
+++ b/docs/sync_orm.md
@@ -32,7 +32,7 @@ this command will create a new project with the following structure:
```text
├── app.py
-├── orm_config.py
+├── config.py
├── main.py
├── src
│ ├── __init__.py
@@ -45,9 +45,9 @@ this command will create a new project with the following structure:
│ ├── ├── examples_module.py
```
-once you have created your app, this is the code that support the asynchronous feature:
+once you have created your app, this should be the code that supports the database connection:
-`orm_config.py`
+`config.py`
```python
from nest.core.database.orm_provider import OrmProvider
@@ -76,7 +76,7 @@ Define your models using SQLAlchemy's declarative base. For example, the Example
`examples_entity.py`
```python
-from orm_config import config
+from config import config
from sqlalchemy import Column, Integer, String
class Examples(config.Base):
@@ -92,9 +92,9 @@ Implement services to handle business logic.
`examples_service.py`
```python
-from orm_config import config
-from src.examples.examples_model import Examples
-from src.examples.examples_entity import Examples as ExamplesEntity
+from config import config
+from .examples_model import Examples
+from .examples_entity import Examples as ExamplesEntity
from nest.core.decorators.database import db_request_handler
from functools import lru_cache
@@ -128,11 +128,11 @@ Finally, create a controller to handle the requests and responses. The controlle
```python
from nest.core import Controller, Get, Post, Depends
-from src.examples.examples_service import ExamplesService
-from src.examples.examples_model import Examples
+from .examples_service import ExamplesService
+from .examples_model import Examples
-@Controller("examples", prefix="examples")
+@Controller("examples")
class ExamplesController:
service: ExamplesService = Depends(ExamplesService)
@@ -146,6 +146,22 @@ class ExamplesController:
return self.service.add_examples(examples)
```
+## Creating Module
+
+create the module file to register the controller and the service
+
+`examples_module.py`
+
+```python
+from .examples_controller import ExamplesController
+from .examples_service import ExamplesService
+
+
+class ExamplesModule:
+ controllers = [ExamplesController]
+ services = [ExamplesService]
+```
+
## db_request_handler decorator
diff --git a/examples/nest_mongo_products/README.md b/examples/BlankApp/README.md
similarity index 64%
rename from examples/nest_mongo_products/README.md
rename to examples/BlankApp/README.md
index 32c53d1..81c72fd 100644
--- a/examples/nest_mongo_products/README.md
+++ b/examples/BlankApp/README.md
@@ -7,24 +7,25 @@ This is a template for a PyNest service.
## Step 1 - Create environment
- install requirements:
-
- ```bash
- pip install -r requirements
- ```
+
+```bash
+pip install -r requirements.txt
+```
## Step 2 - start service local
1. Run service with main method
- ```bash
- python main.py
- ```
+```bash
+python main.py
+```
+
2. Run service using uvicorn
- ```bash
- uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
- ```
-
+```bash
+uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
+```
+
## Step 3 - Send requests
Go to the fastapi docs and use your api endpoints - http://127.0.0.1/docs
diff --git a/examples/BlankApp/app.py b/examples/BlankApp/app.py
new file mode 100644
index 0000000..3f67e8a
--- /dev/null
+++ b/examples/BlankApp/app.py
@@ -0,0 +1,8 @@
+from nest.core.app import App
+from src.example.example_module import ExampleModule
+from src.user.user_module import UserModule
+from src.product.product_module import ProductModule
+
+app = App(
+ description="PyNest service", modules=[ExampleModule, UserModule, ProductModule]
+)
diff --git a/nest/common/templates/main.py b/examples/BlankApp/main.py
similarity index 61%
rename from nest/common/templates/main.py
rename to examples/BlankApp/main.py
index 164cf0e..10b550c 100644
--- a/nest/common/templates/main.py
+++ b/examples/BlankApp/main.py
@@ -1,11 +1,9 @@
-def generate_main():
- return """import uvicorn
+import uvicorn
if __name__ == '__main__':
uvicorn.run(
'app:app',
host="0.0.0.0",
- port=80,
+ port=8000,
reload=True
)
-"""
diff --git a/examples/BlankApp/requirements.txt b/examples/BlankApp/requirements.txt
new file mode 100644
index 0000000..a0eb808
--- /dev/null
+++ b/examples/BlankApp/requirements.txt
@@ -0,0 +1 @@
+pynest-api==0.1.0
\ No newline at end of file
diff --git a/examples/__init__.py b/examples/BlankApp/src/__init__.py
similarity index 100%
rename from examples/__init__.py
rename to examples/BlankApp/src/__init__.py
diff --git a/examples/nest_mongo_products/__init__.py b/examples/BlankApp/src/example/__init__.py
similarity index 100%
rename from examples/nest_mongo_products/__init__.py
rename to examples/BlankApp/src/example/__init__.py
diff --git a/examples/BlankApp/src/example/example_controller.py b/examples/BlankApp/src/example/example_controller.py
new file mode 100644
index 0000000..d3741bf
--- /dev/null
+++ b/examples/BlankApp/src/example/example_controller.py
@@ -0,0 +1,18 @@
+from nest.core import Controller, Get, Post, Depends
+from .example_service import ExampleService
+from .example_model import Example
+
+
+@Controller("example")
+class ExampleController:
+
+ service: ExampleService = Depends(ExampleService)
+
+ @Get("/")
+ def get_example(self):
+ return self.service.get_example()
+
+ @Post("/")
+ def add_example(self, example: Example):
+ return self.service.add_example(example)
+
diff --git a/examples/nest_mongo_products/src/examples/examples_model.py b/examples/BlankApp/src/example/example_model.py
similarity index 63%
rename from examples/nest_mongo_products/src/examples/examples_model.py
rename to examples/BlankApp/src/example/example_model.py
index 9bed257..cf144bb 100644
--- a/examples/nest_mongo_products/src/examples/examples_model.py
+++ b/examples/BlankApp/src/example/example_model.py
@@ -1,5 +1,5 @@
from pydantic import BaseModel
-class Examples(BaseModel):
+class Example(BaseModel):
name: str
diff --git a/examples/BlankApp/src/example/example_module.py b/examples/BlankApp/src/example/example_module.py
new file mode 100644
index 0000000..684d836
--- /dev/null
+++ b/examples/BlankApp/src/example/example_module.py
@@ -0,0 +1,10 @@
+from .example_controller import ExampleController
+from .example_service import ExampleService
+
+
+class ExampleModule:
+
+ def __init__(self):
+ self.controllers = [ExampleController]
+ self.providers = [ExampleService]
+
diff --git a/examples/BlankApp/src/example/example_service.py b/examples/BlankApp/src/example/example_service.py
new file mode 100644
index 0000000..a4d91c5
--- /dev/null
+++ b/examples/BlankApp/src/example/example_service.py
@@ -0,0 +1,17 @@
+from .example_model import Example
+from functools import lru_cache
+
+
+@lru_cache()
+class ExampleService:
+
+ def __init__(self):
+ self.database = []
+
+ def get_example(self):
+ return self.database
+
+ def add_example(self, example: Example):
+ self.database.append(example)
+ return example
+
diff --git a/examples/nest_mongo_products/src/__init__.py b/examples/BlankApp/src/product/__init__.py
similarity index 100%
rename from examples/nest_mongo_products/src/__init__.py
rename to examples/BlankApp/src/product/__init__.py
diff --git a/examples/BlankApp/src/product/product_controller.py b/examples/BlankApp/src/product/product_controller.py
new file mode 100644
index 0000000..6ca471e
--- /dev/null
+++ b/examples/BlankApp/src/product/product_controller.py
@@ -0,0 +1,18 @@
+from nest.core import Controller, Get, Post, Depends
+from .product_service import ProductService
+from .product_model import Product
+
+
+@Controller("product")
+class ProductController:
+
+ service: ProductService = Depends(ProductService)
+
+ @Get("/")
+ def get_product(self):
+ return self.service.get_product()
+
+ @Post("/")
+ def add_product(self, product: Product):
+ return self.service.add_product(product)
+
diff --git a/examples/nest_products/src/products/products_model.py b/examples/BlankApp/src/product/product_model.py
similarity index 52%
rename from examples/nest_products/src/products/products_model.py
rename to examples/BlankApp/src/product/product_model.py
index c6f0c1a..67d9224 100644
--- a/examples/nest_products/src/products/products_model.py
+++ b/examples/BlankApp/src/product/product_model.py
@@ -1,9 +1,6 @@
-from typing import Optional
-
from pydantic import BaseModel
class Product(BaseModel):
name: str
- price: float
- description: str
+
diff --git a/examples/BlankApp/src/product/product_module.py b/examples/BlankApp/src/product/product_module.py
new file mode 100644
index 0000000..be51617
--- /dev/null
+++ b/examples/BlankApp/src/product/product_module.py
@@ -0,0 +1,10 @@
+from .product_controller import ProductController
+from .product_service import ProductService
+
+
+class ProductModule:
+
+ def __init__(self):
+ self.controllers = [ProductController]
+ self.providers = [ProductService]
+
diff --git a/examples/BlankApp/src/product/product_service.py b/examples/BlankApp/src/product/product_service.py
new file mode 100644
index 0000000..e63ab6a
--- /dev/null
+++ b/examples/BlankApp/src/product/product_service.py
@@ -0,0 +1,17 @@
+from .product_model import Product
+from functools import lru_cache
+
+
+@lru_cache()
+class ProductService:
+
+ def __init__(self):
+ self.database = []
+
+ def get_product(self):
+ return self.database
+
+ def add_product(self, product: Product):
+ self.database.append(product)
+ return product
+
diff --git a/examples/nest_mongo_products/src/examples/__init__.py b/examples/BlankApp/src/user/__init__.py
similarity index 100%
rename from examples/nest_mongo_products/src/examples/__init__.py
rename to examples/BlankApp/src/user/__init__.py
diff --git a/examples/BlankApp/src/user/user_controller.py b/examples/BlankApp/src/user/user_controller.py
new file mode 100644
index 0000000..df2c93b
--- /dev/null
+++ b/examples/BlankApp/src/user/user_controller.py
@@ -0,0 +1,18 @@
+from nest.core import Controller, Get, Post, Depends
+from .user_service import UserService
+from .user_model import User
+
+
+@Controller("user")
+class UserController:
+
+ service: UserService = Depends(UserService)
+
+ @Get("/")
+ def get_user(self):
+ return self.service.get_user()
+
+ @Post("/")
+ def add_user(self, user: User):
+ return self.service.add_user(user)
+
diff --git a/examples/nest_products/src/users/users_model.py b/examples/BlankApp/src/user/user_model.py
similarity index 67%
rename from examples/nest_products/src/users/users_model.py
rename to examples/BlankApp/src/user/user_model.py
index 543aa8e..3b3237f 100644
--- a/examples/nest_products/src/users/users_model.py
+++ b/examples/BlankApp/src/user/user_model.py
@@ -3,5 +3,4 @@
class User(BaseModel):
name: str
- email: str
- password: str
+
diff --git a/examples/BlankApp/src/user/user_module.py b/examples/BlankApp/src/user/user_module.py
new file mode 100644
index 0000000..edbf81d
--- /dev/null
+++ b/examples/BlankApp/src/user/user_module.py
@@ -0,0 +1,10 @@
+from .user_controller import UserController
+from .user_service import UserService
+
+
+class UserModule:
+
+ def __init__(self):
+ self.controllers = [UserController]
+ self.providers = [UserService]
+
diff --git a/examples/BlankApp/src/user/user_service.py b/examples/BlankApp/src/user/user_service.py
new file mode 100644
index 0000000..44592ed
--- /dev/null
+++ b/examples/BlankApp/src/user/user_service.py
@@ -0,0 +1,17 @@
+from .user_model import User
+from functools import lru_cache
+
+
+@lru_cache()
+class UserService:
+
+ def __init__(self):
+ self.database = []
+
+ def get_user(self):
+ return self.database
+
+ def add_user(self, user: User):
+ self.database.append(user)
+ return user
+
diff --git a/examples/MongoApp/.gitignore b/examples/MongoApp/.gitignore
new file mode 100644
index 0000000..1e70efa
--- /dev/null
+++ b/examples/MongoApp/.gitignore
@@ -0,0 +1,6 @@
+__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
diff --git a/nest/common/templates/readme.py b/examples/MongoApp/README.md
similarity index 55%
rename from nest/common/templates/readme.py
rename to examples/MongoApp/README.md
index 0c6576c..bd0a4bd 100644
--- a/nest/common/templates/readme.py
+++ b/examples/MongoApp/README.md
@@ -1,5 +1,4 @@
-def generate_readme_template() -> str:
- return """# PyNest service
+# PyNest service
This is a template for a PyNest service.
@@ -8,25 +7,25 @@ def generate_readme_template() -> str:
## Step 1 - Create environment
- install requirements:
-
- ```bash
- pip install -r requirements
- ```
+
+```bash
+pip install -r requirements.txt
+```
## Step 2 - start service local
1. Run service with main method
- ```bash
- python main.py
- ```
+```bash
+python main.py
+```
+
2. Run service using uvicorn
- ```bash
- uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
- ```
-
+```bash
+uvicorn "app:app" --host "0.0.0.0" --port "8000" --reload
+```
+
## Step 3 - Send requests
Go to the fastapi docs and use your api endpoints - http://127.0.0.1/docs
-"""
diff --git a/examples/MongoApp/app.py b/examples/MongoApp/app.py
new file mode 100644
index 0000000..fc03ff5
--- /dev/null
+++ b/examples/MongoApp/app.py
@@ -0,0 +1,14 @@
+from config import config
+from nest.core.app import App
+from src.user.user_module import UserModule
+from src.product.product_module import ProductModule
+from src.example.example_module import ExampleModule
+
+app = App(
+ description="PyNest service", modules=[UserModule, ProductModule, ExampleModule]
+)
+
+
+@app.on_event("startup")
+async def startup():
+ await config.create_all()
diff --git a/examples/MongoApp/config.py b/examples/MongoApp/config.py
new file mode 100644
index 0000000..b781982
--- /dev/null
+++ b/examples/MongoApp/config.py
@@ -0,0 +1,18 @@
+import os
+from dotenv import load_dotenv
+from nest.core.database.odm_provider import OdmProvider
+from src.user.user_entity import User
+from src.product.product_entity import Product
+from src.example.example_entity import Example
+
+load_dotenv()
+config = OdmProvider(
+ config_params={
+ "db_name": os.getenv("DB_NAME", "default_nest_db"),
+ "host": os.getenv("DB_HOST", "localhost"),
+ "user": os.getenv("DB_USER", "root"),
+ "password": os.getenv("DB_PASSWORD", "root"),
+ "port": os.getenv("DB_PORT", 27017),
+ },
+ document_models=[User, Product, Example],
+)
diff --git a/examples/MongoApp/main.py b/examples/MongoApp/main.py
new file mode 100644
index 0000000..10b550c
--- /dev/null
+++ b/examples/MongoApp/main.py
@@ -0,0 +1,9 @@
+import uvicorn
+
+if __name__ == '__main__':
+ uvicorn.run(
+ 'app:app',
+ host="0.0.0.0",
+ port=8000,
+ reload=True
+ )
diff --git a/examples/MongoApp/requirements.txt b/examples/MongoApp/requirements.txt
new file mode 100644
index 0000000..ee13ebf
--- /dev/null
+++ b/examples/MongoApp/requirements.txt
@@ -0,0 +1,2 @@
+pynest-api==0.1.0
+beanie==1.20.0
\ No newline at end of file
diff --git a/examples/nest_products/__init__.py b/examples/MongoApp/src/__init__.py
similarity index 100%
rename from examples/nest_products/__init__.py
rename to examples/MongoApp/src/__init__.py
diff --git a/examples/nest_products/src/__init__.py b/examples/MongoApp/src/example/__init__.py
similarity index 100%
rename from examples/nest_products/src/__init__.py
rename to examples/MongoApp/src/example/__init__.py
diff --git a/examples/MongoApp/src/example/example_controller.py b/examples/MongoApp/src/example/example_controller.py
new file mode 100644
index 0000000..ae89767
--- /dev/null
+++ b/examples/MongoApp/src/example/example_controller.py
@@ -0,0 +1,19 @@
+from nest.core import Controller, Get, Post, Depends
+
+from .example_service import ExampleService
+from .example_model import Example
+
+
+@Controller("example")
+class ExampleController:
+
+ service: ExampleService = Depends(ExampleService)
+
+ @Get("/")
+ async def get_example(self):
+ return await self.service.get_example()
+
+ @Post("/")
+ async def add_example(self, example: Example):
+ return await self.service.add_example(example)
+
\ No newline at end of file
diff --git a/examples/MongoApp/src/example/example_entity.py b/examples/MongoApp/src/example/example_entity.py
new file mode 100644
index 0000000..75184ee
--- /dev/null
+++ b/examples/MongoApp/src/example/example_entity.py
@@ -0,0 +1,12 @@
+from beanie import Document
+
+
+class Example(Document):
+ title: str
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "title": "Example Title",
+ }
+ }
diff --git a/examples/MongoApp/src/example/example_model.py b/examples/MongoApp/src/example/example_model.py
new file mode 100644
index 0000000..7b6a25d
--- /dev/null
+++ b/examples/MongoApp/src/example/example_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Example(BaseModel):
+ name: str
+
diff --git a/examples/MongoApp/src/example/example_module.py b/examples/MongoApp/src/example/example_module.py
new file mode 100644
index 0000000..cc2fcd8
--- /dev/null
+++ b/examples/MongoApp/src/example/example_module.py
@@ -0,0 +1,9 @@
+from .example_service import ExampleService
+from .example_controller import ExampleController
+
+
+class ExampleModule:
+
+ def __init__(self):
+ self.providers = [ExampleService]
+ self.controllers = [ExampleController]
diff --git a/examples/MongoApp/src/example/example_service.py b/examples/MongoApp/src/example/example_service.py
new file mode 100644
index 0000000..23a4230
--- /dev/null
+++ b/examples/MongoApp/src/example/example_service.py
@@ -0,0 +1,20 @@
+from .example_model import Example
+from .example_entity import Example as ExampleEntity
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class ExampleService:
+
+ @db_request_handler
+ async def add_example(self, example: Example):
+ new_example = ExampleEntity(
+ **example.dict()
+ )
+ await new_example.save()
+ return new_example.id
+
+ @db_request_handler
+ async def get_example(self):
+ return await ExampleEntity.find_all().to_list()
diff --git a/examples/nest_products/src/base/__init__.py b/examples/MongoApp/src/product/__init__.py
similarity index 100%
rename from examples/nest_products/src/base/__init__.py
rename to examples/MongoApp/src/product/__init__.py
diff --git a/examples/MongoApp/src/product/product_controller.py b/examples/MongoApp/src/product/product_controller.py
new file mode 100644
index 0000000..f14108e
--- /dev/null
+++ b/examples/MongoApp/src/product/product_controller.py
@@ -0,0 +1,19 @@
+from nest.core import Controller, Get, Post, Depends
+
+from .product_service import ProductService
+from .product_model import Product
+
+
+@Controller("product")
+class ProductController:
+
+ service: ProductService = Depends(ProductService)
+
+ @Get("/")
+ async def get_product(self):
+ return await self.service.get_product()
+
+ @Post("/")
+ async def add_product(self, product: Product):
+ return await self.service.add_product(product)
+
\ No newline at end of file
diff --git a/examples/MongoApp/src/product/product_entity.py b/examples/MongoApp/src/product/product_entity.py
new file mode 100644
index 0000000..0b6b5e1
--- /dev/null
+++ b/examples/MongoApp/src/product/product_entity.py
@@ -0,0 +1,12 @@
+from beanie import Document
+
+
+class Product(Document):
+ title: str
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "title": "Example Title",
+ }
+ }
diff --git a/examples/MongoApp/src/product/product_model.py b/examples/MongoApp/src/product/product_model.py
new file mode 100644
index 0000000..67d9224
--- /dev/null
+++ b/examples/MongoApp/src/product/product_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Product(BaseModel):
+ name: str
+
diff --git a/examples/MongoApp/src/product/product_module.py b/examples/MongoApp/src/product/product_module.py
new file mode 100644
index 0000000..681fd22
--- /dev/null
+++ b/examples/MongoApp/src/product/product_module.py
@@ -0,0 +1,9 @@
+from .product_service import ProductService
+from .product_controller import ProductController
+
+
+class ProductModule:
+
+ def __init__(self):
+ self.providers = [ProductService]
+ self.controllers = [ProductController]
diff --git a/examples/MongoApp/src/product/product_service.py b/examples/MongoApp/src/product/product_service.py
new file mode 100644
index 0000000..572fdaf
--- /dev/null
+++ b/examples/MongoApp/src/product/product_service.py
@@ -0,0 +1,20 @@
+from .product_model import Product
+from .product_entity import Product as ProductEntity
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class ProductService:
+
+ @db_request_handler
+ async def add_product(self, product: Product):
+ new_product = ProductEntity(
+ **product.dict()
+ )
+ await new_product.save()
+ return new_product.id
+
+ @db_request_handler
+ async def get_product(self):
+ return await ProductEntity.find_all().to_list()
diff --git a/examples/nest_products/src/products/__init__.py b/examples/MongoApp/src/user/__init__.py
similarity index 100%
rename from examples/nest_products/src/products/__init__.py
rename to examples/MongoApp/src/user/__init__.py
diff --git a/examples/MongoApp/src/user/user_controller.py b/examples/MongoApp/src/user/user_controller.py
new file mode 100644
index 0000000..281c1b4
--- /dev/null
+++ b/examples/MongoApp/src/user/user_controller.py
@@ -0,0 +1,19 @@
+from nest.core import Controller, Get, Post, Depends
+
+from .user_service import UserService
+from .user_model import User
+
+
+@Controller("user")
+class UserController:
+
+ service: UserService = Depends(UserService)
+
+ @Get("/")
+ async def get_user(self):
+ return await self.service.get_user()
+
+ @Post("/")
+ async def add_user(self, user: User):
+ return await self.service.add_user(user)
+
\ No newline at end of file
diff --git a/examples/nest_mongo_products/src/examples/examples_entity.py b/examples/MongoApp/src/user/user_entity.py
similarity index 54%
rename from examples/nest_mongo_products/src/examples/examples_entity.py
rename to examples/MongoApp/src/user/user_entity.py
index 9ffa313..cc204e5 100644
--- a/examples/nest_mongo_products/src/examples/examples_entity.py
+++ b/examples/MongoApp/src/user/user_entity.py
@@ -1,12 +1,12 @@
from beanie import Document
-
-
-class Examples(Document):
- name: str
-
+
+
+class User(Document):
+ title: str
+
class Config:
schema_extra = {
"example": {
- "title": "Example Name",
+ "title": "Example Title",
}
}
diff --git a/examples/nest_products/src/test/test_model.py b/examples/MongoApp/src/user/user_model.py
similarity index 66%
rename from examples/nest_products/src/test/test_model.py
rename to examples/MongoApp/src/user/user_model.py
index 5f71f68..3b3237f 100644
--- a/examples/nest_products/src/test/test_model.py
+++ b/examples/MongoApp/src/user/user_model.py
@@ -1,5 +1,6 @@
from pydantic import BaseModel
-class Test(BaseModel):
+class User(BaseModel):
name: str
+
diff --git a/examples/MongoApp/src/user/user_module.py b/examples/MongoApp/src/user/user_module.py
new file mode 100644
index 0000000..a0728b9
--- /dev/null
+++ b/examples/MongoApp/src/user/user_module.py
@@ -0,0 +1,9 @@
+from .user_service import UserService
+from .user_controller import UserController
+
+
+class UserModule:
+
+ def __init__(self):
+ self.providers = [UserService]
+ self.controllers = [UserController]
diff --git a/examples/MongoApp/src/user/user_service.py b/examples/MongoApp/src/user/user_service.py
new file mode 100644
index 0000000..c4272c3
--- /dev/null
+++ b/examples/MongoApp/src/user/user_service.py
@@ -0,0 +1,20 @@
+from .user_model import User
+from .user_entity import User as UserEntity
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class UserService:
+
+ @db_request_handler
+ async def add_user(self, user: User):
+ new_user = UserEntity(
+ **user.dict()
+ )
+ await new_user.save()
+ return new_user.id
+
+ @db_request_handler
+ async def get_user(self):
+ return await UserEntity.find_all().to_list()
diff --git a/examples/OrmAsyncApp/.gitignore b/examples/OrmAsyncApp/.gitignore
new file mode 100644
index 0000000..1e70efa
--- /dev/null
+++ b/examples/OrmAsyncApp/.gitignore
@@ -0,0 +1,6 @@
+__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
diff --git a/examples/OrmAsyncApp/README.md b/examples/OrmAsyncApp/README.md
new file mode 100644
index 0000000..81c72fd
--- /dev/null
+++ b/examples/OrmAsyncApp/README.md
@@ -0,0 +1,31 @@
+# PyNest service
+
+This is a template for a PyNest service.
+
+## Start Service
+
+## Step 1 - Create environment
+
+- install requirements:
+
+```bash
+pip install -r requirements.txt
+```
+
+## Step 2 - start service local
+
+1. Run service with main method
+
+```bash
+python main.py
+```
+
+2. Run service using uvicorn
+
+```bash
+uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
+```
+
+## Step 3 - Send requests
+
+Go to the fastapi docs and use your api endpoints - http://127.0.0.1/docs
diff --git a/examples/OrmAsyncApp/app.py b/examples/OrmAsyncApp/app.py
new file mode 100644
index 0000000..ab3efb3
--- /dev/null
+++ b/examples/OrmAsyncApp/app.py
@@ -0,0 +1,14 @@
+from config import config
+from nest.core.app import App
+from src.product.product_module import ProductModule
+from src.user.user_module import UserModule
+from src.example.example_module import ExampleModule
+
+app = App(
+ description="PyNest service", modules=[ProductModule, UserModule, ExampleModule]
+)
+
+
+@app.on_event("startup")
+async def startup():
+ await config.create_all()
diff --git a/examples/OrmAsyncApp/config.py b/examples/OrmAsyncApp/config.py
new file mode 100644
index 0000000..4c6f5e2
--- /dev/null
+++ b/examples/OrmAsyncApp/config.py
@@ -0,0 +1,12 @@
+from nest.core.database.orm_provider import AsyncOrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = AsyncOrmProvider(
+ db_type="sqlite",
+ config_params=dict(
+ db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db"),
+ )
+)
diff --git a/examples/OrmAsyncApp/main.py b/examples/OrmAsyncApp/main.py
new file mode 100644
index 0000000..10b550c
--- /dev/null
+++ b/examples/OrmAsyncApp/main.py
@@ -0,0 +1,9 @@
+import uvicorn
+
+if __name__ == '__main__':
+ uvicorn.run(
+ 'app:app',
+ host="0.0.0.0",
+ port=8000,
+ reload=True
+ )
diff --git a/examples/OrmAsyncApp/requirements.txt b/examples/OrmAsyncApp/requirements.txt
new file mode 100644
index 0000000..6e51cf8
--- /dev/null
+++ b/examples/OrmAsyncApp/requirements.txt
@@ -0,0 +1,2 @@
+pynest-api==0.1.0
+aiosqlite==0.19.0
\ No newline at end of file
diff --git a/examples/nest_products/src/test/__init__.py b/examples/OrmAsyncApp/src/__init__.py
similarity index 100%
rename from examples/nest_products/src/test/__init__.py
rename to examples/OrmAsyncApp/src/__init__.py
diff --git a/examples/nest_products/src/users/__init__.py b/examples/OrmAsyncApp/src/example/__init__.py
similarity index 100%
rename from examples/nest_products/src/users/__init__.py
rename to examples/OrmAsyncApp/src/example/__init__.py
diff --git a/examples/OrmAsyncApp/src/example/example_controller.py b/examples/OrmAsyncApp/src/example/example_controller.py
new file mode 100644
index 0000000..f854e68
--- /dev/null
+++ b/examples/OrmAsyncApp/src/example/example_controller.py
@@ -0,0 +1,22 @@
+from nest.core import Controller, Get, Post, Depends
+from sqlalchemy.ext.asyncio import AsyncSession
+from config import config
+
+
+from .example_service import ExampleService
+from .example_model import Example
+
+
+@Controller("example")
+class ExampleController:
+
+ service: ExampleService = Depends(ExampleService)
+
+ @Get("/")
+ async def get_example(self, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.get_example(session)
+
+ @Post("/")
+ async def add_example(self, example: Example, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.add_example(example, session)
+
\ No newline at end of file
diff --git a/examples/OrmAsyncApp/src/example/example_entity.py b/examples/OrmAsyncApp/src/example/example_entity.py
new file mode 100644
index 0000000..ba5815a
--- /dev/null
+++ b/examples/OrmAsyncApp/src/example/example_entity.py
@@ -0,0 +1,11 @@
+from config import config
+from sqlalchemy import Integer, String
+from sqlalchemy.orm import Mapped, mapped_column
+
+
+class Example(config.Base):
+ __tablename__ = "example"
+
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
+ name: Mapped[str] = mapped_column(String, unique=True)
+
diff --git a/examples/OrmAsyncApp/src/example/example_model.py b/examples/OrmAsyncApp/src/example/example_model.py
new file mode 100644
index 0000000..7b6a25d
--- /dev/null
+++ b/examples/OrmAsyncApp/src/example/example_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Example(BaseModel):
+ name: str
+
diff --git a/examples/OrmAsyncApp/src/example/example_module.py b/examples/OrmAsyncApp/src/example/example_module.py
new file mode 100644
index 0000000..cc2fcd8
--- /dev/null
+++ b/examples/OrmAsyncApp/src/example/example_module.py
@@ -0,0 +1,9 @@
+from .example_service import ExampleService
+from .example_controller import ExampleController
+
+
+class ExampleModule:
+
+ def __init__(self):
+ self.providers = [ExampleService]
+ self.controllers = [ExampleController]
diff --git a/examples/OrmAsyncApp/src/example/example_service.py b/examples/OrmAsyncApp/src/example/example_service.py
new file mode 100644
index 0000000..239cd0a
--- /dev/null
+++ b/examples/OrmAsyncApp/src/example/example_service.py
@@ -0,0 +1,24 @@
+from .example_model import Example
+from .example_entity import Example as ExampleEntity
+from nest.core.decorators import async_db_request_handler
+
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+
+
+class ExampleService:
+
+ @async_db_request_handler
+ async def add_example(self, example: Example, session: AsyncSession):
+ new_example = ExampleEntity(
+ **example.dict()
+ )
+ session.add(new_example)
+ await session.commit()
+ return new_example.id
+
+ @async_db_request_handler
+ async def get_example(self, session: AsyncSession):
+ query = select(ExampleEntity)
+ result = await session.execute(query)
+ return result.scalars().all()
diff --git a/docs/usage.md b/examples/OrmAsyncApp/src/product/__init__.py
similarity index 100%
rename from docs/usage.md
rename to examples/OrmAsyncApp/src/product/__init__.py
diff --git a/examples/OrmAsyncApp/src/product/product_controller.py b/examples/OrmAsyncApp/src/product/product_controller.py
new file mode 100644
index 0000000..d8c7428
--- /dev/null
+++ b/examples/OrmAsyncApp/src/product/product_controller.py
@@ -0,0 +1,22 @@
+from nest.core import Controller, Get, Post, Depends
+from sqlalchemy.ext.asyncio import AsyncSession
+from config import config
+
+
+from .product_service import ProductService
+from .product_model import Product
+
+
+@Controller("product")
+class ProductController:
+
+ service: ProductService = Depends(ProductService)
+
+ @Get("/")
+ async def get_product(self, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.get_product(session)
+
+ @Post("/")
+ async def add_product(self, product: Product, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.add_product(product, session)
+
\ No newline at end of file
diff --git a/examples/OrmAsyncApp/src/product/product_entity.py b/examples/OrmAsyncApp/src/product/product_entity.py
new file mode 100644
index 0000000..6b1874f
--- /dev/null
+++ b/examples/OrmAsyncApp/src/product/product_entity.py
@@ -0,0 +1,11 @@
+from config import config
+from sqlalchemy import Integer, String
+from sqlalchemy.orm import Mapped, mapped_column
+
+
+class Product(config.Base):
+ __tablename__ = "product"
+
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
+ name: Mapped[str] = mapped_column(String, unique=True)
+
diff --git a/examples/OrmAsyncApp/src/product/product_model.py b/examples/OrmAsyncApp/src/product/product_model.py
new file mode 100644
index 0000000..67d9224
--- /dev/null
+++ b/examples/OrmAsyncApp/src/product/product_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Product(BaseModel):
+ name: str
+
diff --git a/examples/OrmAsyncApp/src/product/product_module.py b/examples/OrmAsyncApp/src/product/product_module.py
new file mode 100644
index 0000000..681fd22
--- /dev/null
+++ b/examples/OrmAsyncApp/src/product/product_module.py
@@ -0,0 +1,9 @@
+from .product_service import ProductService
+from .product_controller import ProductController
+
+
+class ProductModule:
+
+ def __init__(self):
+ self.providers = [ProductService]
+ self.controllers = [ProductController]
diff --git a/examples/OrmAsyncApp/src/product/product_service.py b/examples/OrmAsyncApp/src/product/product_service.py
new file mode 100644
index 0000000..ae48cad
--- /dev/null
+++ b/examples/OrmAsyncApp/src/product/product_service.py
@@ -0,0 +1,24 @@
+from .product_model import Product
+from .product_entity import Product as ProductEntity
+from nest.core.decorators import async_db_request_handler
+
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+
+
+class ProductService:
+
+ @async_db_request_handler
+ async def add_product(self, product: Product, session: AsyncSession):
+ new_product = ProductEntity(
+ **product.dict()
+ )
+ session.add(new_product)
+ await session.commit()
+ return new_product.id
+
+ @async_db_request_handler
+ async def get_product(self, session: AsyncSession):
+ query = select(ProductEntity)
+ result = await session.execute(query)
+ return result.scalars().all()
diff --git a/examples/OrmAsyncApp/src/user/__init__.py b/examples/OrmAsyncApp/src/user/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/examples/OrmAsyncApp/src/user/user_controller.py b/examples/OrmAsyncApp/src/user/user_controller.py
new file mode 100644
index 0000000..d9a7574
--- /dev/null
+++ b/examples/OrmAsyncApp/src/user/user_controller.py
@@ -0,0 +1,22 @@
+from nest.core import Controller, Get, Post, Depends
+from sqlalchemy.ext.asyncio import AsyncSession
+from config import config
+
+
+from .user_service import UserService
+from .user_model import User
+
+
+@Controller("user")
+class UserController:
+
+ service: UserService = Depends(UserService)
+
+ @Get("/")
+ async def get_user(self, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.get_user(session)
+
+ @Post("/")
+ async def add_user(self, user: User, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.add_user(user, session)
+
\ No newline at end of file
diff --git a/examples/OrmAsyncApp/src/user/user_entity.py b/examples/OrmAsyncApp/src/user/user_entity.py
new file mode 100644
index 0000000..107b5ef
--- /dev/null
+++ b/examples/OrmAsyncApp/src/user/user_entity.py
@@ -0,0 +1,11 @@
+from config import config
+from sqlalchemy import Integer, String
+from sqlalchemy.orm import Mapped, mapped_column
+
+
+class User(config.Base):
+ __tablename__ = "user"
+
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
+ name: Mapped[str] = mapped_column(String, unique=True)
+
diff --git a/examples/OrmAsyncApp/src/user/user_model.py b/examples/OrmAsyncApp/src/user/user_model.py
new file mode 100644
index 0000000..3b3237f
--- /dev/null
+++ b/examples/OrmAsyncApp/src/user/user_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class User(BaseModel):
+ name: str
+
diff --git a/examples/OrmAsyncApp/src/user/user_module.py b/examples/OrmAsyncApp/src/user/user_module.py
new file mode 100644
index 0000000..a0728b9
--- /dev/null
+++ b/examples/OrmAsyncApp/src/user/user_module.py
@@ -0,0 +1,9 @@
+from .user_service import UserService
+from .user_controller import UserController
+
+
+class UserModule:
+
+ def __init__(self):
+ self.providers = [UserService]
+ self.controllers = [UserController]
diff --git a/examples/OrmAsyncApp/src/user/user_service.py b/examples/OrmAsyncApp/src/user/user_service.py
new file mode 100644
index 0000000..a294012
--- /dev/null
+++ b/examples/OrmAsyncApp/src/user/user_service.py
@@ -0,0 +1,24 @@
+from .user_model import User
+from .user_entity import User as UserEntity
+from nest.core.decorators import async_db_request_handler
+
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+
+
+class UserService:
+
+ @async_db_request_handler
+ async def add_user(self, user: User, session: AsyncSession):
+ new_user = UserEntity(
+ **user.dict()
+ )
+ session.add(new_user)
+ await session.commit()
+ return new_user.id
+
+ @async_db_request_handler
+ async def get_user(self, session: AsyncSession):
+ query = select(UserEntity)
+ result = await session.execute(query)
+ return result.scalars().all()
diff --git a/examples/OrmSyncApp/.dockerignore b/examples/OrmSyncApp/.dockerignore
new file mode 100644
index 0000000..1e70efa
--- /dev/null
+++ b/examples/OrmSyncApp/.dockerignore
@@ -0,0 +1,6 @@
+__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
diff --git a/examples/OrmSyncApp/.gitignore b/examples/OrmSyncApp/.gitignore
new file mode 100644
index 0000000..1e70efa
--- /dev/null
+++ b/examples/OrmSyncApp/.gitignore
@@ -0,0 +1,6 @@
+__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
diff --git a/examples/OrmSyncApp/README.md b/examples/OrmSyncApp/README.md
new file mode 100644
index 0000000..81c72fd
--- /dev/null
+++ b/examples/OrmSyncApp/README.md
@@ -0,0 +1,31 @@
+# PyNest service
+
+This is a template for a PyNest service.
+
+## Start Service
+
+## Step 1 - Create environment
+
+- install requirements:
+
+```bash
+pip install -r requirements.txt
+```
+
+## Step 2 - start service local
+
+1. Run service with main method
+
+```bash
+python main.py
+```
+
+2. Run service using uvicorn
+
+```bash
+uvicorn "app:app" --host "0.0.0.0" --port "80" --reload
+```
+
+## Step 3 - Send requests
+
+Go to the fastapi docs and use your api endpoints - http://127.0.0.1/docs
diff --git a/examples/OrmSyncApp/app.py b/examples/OrmSyncApp/app.py
new file mode 100644
index 0000000..6d3df6c
--- /dev/null
+++ b/examples/OrmSyncApp/app.py
@@ -0,0 +1,14 @@
+from config import config
+from nest.core.app import App
+from src.example.example_module import ExampleModule
+from src.user.user_module import UserModule
+from src.product.product_module import ProductModule
+
+app = App(
+ description="PyNest service", modules=[ExampleModule, UserModule, ProductModule]
+)
+
+
+@app.on_event("startup")
+def startup():
+ config.create_all()
diff --git a/examples/OrmSyncApp/config.py b/examples/OrmSyncApp/config.py
new file mode 100644
index 0000000..f6c9f2a
--- /dev/null
+++ b/examples/OrmSyncApp/config.py
@@ -0,0 +1,12 @@
+from nest.core.database.orm_provider import OrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = OrmProvider(
+ db_type="sqlite",
+ config_params=dict(
+ db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db"),
+ )
+)
diff --git a/examples/OrmSyncApp/main.py b/examples/OrmSyncApp/main.py
new file mode 100644
index 0000000..10b550c
--- /dev/null
+++ b/examples/OrmSyncApp/main.py
@@ -0,0 +1,9 @@
+import uvicorn
+
+if __name__ == '__main__':
+ uvicorn.run(
+ 'app:app',
+ host="0.0.0.0",
+ port=8000,
+ reload=True
+ )
diff --git a/examples/OrmSyncApp/requirements.txt b/examples/OrmSyncApp/requirements.txt
new file mode 100644
index 0000000..a0eb808
--- /dev/null
+++ b/examples/OrmSyncApp/requirements.txt
@@ -0,0 +1 @@
+pynest-api==0.1.0
\ No newline at end of file
diff --git a/examples/OrmSyncApp/src/__init__.py b/examples/OrmSyncApp/src/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/examples/OrmSyncApp/src/example/__init__.py b/examples/OrmSyncApp/src/example/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/examples/OrmSyncApp/src/example/example_controller.py b/examples/OrmSyncApp/src/example/example_controller.py
new file mode 100644
index 0000000..efb1bcb
--- /dev/null
+++ b/examples/OrmSyncApp/src/example/example_controller.py
@@ -0,0 +1,19 @@
+from nest.core import Controller, Get, Post, Depends
+
+from .example_service import ExampleService
+from .example_model import Example
+
+
+@Controller("example")
+class ExampleController:
+
+ service: ExampleService = Depends(ExampleService)
+
+ @Get("/")
+ def get_example(self):
+ return self.service.get_example()
+
+ @Post("/")
+ def add_example(self, example: Example):
+ return self.service.add_example(example)
+
\ No newline at end of file
diff --git a/examples/nest_products/src/products/products_entity.py b/examples/OrmSyncApp/src/example/example_entity.py
similarity index 50%
rename from examples/nest_products/src/products/products_entity.py
rename to examples/OrmSyncApp/src/example/example_entity.py
index 3fa39fd..37a5d8f 100644
--- a/examples/nest_products/src/products/products_entity.py
+++ b/examples/OrmSyncApp/src/example/example_entity.py
@@ -1,11 +1,10 @@
-from orm_config import config
+from config import config
from sqlalchemy import Column, Integer, String, Float
-
-
-class Product(config.Base):
- __tablename__ = "products"
-
+
+
+class Example(config.Base):
+ __tablename__ = "example"
+
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, unique=True)
- price = Column(Float)
- description = Column(String(1000))
+
diff --git a/examples/OrmSyncApp/src/example/example_model.py b/examples/OrmSyncApp/src/example/example_model.py
new file mode 100644
index 0000000..7b6a25d
--- /dev/null
+++ b/examples/OrmSyncApp/src/example/example_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Example(BaseModel):
+ name: str
+
diff --git a/examples/OrmSyncApp/src/example/example_module.py b/examples/OrmSyncApp/src/example/example_module.py
new file mode 100644
index 0000000..cc2fcd8
--- /dev/null
+++ b/examples/OrmSyncApp/src/example/example_module.py
@@ -0,0 +1,9 @@
+from .example_service import ExampleService
+from .example_controller import ExampleController
+
+
+class ExampleModule:
+
+ def __init__(self):
+ self.providers = [ExampleService]
+ self.controllers = [ExampleController]
diff --git a/examples/OrmSyncApp/src/example/example_service.py b/examples/OrmSyncApp/src/example/example_service.py
new file mode 100644
index 0000000..efc06fb
--- /dev/null
+++ b/examples/OrmSyncApp/src/example/example_service.py
@@ -0,0 +1,27 @@
+from .example_model import Example
+from .example_entity import Example as ExampleEntity
+from config import config
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class ExampleService:
+
+ def __init__(self):
+ self.config = config
+ self.session = self.config.get_db()
+
+ @db_request_handler
+ def add_example(self, example: Example):
+ new_example = ExampleEntity(
+ **example.dict()
+ )
+ self.session.add(new_example)
+ self.session.commit()
+ return new_example.id
+
+ @db_request_handler
+ def get_example(self):
+ return self.session.query(ExampleEntity).all()
+
diff --git a/examples/OrmSyncApp/src/product/__init__.py b/examples/OrmSyncApp/src/product/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/examples/OrmSyncApp/src/product/product_controller.py b/examples/OrmSyncApp/src/product/product_controller.py
new file mode 100644
index 0000000..6ca471e
--- /dev/null
+++ b/examples/OrmSyncApp/src/product/product_controller.py
@@ -0,0 +1,18 @@
+from nest.core import Controller, Get, Post, Depends
+from .product_service import ProductService
+from .product_model import Product
+
+
+@Controller("product")
+class ProductController:
+
+ service: ProductService = Depends(ProductService)
+
+ @Get("/")
+ def get_product(self):
+ return self.service.get_product()
+
+ @Post("/")
+ def add_product(self, product: Product):
+ return self.service.add_product(product)
+
diff --git a/examples/OrmSyncApp/src/product/product_model.py b/examples/OrmSyncApp/src/product/product_model.py
new file mode 100644
index 0000000..67d9224
--- /dev/null
+++ b/examples/OrmSyncApp/src/product/product_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Product(BaseModel):
+ name: str
+
diff --git a/examples/OrmSyncApp/src/product/product_module.py b/examples/OrmSyncApp/src/product/product_module.py
new file mode 100644
index 0000000..be51617
--- /dev/null
+++ b/examples/OrmSyncApp/src/product/product_module.py
@@ -0,0 +1,10 @@
+from .product_controller import ProductController
+from .product_service import ProductService
+
+
+class ProductModule:
+
+ def __init__(self):
+ self.controllers = [ProductController]
+ self.providers = [ProductService]
+
diff --git a/examples/OrmSyncApp/src/product/product_service.py b/examples/OrmSyncApp/src/product/product_service.py
new file mode 100644
index 0000000..e63ab6a
--- /dev/null
+++ b/examples/OrmSyncApp/src/product/product_service.py
@@ -0,0 +1,17 @@
+from .product_model import Product
+from functools import lru_cache
+
+
+@lru_cache()
+class ProductService:
+
+ def __init__(self):
+ self.database = []
+
+ def get_product(self):
+ return self.database
+
+ def add_product(self, product: Product):
+ self.database.append(product)
+ return product
+
diff --git a/examples/OrmSyncApp/src/user/__init__.py b/examples/OrmSyncApp/src/user/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/examples/OrmSyncApp/src/user/user_controller.py b/examples/OrmSyncApp/src/user/user_controller.py
new file mode 100644
index 0000000..df2c93b
--- /dev/null
+++ b/examples/OrmSyncApp/src/user/user_controller.py
@@ -0,0 +1,18 @@
+from nest.core import Controller, Get, Post, Depends
+from .user_service import UserService
+from .user_model import User
+
+
+@Controller("user")
+class UserController:
+
+ service: UserService = Depends(UserService)
+
+ @Get("/")
+ def get_user(self):
+ return self.service.get_user()
+
+ @Post("/")
+ def add_user(self, user: User):
+ return self.service.add_user(user)
+
diff --git a/examples/OrmSyncApp/src/user/user_model.py b/examples/OrmSyncApp/src/user/user_model.py
new file mode 100644
index 0000000..3b3237f
--- /dev/null
+++ b/examples/OrmSyncApp/src/user/user_model.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class User(BaseModel):
+ name: str
+
diff --git a/examples/OrmSyncApp/src/user/user_module.py b/examples/OrmSyncApp/src/user/user_module.py
new file mode 100644
index 0000000..edbf81d
--- /dev/null
+++ b/examples/OrmSyncApp/src/user/user_module.py
@@ -0,0 +1,10 @@
+from .user_controller import UserController
+from .user_service import UserService
+
+
+class UserModule:
+
+ def __init__(self):
+ self.controllers = [UserController]
+ self.providers = [UserService]
+
diff --git a/examples/OrmSyncApp/src/user/user_service.py b/examples/OrmSyncApp/src/user/user_service.py
new file mode 100644
index 0000000..44592ed
--- /dev/null
+++ b/examples/OrmSyncApp/src/user/user_service.py
@@ -0,0 +1,17 @@
+from .user_model import User
+from functools import lru_cache
+
+
+@lru_cache()
+class UserService:
+
+ def __init__(self):
+ self.database = []
+
+ def get_user(self):
+ return self.database
+
+ def add_user(self, user: User):
+ self.database.append(user)
+ return user
+
diff --git a/examples/nest_mongo_products/app.py b/examples/nest_mongo_products/app.py
deleted file mode 100644
index e889e79..0000000
--- a/examples/nest_mongo_products/app.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from orm_config import config
-from nest.core.app import App
-from src.examples.examples_module import ExamplesModule
-
-app = App(
- description="PyNest service",
- modules=[
- ExamplesModule,
- ],
-)
-
-
-@app.on_event("startup")
-async def startup():
- await config.create_all()
diff --git a/examples/nest_mongo_products/main.py b/examples/nest_mongo_products/main.py
deleted file mode 100644
index ff25902..0000000
--- a/examples/nest_mongo_products/main.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import uvicorn
-
-if __name__ == "__main__":
- uvicorn.run("app:app", host="0.0.0.0", port=8890, reload=True)
diff --git a/examples/nest_mongo_products/orm_config.py b/examples/nest_mongo_products/orm_config.py
deleted file mode 100644
index ad04257..0000000
--- a/examples/nest_mongo_products/orm_config.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from nest.core.database.odm_provider import OdmService
-from src.examples.examples_entity import Examples
-import os
-from dotenv import load_dotenv
-
-load_dotenv()
-
-
-config = OdmService(
- db_type="mongodb",
- config_params={
- "db_name": os.getenv("DB_NAME"),
- "host": os.getenv("DB_HOST"),
- "port": os.getenv("DB_PORT"),
- },
- document_models=[Examples],
-)
diff --git a/examples/nest_mongo_products/requirements.txt b/examples/nest_mongo_products/requirements.txt
deleted file mode 100644
index b1d5e6c..0000000
--- a/examples/nest_mongo_products/requirements.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-anyio==3.6.2
-click==8.1.3
-fastapi==0.95.1
-fastapi-utils==0.2.1
-greenlet==2.0.2
-h11==0.14.0
-idna==3.4
-pydantic==1.10.7
-python-dotenv==1.0.0
-sniffio==1.3.0
-SQLAlchemy==1.4.48
-starlette==0.26.1
-typing_extensions==4.5.0
-uvicorn==0.22.0
-pynest-api==0.0.3
-
\ No newline at end of file
diff --git a/examples/nest_mongo_products/src/examples/examples_controller.py b/examples/nest_mongo_products/src/examples/examples_controller.py
deleted file mode 100644
index 34e7b81..0000000
--- a/examples/nest_mongo_products/src/examples/examples_controller.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from nest.core import Controller, Get, Post, Depends
-
-from .examples_service import ExamplesService
-from .examples_model import Examples
-
-
-@Controller("examples")
-class ExamplesController:
- service: ExamplesService = Depends(ExamplesService)
-
- @Get("/get_examples")
- async def get_examples(self):
- return await self.service.get_examples()
-
- @Post("/add_examples")
- async def add_examples(self, examples: Examples):
- return await self.service.add_examples(examples)
diff --git a/examples/nest_mongo_products/src/examples/examples_module.py b/examples/nest_mongo_products/src/examples/examples_module.py
deleted file mode 100644
index f256de3..0000000
--- a/examples/nest_mongo_products/src/examples/examples_module.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .examples_service import ExamplesService
-from .examples_controller import ExamplesController
-
-
-class ExamplesModule:
- def __init__(self):
- self.providers = [ExamplesService]
- self.controllers = [ExamplesController]
diff --git a/examples/nest_mongo_products/src/examples/examples_service.py b/examples/nest_mongo_products/src/examples/examples_service.py
deleted file mode 100644
index c6fda42..0000000
--- a/examples/nest_mongo_products/src/examples/examples_service.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from .examples_model import Examples
-from .examples_entity import Examples as ExamplesEntity
-from nest.core.decorators import db_request_handler
-from functools import lru_cache
-
-
-@lru_cache()
-class ExamplesService:
- @db_request_handler
- async def add_examples(self, examples: Examples):
- new_examples = ExamplesEntity(**examples.dict())
- await new_examples.save()
- return new_examples.id
-
- @db_request_handler
- async def get_examples(self):
- return await ExamplesEntity.find_all().to_list()
diff --git a/examples/nest_products/app.py b/examples/nest_products/app.py
deleted file mode 100644
index 9171aed..0000000
--- a/examples/nest_products/app.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from orm_config import config
-from nest.core import App
-from src.users.users_module import UsersModule
-from src.products.products_module import ProductsModule
-from src.test.test_module import TestModule
-
-app = App(
- description="FastAPI + SQLAlchemy + PostgreSQL",
- modules=[UsersModule, ProductsModule, TestModule],
- init_db=config.create_all(),
-)
diff --git a/examples/nest_products/main.py b/examples/nest_products/main.py
deleted file mode 100644
index b11383e..0000000
--- a/examples/nest_products/main.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import uvicorn
-
-
-if __name__ == "__main__":
- uvicorn.run(
- "app:app",
- host="0.0.0.0",
- port=8080,
- )
diff --git a/examples/nest_products/orm_config.py b/examples/nest_products/orm_config.py
deleted file mode 100644
index 6d669c1..0000000
--- a/examples/nest_products/orm_config.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from nest.core import OrmService
-import os
-from dotenv import load_dotenv
-
-load_dotenv()
-
-config = OrmService(
- db_type="postgresql",
- config_params=dict(
- host=os.getenv("POSTGRESQL_HOST"),
- db_name=os.getenv("POSTGRESQL_DB_NAME"),
- user=os.getenv("POSTGRESQL_USER"),
- password=os.getenv("POSTGRESQL_PASSWORD"),
- port=int(os.getenv("POSTGRESQL_PORT")),
- ),
-)
diff --git a/examples/nest_products/src/base/base_controller.py b/examples/nest_products/src/base/base_controller.py
deleted file mode 100644
index c5aa1aa..0000000
--- a/examples/nest_products/src/base/base_controller.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from typing import Any, Dict
-
-
-class BaseController:
- def __init__(self, service: Any) -> None:
- self.service = service
diff --git a/examples/nest_products/src/base/base_module.py b/examples/nest_products/src/base/base_module.py
deleted file mode 100644
index 840e290..0000000
--- a/examples/nest_products/src/base/base_module.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import List, Type
-from examples.nest_products.src import BaseController
-from examples.nest_products.src import BaseProvider
-
-
-class BaseModule:
- def __init__(
- self,
- providers: List[Type[BaseProvider]] = None,
- controllers: List[Type[BaseController]] = None,
- ):
- providers: List[Type[BaseProvider]] = providers
- controllers: List[Type[BaseController]] = controllers
diff --git a/examples/nest_products/src/base/base_provider.py b/examples/nest_products/src/base/base_provider.py
deleted file mode 100644
index f4176a3..0000000
--- a/examples/nest_products/src/base/base_provider.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import Any
-
-
-class BaseProvider:
- def __init__(self, *args: Any, **kwargs: Any) -> None:
- pass
-
- async def on_startup(self) -> None:
- pass
-
- async def on_shutdown(self) -> None:
- pass
diff --git a/examples/nest_products/src/products/products_controller.py b/examples/nest_products/src/products/products_controller.py
deleted file mode 100644
index 3fcac71..0000000
--- a/examples/nest_products/src/products/products_controller.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from nest.core import Depends, Controller, Get, Post
-from .products_service import ProductsService
-from .products_model import Product
-
-
-@Controller("products")
-class ProductsController:
- service: ProductsService = Depends(ProductsService)
-
- @Get("/get_products")
- def get_products(self):
- return self.service.get_products()
-
- @Get("/get_product/{product_id}")
- def get_product(self, product_id: int):
- return self.service.get_product(product_id)
-
- @Post("/add_product")
- def add_product(self, product: Product):
- return self.service.add_product(product)
-
- @Get("last_product")
- def last_product(self):
- return self.service.last_product()
diff --git a/examples/nest_products/src/products/products_module.py b/examples/nest_products/src/products/products_module.py
deleted file mode 100644
index f902a42..0000000
--- a/examples/nest_products/src/products/products_module.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .products_controller import ProductsController
-from .products_service import ProductsService
-
-
-class ProductsModule:
- def __init__(self):
- self.providers = [ProductsService]
- self.controllers = [ProductsController]
diff --git a/examples/nest_products/src/products/products_service.py b/examples/nest_products/src/products/products_service.py
deleted file mode 100644
index f7ad4bc..0000000
--- a/examples/nest_products/src/products/products_service.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from .products_model import Product
-from .products_entity import Product as ProductEntity
-from orm_config import config
-from nest.core.decorators import db_request_handler
-
-
-class ProductsService:
- def __init__(self):
- self.config = config
- self.session = self.config.get_db()
-
- @db_request_handler
- def add_product(self, product: Product):
- product_entity = ProductEntity(
- name=product.name, price=product.price, description=product.description
- )
- self.session.add(product_entity)
- self.session.commit()
- return product_entity.id
-
- @db_request_handler
- def get_products(self):
- return self.session.query(ProductEntity).all()
-
- @db_request_handler
- def get_product(self, product_id: int):
- return (
- self.session.query(ProductEntity)
- .filter(ProductEntity.id == product_id)
- .first()
- )
-
- @db_request_handler
- def last_product(self):
- return (
- self.session.query(ProductEntity).order_by(ProductEntity.id.desc()).first()
- )
diff --git a/examples/nest_products/src/test/test_controller.py b/examples/nest_products/src/test/test_controller.py
deleted file mode 100644
index b6e5ed3..0000000
--- a/examples/nest_products/src/test/test_controller.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from nest.core import Controller, Get, Post, Depends
-
-from .test_service import TestService
-from .test_model import Test
-
-
-@Controller("test")
-class TestController:
- service: TestService = Depends(TestService)
-
- @Get("/get_test")
- def get_test(self):
- return self.service.get_test()
-
- @Post("/add_test")
- def add_test(self, test: Test):
- return self.service.add_test(test)
diff --git a/examples/nest_products/src/test/test_entity.py b/examples/nest_products/src/test/test_entity.py
deleted file mode 100644
index a7b249b..0000000
--- a/examples/nest_products/src/test/test_entity.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from orm_config import config
-from sqlalchemy import Column, Integer, String
-
-
-class Test(config.Base):
- __tablename__ = "test"
-
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(String, unique=True)
diff --git a/examples/nest_products/src/test/test_module.py b/examples/nest_products/src/test/test_module.py
deleted file mode 100644
index f171a9f..0000000
--- a/examples/nest_products/src/test/test_module.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .test_service import TestService
-from .test_controller import TestController
-
-
-class TestModule:
- def __init__(self):
- self.providers = [TestService]
- self.controllers = [TestController]
diff --git a/examples/nest_products/src/test/test_service.py b/examples/nest_products/src/test/test_service.py
deleted file mode 100644
index c501f73..0000000
--- a/examples/nest_products/src/test/test_service.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from .test_model import Test
-from .test_entity import Test as TestEntity
-from orm_config import config
-from nest.core.decorators import db_request_handler
-
-
-class TestService:
- def __init__(self):
- self.orm_config = config
- self.session = self.orm_config.get_db()
-
- @db_request_handler
- def add_test(self, test: Test):
- new_test = TestEntity(**test.dict())
- self.session.add(new_test)
- self.session.commit()
- return new_test.id
-
- @db_request_handler
- def get_test(self):
- return self.session.query(TestEntity).all()
diff --git a/examples/nest_products/src/users/users_controller.py b/examples/nest_products/src/users/users_controller.py
deleted file mode 100644
index b34496c..0000000
--- a/examples/nest_products/src/users/users_controller.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from nest.core import Controller, Get, Post, Depends
-
-from .users_service import UsersService
-from .users_model import User
-
-
-@Controller("users")
-class UsersController:
- users_service: UsersService = Depends(UsersService)
-
- @Get(path="/get_users")
- def get_users(self):
- return self.users_service.get_users()
-
- @Get("/get_user/{user_id}")
- def get_user(self, user_id: int):
- return self.users_service.get_user(user_id)
-
- @Post("/add_user")
- def add_users(self, user: User):
- return self.users_service.add_user(user)
diff --git a/examples/nest_products/src/users/users_entity.py b/examples/nest_products/src/users/users_entity.py
deleted file mode 100644
index 9c88c7f..0000000
--- a/examples/nest_products/src/users/users_entity.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from orm_config import config
-from sqlalchemy import Column, Integer, String
-
-
-class User(config.Base):
- __tablename__ = "users"
-
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(String, unique=True)
- email = Column(String, unique=True)
- password = Column(String)
diff --git a/examples/nest_products/src/users/users_module.py b/examples/nest_products/src/users/users_module.py
deleted file mode 100644
index 412f53d..0000000
--- a/examples/nest_products/src/users/users_module.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .users_controller import UsersController
-from .users_service import UsersService
-
-
-class UsersModule:
- def __init__(self):
- self.providers = [UsersService]
- self.controllers = [UsersController]
diff --git a/examples/nest_products/src/users/users_service.py b/examples/nest_products/src/users/users_service.py
deleted file mode 100644
index 0f5b2e8..0000000
--- a/examples/nest_products/src/users/users_service.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from .users_model import User
-from .users_entity import User as UserEntity
-from orm_config import config
-from nest.core.decorators import db_request_handler
-
-
-class UsersService:
- def __init__(self):
- self.config = config
- self.session = self.config.get_db()
-
- @db_request_handler
- def add_user(self, user: User):
- user_entity = UserEntity(
- name=user.name, email=user.email, password=user.password
- )
- self.session.add(user_entity)
- self.session.commit()
- return user_entity.id
-
- @db_request_handler
- def get_users(self):
- return self.session.query(UserEntity).all()
-
- @db_request_handler
- def get_user(self, user_id: int):
- return self.session.query(UserEntity).filter(UserEntity.id == user_id).first()
diff --git a/nest/cli/cli.py b/nest/cli/cli.py
index b3389b0..ae8228c 100644
--- a/nest/cli/cli.py
+++ b/nest/cli/cli.py
@@ -1,46 +1,71 @@
import click
from nest.cli.click_handlers import create_nest_app, create_nest_module
-
-@click.group()
-def nest_cli() -> None:
- pass
-
-
-@nest_cli.command(
- name="create-nest-app",
- help="Create a new nest app.",
-)
-@click.option(
- "--app-name",
+### Options ###
+APP_NAME = click.option(
+ "--app-name", # Changed the underscore to a hyphen for consistency
"-n",
help="The name of the nest app.",
required=True,
type=str,
+ default=".",
)
-@click.option(
+
+DB_TYPE = click.option(
"--db-type",
"-db",
- help="The type of the database (postgresql, mysql, sqlite).",
+ help="The type of the database (postgresql, mysql, sqlite, or mongo db).",
required=False,
- default="sqlite",
+ default=None,
type=str,
)
-def create_nest_app_command(app_name: str, db_type: str = "sqlite"):
- create_nest_app(app_name, db_type)
-
-@nest_cli.command(
- name="generate-module",
- help="Generate a new module (controller, service, entity, model, module).",
-)
-@click.option(
+MODULE_NAME = click.option(
"--name",
"-n",
help="The name of the module.",
- required=True,
+ required=False,
type=str,
)
+
+IS_ASYNC = click.option(
+ "--is-async", # Changed the underscore to a hyphen for consistency
+ help="Whether the project should be async or not (only for relational databases).",
+ required=False,
+ is_flag=True, # Set is_flag=True to make it a flag option
+)
+
+
+@click.group()
+def nest_cli() -> None:
+ pass
+
+
+@nest_cli.command(
+ name="create-nest-app",
+ help="Create a new nest app.",
+)
+@APP_NAME
+@DB_TYPE
+@IS_ASYNC
+def create_nest_app_command(
+ app_name: str = ".", db_type: str = None, is_async: bool = False
+):
+ print(app_name, db_type, is_async)
+ create_nest_app(app_name=app_name, db_type=db_type, is_async=is_async)
+
+
+# Create a new group for generating boilerplate
+@nest_cli.group("g", short_help="Generate boilerplate code.")
+def generate():
+ pass
+
+
+@generate.command(
+ name="module",
+ help="Generate a new module (controller, service, entity, model, module).",
+)
+@MODULE_NAME
def generate_module(name: str):
create_nest_module(name=name)
diff --git a/nest/cli/click_handlers.py b/nest/cli/click_handlers.py
index ca3225c..e4e2250 100644
--- a/nest/cli/click_handlers.py
+++ b/nest/cli/click_handlers.py
@@ -1,241 +1,34 @@
-import subprocess
-import os
-import time
+import yaml
from pathlib import Path
+from nest.common.templates.templates_factory import TemplateFactory
-from nest.common.templates.controller import generate_controller
-from nest.common.templates.module import generate_module
-from nest.common.templates.service import generate_service
-from nest.common.templates.model import generate_model
-from nest.common.templates.app import generate_app
-from nest.common.templates.main import generate_main
-from nest.common.templates.orm_config import generate_orm_config
-from nest.common.templates.readme import generate_readme_template
-from nest.common.templates.requierments import generate_requirements
-from nest.common.templates.entity import generate_entity
-from nest.common.templates.dockerfile import generate_dockerfile
+def get_metadata():
+ setting_path = Path(__file__).parent.parent / "settings.yaml"
+ assert setting_path.exists(), "settings.yaml file not found"
+ with open(setting_path, "r") as file:
+ file = yaml.load(file, Loader=yaml.FullLoader)
-def create_file(path: Path, content: str) -> None:
- """
- Create a file at the specified path with the given content.
-
- Args:
- path (Path): The path to the file.
- content (str): The content to be written to the file.
-
- Returns:
- None
- """
- with open(path, "w") as f:
- f.write(content)
-
-
-def create_folder(path: Path) -> None:
- """
- Create a folder at the specified path.
-
- Args:
- path (Path): The path to the folder.
-
- Returns:
- None
- """
- if not os.path.exists(path):
- os.makedirs(path)
-
-
-def create_readme(path: Path) -> None:
- """
- Create a README.md file at the specified path using a template.
-
- Args:
- path (Path): The path to the README.md file.
-
- Returns:
- None
- """
- readme_template = generate_readme_template()
- create_file(path, readme_template)
-
-
-def create_main(path: Path) -> None:
- """
- Create a main.py file at the specified path using a template.
-
- Args:
- path (Path): The path to the main.py file.
-
- Returns:
- None
- """
- main_template = generate_main()
- create_file(path, main_template)
-
-
-def create_models(path: Path, name: str) -> None:
- """
- Create a models file at the specified path using a template.
-
- Args:
- path (Path): The path to the models file.
- name (str): The name of the model.
-
- Returns:
- None
- """
- models_template = generate_model(name)
- create_file(path, models_template)
+ config = file["config"]
+ db_type = config["db_type"]
+ is_async = config["is_async"]
+ return db_type, is_async
-def create_requirements(path: Path) -> None:
- """
- Create a requirements.txt file at the specified path using a template.
-
- Args:
- path (Path): The path to the requirements.txt file.
-
- Returns:
- None
- """
- requirements_template = generate_requirements()
- create_file(path, requirements_template)
-
-
-def create_app(path: Path, db_type) -> None:
- """
- Create an app.py file at the specified path using a template.
-
- Args:
- path (Path): The path to the app.py file.
-
- Returns:
- None
- """
- app_template = generate_app(db_type)
- create_file(path, app_template)
-
-
-def create_orm_config(path: Path, db_type: str) -> None:
- """
- Create an orm_config.py file at the specified path using a template.
-
- Args:
- path (Path): The path to the orm_config.py file.
- db_type (str): The type of the database.
-
- Returns:
- None
- """
- orm_config_template = generate_orm_config(db_type)
- create_file(path, orm_config_template)
-
-
-def create_controller(path: Path, name: str, db_type: str) -> None:
- """
- Create a controller file at the specified path using a template.
-
- Args:
- path (Path): The path to the controller file.
- name (str): The name of the controller.
-
- Returns:
- None
- """
- controller_template = generate_controller(name, db_type)
- create_file(path, controller_template)
-
-
-def create_service(path: Path, name: str, db_type: str) -> None:
- """
- Create a service file at the specified path using a template.
-
- Args:
- path (Path): The path to the service file.
- name (str): The name of the service.
- db_type (str): The type of the database.
-
- Returns:
- None
- """
- service_template = generate_service(name, db_type)
- create_file(path, service_template)
-
-
-def create_module(path: Path, name: str) -> None:
- """
- Create a module file at the specified path using a template.
-
- Args:
- path (Path): The path to the module file.
- name (str): The name of the module.
-
- Returns:
- None
- """
- module_template = generate_module(name)
- create_file(path, module_template)
-
-
-def create_entity(path: Path, name: str, db_type: str) -> None:
- """
- Create an entity file at the specified path using a template.
-
- Args:
- path (Path): The path to the entity file.
- name (str): The name of the entity.
-
- Returns:
- None
- """
- entity_template = generate_entity(name, db_type)
- create_file(path, entity_template)
-
-
-def create_dockerfile(path: Path) -> None:
- """
- Create a Dockerfile file at the specified path using a template.
-
- Args:
- path (Path): The path to the Dockerfile file.
-
- Returns:
- None
- """
- dockerfile_template = generate_dockerfile()
- create_file(path, dockerfile_template)
-
-
-def install_requirements(path: Path, db_type: str) -> None:
- os.chdir(path)
- # subprocess.run("python -m venv venv && source venv/bin/activate", shell=True)
- # subprocess.run(["python", "-m", "pip", "install", "--upgrade", "pip"])
- # subprocess.run(["pip", "install", "-r", "requirements.txt"])
- if db_type == "mysql":
- subprocess.run(["pip", "install", "mysql-connector-python==8.0.33"])
- elif db_type == "postgresql":
- subprocess.run(["pip", "install", "psycopg2-binary==2.9.6"])
- print(
- "You need to install postgresql in your system\nfor production use only psycopg2"
- )
- elif db_type == "mongodb":
- subprocess.run(["pip", "install", "motor", "beanie"])
-
-
-def create_nest_app(name: str, db_type: str = "sqlite"):
+def create_nest_app(app_name: str = ".", db_type: str = None, is_async: bool = False):
"""
Create a new nest app
- :param name: The name of the app
+ :param app_name: The name of the app
:param db_type: The type of the database (sqlite, mysql, postgresql)
+ :param is_async: whether the project should be async or not (only for relational databases)
The files structure are:
├── app.py
- ├── orm_config.py
+ ├── config.py (only for databases)
├── main.py
├── requirements.txt
- ├── .env
├── .gitignore
├── src
│ ├── __init__.py
@@ -244,247 +37,20 @@ def create_nest_app(name: str, db_type: str = "sqlite"):
│ │ ├── examples_controller.py
│ │ ├── examples_service.py
│ │ ├── examples_model.py
- │ ├── ├── examples_entity.py
+ │ ├── ├── examples_entity.py (only for databases)
│ ├── ├── examples_module.py
.....................
│ ├── another module
- """
-
- path = Path(os.getcwd())
- root_path = path / name
- create_folder(path / name)
- print("Start creating nest app ...")
- create_app(root_path / "app.py", db_type)
- print("app.py created successfully")
- create_orm_config(root_path / "orm_config.py", db_type)
- print("orm_config.py created successfully")
- create_main(root_path / "main.py")
- print("main.py created successfully")
- create_requirements(root_path / "requirements.txt")
- print("requirements.txt created successfully")
- create_readme(root_path / "README.md")
- print("README.md created successfully")
-
- time.sleep(1)
-
- print("creating src folder ...")
- src_path = root_path / "src"
- create_folder(src_path)
- create_file(src_path / "__init__.py", "")
-
- print("creating examples module folder ... ")
- examples_path = src_path / "examples"
- create_folder(examples_path)
- create_file(examples_path / "__init__.py", "")
- create_controller(examples_path / "examples_controller.py", "examples", db_type)
- print("controller created successfully")
- create_service(examples_path / "examples_service.py", "examples", db_type)
- print("service created successfully")
- create_models(examples_path / "examples_model.py", "examples")
- print("model created successfully")
- create_entity(examples_path / "examples_entity.py", "examples", db_type)
- print("entity created successfully")
- create_module(examples_path / "examples_module.py", "examples")
- print("module created successfully")
- if db_type == "sqlite":
- create_dockerfile(root_path / "Dockerfile")
- print("Dockerfile created successfully")
- install_requirements(root_path, db_type)
-
- time.sleep(1)
- print("Project created successfully")
-
-
-def find_target_folder(path, target="src"):
- """
- Find the target folder within the specified path.
-
- Args:
- path (str): The starting path to search from.
- target (str, optional): The name of the target folder. Defaults to "src".
-
- Returns:
- str: The path of the target folder if found, or None if not found.
- """
- copy_path = Path(path).resolve()
-
- # Check if the current path contains the target folder
- src_path = copy_path / target
- if src_path.is_dir():
- return str(src_path)
-
- # Traverse up the directory tree until the target folder is found or root is reached
- while copy_path.parent != copy_path:
- copy_path = copy_path.parent
- src_path = copy_path / target
- if src_path.is_dir():
- return str(src_path)
-
- # Traverse down the directory tree until the target folder is found or leaf is reached
- for root, dirs, files in os.walk(path):
- for dir in dirs:
- if dir == target:
- return os.path.join(root, dir)
-
- # If target folder is not found, return None
- return None
-
-
-def get_import_string(path_to_file: Path, new_module: str, db_type: str):
- split_new_module = new_module.split("_")
- capitalized_new_module = "".join([word.capitalize() for word in split_new_module])
-
- if path_to_file.name == "app.py":
- new_import = f"from src.{new_module}.{new_module}_module import {capitalized_new_module}Module\n"
- elif path_to_file.name == "orm_config.py":
- new_import = f"from src.{new_module}.{new_module}_entity import {capitalized_new_module}\n"
- else:
- raise ValueError(f"File {path_to_file} is not supported")
-
- return new_import, capitalized_new_module
-
-
-def append_import(path_to_app_py: Path, new_module: str, db_type: str):
- if not os.path.exists(path_to_app_py):
- raise FileNotFoundError(f"File {path_to_app_py} not found")
- with open(path_to_app_py, "r") as file:
- lines = file.readlines()
-
- new_module_import, capitalized_new_module = get_import_string(
- path_to_app_py, new_module, db_type
- )
-
- imports_end_index = [i for i, line in enumerate(lines) if " import " in line][-1]
-
- lines = (
- lines[: imports_end_index + 1]
- + [new_module_import]
- + lines[imports_end_index + 1 :]
- )
-
- return lines, capitalized_new_module
-
-def get_module_end_index(lines, modules_start_index):
- modules_end_index = next(
- (
- i
- for i, line in enumerate(
- lines[modules_start_index:], start=modules_start_index
- )
- if "]" in line
- ),
- len(lines)
- - 1, # If closing bracket not found, append the new module at the end
- )
- return modules_end_index
-
-
-def append_module_to_app(path_to_app_py: Path, new_module: str, db_type: str):
- """
- Append a module import statement to the app.py file.
-
- Args:
- path_to_app_py (Path): The path to the app.py file.
- new_module (str): The name of the new module to import.
- db_type (str): The type of database to use.
-
- return new_import, capitalized_new_module
-
- Returns:
- None
- """
- split_new_module = new_module.split("_")
- capitalized_new_module = "".join([word.capitalize() for word in split_new_module])
-
- lines, _ = append_import(path_to_app_py, new_module, db_type)
- # Find the line index where the modules list starts
- modules_start_index = next(
- (i for i, line in enumerate(lines) if "modules=[" in line),
- len(lines) - 1, # If modules list not found, append the new module at the end
- )
- return modules_end_index
-
-
-def append_module_to_app(path_to_app_py: Path, new_module: str, db_type: str):
+ in addition to those files, a setting.yaml file will be created in the package level that will help managed configurations
"""
- Append a module import statement to the app.py file.
-
- Args:
- path_to_app_py (Path): The path to the app.py file.
- new_module (str): The name of the new module to import.
- db_type (str): The type of database to use.
-
- Raises:
- FileNotFoundError: If the app.py file does not exist.
-
- Returns:
- None
- """
- split_new_module = new_module.split("_")
- capitalized_new_module = "".join([word.capitalize() for word in split_new_module])
-
- lines, _ = append_import(path_to_app_py, new_module, db_type)
- # Find the line index where the modules list starts
- modules_start_index = next(
- (i for i, line in enumerate(lines) if "modules=[" in line),
- len(lines) - 1, # If modules list not found, append the new module at the end
+ template_factory = TemplateFactory()
+ template = template_factory.get_template(
+ module_name="example", db_type=db_type, is_async=is_async
)
-
- # Find the line index where the modules list ends
- modules_end_index = get_module_end_index(lines, modules_start_index)
-
- # Find the line index where the modules list ends
- modules_end_index = get_module_end_index(lines, modules_start_index)
-
- # Insert the new module before the closing bracket or at the end of the file
- new_lines = (
- lines[:modules_end_index]
- + [f" {capitalized_new_module}Module,\n"]
- + lines[modules_end_index:]
- )
-
- with open(path_to_app_py, "w") as file:
- file.writelines(new_lines)
-
-
-def get_db_type(config_file: Path):
- with open(config_file, "r") as file:
- lines = file.readlines()
- for line in lines:
- if "db_type" in line:
- return line.split("=")[1].strip().replace('"', "").replace(",", "")
- raise Exception("db_type not found in orm_config.py")
-
-
-def add_document_to_odm_config(config_file: Path, new_module: str, db_type: str):
- split_new_module = new_module.split("_")
- capitalized_new_module = "".join([word.capitalize() for word in split_new_module])
-
- lines, _ = append_import(config_file, new_module, db_type)
- modules_start_index = next(
- (i for i, line in enumerate(lines) if "document_models=[" in line),
- len(lines) - 1, # If modules list not found, append the new module at the end
- )
-
- modules_end_index = get_module_end_index(lines, modules_start_index)
- if modules_end_index - modules_start_index == 0:
- lines[modules_start_index] = (
- lines[modules_start_index].split("[")[0]
- + f"[{capitalized_new_module}, "
- + lines[modules_end_index].split("[")[1]
- )
- else:
- lines = (
- lines[:modules_end_index]
- + [f" {capitalized_new_module}Module,\n"]
- + lines[modules_end_index:]
- )
-
- with open(config_file, "w") as file:
- file.writelines(lines)
+ template.generate_project(app_name)
def create_nest_module(name: str):
@@ -499,30 +65,12 @@ def create_nest_module(name: str):
├── module_name_controller.py
├── module_name_service.py
├── module_name_model.py
- ├── module_name_entity.py
+ ├── module_name_entity.py (only for databases)
├── module_name_module.py
"""
- src_path = Path(find_target_folder(os.getcwd(), "src"))
-
- if name in [x.name for x in src_path.iterdir()]:
- raise Exception(f"module {name} already exists")
- if not src_path:
- raise Exception("src folder not found")
-
- config_file = src_path.parent / "orm_config.py"
- if not config_file.exists():
- raise Exception("orm_config.py file not found")
- db_type = get_db_type(config_file)
- if db_type == "mongodb":
- add_document_to_odm_config(config_file, name, db_type)
- module_path = src_path / name
- create_folder(module_path)
- create_file(module_path / "__init__.py", "")
- create_controller(module_path / f"{name}_controller.py", name, db_type)
- create_service(module_path / f"{name}_service.py", name, db_type)
- create_models(module_path / f"{name}_model.py", name)
- create_entity(module_path / f"{name}_entity.py", name, db_type)
- create_module(module_path / f"{name}_module.py", name)
- append_module_to_app(src_path.parent / "app.py", name, db_type)
-
- print(f"Module {name} created successfully!")
+ db_type, is_async = get_metadata()
+ template_factory = TemplateFactory()
+ template = template_factory.get_template(
+ module_name=name, db_type=db_type, is_async=is_async
+ )
+ template.generate_module(name)
diff --git a/nest/common/templates/__init__.py b/nest/common/templates/__init__.py
index e69de29..db19f40 100644
--- a/nest/common/templates/__init__.py
+++ b/nest/common/templates/__init__.py
@@ -0,0 +1,11 @@
+from enum import Enum
+
+
+class Database(Enum):
+ POSTGRESQL = "postgresql"
+ MYSQL = "mysql"
+ SQLITE = "sqlite"
+ MONGODB = "mongodb"
+
+ def __str__(self):
+ return self.value
diff --git a/nest/common/templates/app.py b/nest/common/templates/app.py
deleted file mode 100644
index cf2d59f..0000000
--- a/nest/common/templates/app.py
+++ /dev/null
@@ -1,17 +0,0 @@
-def generate_app(db_type: str):
- return f"""from orm_config import config
-from nest.core.app import App
-from src.examples.examples_module import ExamplesModule
-
-app = App(
- description="PyNest service",
- modules=[
- ExamplesModule,
- ]
-)
-
-
-@app.on_event("startup")
-async def startup():
- {'await config.create_all()' if db_type == 'mongodb' else 'config.create_all()'}
-"""
diff --git a/nest/common/templates/base_template.py b/nest/common/templates/base_template.py
new file mode 100644
index 0000000..6b87d8e
--- /dev/null
+++ b/nest/common/templates/base_template.py
@@ -0,0 +1,321 @@
+from abc import ABC, abstractmethod
+from pathlib import Path
+import os
+from typing import List, Tuple, Union, Callable
+from nest import __version__
+import subprocess
+import ast
+import astor
+
+
+def get_module_strings(module_name: str) -> Tuple[List[str], str]:
+ split_module_name = module_name.split("_")
+ capitalized_module_name = "".join([word.capitalize() for word in split_module_name])
+ return split_module_name, capitalized_module_name
+
+
+class BaseTemplate(ABC):
+ def __init__(self, module_name: str):
+ self.module_name = module_name
+ self.split_module_name, self.capitalized_module_name = get_module_strings(
+ module_name
+ )
+ self.class_name = f"{self.capitalized_module_name}Module"
+ self.base_path = Path(os.getcwd())
+ self.version = __version__
+ self.nest_path = Path(__file__).parent.parent.parent
+
+ @staticmethod
+ def main_file():
+ return """import uvicorn
+
+if __name__ == '__main__':
+ uvicorn.run(
+ 'app:app',
+ host="0.0.0.0",
+ port=8000,
+ reload=True
+ )
+"""
+
+ @abstractmethod
+ def app_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def config_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def requirements_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def docker_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def dockerignore_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def gitignore_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def settings_file(self):
+ raise NotImplementedError
+
+ @staticmethod
+ def readme_file():
+ return """# PyNest service
+
+This is a template for a PyNest service.
+
+## Start Service
+
+## Step 1 - Create environment
+
+- install requirements:
+
+```bash
+pip install -r requirements.txt
+```
+
+## Step 2 - start service local
+
+1. Run service with main method
+
+```bash
+python main.py
+```
+
+2. Run service using uvicorn
+
+```bash
+uvicorn "app:app" --host "0.0.0.0" --port "8000" --reload
+```
+
+## Step 3 - Send requests
+
+Go to the fastapi docs and use your api endpoints - http://127.0.0.1/docs
+"""
+
+ @abstractmethod
+ def module_file(self):
+ return """
+class ExampleModule:
+ self.controllers = []
+ self.providers = []
+
+"""
+
+ @abstractmethod
+ def model_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def service_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def controller_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def entity_file(self):
+ raise NotImplementedError
+
+ @staticmethod
+ def create_template(path: Path, content: Union[str, Callable]) -> None:
+ """
+ Create a file at the specified path with the given content.
+
+ Args:
+ path (Path): The path to the file.
+ content (str): The content to be written to the file.
+
+ Returns:
+ None
+ """
+ if callable(content):
+ content = content()
+ print("Generate file: ", path.stem)
+ with open(path, "w") as f:
+ f.write(content)
+
+ @staticmethod
+ def create_folder(path: Path) -> None:
+ """
+ Create a folder at the specified path.
+
+ Args:
+ path (Path): The path to the folder.
+
+ Returns:
+ None
+ """
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+ @abstractmethod
+ def generate_project(self, project_name: str):
+ raise NotImplementedError
+
+ def print_all_templates(self):
+ for attr in dir(self):
+ if attr.endswith("_file"):
+ print(f"Template: {attr}\n")
+ print(getattr(self, attr)())
+ print("-" * 100)
+
+ @staticmethod
+ def find_target_folder(path: str, target: str = "src"):
+ """
+ Find the target folder within the specified path.
+
+ Args:
+ path (str): The starting path to search from.
+ target (str, optional): The name of the target folder. Defaults to "src".
+
+ Returns:
+ str: The path of the target folder if found, or None if not found.
+ """
+ copy_path = Path(path).resolve()
+
+ # Check if the current path contains the target folder
+ src_path = copy_path / target
+ if src_path.is_dir():
+ return str(src_path)
+
+ # Traverse up the directory tree until the target folder is found or root is reached
+ while copy_path.parent != copy_path:
+ copy_path = copy_path.parent
+ src_path = copy_path / target
+ if src_path.is_dir():
+ return str(src_path)
+
+ # Traverse down the directory tree until the target folder is found or leaf is reached
+ for root, dirs, files in os.walk(path):
+ for directory in dirs:
+ if directory == target:
+ return os.path.join(root, directory)
+
+ # If target folder is not found, return None
+ return None
+
+ @staticmethod
+ def format_with_black(file_path):
+ subprocess.run(["black", file_path], check=True)
+
+ @staticmethod
+ def save_file_with_astor(file_path, tree):
+ with open(file_path, "w") as file:
+ file.write(astor.to_source(tree))
+
+ @staticmethod
+ def get_ast_tree(file_path: Union[str, Path]) -> ast.Module:
+ with open(file_path, "r") as file:
+ source = file.read()
+
+ return ast.parse(source)
+
+ def append_import(
+ self, file_path: str, module_path: str, class_name: str, import_exception: str
+ ) -> ast.Module:
+ tree = self.get_ast_tree(file_path)
+ import_node = ast.ImportFrom(
+ module=module_path, names=[ast.alias(name=class_name, asname=None)], level=0
+ )
+
+ # Find the last import in the file
+ last_import_index = -1
+ for i, node in enumerate(tree.body):
+ if isinstance(node, (ast.Import, ast.ImportFrom)):
+ last_import_index = i
+
+ if last_import_index == -1:
+ raise ValueError(f"You must have at least one import - {import_exception}")
+ # Insert the new import after the last existing import
+ tree.body.insert(last_import_index + 1, import_node)
+
+ return tree
+
+ def append_module_to_app(self, path_to_app_py: str):
+ tree = self.append_import(
+ file_path=path_to_app_py,
+ module_path=f"src.{self.module_name}.{self.module_name}_module",
+ class_name=self.class_name,
+ import_exception="from nest.core import App",
+ )
+ modified = False
+
+ for node in ast.walk(tree):
+ if (
+ isinstance(node, ast.Call)
+ and hasattr(node.func, "id")
+ and node.func.id == "App"
+ ):
+ for keyword in node.keywords:
+ if keyword.arg == "modules":
+ if (
+ isinstance(keyword.value, ast.List)
+ and len(keyword.value.elts) > 0
+ ):
+ # Append to existing list
+ keyword.value.elts.append(
+ ast.Name(id=self.class_name, ctx=ast.Load())
+ )
+ else:
+ # Create a new list with the module
+ keyword.value = ast.List(
+ elts=[ast.Name(id=self.class_name, ctx=ast.Load())],
+ ctx=ast.Load(),
+ )
+ modified = True
+ break
+
+ if modified:
+ with open(path_to_app_py, "w") as file:
+ file.write(astor.to_source(tree))
+ self.format_with_black(path_to_app_py)
+
+ def validate_new_module(self, module_name: str):
+ src_path = Path(self.find_target_folder(self.base_path, "src"))
+
+ if module_name in [x.name for x in src_path.iterdir()]:
+ raise Exception(f"module {module_name} already exists")
+ if not src_path:
+ raise Exception("src folder not found")
+
+ return src_path
+
+ @abstractmethod
+ def create_module(self, module_name: str, src_path: Path):
+ raise NotImplementedError
+
+ @abstractmethod
+ def generate_module(self, module_name: str):
+ """
+ Create a new nest module with the following structure:
+
+ ├── __init__.py
+ ├── module_name_controller.py
+ ├── module_name_service.py
+ ├── module_name_model.py
+ ├── module_name_entity.py
+ ├── module_name_module.py
+ """
+ raise NotImplementedError
+
+
+if __name__ == "__main__":
+ base_template = BaseTemplate(
+ module_name="example",
+ )
+ base_template.append_module_to_app(
+ path_to_app_py="/Users/itayd/PycharmProjects/PyNestRepo/examples/MyApp/app.py"
+ )
+ base_template.append_module_to_app(
+ path_to_app_py="/Users/itayd/PycharmProjects/PyNestRepo/examples/MyApp/app.py"
+ )
diff --git a/nest/common/templates/blank_template.py b/nest/common/templates/blank_template.py
new file mode 100644
index 0000000..b0d9036
--- /dev/null
+++ b/nest/common/templates/blank_template.py
@@ -0,0 +1,139 @@
+from pathlib import Path
+
+from nest.common.templates.base_template import BaseTemplate, get_module_strings
+from abc import ABC
+
+
+class BlankTemplate(BaseTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(module_name)
+
+ def app_file(self):
+ return f"""from nest.core.app import App
+
+
+app = App(
+ description="PyNest service",
+ modules=[]
+)
+ """
+
+ def config_file(self):
+ pass
+
+ def docker_file(self):
+ pass
+
+ def dockerignore_file(self):
+ pass
+
+ def gitignore_file(self):
+ pass
+
+ def module_file(self):
+ return f"""from .{self.module_name}_controller import {self.capitalized_module_name}Controller
+from .{self.module_name}_service import {self.capitalized_module_name}Service
+
+
+class {self.capitalized_module_name}Module:
+
+ def __init__(self):
+ self.controllers = [{self.capitalized_module_name}Controller]
+ self.providers = [{self.capitalized_module_name}Service]
+
+"""
+
+ def model_file(self):
+ return f"""from pydantic import BaseModel
+
+
+class {self.capitalized_module_name}(BaseModel):
+ name: str
+
+"""
+
+ def service_file(self):
+ return f"""from .{self.module_name}_model import {self.capitalized_module_name}
+from functools import lru_cache
+
+
+@lru_cache()
+class {self.capitalized_module_name}Service:
+
+ def __init__(self):
+ self.database = []
+
+ def get_{self.module_name}(self):
+ return self.database
+
+ def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ self.database.append({self.module_name})
+ return {self.module_name}
+
+"""
+
+ def controller_file(self):
+ return f"""from nest.core import Controller, Get, Post, Depends
+from .{self.module_name}_service import {self.capitalized_module_name}Service
+from .{self.module_name}_model import {self.capitalized_module_name}
+
+
+@Controller("{self.module_name}")
+class {self.capitalized_module_name}Controller:
+
+ service: {self.capitalized_module_name}Service = Depends({self.capitalized_module_name}Service)
+
+ @Get("/")
+ def get_{self.module_name}(self):
+ return self.service.get_{self.module_name}()
+
+ @Post("/")
+ def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ return self.service.add_{self.module_name}({self.module_name})
+
+"""
+
+ def entity_file(self):
+ pass
+
+ def create_module(self, module_name: str, src_path: Path):
+ module_path = src_path / module_name
+ self.create_folder(module_path)
+ self.create_template(module_path / "__init__.py", "")
+ self.create_template(
+ module_path / f"{module_name}_module.py", self.module_file()
+ )
+ self.create_template(
+ module_path / f"{module_name}_controller.py", self.controller_file()
+ )
+ self.create_template(
+ module_path / f"{module_name}_service.py", self.service_file()
+ )
+ self.create_template(module_path / f"{module_name}_model.py", self.model_file())
+ self.append_module_to_app(path_to_app_py=src_path.parent / "app.py")
+
+ def generate_module(self, module_name: str):
+ src_path = self.validate_new_module(module_name)
+ self.create_module(module_name, src_path)
+
+ def generate_project(self, project_name: str):
+ self.create_template(self.nest_path / "settings.yaml", self.settings_file())
+ root = self.base_path / project_name
+ src_path = root / "src"
+ self.create_folder(root)
+ self.create_template(root / "main.py", self.main_file())
+ self.create_template(root / "app.py", self.app_file())
+ self.create_template(root / "README.md", self.readme_file())
+ self.create_template(root / "requirements.txt", self.requirements_file())
+ self.create_folder(src_path)
+ self.create_template(src_path / "__init__.py", "")
+
+ def settings_file(self):
+ return f"""# This file is used to configure the nest server.
+config:
+ db_type: null
+ is_async: false
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}"""
diff --git a/nest/common/templates/controller.py b/nest/common/templates/controller.py
deleted file mode 100644
index d177a49..0000000
--- a/nest/common/templates/controller.py
+++ /dev/null
@@ -1,27 +0,0 @@
-def generate_controller(controller_name: str, db_type: str) -> str:
- split_controller_name = controller_name.split("_")
- capitalized_controller_name = "".join(
- [word.capitalize() for word in split_controller_name]
- )
- is_async = "async " if db_type == "mongodb" else ""
- is_await = "await " if db_type == "mongodb" else ""
- template = f"""from nest.core import Controller, Get, Post, Depends
-
-from src.{controller_name}.{controller_name}_service import {capitalized_controller_name}Service
-from src.{controller_name}.{controller_name}_model import {capitalized_controller_name}
-
-
-@Controller("{controller_name}")
-class {capitalized_controller_name}Controller:
-
- service: {capitalized_controller_name}Service = Depends({capitalized_controller_name}Service)
-
- @Get("/get_{controller_name}")
- {is_async}def get_{controller_name}(self):
- return {is_await}self.service.get_{controller_name}()
-
- @Post("/add_{controller_name}")
- {is_async}def add_{controller_name}(self, {controller_name}: {capitalized_controller_name}):
- return {is_await}self.service.add_{controller_name}({controller_name})
- """
- return template
diff --git a/nest/common/templates/dockerfile.py b/nest/common/templates/dockerfile.py
deleted file mode 100644
index 344065d..0000000
--- a/nest/common/templates/dockerfile.py
+++ /dev/null
@@ -1,9 +0,0 @@
-def generate_dockerfile() -> str:
- template = f"""FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8
-
-RUN pip install --upgrade pip
-RUN pip install --no-cache-dir -r requirements.txt
-
-CMD ["uvicorn", "app.app:app", "--host", "0.0.0.0", "--port", "80", "--reload"]
-"""
- return template
diff --git a/nest/common/templates/entity.py b/nest/common/templates/entity.py
deleted file mode 100644
index 7166449..0000000
--- a/nest/common/templates/entity.py
+++ /dev/null
@@ -1,31 +0,0 @@
-def generate_entity(name: str, db_type: str) -> str:
- split_name = name.split("_")
- capitalized_name = "".join([word.capitalize() for word in split_name])
- if db_type == "mongodb":
- template = f"""from beanie import Document
-
-
-class {capitalized_name}(Document):
- title: str
-
- class Config:
- schema_extra = {{
- "example": {{
- "title": "Example Name",
- }}
- }}
-"""
- else:
- template = f"""from orm_config import config
-from sqlalchemy import Column, Integer, String, Float
-
-
-class {capitalized_name}(config.Base):
- __tablename__ = "{name}"
-
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(String, unique=True)
-
- """
-
- return template
diff --git a/nest/common/templates/model.py b/nest/common/templates/model.py
deleted file mode 100644
index 429ffde..0000000
--- a/nest/common/templates/model.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def generate_model(controller_name: str) -> str:
- split_controller_name = controller_name.split("_")
- capitalized_controller_name = "".join(
- [word.capitalize() for word in split_controller_name]
- )
- template = f"""from pydantic import BaseModel
-
-
-class {capitalized_controller_name}(BaseModel):
- name: str
- """
- return template
diff --git a/nest/common/templates/module.py b/nest/common/templates/module.py
deleted file mode 100644
index f7a5af1..0000000
--- a/nest/common/templates/module.py
+++ /dev/null
@@ -1,19 +0,0 @@
-def generate_module(controller_name: str) -> str:
- split_controller_name = controller_name.split("_")
- capitalized_controller_name = "".join(
- [word.capitalize() for word in split_controller_name]
- )
- template = f"""from src.{controller_name}.{controller_name}_service import {capitalized_controller_name}Service
-from src.{controller_name}.{controller_name}_controller import {capitalized_controller_name}Controller
-
-
-class {capitalized_controller_name}Module:
-
- def __init__(self):
- self.providers = [{capitalized_controller_name}Service]
- self.controllers = [{capitalized_controller_name}Controller]
-
-
-
-"""
- return template
diff --git a/nest/common/templates/mongo_template.py b/nest/common/templates/mongo_template.py
new file mode 100644
index 0000000..2a3a58d
--- /dev/null
+++ b/nest/common/templates/mongo_template.py
@@ -0,0 +1,138 @@
+import ast
+from abc import ABC
+from pathlib import Path
+from nest.common.templates.orm_template import AsyncORMTemplate
+from nest.common.templates import Database
+
+
+class MongoTemplate(AsyncORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.MONGODB,
+ )
+
+ def config_file(self):
+ return f"""import os
+from dotenv import load_dotenv
+from nest.core.database.odm_provider import OdmProvider
+
+load_dotenv()
+
+config = OdmProvider(
+ config_params={{
+ "db_name": os.getenv("DB_NAME", "default_nest_db"),
+ "host": os.getenv("DB_HOST", "localhost"),
+ "user": os.getenv("DB_USER", "root"),
+ "password": os.getenv("DB_PASSWORD", "root"),
+ "port": os.getenv("DB_PORT", 27017),
+ }},
+ document_models=[]
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+beanie==1.20.0"""
+
+ def docker_file(self):
+ return ""
+
+ def entity_file(self):
+ return f"""from beanie import Document
+
+
+class {self.capitalized_module_name}(Document):
+ title: str
+
+ class Config:
+ schema_extra = {{
+ "example": {{
+ "title": "Example Title",
+ }}
+ }}
+"""
+
+ def controller_file(self):
+ return f"""from nest.core import Controller, Get, Post, Depends
+
+from .{self.module_name}_service import {self.capitalized_module_name}Service
+from .{self.module_name}_model import {self.capitalized_module_name}
+
+
+@Controller("{self.module_name}")
+class {self.capitalized_module_name}Controller:
+
+ service: {self.capitalized_module_name}Service = Depends({self.capitalized_module_name}Service)
+
+ @Get("/")
+ async def get_{self.module_name}(self):
+ return await self.service.get_{self.module_name}()
+
+ @Post("/")
+ async def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ return await self.service.add_{self.module_name}({self.module_name})
+ """
+
+ def service_file(self):
+ return f"""from .{self.module_name}_model import {self.capitalized_module_name}
+from .{self.module_name}_entity import {self.capitalized_module_name} as {self.capitalized_module_name}Entity
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class {self.capitalized_module_name}Service:
+
+ @db_request_handler
+ async def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ new_{self.module_name} = {self.capitalized_module_name}Entity(
+ **{self.module_name}.dict()
+ )
+ await new_{self.module_name}.save()
+ return new_{self.module_name}.id
+
+ @db_request_handler
+ async def get_{self.module_name}(self):
+ return await {self.capitalized_module_name}Entity.find_all().to_list()
+"""
+
+ def add_document_to_odm_config(self, config_file: Path):
+ tree = self.append_import(
+ file_path=config_file,
+ module_path=f"src.{self.module_name}.{self.module_name}_entity",
+ class_name=self.capitalized_module_name,
+ import_exception="from nest.core.database.odm_provider import OdmProvider",
+ )
+ modified = False
+
+ for node in ast.walk(tree):
+ if (
+ isinstance(node, ast.Call)
+ and hasattr(node.func, "id")
+ and node.func.id == "OdmProvider"
+ ):
+ for keyword in node.keywords:
+ if keyword.arg == "document_models":
+ if isinstance(keyword.value, ast.List):
+ # Append to existing list
+ keyword.value.elts.append(
+ ast.Name(
+ id=self.capitalized_module_name, ctx=ast.Load()
+ )
+ )
+ modified = True
+ break
+
+ if modified:
+ self.save_file_with_astor(config_file, tree)
+ self.format_with_black(config_file)
+
+ def generate_module(self, module_name: str):
+ src_path = self.validate_new_module(module_name)
+ config_file = self.validate_config_file(src_path)
+ self.create_module(
+ src_path=src_path,
+ module_name=module_name,
+ )
+ self.add_document_to_odm_config(config_file)
diff --git a/nest/common/templates/mysql_template.py b/nest/common/templates/mysql_template.py
new file mode 100644
index 0000000..8158b60
--- /dev/null
+++ b/nest/common/templates/mysql_template.py
@@ -0,0 +1,86 @@
+from abc import ABC
+
+from nest.common.templates.orm_template import ORMTemplate, AsyncORMTemplate
+from nest.common.templates import Database
+
+
+class MySQLTemplate(ORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.MYSQL,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import OrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = OrmProvider(
+ db_type="mysql",
+ config_params=dict(
+ host=os.getenv("MYSQL_HOST"),
+ db_name=os.getenv("MYSQL_DB_NAME"),
+ user=os.getenv("MYSQL_USER"),
+ password=os.getenv("MYSQL_PASSWORD"),
+ port=int(os.getenv("MYSQL_PORT")),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+mysql-connector-python==8.2.0
+"""
+
+ def docker_file(self):
+ pass
+
+ def dockerignore_file(self):
+ pass
+
+ def gitignore_file(self):
+ pass
+
+
+class AsyncMySQLTemplate(AsyncORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.MYSQL,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import AsyncOrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = AsyncOrmProvider(
+ db_type="mysql",
+ config_params=dict(
+ host=os.getenv("MYSQL_HOST"),
+ db_name=os.getenv("MYSQL_DB_NAME"),
+ user=os.getenv("MYSQL_USER"),
+ password=os.getenv("MYSQL_PASSWORD"),
+ port=int(os.getenv("MYSQL_PORT")),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+aiomysql==0.2.0
+"""
+
+ def docker_file(self):
+ pass
+
+ def dockerignore_file(self):
+ pass
+
+ def gitignore_file(self):
+ pass
diff --git a/nest/common/templates/orm_config.py b/nest/common/templates/orm_config.py
deleted file mode 100644
index bbd6dc3..0000000
--- a/nest/common/templates/orm_config.py
+++ /dev/null
@@ -1,68 +0,0 @@
-def generate_orm_config(db_type: str):
- if db_type == "mongodb":
- service_import = (
- "from nest.core.database.odm_provider import OdmProvider\n"
- "from src.examples.examples_entity import Examples"
- )
- else:
- service_import = "from nest.core.database.orm_provider import OrmProvider"
-
- base_template = f"""{service_import}
-import os
-from dotenv import load_dotenv
-
-load_dotenv()
- """
-
- if db_type == "sqlite":
- return f"""{base_template}
-config = OrmProvider(
- db_type="{db_type}",
- config_params=dict(
- db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db"),
- )
-)
- """
- elif db_type == "postgresql":
- return f"""{base_template}
-config = OrmProvider(
- db_type="{db_type}",
- config_params=dict(
- host=os.getenv("POSTGRESQL_HOST"),
- db_name=os.getenv("POSTGRESQL_DB_NAME"),
- user=os.getenv("POSTGRESQL_USER"),
- password=os.getenv("POSTGRESQL_PASSWORD"),
- port=int(os.getenv("POSTGRESQL_PORT")),
- )
-)
- """
- elif db_type == "mysql":
- return f"""{base_template}
-config = OrmProvider(
- db_type="{db_type}",
- config_params=dict(
- host=os.getenv("MYSQL_HOST"),
- db_name=os.getenv("MYSQL_DB_NAME"),
- user=os.getenv("MYSQL_USER"),
- password=os.getenv("MYSQL_PASSWORD"),
- port=int(os.getenv("MYSQL_PORT")),
- )
-)
- """
- elif db_type == "mongodb":
- return f"""{base_template}
-
-config = OrmProvider(
- db_type="{db_type}",
- config_params={{
- "db_name": os.getenv("DB_NAME"),
- "host": os.getenv("DB_HOST"),
- "user": os.getenv("DB_USER"),
- "password": os.getenv("DB_PASSWORD"),
- "port": os.getenv("DB_PORT"),
- }},
- document_models=[Examples]
-)
- """
- else:
- raise ValueError(f"Unsupported db type: {db_type}")
diff --git a/nest/common/templates/orm_template.py b/nest/common/templates/orm_template.py
new file mode 100644
index 0000000..0a2d96f
--- /dev/null
+++ b/nest/common/templates/orm_template.py
@@ -0,0 +1,303 @@
+from abc import ABC, abstractmethod
+from pathlib import Path
+
+from nest.common.templates.base_template import BaseTemplate, get_module_strings
+from nest.common.templates import Database
+
+
+class ORMTemplate(BaseTemplate, ABC):
+ def __init__(self, module_name: str, db_type: Database):
+ super().__init__(
+ module_name=module_name,
+ )
+ self.db_type = db_type
+
+ def app_file(self):
+ return f"""from config import config
+from nest.core.app import App
+
+app = App(
+ description="PyNest service",
+ modules=[]
+)
+
+
+@app.on_event("startup")
+def startup():
+ config.create_all()
+"""
+
+ @abstractmethod
+ def config_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def requirements_file(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def docker_file(self):
+ raise NotImplementedError
+
+ def dockerignore_file(self):
+ return """__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
+"""
+
+ def gitignore_file(self):
+ return """__pycache__
+*.pyc
+*.pyo
+*.pyd
+.DS_Store
+.env
+"""
+
+ def module_file(self):
+ return f"""from .{self.module_name}_service import {self.capitalized_module_name}Service
+from .{self.module_name}_controller import {self.capitalized_module_name}Controller
+
+
+class {self.capitalized_module_name}Module:
+
+ def __init__(self):
+ self.providers = [{self.capitalized_module_name}Service]
+ self.controllers = [{self.capitalized_module_name}Controller]
+"""
+
+ def model_file(self):
+ return f"""from pydantic import BaseModel
+
+
+class {self.capitalized_module_name}(BaseModel):
+ name: str
+
+"""
+
+ def entity_file(self):
+ return f"""from config import config
+from sqlalchemy import Column, Integer, String, Float
+
+
+class {self.capitalized_module_name}(config.Base):
+ __tablename__ = "{self.module_name}"
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ name = Column(String, unique=True)
+
+"""
+
+ def service_file(self):
+ return f"""from .{self.module_name}_model import {self.capitalized_module_name}
+from .{self.module_name}_entity import {self.capitalized_module_name} as {self.capitalized_module_name}Entity
+from config import config
+from nest.core.decorators import db_request_handler
+from functools import lru_cache
+
+
+@lru_cache()
+class {self.capitalized_module_name}Service:
+
+ def __init__(self):
+ self.config = config
+ self.session = self.config.get_db()
+
+ @db_request_handler
+ def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ new_{self.module_name} = {self.capitalized_module_name}Entity(
+ **{self.module_name}.dict()
+ )
+ self.session.add(new_{self.module_name})
+ self.session.commit()
+ return new_{self.module_name}.id
+
+ @db_request_handler
+ def get_{self.module_name}(self):
+ return self.session.query({self.capitalized_module_name}Entity).all()
+
+"""
+
+ def controller_file(self):
+ return f"""from nest.core import Controller, Get, Post, Depends
+
+from .{self.module_name}_service import {self.capitalized_module_name}Service
+from .{self.module_name}_model import {self.capitalized_module_name}
+
+
+@Controller("{self.module_name}")
+class {self.capitalized_module_name}Controller:
+
+ service: {self.capitalized_module_name}Service = Depends({self.capitalized_module_name}Service)
+
+ @Get("/")
+ def get_{self.module_name}(self):
+ return self.service.get_{self.module_name}()
+
+ @Post("/")
+ def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}):
+ return self.service.add_{self.module_name}({self.module_name})
+ """
+
+ def settings_file(self):
+ return f"""# This file is used to configure the nest server.
+config:
+ db_type: {self.db_type.value}
+ is_async: false
+"""
+
+ @staticmethod
+ def validate_config_file(src_path: Path) -> Path:
+ config_file = src_path.parent / "config.py"
+ if not config_file.exists():
+ raise Exception("orm_config.py file not found")
+ return config_file
+
+ def create_module(self, module_name: str, src_path: Path):
+ module_path = src_path / module_name
+ self.create_folder(module_path)
+ self.create_template(module_path / "__init__.py", "")
+ self.create_template(
+ module_path / f"{module_name}_module.py", self.module_file()
+ )
+ self.create_template(
+ module_path / f"{module_name}_controller.py", self.controller_file()
+ )
+ self.create_template(
+ module_path / f"{module_name}_service.py", self.service_file()
+ )
+ self.create_template(module_path / f"{module_name}_model.py", self.model_file())
+ self.create_template(
+ module_path / f"{module_name}_entity.py", self.entity_file()
+ )
+ self.append_module_to_app(src_path.parent / "app.py")
+
+ def generate_module(self, module_name: str):
+ src_path = self.validate_new_module(module_name)
+ self.validate_config_file(src_path)
+ self.create_module(module_name, src_path)
+
+ def generate_project(self, project_name: str):
+ self.create_template(self.nest_path / "settings.yaml", self.settings_file())
+ # define paths: root, src, module
+ root_path = self.base_path / project_name
+ src_path = self.base_path / project_name / "src"
+
+ # create folders
+ self.create_folder(root_path)
+ self.create_folder(src_path)
+
+ # create root level files
+ self.create_template(root_path / "main.py", self.main_file())
+ self.create_template(root_path / "README.md", self.readme_file())
+ self.create_template(root_path / "app.py", self.app_file())
+ self.create_template(root_path / "config.py", self.config_file())
+ self.create_template(root_path / "requirements.txt", self.requirements_file())
+ self.create_template(root_path / ".gitignore", self.gitignore_file())
+
+ # create src level files
+ self.create_template(src_path / "__init__.py", "")
+
+
+class AsyncORMTemplate(ORMTemplate, ABC):
+ def app_file(self):
+ return f"""from config import config
+from nest.core.app import App
+
+app = App(
+ description="PyNest service",
+ modules=[]
+)
+
+
+@app.on_event("startup")
+async def startup():
+ await config.create_all()
+"""
+
+ @abstractmethod
+ def config_file(self):
+ pass
+
+ @abstractmethod
+ def requirements_file(self):
+ pass
+
+ @abstractmethod
+ def docker_file(self):
+ pass
+
+ def entity_file(self):
+ return f"""from config import config
+from sqlalchemy import Integer, String
+from sqlalchemy.orm import Mapped, mapped_column
+
+
+class {self.capitalized_module_name}(config.Base):
+ __tablename__ = "{self.module_name}"
+
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
+ name: Mapped[str] = mapped_column(String, unique=True)
+
+"""
+
+ def service_file(self):
+ return f"""from .{self.module_name}_model import {self.capitalized_module_name}
+from .{self.module_name}_entity import {self.capitalized_module_name} as {self.capitalized_module_name}Entity
+from nest.core.decorators import async_db_request_handler
+
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+
+
+class {self.capitalized_module_name}Service:
+
+ @async_db_request_handler
+ async def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}, session: AsyncSession):
+ new_{self.module_name} = {self.capitalized_module_name}Entity(
+ **{self.module_name}.dict()
+ )
+ session.add(new_{self.module_name})
+ await session.commit()
+ return new_{self.module_name}.id
+
+ @async_db_request_handler
+ async def get_{self.module_name}(self, session: AsyncSession):
+ query = select({self.capitalized_module_name}Entity)
+ result = await session.execute(query)
+ return result.scalars().all()
+"""
+
+ def controller_file(self):
+ return f"""from nest.core import Controller, Get, Post, Depends
+from sqlalchemy.ext.asyncio import AsyncSession
+from config import config
+
+
+from .{self.module_name}_service import {self.capitalized_module_name}Service
+from .{self.module_name}_model import {self.capitalized_module_name}
+
+
+@Controller("{self.module_name}")
+class {self.capitalized_module_name}Controller:
+
+ service: {self.capitalized_module_name}Service = Depends({self.capitalized_module_name}Service)
+
+ @Get("/")
+ async def get_{self.module_name}(self, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.get_{self.module_name}(session)
+
+ @Post("/")
+ async def add_{self.module_name}(self, {self.module_name}: {self.capitalized_module_name}, session: AsyncSession = Depends(config.get_db)):
+ return await self.service.add_{self.module_name}({self.module_name}, session)
+ """
+
+ def settings_file(self):
+ return f"""# This file is used to configure the nest server.
+config:
+ db_type: {self.db_type.value}
+ is_async: true
+"""
diff --git a/nest/common/templates/postgres_template.py b/nest/common/templates/postgres_template.py
new file mode 100644
index 0000000..b9ef936
--- /dev/null
+++ b/nest/common/templates/postgres_template.py
@@ -0,0 +1,86 @@
+from abc import ABC
+
+from nest.common.templates.orm_template import ORMTemplate, AsyncORMTemplate
+from nest.common.templates import Database
+
+
+class PostgresqlTemplate(ORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.POSTGRESQL,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import OrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = OrmProvider(
+ db_type="postgresql",
+ config_params=dict(
+ host=os.getenv("POSTGRESQL_HOST", "localhost"),
+ db_name=os.getenv("POSTGRESQL_DB_NAME", "default_nest_db"),
+ user=os.getenv("POSTGRESQL_USER", "postgres"),
+ password=os.getenv("POSTGRESQL_PASSWORD", "postgres"),
+ port=int(os.getenv("POSTGRESQL_PORT", 5432)),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+psycopg2==2.9.6
+"""
+
+ def docker_file(self):
+ pass
+
+ def dockerignore_file(self):
+ pass
+
+ def gitignore_file(self):
+ pass
+
+
+class AsyncPostgresqlTemplate(AsyncORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.POSTGRESQL,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import AsyncOrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = AsyncOrmProvider(
+ db_type="postgresql",
+ config_params=dict(
+ host=os.getenv("POSTGRESQL_HOST", "localhost"),
+ db_name=os.getenv("POSTGRESQL_DB_NAME", "default_nest_db"),
+ user=os.getenv("POSTGRESQL_USER", "postgres"),
+ password=os.getenv("POSTGRESQL_PASSWORD", "postgres"),
+ port=int(os.getenv("POSTGRESQL_PORT", 5432)),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+asyncpg==0.29.0
+"""
+
+ def docker_file(self):
+ pass
+
+ def dockerignore_file(self):
+ pass
+
+ def gitignore_file(self):
+ pass
diff --git a/nest/common/templates/requierments.py b/nest/common/templates/requierments.py
deleted file mode 100644
index b02e970..0000000
--- a/nest/common/templates/requierments.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from nest import __version__ as version
-
-
-def generate_requirements():
- template = f"""anyio==3.6.2
-click==8.1.3
-fastapi==0.95.1
-fastapi-utils==0.2.1
-greenlet==2.0.2
-h11==0.14.0
-idna==3.4
-pydantic==1.10.7
-python-dotenv==1.0.0
-sniffio==1.3.0
-SQLAlchemy==1.4.48
-starlette==0.26.1
-typing_extensions==4.5.0
-uvicorn==0.22.0
-pynest-api=={version}
- """
- return template
diff --git a/nest/common/templates/service.py b/nest/common/templates/service.py
deleted file mode 100644
index 00fb54f..0000000
--- a/nest/common/templates/service.py
+++ /dev/null
@@ -1,57 +0,0 @@
-def generate_service(controller_name: str, db_type: str) -> str:
- split_controller_name = controller_name.split("_")
- capitalized_controller_name = "".join(
- [word.capitalize() for word in split_controller_name]
- )
- if db_type == "mongodb":
- template = f"""from src.{controller_name}.{controller_name}_model import {capitalized_controller_name}
-from src.{controller_name}.{controller_name}_entity import {capitalized_controller_name} as {capitalized_controller_name}Entity
-from nest.core.decorators import db_request_handler
-from functools import lru_cache
-
-
-@lru_cache()
-class {capitalized_controller_name}Service:
-
- @db_request_handler
- async def add_{controller_name}(self, {controller_name}: {capitalized_controller_name}):
- new_{controller_name} = {capitalized_controller_name}Entity(
- **{controller_name}.dict()
- )
- await new_{controller_name}.save()
- return new_{controller_name}.id
-
- @db_request_handler
- async def get_{controller_name}(self):
- return await {capitalized_controller_name}Entity.find_all().to_list()
-
-"""
- else:
- template = f"""from src.{controller_name}.{controller_name}_model import {capitalized_controller_name}
-from src.{controller_name}.{controller_name}_entity import {capitalized_controller_name} as {capitalized_controller_name}Entity
-from orm_config import config
-from nest.core.decorators import db_request_handler
-from functools import lru_cache
-
-
-@lru_cache()
-class {capitalized_controller_name}Service:
-
- def __init__(self):
- self.orm_config = config
- self.session = self.orm_config.get_db()
-
- @db_request_handler
- def add_{controller_name}(self, {controller_name}: {capitalized_controller_name}):
- new_{controller_name} = {capitalized_controller_name}Entity(
- **{controller_name}.dict()
- )
- self.session.add(new_{controller_name})
- self.session.commit()
- return new_{controller_name}.id
-
- @db_request_handler
- def get_{controller_name}(self):
- return self.session.query({capitalized_controller_name}Entity).all()
- """
- return template
diff --git a/nest/common/templates/sqlite_template.py b/nest/common/templates/sqlite_template.py
new file mode 100644
index 0000000..7762b5a
--- /dev/null
+++ b/nest/common/templates/sqlite_template.py
@@ -0,0 +1,71 @@
+from abc import ABC
+
+from nest.common.templates.orm_template import ORMTemplate, AsyncORMTemplate
+from nest.common.templates import Database
+
+
+class SQLiteTemplate(ORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.SQLITE,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import OrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = OrmProvider(
+ db_type="sqlite",
+ config_params=dict(
+ db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db"),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}"""
+
+ def docker_file(self):
+ return """FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11
+
+COPY ./app /app/app
+COPY ./requirements.txt /app/requirements.txt
+"""
+
+
+class AsyncSQLiteTemplate(AsyncORMTemplate, ABC):
+ def __init__(self, module_name: str):
+ super().__init__(
+ module_name=module_name,
+ db_type=Database.SQLITE,
+ )
+
+ def config_file(self):
+ return """from nest.core.database.orm_provider import AsyncOrmProvider
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+config = AsyncOrmProvider(
+ db_type="sqlite",
+ config_params=dict(
+ db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db"),
+ )
+)
+"""
+
+ def requirements_file(self):
+ return f"""pynest-api=={self.version}
+aiosqlite==0.19.0"""
+
+ def docker_file(self):
+ return """FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11
+
+COPY ./app /app/app
+COPY ./requirements.txt /app/requirements.txt
+"""
diff --git a/nest/common/templates/templates_factory.py b/nest/common/templates/templates_factory.py
new file mode 100644
index 0000000..b2b79ee
--- /dev/null
+++ b/nest/common/templates/templates_factory.py
@@ -0,0 +1,41 @@
+from nest.common.templates.postgres_template import (
+ PostgresqlTemplate,
+ AsyncPostgresqlTemplate,
+)
+from nest.common.templates.sqlite_template import SQLiteTemplate, AsyncSQLiteTemplate
+from nest.common.templates.mysql_template import MySQLTemplate, AsyncMySQLTemplate
+from nest.common.templates.mongo_template import MongoTemplate
+from nest.common.templates.base_template import BaseTemplate
+from nest.common.templates.blank_template import BlankTemplate
+from nest.common.templates import Database
+from typing import Union, Optional
+
+
+class TemplateFactory:
+ @staticmethod
+ def get_template(
+ db_type: Union[Database, str, None],
+ module_name: str,
+ is_async: Optional[bool] = False,
+ ) -> BaseTemplate:
+ if not db_type:
+ return BlankTemplate(module_name=module_name)
+ elif db_type == Database.POSTGRESQL.value:
+ if is_async:
+ return AsyncPostgresqlTemplate(module_name=module_name)
+ else:
+ return PostgresqlTemplate(module_name=module_name)
+ elif db_type == Database.MYSQL.value:
+ if is_async:
+ return AsyncMySQLTemplate(module_name=module_name)
+ else:
+ return MySQLTemplate(module_name=module_name)
+ elif db_type == Database.SQLITE.value:
+ if is_async:
+ return AsyncSQLiteTemplate(module_name=module_name)
+ else:
+ return SQLiteTemplate(module_name=module_name)
+ elif db_type == Database.MONGODB.value:
+ return MongoTemplate(module_name=module_name)
+ else:
+ raise ValueError(f"Unknown database type: {db_type}")
diff --git a/nest/core/app.py b/nest/core/app.py
index 37cc318..300e3f5 100644
--- a/nest/core/app.py
+++ b/nest/core/app.py
@@ -3,7 +3,13 @@
class App(FastAPI):
- def __init__(self, description: str, modules: List, *args, **kwargs):
+ def __init__(
+ self,
+ description: str,
+ modules: List,
+ title: str = "PyNest Service",
+ **kwargs
+ ):
"""
Initializes the App instance.
@@ -12,7 +18,9 @@ def __init__(self, description: str, modules: List, *args, **kwargs):
modules (List): A list of modules to register.
"""
- super().__init__(description=description, *args, **kwargs)
+ super().__init__(
+ description=description, title=title, **kwargs
+ )
self.modules = modules
self._register_controllers()
diff --git a/nest/core/database/orm_provider.py b/nest/core/database/orm_provider.py
index a320039..030f17b 100644
--- a/nest/core/database/orm_provider.py
+++ b/nest/core/database/orm_provider.py
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
from contextlib import asynccontextmanager
-from typing import AsyncGenerator, Dict, Any
+from typing import Dict, Any
from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
diff --git a/nest/core/decorators/__init__.py b/nest/core/decorators/__init__.py
index 7baad0d..5924fb8 100644
--- a/nest/core/decorators/__init__.py
+++ b/nest/core/decorators/__init__.py
@@ -1,2 +1,2 @@
from nest.core.decorators.controller import Controller, Get, Post, Delete, Put, Patch
-from nest.core.decorators.database import db_request_handler
+from nest.core.decorators.database import db_request_handler, async_db_request_handler
diff --git a/nest/core/decorators/controller.py b/nest/core/decorators/controller.py
index bdf2974..fe02f87 100644
--- a/nest/core/decorators/controller.py
+++ b/nest/core/decorators/controller.py
@@ -14,11 +14,14 @@ def Controller(tag: str = None, prefix: str = None):
class: The decorated class.
"""
- if prefix:
- if not prefix.startswith("/"):
- prefix = "/" + prefix
- if prefix.endswith("/"):
- prefix = prefix[:-1]
+ # Use tag as default prefix if prefix is None
+ if prefix is None:
+ prefix = tag
+
+ if not prefix.startswith("/"):
+ prefix = "/" + prefix
+ if prefix.endswith("/"):
+ prefix = prefix[:-1]
def wrapper(cls) -> ClassBasedView:
router = APIRouter(tags=[tag] if tag else None)
diff --git a/nest/plugins/modules/redis/redis_controller.py b/nest/plugins/modules/redis/redis_controller.py
index 05dc374..87884a1 100644
--- a/nest/plugins/modules/redis/redis_controller.py
+++ b/nest/plugins/modules/redis/redis_controller.py
@@ -1,4 +1,4 @@
-from nest.core import Controller, Get, Post, Depends
+from nest.core import Controller, Get, Post, Depends, Delete
from nest.plugins.modules.redis.redis_service import RedisService
from nest.plugins.modules.redis.redis_model import RedisInput
@@ -8,18 +8,18 @@
class RedisController:
redis_service: RedisService = Depends(RedisService)
- @Get("get/{key}")
+ @Get("/{key}")
def get(self, key: str):
return self.redis_service.get(key)
- @Post("set")
+ @Post("/")
def set(self, redis_input: RedisInput):
return self.redis_service.set(redis_input)
- @Post("delete/{key}")
+ @Delete("/{key}")
def delete(self, key: str):
return self.redis_service.delete(key)
- @Get("exists/{key}")
+ @Get("/exists/{key}")
def exists(self, key: str):
return self.redis_service.exists(key)
diff --git a/pyproject.toml b/pyproject.toml
index 3ab268c..daece32 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -27,6 +27,9 @@ dependencies = [
"python-dotenv==1.0.0",
"SQLAlchemy==2.0.19",
"uvicorn==0.23.1",
+ "PyYAML==6.0.1",
+ "astor==0.8.1",
+ "black==23.11.0"
]
[tool.setuptools.dynamic]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 4271f3f..71f3499 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -3,4 +3,5 @@ black==21.12b0
SQLAlchemy==2.0.19
motor==3.2.0
beanie==1.20.0
-click==8.1.6
\ No newline at end of file
+click==8.1.6
+PyYAML==6.0.1
\ No newline at end of file
diff --git a/requirements-release.txt b/requirements-release.txt
index c3b2b12..18bb153 100644
--- a/requirements-release.txt
+++ b/requirements-release.txt
@@ -3,3 +3,15 @@ black==21.12b0
SQLAlchemy==2.0.19
motor==3.2.0
beanie==1.20.0
+PyYAML==6.0.1
+
+# package release
+setuptools
+wheel
+build
+twine
+git-changelog
+
+# docs release
+mkdocstrings-python
+mkdocs-material
\ No newline at end of file
diff --git a/requirements-tests.txt b/requirements-tests.txt
index 868b369..879d994 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -4,4 +4,4 @@ black==21.12b0
SQLAlchemy==2.0.19
motor==3.2.0
beanie==1.20.0
-
+PyYAML==6.0.1
diff --git a/tests/test_core/test_app.py b/tests/test_core/test_app.py
index cd00b31..0e57a24 100644
--- a/tests/test_core/test_app.py
+++ b/tests/test_core/test_app.py
@@ -39,6 +39,7 @@ def app():
@pytest.mark.parametrize("route", ["/get", "/post", "/put", "/delete", "/patch"])
def test_get(app, route):
+ route = "/test" + route # if prefix is not defined in the controller, then the given tag will be used as prefix
route_exist = False
for app_route in app.routes:
if isinstance(app_route, APIRoute):