From f9b7c80b981542b4d0d1e9b81c7abb0cfea6caef Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Mon, 17 Jul 2023 07:18:00 +0300 Subject: [PATCH 01/16] add license tag --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0487b0f..5132b2a 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,12 @@ Supported Python versions + + Supported Python versions +

-# PyNest - Description +# Description PyNest is designed to help structure your APIs in an intuitive, easy to understand, and enjoyable way. From 04563495f98c156fb22a262d5af1c85d974b9a6a Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Wed, 19 Jul 2023 19:29:45 +0300 Subject: [PATCH 02/16] add support in ODM --- nest/core/database/base_orm.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nest/core/database/base_orm.py b/nest/core/database/base_orm.py index f07f2ad..e5a545e 100644 --- a/nest/core/database/base_orm.py +++ b/nest/core/database/base_orm.py @@ -68,3 +68,15 @@ def get_db(self) -> Session: return session() except Exception as e: raise e + + +class MongoService: + + def __init__(self, config_params: dict = None): + self.config = ConfigFactory(db_type="mongodb").get_config() + self.config_url = self.config(**config_params).get_engine_url() + + + + + From 0102176fe9a254514e345b713658c6461f8b4c7c Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 00:40:54 +0300 Subject: [PATCH 03/16] change the src type in module creation --- nest/cli/click_handlers.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/nest/cli/click_handlers.py b/nest/cli/click_handlers.py index 80d5644..11ac9ed 100644 --- a/nest/cli/click_handlers.py +++ b/nest/cli/click_handlers.py @@ -380,6 +380,32 @@ def get_module_end_index(lines, modules_start_index): 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): """ Append a module import statement to the app.py file. @@ -408,6 +434,9 @@ def append_module_to_app(path_to_app_py: Path, new_module: str, db_type: str): # 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] @@ -467,10 +496,10 @@ def create_nest_module(name: str): ├── module_name_entity.py ├── module_name_module.py """ - src_path = Path("/Users/itayd/PycharmProjects/testMongo") / "src" + 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") - # src_path = Path(find_target_folder(os.getcwd(), "src")) if not src_path: raise Exception("src folder not found") From 99e07847c5bf9a34183a1ec5b1ee0cf97dfd64fd Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 14:33:57 +0300 Subject: [PATCH 04/16] bump version. make requierments granular update readme badges remove deprecated `InferringRouter` --- README.md | 22 +++++++++++++--------- nest/core/decorators/controller.py | 4 ++-- pyproject.toml | 20 ++++++-------------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 5132b2a..0edaba8 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,21 @@ PyNest is a Python framework built on top of FastAPI that follows the modular architecture of NestJS

- - Package version - - - Supported Python versions - - - Supported Python versions - + + Version + + + Python + + + Downloads + + + License +

+ # Description PyNest is designed to help structure your APIs in an intuitive, easy to understand, and enjoyable way. diff --git a/nest/core/decorators/controller.py b/nest/core/decorators/controller.py index 44cc904..16a5f98 100644 --- a/nest/core/decorators/controller.py +++ b/nest/core/decorators/controller.py @@ -1,5 +1,5 @@ from fastapi_utils.cbv import _cbv as ClassBasedView -from fastapi_utils.inferring_router import InferringRouter +from fastapi.routing import APIRouter def Controller(tag: str = None, prefix: str = None): @@ -21,7 +21,7 @@ def Controller(tag: str = None, prefix: str = None): prefix = prefix[:-1] def wrapper(cls): - router = InferringRouter(tags=[tag] if tag else None) + router = APIRouter(tags=[tag] if tag else None) for name, method in cls.__dict__.items(): if callable(method) and hasattr(method, "method"): diff --git a/pyproject.toml b/pyproject.toml index f9866eb..eb8fe41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,26 +17,18 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ] dependencies = [ - "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", + "click==8.1.6", + "fastapi==0.100.0", + "fastapi-utils==0.3.0", + "pydantic==2.0.3", "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", + "SQLAlchemy==2.0.19", + "uvicorn==0.23.1", ] [tool.setuptools.dynamic] From 244a22a883e88256ea60fd11be49220ca34855c9 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 14:51:18 +0300 Subject: [PATCH 05/16] unbump version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index eb8fe41..dbef19a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ dependencies = [ "click==8.1.6", "fastapi==0.100.0", - "fastapi-utils==0.3.0", + "fastapi-utils==0.2.1", "pydantic==2.0.3", "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", From de25b625bc8c365343cf919bdebcdcb79c1c667a Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 23:44:46 +0300 Subject: [PATCH 06/16] remove pydatinc since it's a deps --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dbef19a..4562971 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,6 @@ dependencies = [ "click==8.1.6", "fastapi==0.100.0", "fastapi-utils==0.2.1", - "pydantic==2.0.3", "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", "uvicorn==0.23.1", From edb4305ab290a07d4d7af3651712d80ea5c5c3db Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 23:51:44 +0300 Subject: [PATCH 07/16] remove fastapi-utils and use only the single file that create the abstract class based view --- nest/core/decorators/controller.py | 2 +- nest/core/decorators/helpers.py | 84 ++++++++++++++++++++++++++++++ pyproject.toml | 1 - 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 nest/core/decorators/helpers.py diff --git a/nest/core/decorators/controller.py b/nest/core/decorators/controller.py index 16a5f98..f9fb57c 100644 --- a/nest/core/decorators/controller.py +++ b/nest/core/decorators/controller.py @@ -1,5 +1,5 @@ -from fastapi_utils.cbv import _cbv as ClassBasedView from fastapi.routing import APIRouter +from nest.core.decorators.helpers import class_based_view as ClassBasedView def Controller(tag: str = None, prefix: str = None): diff --git a/nest/core/decorators/helpers.py b/nest/core/decorators/helpers.py new file mode 100644 index 0000000..f67eaf2 --- /dev/null +++ b/nest/core/decorators/helpers.py @@ -0,0 +1,84 @@ +import inspect +from typing import Any, Callable, List, Type, TypeVar, Union, get_type_hints + +from fastapi import APIRouter, Depends +from pydantic.typing import is_classvar +from starlette.routing import Route, WebSocketRoute + +T = TypeVar("T") + +CBV_CLASS_KEY = "__cbv_class__" + + +def class_based_view(router: APIRouter, cls: Type[T]) -> Type[T]: + """ + Replaces any methods of the provided class `cls` that are endpoints of routes in `router` with updated + function calls that will properly inject an instance of `cls`. + """ + _init_cbv(cls) + cbv_router = APIRouter() + function_members = inspect.getmembers(cls, inspect.isfunction) + functions_set = set(func for _, func in function_members) + cbv_routes = [ + route + for route in router.routes + if isinstance(route, (Route, WebSocketRoute)) and route.endpoint in functions_set + ] + for route in cbv_routes: + router.routes.remove(route) + _update_cbv_route_endpoint_signature(cls, route) + cbv_router.routes.append(route) + router.include_router(cbv_router) + return cls + + +def _init_cbv(cls: Type[Any]) -> None: + """ + Idempotently modifies the provided `cls`, performing the following modifications: + * The `__init__` function is updated to set any class-annotated dependencies as instance attributes + * The `__signature__` attribute is updated to indicate to FastAPI what arguments should be passed to the initializer + """ + if getattr(cls, CBV_CLASS_KEY, False): # pragma: no cover + return # Already initialized + old_init: Callable[..., Any] = cls.__init__ + old_signature = inspect.signature(old_init) + old_parameters = list(old_signature.parameters.values())[1:] # drop `self` parameter + new_parameters = [ + x for x in old_parameters if x.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) + ] + dependency_names: List[str] = [] + for name, hint in get_type_hints(cls).items(): + if is_classvar(hint): + continue + parameter_kwargs = {"default": getattr(cls, name, Ellipsis)} + dependency_names.append(name) + new_parameters.append( + inspect.Parameter(name=name, kind=inspect.Parameter.KEYWORD_ONLY, annotation=hint, **parameter_kwargs) + ) + new_signature = old_signature.replace(parameters=new_parameters) + + def new_init(self: Any, *args: Any, **kwargs: Any) -> None: + for dep_name in dependency_names: + dep_value = kwargs.pop(dep_name) + setattr(self, dep_name, dep_value) + old_init(self, *args, **kwargs) + + setattr(cls, "__signature__", new_signature) + setattr(cls, "__init__", new_init) + setattr(cls, CBV_CLASS_KEY, True) + + +def _update_cbv_route_endpoint_signature(cls: Type[Any], route: Union[Route, WebSocketRoute]) -> None: + """ + Fixes the endpoint signature for a cbv route to ensure FastAPI performs dependency injection properly. + """ + old_endpoint = route.endpoint + old_signature = inspect.signature(old_endpoint) + old_parameters: List[inspect.Parameter] = list(old_signature.parameters.values()) + old_first_parameter = old_parameters[0] + new_first_parameter = old_first_parameter.replace(default=Depends(cls)) + new_parameters = [new_first_parameter] + [ + parameter.replace(kind=inspect.Parameter.KEYWORD_ONLY) for parameter in old_parameters[1:] + ] + new_signature = old_signature.replace(parameters=new_parameters) + setattr(route.endpoint, "__signature__", new_signature) diff --git a/pyproject.toml b/pyproject.toml index 4562971..587464c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ classifiers = [ dependencies = [ "click==8.1.6", "fastapi==0.100.0", - "fastapi-utils==0.2.1", "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", "uvicorn==0.23.1", From 9b501b34d8dbedccb384ee6cef889f6f6e043b18 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Sun, 23 Jul 2023 23:53:15 +0300 Subject: [PATCH 08/16] remove fastapi-utils and use only the single file that create the abstract class based view --- nest/core/decorators/helpers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nest/core/decorators/helpers.py b/nest/core/decorators/helpers.py index f67eaf2..e5fa78d 100644 --- a/nest/core/decorators/helpers.py +++ b/nest/core/decorators/helpers.py @@ -1,3 +1,9 @@ +''' +Credit: FastAPI-Utils +Source: https://github.com/dmontagu/fastapi-utils/blob/master/fastapi_utils/cbv.py +''' + + import inspect from typing import Any, Callable, List, Type, TypeVar, Union, get_type_hints From b7dba03585faddbeb8ecddcb141646a76591d5cd Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Mon, 24 Jul 2023 07:00:26 +0300 Subject: [PATCH 09/16] fix pydantic error --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 587464c..50e2ff8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ dependencies = [ "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", "uvicorn==0.23.1", + "pydantic==1.8.2", ] [tool.setuptools.dynamic] From 64f328e05b70a1fd88087ee896ffe1cd712148a9 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Mon, 24 Jul 2023 07:05:58 +0300 Subject: [PATCH 10/16] fix pydantic error --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 50e2ff8..aa56a8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] dependencies = [ "click==8.1.6", - "fastapi==0.100.0", + "fastapi==0.95.1", "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", "uvicorn==0.23.1", From ecf089d8b4d0dc5d908edfe7db819f96f28ace18 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Tue, 25 Jul 2023 17:49:34 +0300 Subject: [PATCH 11/16] fix pydantic error --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index aa56a8a..c1970c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ dependencies = [ "python-dotenv==1.0.0", "SQLAlchemy==2.0.19", "uvicorn==0.23.1", - "pydantic==1.8.2", ] [tool.setuptools.dynamic] From 424875b99f885e7e15fd5fcc2ff67a5dd46800b8 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Tue, 25 Jul 2023 20:42:04 +0300 Subject: [PATCH 12/16] add user name and password for odm config object for better security --- nest/core/database/base_odm.py | 14 +++++--------- nest/core/database/base_orm.py | 12 ------------ nest/core/database/config.py | 17 ++++++++--------- nest/core/database/odm_config.py | 14 +++++++++++--- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/nest/core/database/base_odm.py b/nest/core/database/base_odm.py index b4caa01..81cc365 100644 --- a/nest/core/database/base_odm.py +++ b/nest/core/database/base_odm.py @@ -18,10 +18,8 @@ class OdmService: document_models (beanie.Document): a list of beanie.Document instances Attributes: - Base: The declarative base class for defining ORM models. config: The configuration factory for the chosen database type. config_url: The URL generated from the database configuration parameters. - client: The Motor client for database connection. """ @@ -53,13 +51,11 @@ def check_document_models(self): """ Checks that the document_models argument is a list of beanie.Document instances. - Args: - document_models (beanie.Document): a list of beanie.Document instances """ if not isinstance(self.document_models, list): raise Exception("document_models should be a list") - # for document_model in self.document_models: - # if not isinstance(document_model, Document): - # raise Exception( - # "Each item in document_models should be an instance of beanie.Document" - # ) + for document_model in self.document_models: + if not issubclass(document_model, Document): + raise Exception( + "Each item in document_models should be a subclass of beanie.Document" + ) diff --git a/nest/core/database/base_orm.py b/nest/core/database/base_orm.py index e5a545e..f07f2ad 100644 --- a/nest/core/database/base_orm.py +++ b/nest/core/database/base_orm.py @@ -68,15 +68,3 @@ def get_db(self) -> Session: return session() except Exception as e: raise e - - -class MongoService: - - def __init__(self, config_params: dict = None): - self.config = ConfigFactory(db_type="mongodb").get_config() - self.config_url = self.config(**config_params).get_engine_url() - - - - - diff --git a/nest/core/database/config.py b/nest/core/database/config.py index aa7bcb0..77bb799 100644 --- a/nest/core/database/config.py +++ b/nest/core/database/config.py @@ -34,7 +34,7 @@ def get_config(self): assert self.db_type, "db_type is required" -class BaseOdmConfig: +class BaseConfig: """ Base abstract class for ODM (Object-Document Mapping) configurations. @@ -52,18 +52,13 @@ def get_engine_url(self) -> str: pass -class BaseProvider(BaseOdmConfig): +class BaseProvider(BaseConfig): """ - Base class for ODM providers that implement the BaseOdmConfig interface. - - Args: - host (str): The database host. - db_name (str): The name of the database. - port (int): The database port number. + Base class for Objets Mapping providers that implement the BaseConfig interface. """ - def __init__(self, host: str, db_name: str, port: int): + def __init__(self, host: str, db_name: str, port: int, user: str, password: str): """ Initializes the BaseOdmProvider instance. @@ -71,11 +66,15 @@ def __init__(self, host: str, db_name: str, port: int): host (str): The database host. db_name (str): The name of the database. port (int): The database port number. + user (str): The username for database authentication. + password (str): The password for database authentication. """ self.host = host self.db_name = db_name self.port = port + self.user = user + self.password = password def get_engine_url(self) -> str: """ diff --git a/nest/core/database/odm_config.py b/nest/core/database/odm_config.py index 163baa0..6e13043 100644 --- a/nest/core/database/odm_config.py +++ b/nest/core/database/odm_config.py @@ -1,4 +1,4 @@ -from nest.core.database.config import BaseOdmConfig, ConfigFactoryBase, BaseProvider +from nest.core.database.config import ConfigFactoryBase, BaseProvider class MongoDBConfig(BaseProvider): @@ -15,7 +15,15 @@ class MongoDBConfig(BaseProvider): """ - def __init__(self, host: str, db_name: str, port: int = 27017, srv: bool = False): + def __init__( + self, + host: str, + db_name: str, + port: int = 27017, + user: str = None, + password: str = None, + srv: bool = False + ): """ Initializes the MongoDBConfig instance. @@ -29,7 +37,7 @@ def __init__(self, host: str, db_name: str, port: int = 27017, srv: bool = False """ self.srv = srv - super().__init__(host, db_name, port) + super().__init__(host, db_name, port, user, password) def get_engine_url(self) -> str: """ From fe1265a1f548309f976bf4c0bbef92fff27d1f5d Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Fri, 28 Jul 2023 07:11:59 +0300 Subject: [PATCH 13/16] fix orm misconfiguration. add more unit test to verify databases urls --- nest/common/templates/orm_config.py | 2 + nest/core/database/config.py | 4 +- nest/core/database/odm_config.py | 6 +- nest/core/database/orm_config.py | 2 +- nest/core/decorators/controller.py | 4 +- tests/test_core/test_database/test_odm.py | 23 +++-- tests/test_core/test_database/test_orm.py | 103 ++++++++++++++++------ 7 files changed, 105 insertions(+), 39 deletions(-) diff --git a/nest/common/templates/orm_config.py b/nest/common/templates/orm_config.py index ee8f6d7..6715f17 100644 --- a/nest/common/templates/orm_config.py +++ b/nest/common/templates/orm_config.py @@ -55,6 +55,8 @@ def generate_orm_config(db_type: str): 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] diff --git a/nest/core/database/config.py b/nest/core/database/config.py index 77bb799..32a1707 100644 --- a/nest/core/database/config.py +++ b/nest/core/database/config.py @@ -58,7 +58,7 @@ class BaseProvider(BaseConfig): """ - def __init__(self, host: str, db_name: str, port: int, user: str, password: str): + def __init__(self, host: str, db_name: str, user: str, password: str, port: int): """ Initializes the BaseOdmProvider instance. @@ -72,9 +72,9 @@ def __init__(self, host: str, db_name: str, port: int, user: str, password: str) """ self.host = host self.db_name = db_name - self.port = port self.user = user self.password = password + self.port = port def get_engine_url(self) -> str: """ diff --git a/nest/core/database/odm_config.py b/nest/core/database/odm_config.py index 6e13043..b2f79d1 100644 --- a/nest/core/database/odm_config.py +++ b/nest/core/database/odm_config.py @@ -19,9 +19,9 @@ def __init__( self, host: str, db_name: str, - port: int = 27017, user: str = None, password: str = None, + port: int = 27017, srv: bool = False ): """ @@ -37,7 +37,7 @@ def __init__( """ self.srv = srv - super().__init__(host, db_name, port, user, password) + super().__init__(host, db_name, user, password, port) def get_engine_url(self) -> str: """ @@ -47,7 +47,7 @@ def get_engine_url(self) -> str: str: The engine URL. """ - return f"mongodb{'+srv' if self.srv else ''}://{self.host}:{self.port}" + return f"mongodb{'+srv' if self.srv else ''}://{self.user}:{self.password}@{self.host}:{self.port}" class ConfigFactory(ConfigFactoryBase): diff --git a/nest/core/database/orm_config.py b/nest/core/database/orm_config.py index 946986b..9a452a4 100644 --- a/nest/core/database/orm_config.py +++ b/nest/core/database/orm_config.py @@ -76,7 +76,7 @@ def get_engine_url(self) -> str: str: The engine URL. """ - return f"mysql+mysqlconnector://{self.user}:{self.password}@{self.host}" + return f"mysql+mysqlconnector://{self.user}:{self.password}@{self.host}:{self.port}/{self.db_name}" class SQLiteConfig(BaseConfig): diff --git a/nest/core/decorators/controller.py b/nest/core/decorators/controller.py index f9fb57c..d5d3912 100644 --- a/nest/core/decorators/controller.py +++ b/nest/core/decorators/controller.py @@ -70,10 +70,10 @@ def wrapper(cls): else: raise Exception("Invalid method") - def get_router(): + def get_router() -> APIRouter: """ Returns: - InferringRouter: The router associated with the controller. + APIRouter: The router associated with the controller. """ return router diff --git a/tests/test_core/test_database/test_odm.py b/tests/test_core/test_database/test_odm.py index 66c65c9..67284be 100644 --- a/tests/test_core/test_database/test_odm.py +++ b/tests/test_core/test_database/test_odm.py @@ -14,9 +14,11 @@ def odm_service(): return OdmService( db_type="mongodb", config_params={ - "db_name": "test", - "host": "test", - "port": "test", + "db_name": "db_name", + "host": "host", + "user": "user", + "password": "password", + "port": "port", }, document_models=[], ) @@ -24,7 +26,13 @@ def odm_service(): @pytest.fixture(scope="module") def mongodb_config(): - return MongoDBConfig("test", "test", "test") + return MongoDBConfig( + db_name="db_name" + , host="host" + , user="user" + , password="password" + , port="port" + ) def test_odm_service_definition(odm_service): @@ -35,4 +43,9 @@ 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://test:test" + assert config_url == "mongodb://user:password@host:port" + + +def test_mongo_config_definition(mongodb_config): + assert mongodb_config + assert mongodb_config.get_engine_url diff --git a/tests/test_core/test_database/test_orm.py b/tests/test_core/test_database/test_orm.py index b9582e1..86f3225 100644 --- a/tests/test_core/test_database/test_orm.py +++ b/tests/test_core/test_database/test_orm.py @@ -2,49 +2,100 @@ import pytest from nest.core.database.orm_config import ( - ConfigFactory, - SQLiteConfig, - PostgresConfig, - MySQLConfig, + ConfigFactory ) from nest.core.database.base_orm import OrmService @pytest.fixture(scope="module") -def orm_service(): - return OrmService( - db_type="sqlite", - config_params=dict(db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db")), - ) +def config_factory(): + return ConfigFactory -@pytest.fixture(scope="module") -def sqlite_config(): - return SQLiteConfig("test") +def test_config_factory_definition(config_factory): + assert config_factory + assert config_factory.get_config @pytest.fixture(scope="module") -def postgres_config(): - return PostgresConfig("test", "test", "test", "test", "test") +def sqlite_config_factory(config_factory): + config = config_factory(db_type="sqlite").get_config() + params = dict(db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db")) + return config(**params) @pytest.fixture(scope="module") -def mysql_config(): - return MySQLConfig("test", "test", "test", "test", "test") +def postgres_config_factory(config_factory): + config = config_factory(db_type="postgresql").get_config() + params = dict( + db_name=os.getenv("POSTGRES_DB_NAME", "default_nest_db"), + host=os.getenv("POSTGRES_HOST", "localhost"), + user=os.getenv("POSTGRES_USER", "postgres"), + password=os.getenv("POSTGRES_PASSWORD", "postgres"), + port=os.getenv("POSTGRES_PORT", "5432"), + ) + return config(**params) -def test_orm_service_definition(orm_service): - assert orm_service.Base - assert orm_service.config - assert orm_service.config_url - assert orm_service.engine +@pytest.fixture(scope="module") +def mysql_config_factory(config_factory): + config = config_factory(db_type="mysql").get_config() + params = dict( + db_name=os.getenv("MYSQL_DB_NAME", "default_nest_db"), + host=os.getenv("MYSQL_HOST", "localhost"), + user=os.getenv("MYSQL_USER", "root"), + password=os.getenv("MYSQL_PASSWORD", "root"), + port=os.getenv("MYSQL_PORT", "3306"), + ) + return config(**params) -def test_orm_service_config_url(orm_service): - config_url = orm_service.config_url +def test_sqlite_url(sqlite_config_factory): + config_url = sqlite_config_factory.get_engine_url() assert config_url == "sqlite:///default_nest_db.db" -def test_orm_service_engine(orm_service): - engine = orm_service.engine - assert engine +def test_postgres_url(postgres_config_factory): + config_url = postgres_config_factory.get_engine_url() + assert config_url == "postgresql+psycopg2://postgres:postgres@localhost:5432/default_nest_db" + + +def test_mysql_url(mysql_config_factory): + config_url = mysql_config_factory.get_engine_url() + assert config_url == "mysql+mysqlconnector://root:root@localhost:3306/default_nest_db" + + +@pytest.fixture(scope="module") +def sqlite_orm_service(): + return OrmService( + db_type="sqlite", + config_params=dict(db_name=os.getenv("SQLITE_DB_NAME", "default_nest_db")), + ) + + +@pytest.fixture(scope="module") +def postgres_orm_service(): + return OrmService( + db_type="postgresql", + config_params=dict( + db_name=os.getenv("POSTGRES_DB_NAME", "default_nest_db"), + host=os.getenv("POSTGRES_HOST", "localhost"), + user=os.getenv("POSTGRES_USER", "postgres"), + password=os.getenv("POSTGRES_PASSWORD", "postgres"), + port=os.getenv("POSTGRES_PORT", "5432"), + ), + ) + + +@pytest.fixture(scope="module") +def mysql_orm_service(): + return OrmService( + db_type="mysql", + config_params=dict( + db_name=os.getenv("MYSQL_DB_NAME", "default_nest_db"), + host=os.getenv("MYSQL_HOST", "localhost"), + user=os.getenv("MYSQL_USER", "root"), + password=os.getenv("MYSQL_PASSWORD", "root"), + port=os.getenv("MYSQL_PORT", "3306"), + ), + ) From 7a3eb02f5f62381a7a87e17a5e9421fffd2515cd Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Fri, 28 Jul 2023 07:12:41 +0300 Subject: [PATCH 14/16] remove empty file --- nest/common/common.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 nest/common/common.py diff --git a/nest/common/common.py b/nest/common/common.py deleted file mode 100644 index e69de29..0000000 From 3fa413de326efbf59487e1abbff6dcc94e3688ef Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Fri, 28 Jul 2023 07:17:08 +0300 Subject: [PATCH 15/16] add return in inner controller function add restriction to mongodb in creating new module --- nest/cli/click_handlers.py | 3 ++- nest/core/decorators/controller.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nest/cli/click_handlers.py b/nest/cli/click_handlers.py index 11ac9ed..3af6ea1 100644 --- a/nest/cli/click_handlers.py +++ b/nest/cli/click_handlers.py @@ -507,7 +507,8 @@ def create_nest_module(name: str): if not config_file.exists(): raise Exception("orm_config.py file not found") db_type = get_db_type(config_file) - add_document_to_odm_config(config_file, name, db_type) + 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", "") diff --git a/nest/core/decorators/controller.py b/nest/core/decorators/controller.py index d5d3912..62fcf30 100644 --- a/nest/core/decorators/controller.py +++ b/nest/core/decorators/controller.py @@ -20,7 +20,7 @@ def Controller(tag: str = None, prefix: str = None): if prefix.endswith("/"): prefix = prefix[:-1] - def wrapper(cls): + def wrapper(cls) -> ClassBasedView: router = APIRouter(tags=[tag] if tag else None) for name, method in cls.__dict__.items(): From 374cd5325ac36a1a436ae7718db578c60f230e65 Mon Sep 17 00:00:00 2001 From: ItayTheDar Date: Wed, 2 Aug 2023 08:11:32 +0300 Subject: [PATCH 16/16] change requirements_dev.txt --- .github/workflows/tests.yaml | 2 +- nest/common/templates/app.py | 2 +- requirements_dev.txt => requirements-dev.txt | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename requirements_dev.txt => requirements-dev.txt (100%) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index acc4250..ffe3d69 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -21,5 +21,5 @@ jobs: - name: Run tests run: | - pip install -r requirements_dev.txt + pip install -r requirements-dev.txt pytest tests \ No newline at end of file diff --git a/nest/common/templates/app.py b/nest/common/templates/app.py index 1e3fbbd..cf2d59f 100644 --- a/nest/common/templates/app.py +++ b/nest/common/templates/app.py @@ -14,4 +14,4 @@ def generate_app(db_type: str): @app.on_event("startup") async def startup(): {'await config.create_all()' if db_type == 'mongodb' else 'config.create_all()'} -""" \ No newline at end of file +""" diff --git a/requirements_dev.txt b/requirements-dev.txt similarity index 100% rename from requirements_dev.txt rename to requirements-dev.txt