diff --git a/.github/workflows/cli_test.yaml b/.github/workflows/cli_test.yaml index 7feef03..f6fb914 100644 --- a/.github/workflows/cli_test.yaml +++ b/.github/workflows/cli_test.yaml @@ -1,6 +1,6 @@ name: CLI Test -on: [ push, workflow_call, pull_request ] +on: [push, workflow_call, pull_request] jobs: test: @@ -8,54 +8,54 @@ jobs: strategy: matrix: - app_type: [ "Blank", "SyncORM", "AsyncORM", "MongoDB", "PostgresSync", "PostgresAsync", "MySQLSync", "MySQLAsync" ] + app_type: ["Blank", "SyncORM", "AsyncORM", "MongoDB", "PostgresSync", "PostgresAsync", "MySQLSync", "MySQLAsync"] steps: - name: Check out repository code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.9' - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install . + pip install poetry + poetry install - name: Test CLI Commands run: | app_name="${{ matrix.app_type }}App" case "${{ matrix.app_type }}" in "Blank") - pynest generate application -n "$app_name" + poetry run pynest generate application -n "$app_name" ;; "SyncORM") - pynest generate application -n "$app_name" -db sqlite + poetry run pynest generate application -n "$app_name" -db sqlite ;; "AsyncORM") - pynest generate application -n "$app_name" -db sqlite --is-async + poetry run pynest generate application -n "$app_name" -db sqlite --is-async ;; "MongoDB") - pynest generate application -n "$app_name" -db mongodb + poetry run pynest generate application -n "$app_name" -db mongodb ;; "PostgresSync") - pynest generate application -n "$app_name" -db postgresql + poetry run pynest generate application -n "$app_name" -db postgresql ;; "PostgresAsync") - pynest generate application -n "$app_name" -db postgresql --is-async + poetry run pynest generate application -n "$app_name" -db postgresql --is-async ;; "MySQLSync") - pynest generate application -n "$app_name" -db mysql + poetry run pynest generate application -n "$app_name" -db mysql ;; "MySQLAsync") - pynest generate application -n "$app_name" -db mysql --is-async + poetry run pynest generate application -n "$app_name" -db mysql --is-async ;; esac cd "$app_name" - pynest generate resource -n user + poetry run pynest generate resource -n user - name: Verify Boilerplate run: | @@ -74,7 +74,6 @@ jobs: exit 1 fi - # List of expected files declare -a files=("main.py" "requirements.txt" "README.md") declare -a src_level_files=("app_module.py" "app_service.py" "app_controller.py") @@ -100,7 +99,6 @@ jobs: fi done - # Check each file in the list of module_files for file in "${module_files[@]}"; do if [ -f "$app_name/src/user/$file" ]; then @@ -111,4 +109,4 @@ jobs: fi done - echo "Boilerplate for ${{ matrix.app_type }} generated successfully." + echo "Boilerplate for ${{ matrix.app_type }} generated successfully." \ No newline at end of file diff --git a/.github/workflows/deploy_docs.yaml b/.github/workflows/deploy_docs.yaml index ffe9bdb..c9073cd 100644 --- a/.github/workflows/deploy_docs.yaml +++ b/.github/workflows/deploy_docs.yaml @@ -2,13 +2,11 @@ name: Deploy Docs on: [workflow_call, workflow_dispatch] -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write -# Allow one concurrent deployment concurrency: group: "pages" cancel-in-progress: true @@ -27,15 +25,15 @@ jobs: - name: Copy License File run: cp LICENSE docs/license.md - - name: Set up Python 3.10 + - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.9" - - name: Install MKDocs + - name: Install dependencies run: | - pip install --upgrade pip - pip install mkdocs-material mkdocstrings-python + pip install poetry + poetry install --with docs - name: Build docs run: mkdocs build --clean diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index c82f624..2f122cc 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -12,17 +12,17 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.9' - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install . + pip install poetry + poetry install - name: Start Application run: | @@ -38,15 +38,15 @@ jobs: fi if [ "${{ matrix.app_type }}" == "Blank" ]; then - pynest generate application -n "$app_name" + poetry run pynest generate application -n "$app_name" else - pynest generate application -n "$app_name" -db sqlite $is_async - pip install aiosqlite + poetry run pynest generate application -n "$app_name" -db sqlite $is_async + poetry add aiosqlite fi cd "$app_name" - pynest generate resource -n user - uvicorn "src.app_module:http_server" --host "0.0.0.0" --port 8000 --reload & + poetry run pynest generate resource -n user + poetry run uvicorn "src.app_module:http_server" --host "0.0.0.0" --port 8000 --reload & - name: Wait for the server to start run: sleep 10 @@ -62,4 +62,4 @@ jobs: curl -f http://localhost:8000/user/ - name: Kill the server - run: kill $(jobs -p) || true + run: kill $(jobs -p) || true \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7388708..4717296 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,7 +17,7 @@ on: - patch env: - VERSION_FILE_PATH: nest/__init__.py + VERSION_FILE_PATH: pyproject.toml CHANGELOG_FILE_PATH: CHANGELOG.md jobs: @@ -36,65 +36,60 @@ jobs: with: token: ${{ secrets.RELEASE_GIT_TOKEN }} - - name: Set version + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install Poetry run: | - VERSION_REGEX='^(__version__ = \")(([[:digit:]]+\.)*[[:digit:]]+)((a|b|rc)[[:digit:]]+)?(\.post[[:digit:]]+)?(.dev[[:digit:]]+)?(\")$' - - # get current version from version file - CURRENT_VERSION=`sed -n -E "s/$VERSION_REGEX/\2/p" $VERSION_FILE_PATH` + pip install poetry + + - name: Set version using Poetry + run: | + # Extract the current version + CURRENT_VERSION=$(poetry version -s) echo "Current version: $CURRENT_VERSION" - # switch case for incrementing_version based on input + # Increment version case ${{ inputs.increment_version }} in major) - NEW_VERSION=$(echo $CURRENT_VERSION | awk -F. '{$1=$1+1; $2=0; $3=0; print $0}' OFS=".") - ;; + NEW_VERSION=$(poetry version major | awk '{print $NF}') + ;; minor) - NEW_VERSION=$(echo $CURRENT_VERSION | awk -F. '{$2=$2+1; $3=0; print $0}' OFS=".") - ;; + NEW_VERSION=$(poetry version minor | awk '{print $NF}') + ;; patch) - NEW_VERSION=$(echo $CURRENT_VERSION | awk -F. '{$3=$3+1; print $0}' OFS=".") - ;; + NEW_VERSION=$(poetry version patch | awk '{print $NF}') + ;; *) - echo "Invalid input for increment_version" - exit 1 - ;; + echo "Invalid input for increment_version" + exit 1 + ;; esac echo "New version: $NEW_VERSION" echo "RELEASE_VERSION=$NEW_VERSION" >> $GITHUB_ENV - - # update version file - sed -i -E "s/$VERSION_REGEX/\1$NEW_VERSION\8/" $VERSION_FILE_PATH - - - name: Install python - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Install python dependencies - run: | - pip install --upgrade pip - pip install -r requirements-release.txt - name: Update CHANGELOG.md run: git-changelog . -o $CHANGELOG_FILE_PATH - - name: Build package - run: python -m build + - name: Build package with Poetry + run: | + poetry build - name: Commit and push changes run: | git config --global user.name "github-actions" git config --global user.email "github@actions.com" - git add $CHANGELOG_FILE_PATH $VERSION_FILE_PATH + git add $CHANGELOG_FILE_PATH pyproject.toml git commit -m "Increment version to $NEW_VERSION" git push - - name: Publish a Python distribution to PyPI + - name: Publish package to PyPI run: | - python -m twine upload dist/* -u ${{ secrets.PYPI_API_USER }} -p ${{ secrets.PYPI_API_TOKEN }} + poetry publish --username ${{ secrets.PYPI_API_USER }} --password ${{ secrets.PYPI_API_TOKEN }} - - name: Release New Version + - name: Create GitHub release uses: softprops/action-gh-release@v1 with: name: v${{ env.RELEASE_VERSION }} @@ -106,4 +101,4 @@ jobs: contents: read pages: write id-token: write - uses: ./.github/workflows/deploy_docs.yaml \ No newline at end of file + uses: ./.github/workflows/deploy_docs.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b9c3ae7..2ac7ca9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -5,21 +5,23 @@ on: [push, workflow_call, pull_request] jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11"] + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 + + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} - - name: Install Python - id: setup_python - uses: actions/setup-python@v4 - with: - python-version: | - 3.7 - 3.8 - 3.9 - 3.10 - 3.11 + - name: Install dependencies + run: | + pip install poetry + poetry install --with test - - name: Run tests - run: | - pip install -r requirements-tests.txt - pytest tests \ No newline at end of file + - name: Run tests + run: | + poetry run pytest tests \ No newline at end of file diff --git a/nest/cli/templates/abstract_empty_template.py b/nest/cli/templates/abstract_empty_template.py index 702464e..dfeaa41 100644 --- a/nest/cli/templates/abstract_empty_template.py +++ b/nest/cli/templates/abstract_empty_template.py @@ -24,7 +24,7 @@ def generate_controller_file(self, name) -> str: from src.{name}.{name}_model import {self.capitalized_name} - @Controller("{name}") + @Controller("{name}", tag="{name}") class {self.capitalized_name}Controller: service: {self.capitalized_name}Service = Depends({self.capitalized_name}Service) diff --git a/nest/cli/templates/base_template.py b/nest/cli/templates/base_template.py index 7d490a3..36e412f 100644 --- a/nest/cli/templates/base_template.py +++ b/nest/cli/templates/base_template.py @@ -25,7 +25,6 @@ def __init__(self, module_name: str): ) 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 @@ -368,7 +367,7 @@ def generate_module(self, module_name: str, path: str = None): def generate_empty_controller_file(self) -> str: return f"""from nest.core import Controller -@Controller("{self.module_name}") +@Controller("{self.module_name}", tag="{self.module_name}") class {self.capitalized_module_name}Controller: ... """ diff --git a/nest/cli/templates/blank_template.py b/nest/cli/templates/blank_template.py index 50b1fd2..cde225c 100644 --- a/nest/cli/templates/blank_template.py +++ b/nest/cli/templates/blank_template.py @@ -78,7 +78,7 @@ def controller_file(self): from .{self.module_name}_model import {self.capitalized_module_name} -@Controller("{self.module_name}") +@Controller("{self.module_name}", tag="{self.module_name}") class {self.capitalized_module_name}Controller: def __init__(self, {self.module_name}_service: {self.capitalized_module_name}Service): @@ -139,4 +139,4 @@ def settings_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version}""" + return f"""pynest-api""" diff --git a/nest/cli/templates/cli_templates.py b/nest/cli/templates/cli_templates.py index 2720fcc..1f83e9e 100644 --- a/nest/cli/templates/cli_templates.py +++ b/nest/cli/templates/cli_templates.py @@ -144,7 +144,7 @@ def generate_project(self, project_name: str): self.create_template(src_path / "app_service.py", self.app_service_file()) def requirements_file(self): - return f"""pynest-api=={self.version}""" + return f"""pynest-api""" def config_file(self): return "" diff --git a/nest/cli/templates/mongo_db_template.py b/nest/cli/templates/mongo_db_template.py index eb1338c..ac39e99 100644 --- a/nest/cli/templates/mongo_db_template.py +++ b/nest/cli/templates/mongo_db_template.py @@ -82,13 +82,8 @@ class Config: """ def generate_requirements_file(self) -> str: - return f"""click==8.1.6 -fastapi==0.95.1 -python-dotenv==1.0.0 -uvicorn==0.23.1 -motor==3.2.0 -beanie==1.20.0 -pynest-api=={self.version} + return f""" +pynest-api """ def generate_dockerfile(self) -> str: diff --git a/nest/cli/templates/mongo_template.py b/nest/cli/templates/mongo_template.py index c36bdab..6ef3605 100644 --- a/nest/cli/templates/mongo_template.py +++ b/nest/cli/templates/mongo_template.py @@ -33,7 +33,7 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api beanie==1.20.0""" def docker_file(self): @@ -61,7 +61,7 @@ def controller_file(self): from .{self.module_name}_model import {self.capitalized_module_name} -@Controller("{self.module_name}") +@Controller("{self.module_name}", tag="{self.module_name}") class {self.capitalized_module_name}Controller: def __init__(self, {self.module_name}_service: {self.capitalized_module_name}Service): diff --git a/nest/cli/templates/mysql_template.py b/nest/cli/templates/mysql_template.py index 83a22f7..fc19afc 100644 --- a/nest/cli/templates/mysql_template.py +++ b/nest/cli/templates/mysql_template.py @@ -31,7 +31,7 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api mysql-connector-python==8.2.0 """ @@ -63,6 +63,6 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api aiomysql==0.2.0 """ diff --git a/nest/cli/templates/orm_template.py b/nest/cli/templates/orm_template.py index 5230e4a..67f8795 100644 --- a/nest/cli/templates/orm_template.py +++ b/nest/cli/templates/orm_template.py @@ -127,7 +127,7 @@ def controller_file(self): from .{self.module_name}_model import {self.capitalized_module_name} -@Controller("{self.module_name}") +@Controller("{self.module_name}", tag="{self.module_name}") class {self.capitalized_module_name}Controller: def __init__(self, {self.module_name}_service: {self.capitalized_module_name}Service): @@ -293,7 +293,7 @@ def controller_file(self): from .{self.module_name}_model import {self.capitalized_module_name} -@Controller("{self.module_name}") +@Controller("{self.module_name}", tag="{self.module_name}") class {self.capitalized_module_name}Controller: def __init__(self, {self.module_name}_service: {self.capitalized_module_name}Service): diff --git a/nest/cli/templates/postgres_template.py b/nest/cli/templates/postgres_template.py index ced9957..191cc95 100644 --- a/nest/cli/templates/postgres_template.py +++ b/nest/cli/templates/postgres_template.py @@ -31,7 +31,7 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api psycopg2==2.9.6 """ @@ -63,6 +63,6 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api asyncpg==0.29.0 """ diff --git a/nest/cli/templates/sqlite_template.py b/nest/cli/templates/sqlite_template.py index 28e29fe..461a7d9 100644 --- a/nest/cli/templates/sqlite_template.py +++ b/nest/cli/templates/sqlite_template.py @@ -27,7 +27,7 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version}""" + return f"""pynest-api""" def docker_file(self): return """FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11 @@ -60,7 +60,7 @@ def config_file(self): """ def requirements_file(self): - return f"""pynest-api=={self.version} + return f"""pynest-api aiosqlite==0.19.0""" def docker_file(self): diff --git a/nest/core/database/odm_config.py b/nest/core/database/odm_config.py index 8dc13b2..ac1b0d9 100644 --- a/nest/core/database/odm_config.py +++ b/nest/core/database/odm_config.py @@ -1,4 +1,5 @@ from nest.core.database.base_config import BaseProvider, ConfigFactoryBase +from urllib.parse import urlencode class MongoDBConfig(BaseProvider): @@ -12,17 +13,21 @@ class MongoDBConfig(BaseProvider): password (str): The password for database authentication. port (int): The database port number. srv (bool): Whether to use the SRV connection string. + uri (str): Optional pre-built MongoDB URI. + **kwargs: Additional keyword arguments to include in the URI query parameters. """ def __init__( self, - host: str, - db_name: str, + host: str = None, + db_name: str = None, user: str = None, password: str = None, port: int = 27017, srv: bool = False, + uri: str = None, + **kwargs, ): """ Initializes the MongoDBConfig instance. @@ -34,22 +39,48 @@ def __init__( password (str): The password for database authentication. port (int): The database port number. srv (bool): Whether to use the SRV connection string. + uri (str): Optional pre-built MongoDB URI. + **kwargs: Additional keyword arguments to include in the URI query parameters. """ self.srv = srv + self.uri = uri + self.kwargs = kwargs or {} super().__init__(host, db_name, user, password, port) def get_engine_url(self) -> str: """ - Returns the engine URL for the ORM. + Returns the engine URL for the ODM. Returns: str: The engine URL. """ + if self.uri: + return self.uri + + # Build the base URI + protocol = "mongodb+srv" if self.srv else "mongodb" + credentials = "" if self.user and self.password: - return f"mongodb{'+srv' if self.srv else ''}://{self.user}:{self.password}@{self.host}:{self.port}" - return f"mongodb{'+srv' if self.srv else ''}://{self.host}:{self.port}" + credentials = f"{self.user}:{self.password}@" + elif self.user: + credentials = f"{self.user}@" + + host_port = self.host or "" + if not self.srv and self.port: + host_port = f"{host_port}:{self.port}" + + db_name = f"/{self.db_name}" if self.db_name else "" + + # Build the query string from kwargs + query = "" + if self.kwargs: + query = "?" + urlencode(self.kwargs) + + # Build the full URI + uri = f"{protocol}://{credentials}{host_port}{db_name}{query}" + return uri class ConfigFactory(ConfigFactoryBase): diff --git a/nest/core/database/odm_provider.py b/nest/core/database/odm_provider.py index ead8d06..8f9f8e5 100644 --- a/nest/core/database/odm_provider.py +++ b/nest/core/database/odm_provider.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Type from beanie import Document, init_beanie from motor.motor_asyncio import AsyncIOMotorClient @@ -14,7 +14,7 @@ class OdmProvider: db_type (str, optional): The type of database. Defaults to "mongodb". config_params (dict, optional): Configuration parameters specific to the chosen database type. Defaults to None. - document_models (beanie.Document): a list of beanie.Document instances + document_models (List[Type[Document]]): A list of beanie.Document subclasses. Attributes: config: The configuration factory for the chosen database type. @@ -26,34 +26,35 @@ def __init__( self, db_type="mongodb", config_params: dict = None, - document_models: List[Document] = None, + document_models: list[Type[Document]] = None, ): """ - Initializes the OrmService instance. + Initializes the OdmProvider instance. Args: db_type (str, optional): The type of database. Defaults to "mongodb". config_params (dict, optional): Configuration parameters specific to the chosen database type. Defaults to None. - document_models (beanie.Document): a list of beanie.Document instances + document_models (List[Type[Document]]): A list of beanie.Document subclasses. """ - self.config_object = ConfigFactory(db_type=db_type).get_config() self.config = self.config_object(**config_params) self.config_url = self.config.get_engine_url() - self.document_models = document_models + self.document_models = document_models or [] async def create_all(self): + """ + Initializes the Beanie ODM with the provided document models. + """ self.check_document_models() client = AsyncIOMotorClient(self.config_url) await init_beanie( - database=client[self.config.db_name], document_models=self.document_models + database=client.get_default_database(), document_models=self.document_models ) def check_document_models(self): """ - Checks that the document_models argument is a list of beanie.Document instances. - + Checks that the document_models argument is a list of beanie.Document subclasses. """ if not isinstance(self.document_models, list): raise Exception("document_models should be a list") diff --git a/nest/core/decorators/class_based_view.py b/nest/core/decorators/class_based_view.py index a465542..18cf0b8 100644 --- a/nest/core/decorators/class_based_view.py +++ b/nest/core/decorators/class_based_view.py @@ -4,16 +4,24 @@ """ import inspect -from typing import Any, Callable, List, Type, TypeVar, Union, get_type_hints +from typing import ( + Any, + Callable, + ClassVar, + List, + Type, + TypeVar, + Union, + get_origin, + get_type_hints, +) from fastapi import APIRouter, Depends -from pydantic.typing import is_classvar from starlette.routing import Route, WebSocketRoute T = TypeVar("T") K = TypeVar("K", bound=Callable[..., Any]) - CBV_CLASS_KEY = "__cbv_class__" @@ -61,7 +69,7 @@ def _init_cbv(cls: Type[Any]) -> None: ] dependency_names: List[str] = [] for name, hint in get_type_hints(cls).items(): - if is_classvar(hint): + if get_origin(hint) is ClassVar: continue parameter_kwargs = {"default": getattr(cls, name, Ellipsis)} dependency_names.append(name) @@ -102,4 +110,4 @@ def _update_cbv_route_endpoint_signature( for parameter in old_parameters[1:] ] new_signature = old_signature.replace(parameters=new_parameters) - setattr(route.endpoint, "__signature__", new_signature) + setattr(route.endpoint, "__signature__", new_signature) \ No newline at end of file diff --git a/nest/plugins/__init__.py b/nest/plugins/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nest/plugins/controllers/__init__.py b/nest/plugins/controllers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nest/plugins/modules/__init__.py b/nest/plugins/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nest/plugins/modules/auth/__init__.py b/nest/plugins/modules/auth/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nest/plugins/modules/redis/__init__.py b/nest/plugins/modules/redis/__init__.py deleted file mode 100644 index 013a218..0000000 --- a/nest/plugins/modules/redis/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from nest.plugins.modules.redis.redis_controller import RedisController -from nest.plugins.modules.redis.redis_model import RedisInput -from nest.plugins.modules.redis.redis_module import RedisModule -from nest.plugins.modules.redis.redis_service import RedisService diff --git a/nest/plugins/modules/redis/redis_controller.py b/nest/plugins/modules/redis/redis_controller.py deleted file mode 100644 index c5c4954..0000000 --- a/nest/plugins/modules/redis/redis_controller.py +++ /dev/null @@ -1,26 +0,0 @@ -from nest.core import Controller, Delete, Depends, Get, Post -from nest.plugins.modules.redis.redis_model import RedisInput -from nest.plugins.modules.redis.redis_service import RedisService - - -@Controller("redis") -class RedisController: - - def __init__(self, redis_service: RedisService): - self.redis_service = redis_service - - @Get("/{key}") - def get(self, key: str): - return self.redis_service.get(key) - - @Post("/") - def set(self, redis_input: RedisInput): - return self.redis_service.set(redis_input) - - @Delete("/{key}") - def delete(self, key: str): - return self.redis_service.delete(key) - - @Get("/exists/{key}") - def exists(self, key: str): - return self.redis_service.exists(key) diff --git a/nest/plugins/modules/redis/redis_model.py b/nest/plugins/modules/redis/redis_model.py deleted file mode 100644 index d852228..0000000 --- a/nest/plugins/modules/redis/redis_model.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import Any - -from pydantic import BaseModel, BaseSettings - - -class RedisInput(BaseModel): - key: str - value: Any - - -class RedisConfig(BaseSettings): - REDIS_HOST: str = "localhost" - REDIS_PORT: int = 6379 - REDIS_DB: int = 0 diff --git a/nest/plugins/modules/redis/redis_module.py b/nest/plugins/modules/redis/redis_module.py deleted file mode 100644 index 95ff6ab..0000000 --- a/nest/plugins/modules/redis/redis_module.py +++ /dev/null @@ -1,8 +0,0 @@ -from nest.core import Module -from nest.plugins.modules.redis.redis_controller import RedisController -from nest.plugins.modules.redis.redis_service import RedisService - - -@Module(controllers=[RedisController], providers=[RedisService], imports=[]) -class RedisModule: - pass diff --git a/nest/plugins/modules/redis/redis_service.py b/nest/plugins/modules/redis/redis_service.py deleted file mode 100644 index 8464873..0000000 --- a/nest/plugins/modules/redis/redis_service.py +++ /dev/null @@ -1,30 +0,0 @@ -import redis -from fastapi import HTTPException - -from nest.core import Injectable -from nest.plugins.modules.redis.redis_model import RedisConfig, RedisInput - - -@Injectable() -class RedisService: - def __init__(self): - self.redis_config = RedisConfig() - self.redis_client = redis.StrictRedis( - host=self.redis_config.REDIS_HOST, - port=self.redis_config.REDIS_PORT, - db=self.redis_config.REDIS_DB, - ) - - def set(self, redis_input: RedisInput): - if self.exists(redis_input.key): - raise HTTPException(status_code=400, detail="Key already exists") - self.redis_client.set(redis_input.key, redis_input.value) - - def get(self, redis_key: str): - return self.redis_client.get(redis_key) - - def exists(self, redis_key: str): - return self.redis_client.exists(redis_key) - - def delete(self, redis_key: str): - self.redis_client.delete(redis_key) diff --git a/nest/plugins/services/__init__.py b/nest/plugins/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pyproject.toml b/pyproject.toml index 9acd99e..59ca17d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,48 +1,85 @@ [build-system] -requires = ["setuptools>=61.0", "wheel>=0.37.0"] -build-backend = "setuptools.build_meta" +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" -[project] +[tool.poetry] name = "pynest-api" +version = "0.3.1" description = "PyNest is a FastAPI Abstraction for building microservices, influenced by NestJS." +authors = ["itay.dar "] readme = "README.md" -requires-python = ">=3.8.1" -license = { file = "LICENSE" } -authors = [ - { name = "Itay Dar", email = "itay2803@gmail.com" }, +homepage = "https://github.com/PythonNest/PyNest" +documentation = "https://pythonnest.github.io/PyNest/" +packages = [ + { include = "nest" } ] -dynamic = ["version"] classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "License :: OSI Approved :: MIT License", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", ] -dependencies = [ - "click>=8.1.6", - "fastapi>=0.88.0,<1.0.0", - "python-dotenv>=1.0.0", - "uvicorn>=0.23.1", - "PyYAML>=6.0.1", - "astor>=0.8.1", - "black>=23.11.0", - "injector>=0.20.1", - "pydantic<2.0.0", - "sqlalchemy == 2.0.19", - "alembic == 1.7.5", -] -[tool.setuptools.dynamic] -version = { attr = "nest.__init__.__version__" } -[tool.pip] -index-url = "https://pypi.org/simple" -trusted-host = ["pypi.org", "files.pythonhosted.org"] -[tools.black] +[tool.poetry.dependencies] +python = "^3.9" +# Core dependencies +click = "^8.1.7" +injector = "^0.22.0" +astor = "^0.8.1" +pyyaml = "^6.0.2" +fastapi = "^0.115.4" +pydantic = "^2.9.2" +uvicorn = "^0.32.0" + + +# Optional dependencies +sqlalchemy = { version = "^2.0.36", optional = true } +asyncpg = { version = "^0.30.0", optional = true } +psycopg2 = { version = "^2.9.3", optional = true } +alembic = { version = "^1.13.3", optional = true } +beanie = { version = "^1.27.0", optional = true } +python-dotenv = { version = "^1.0.1", optional = true } +greenlet = { version = "^3.1.1", optional = true } +black = "^24.10.0" + + + +[tool.poetry.extras] +postgres = ["sqlalchemy", "asyncpg", "psycopg2", "alembic", "greenlet", "python-dotenv"] +mongo = ["beanie", "python-dotenv"] +test = ["pytest"] + +[tool.poetry.group.build.dependencies] +setuptools = "^75.3.0" +wheel = "^0.44.0" +build = "^1.2.2.post1" +twine = "^5.1.1" +git-changelog = "^2.5.2" + +[tool.poetry.group.test.dependencies] +pytest = "^7.0.1" +fastapi = "^0.115.4" +sqlalchemy = "^2.0.36" +motor = "^3.2.0" +beanie = "^1.27.0" +pydantic = "^2.9.2" +python-dotenv = "^1.0.1" +uvicorn = "^0.32.0" + +[tool.poetry.group.docs.dependencies] +mkdocs-material = "^9.5.43" +mkdocstrings-python = "^1.12.2" + + +[tool.black] force-exclude = ''' /( | /*venv* @@ -58,34 +95,17 @@ force-exclude = ''' )/ ''' -[project.optional-dependencies] -test = [ - "pytest == 6.2.5", -] - -orm = [ - "sqlalchemy == 2.0.19", - "alembic == 1.7.4", -] -mongo = [ - "pymongo == 3.12.0", - "motor == 3.2.0", - "beanie == 1.20.0", -] - -[project.scripts] -pynest = "nest.cli.cli:nest_cli" - -[tool.setuptools.packages.find] -include = ["nest*"] -namespaces = false - [tool.mypy] exclude = [ "/*venv*" ] ignore_missing_imports = true -[project.urls] -"Homepage" = "https://github.com/PythonNest/PyNest" -"Documentation" = "https://pythonnest.github.io/PyNest/" +[tool.poetry.urls] +Homepage = "https://github.com/PythonNest/PyNest" +Documentation = "https://pythonnest.github.io/PyNest/" + +[tool.poetry.scripts] +pynest = "nest.cli.cli:nest_cli" + + diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index aa977bb..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,10 +0,0 @@ -fastapi==0.95.1 -black==21.12b0 -SQLAlchemy==2.0.19 -motor==3.2.0 -beanie==1.20.0 -click==8.1.6 -PyYAML==6.0.1 -injector==0.21.0 -pydantic<2.0.0 -astor>=0.8.1 \ No newline at end of file diff --git a/requirements-release.txt b/requirements-release.txt deleted file mode 100644 index 0d30d21..0000000 --- a/requirements-release.txt +++ /dev/null @@ -1,19 +0,0 @@ -fastapi==0.95.1 -black==21.12b0 -SQLAlchemy==2.0.19 -motor==3.2.0 -beanie==1.20.0 -PyYAML==6.0.1 -injector==0.21.0 -astor==0.8.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 deleted file mode 100644 index a17e03c..0000000 --- a/requirements-tests.txt +++ /dev/null @@ -1,12 +0,0 @@ -pytest>=7.0.1 -fastapi>=0.95.1 -black>=21.12b0 -SQLAlchemy>=2.0.19 -motor>=3.2.0 -beanie>=1.20.0 -PyYAML>=6.0.1 -injector>=0.21.0 -pydantic<2.0.0 -python-dotenv>=1.0.0 -uvicorn>=0.23.1 -astor>=0.8.1 diff --git a/tests/test_core/test_database/test_odm.py b/tests/test_core/test_database/test_odm.py index 7b2ffb2..ac51013 100644 --- a/tests/test_core/test_database/test_odm.py +++ b/tests/test_core/test_database/test_odm.py @@ -36,7 +36,7 @@ def test_odm_service_definition(odm_service): def test_odm_service_config_url(odm_service): config_url = odm_service.config_url - assert config_url == "mongodb://user:password@host:port" + assert config_url == "mongodb://user:password@host:port/db_name" def test_mongo_config_definition(mongodb_config):