hexcore 2.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. hexcore-2.1.1/LICENSE +25 -0
  2. hexcore-2.1.1/PKG-INFO +38 -0
  3. hexcore-2.1.1/README.md +14 -0
  4. hexcore-2.1.1/hexcore/__init__.py +42 -0
  5. hexcore-2.1.1/hexcore/__init__.pyi +10 -0
  6. hexcore-2.1.1/hexcore/application/__init__.py +8 -0
  7. hexcore-2.1.1/hexcore/application/__init__.pyi +4 -0
  8. hexcore-2.1.1/hexcore/application/dtos/__init__.py +7 -0
  9. hexcore-2.1.1/hexcore/application/dtos/__init__.pyi +3 -0
  10. hexcore-2.1.1/hexcore/application/dtos/base.py +13 -0
  11. hexcore-2.1.1/hexcore/application/dtos/base.pyi +4 -0
  12. hexcore-2.1.1/hexcore/application/use_cases/__init__.py +7 -0
  13. hexcore-2.1.1/hexcore/application/use_cases/__init__.pyi +3 -0
  14. hexcore-2.1.1/hexcore/application/use_cases/base.py +15 -0
  15. hexcore-2.1.1/hexcore/application/use_cases/base.pyi +11 -0
  16. hexcore-2.1.1/hexcore/config.py +99 -0
  17. hexcore-2.1.1/hexcore/config.pyi +35 -0
  18. hexcore-2.1.1/hexcore/domain/__init__.py +0 -0
  19. hexcore-2.1.1/hexcore/domain/__init__.pyi +0 -0
  20. hexcore-2.1.1/hexcore/domain/auth/__init__.py +17 -0
  21. hexcore-2.1.1/hexcore/domain/auth/__init__.pyi +4 -0
  22. hexcore-2.1.1/hexcore/domain/auth/permissions.py +73 -0
  23. hexcore-2.1.1/hexcore/domain/auth/permissions.pyi +25 -0
  24. hexcore-2.1.1/hexcore/domain/auth/value_objects.py +18 -0
  25. hexcore-2.1.1/hexcore/domain/auth/value_objects.pyi +12 -0
  26. hexcore-2.1.1/hexcore/domain/base.py +80 -0
  27. hexcore-2.1.1/hexcore/domain/base.pyi +19 -0
  28. hexcore-2.1.1/hexcore/domain/events.py +70 -0
  29. hexcore-2.1.1/hexcore/domain/events.pyi +35 -0
  30. hexcore-2.1.1/hexcore/domain/exceptions.py +5 -0
  31. hexcore-2.1.1/hexcore/domain/exceptions.pyi +2 -0
  32. hexcore-2.1.1/hexcore/domain/repositories.py +51 -0
  33. hexcore-2.1.1/hexcore/domain/repositories.pyi +25 -0
  34. hexcore-2.1.1/hexcore/domain/services.py +11 -0
  35. hexcore-2.1.1/hexcore/domain/services.pyi +8 -0
  36. hexcore-2.1.1/hexcore/domain/uow.py +55 -0
  37. hexcore-2.1.1/hexcore/domain/uow.pyi +18 -0
  38. hexcore-2.1.1/hexcore/infrastructure/__init__.py +0 -0
  39. hexcore-2.1.1/hexcore/infrastructure/__init__.pyi +0 -0
  40. hexcore-2.1.1/hexcore/infrastructure/api/__init__.py +0 -0
  41. hexcore-2.1.1/hexcore/infrastructure/api/__init__.pyi +0 -0
  42. hexcore-2.1.1/hexcore/infrastructure/api/utils.py +20 -0
  43. hexcore-2.1.1/hexcore/infrastructure/api/utils.pyi +9 -0
  44. hexcore-2.1.1/hexcore/infrastructure/cache/__init__.py +16 -0
  45. hexcore-2.1.1/hexcore/infrastructure/cache/__init__.pyi +10 -0
  46. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/__init__.py +0 -0
  47. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/__init__.pyi +0 -0
  48. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/memory.py +24 -0
  49. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/memory.pyi +10 -0
  50. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/redis.py +31 -0
  51. hexcore-2.1.1/hexcore/infrastructure/cache/cache_backends/redis.pyi +12 -0
  52. hexcore-2.1.1/hexcore/infrastructure/cli.py +397 -0
  53. hexcore-2.1.1/hexcore/infrastructure/cli.pyi +21 -0
  54. hexcore-2.1.1/hexcore/infrastructure/events/__init__.py +0 -0
  55. hexcore-2.1.1/hexcore/infrastructure/events/__init__.pyi +0 -0
  56. hexcore-2.1.1/hexcore/infrastructure/events/events_backends/__init__.py +0 -0
  57. hexcore-2.1.1/hexcore/infrastructure/events/events_backends/__init__.pyi +0 -0
  58. hexcore-2.1.1/hexcore/infrastructure/events/events_backends/memory.py +20 -0
  59. hexcore-2.1.1/hexcore/infrastructure/events/events_backends/memory.pyi +8 -0
  60. hexcore-2.1.1/hexcore/infrastructure/repositories/__init__.py +0 -0
  61. hexcore-2.1.1/hexcore/infrastructure/repositories/__init__.pyi +0 -0
  62. hexcore-2.1.1/hexcore/infrastructure/repositories/base.py +24 -0
  63. hexcore-2.1.1/hexcore/infrastructure/repositories/base.pyi +13 -0
  64. hexcore-2.1.1/hexcore/infrastructure/repositories/decorators.py +50 -0
  65. hexcore-2.1.1/hexcore/infrastructure/repositories/decorators.pyi +5 -0
  66. hexcore-2.1.1/hexcore/infrastructure/repositories/implementations.py +192 -0
  67. hexcore-2.1.1/hexcore/infrastructure/repositories/implementations.pyi +39 -0
  68. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/__init__.py +0 -0
  69. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/__init__.pyi +0 -0
  70. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/__init__.py +0 -0
  71. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/__init__.pyi +0 -0
  72. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/beanie.py +20 -0
  73. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/beanie.pyi +14 -0
  74. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/utils.py +134 -0
  75. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/nosql/utils.pyi +18 -0
  76. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/__init__.py +0 -0
  77. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/__init__.pyi +0 -0
  78. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/alchemy.py +44 -0
  79. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/alchemy.pyi +19 -0
  80. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/session.py +31 -0
  81. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/session.pyi +9 -0
  82. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/utils.py +171 -0
  83. hexcore-2.1.1/hexcore/infrastructure/repositories/orms/sql/utils.pyi +22 -0
  84. hexcore-2.1.1/hexcore/infrastructure/repositories/utils.py +119 -0
  85. hexcore-2.1.1/hexcore/infrastructure/repositories/utils.pyi +13 -0
  86. hexcore-2.1.1/hexcore/infrastructure/uow/__init__.py +121 -0
  87. hexcore-2.1.1/hexcore/infrastructure/uow/__init__.pyi +22 -0
  88. hexcore-2.1.1/hexcore/infrastructure/uow/decorators.py +16 -0
  89. hexcore-2.1.1/hexcore/infrastructure/uow/decorators.pyi +4 -0
  90. hexcore-2.1.1/hexcore/infrastructure/uow/helpers.py +9 -0
  91. hexcore-2.1.1/hexcore/infrastructure/uow/helpers.pyi +7 -0
  92. hexcore-2.1.1/hexcore/types.py +33 -0
  93. hexcore-2.1.1/hexcore/types.pyi +21 -0
  94. hexcore-2.1.1/hexcore.egg-info/PKG-INFO +38 -0
  95. hexcore-2.1.1/hexcore.egg-info/SOURCES.txt +100 -0
  96. hexcore-2.1.1/hexcore.egg-info/dependency_links.txt +1 -0
  97. hexcore-2.1.1/hexcore.egg-info/entry_points.txt +2 -0
  98. hexcore-2.1.1/hexcore.egg-info/requires.txt +13 -0
  99. hexcore-2.1.1/hexcore.egg-info/top_level.txt +4 -0
  100. hexcore-2.1.1/pyproject.toml +64 -0
  101. hexcore-2.1.1/setup.cfg +4 -0
  102. hexcore-2.1.1/tests/test_basic.py +2 -0
hexcore-2.1.1/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2
+ copies of the Software, and to permit persons to whom the Software is
3
+ furnished to do so, subject to the following conditions:
4
+
5
+ MIT License
6
+
7
+ Copyright (c) 2025 David Latosefki (Indroic)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
hexcore-2.1.1/PKG-INFO ADDED
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: hexcore
3
+ Version: 2.1.1
4
+ Summary: Núcleo reutilizable para proyectos Python con arquitectura hexagonal y event handling. Provee abstracciones, utilidades y contratos para DDD, eventos y desacoplamiento de infraestructura.
5
+ Author-email: "David Latosefki (Indroic)" <indroic@outlook.com>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: aiosqlite>=0.21.0
11
+ Requires-Dist: alembic>=1.16.5
12
+ Requires-Dist: async-typer>=0.1.10
13
+ Requires-Dist: asyncpg>=0.30.0
14
+ Requires-Dist: beanie>=2.0.0
15
+ Requires-Dist: build>=1.3.0
16
+ Requires-Dist: fastapi>=0.116.1
17
+ Requires-Dist: pika>=1.3.2
18
+ Requires-Dist: pydantic>=2.11.7
19
+ Requires-Dist: redis>=6.4.0
20
+ Requires-Dist: sqlalchemy>=2.0.43
21
+ Requires-Dist: twine>=6.2.0
22
+ Requires-Dist: typer>=0.17.3
23
+ Dynamic: license-file
24
+
25
+ # HexCore
26
+
27
+ HexCore es un módulo base reutilizable para proyectos Python que implementan arquitectura hexagonal y event handling.
28
+
29
+ ## ¿Qué provee HexCore?
30
+
31
+ - **Clases base y abstracciones** para entidades, repositorios, servicios y unidad de trabajo (UoW), siguiendo los principios de DDD y arquitectura hexagonal.
32
+ - **Interfaces y contratos** para caché, eventos y manejo de dependencias, desacoplando la lógica de negocio de la infraestructura.
33
+ - **Utilidades para event sourcing y event dispatching** listas para usar en cualquier proyecto.
34
+ - **Estructura flexible** para que puedas construir microservicios o aplicaciones monolíticas desacopladas y testeables.
35
+
36
+ ---
37
+
38
+ **Versión:** 2.1.1
@@ -0,0 +1,14 @@
1
+ # HexCore
2
+
3
+ HexCore es un módulo base reutilizable para proyectos Python que implementan arquitectura hexagonal y event handling.
4
+
5
+ ## ¿Qué provee HexCore?
6
+
7
+ - **Clases base y abstracciones** para entidades, repositorios, servicios y unidad de trabajo (UoW), siguiendo los principios de DDD y arquitectura hexagonal.
8
+ - **Interfaces y contratos** para caché, eventos y manejo de dependencias, desacoplando la lógica de negocio de la infraestructura.
9
+ - **Utilidades para event sourcing y event dispatching** listas para usar en cualquier proyecto.
10
+ - **Estructura flexible** para que puedas construir microservicios o aplicaciones monolíticas desacopladas y testeables.
11
+
12
+ ---
13
+
14
+ **Versión:** 2.1.1
@@ -0,0 +1,42 @@
1
+ """
2
+ Euphoria Kernel Core
3
+ Submódulo principal con entidades, eventos y repositorios.
4
+ """
5
+
6
+ from .domain.base import BaseEntity
7
+ from .domain.auth.permissions import PermissionsEnum
8
+ from .domain.auth.value_objects import TokenClaims
9
+ from .application.dtos.base import DTO
10
+ from .domain.events import (
11
+ DomainEvent,
12
+ EntityCreatedEvent,
13
+ EntityDeletedEvent,
14
+ EntityUpdatedEvent,
15
+ )
16
+ from .infrastructure.repositories.base import (
17
+ IBaseRepository,
18
+ IBaseTenantAwareRepository,
19
+ BaseSQLRepository,
20
+ BaseSQLTenantAwareRepository,
21
+ )
22
+ from .infrastructure import cli
23
+ from .infrastructure import cache
24
+ from . import config
25
+
26
+ __all__ = [
27
+ "BaseEntity",
28
+ "PermissionsEnum",
29
+ "TokenClaims",
30
+ "DTO",
31
+ "DomainEvent",
32
+ "EntityCreatedEvent",
33
+ "EntityDeletedEvent",
34
+ "EntityUpdatedEvent",
35
+ "IBaseRepository",
36
+ "IBaseTenantAwareRepository",
37
+ "BaseSQLRepository",
38
+ "BaseSQLTenantAwareRepository",
39
+ "cli",
40
+ "cache",
41
+ "config",
42
+ ]
@@ -0,0 +1,10 @@
1
+ from . import config as config
2
+ from .application.dtos.base import DTO as DTO
3
+ from .domain.auth.permissions import PermissionsEnum as PermissionsEnum
4
+ from .domain.auth.value_objects import TokenClaims as TokenClaims
5
+ from .domain.base import BaseEntity as BaseEntity
6
+ from .domain.events import DomainEvent as DomainEvent, EntityCreatedEvent as EntityCreatedEvent, EntityDeletedEvent as EntityDeletedEvent, EntityUpdatedEvent as EntityUpdatedEvent
7
+ from .infrastructure import cache as cache, cli as cli
8
+ from .infrastructure.repositories.base import BaseSQLRepository as BaseSQLRepository, BaseSQLTenantAwareRepository as BaseSQLTenantAwareRepository, IBaseRepository as IBaseRepository, IBaseTenantAwareRepository as IBaseTenantAwareRepository
9
+
10
+ __all__ = ['BaseEntity', 'PermissionsEnum', 'TokenClaims', 'DTO', 'DomainEvent', 'EntityCreatedEvent', 'EntityDeletedEvent', 'EntityUpdatedEvent', 'IBaseRepository', 'IBaseTenantAwareRepository', 'BaseSQLRepository', 'BaseSQLTenantAwareRepository', 'cli', 'cache', 'config']
@@ -0,0 +1,8 @@
1
+ """
2
+ Submódulo de aplicación: DTOs y casos de uso.
3
+ """
4
+
5
+ from .dtos.base import DTO
6
+ from .use_cases.base import UseCase
7
+
8
+ __all__ = ["DTO", "UseCase"]
@@ -0,0 +1,4 @@
1
+ from .dtos.base import DTO as DTO
2
+ from .use_cases.base import UseCase as UseCase
3
+
4
+ __all__ = ['DTO', 'UseCase']
@@ -0,0 +1,7 @@
1
+ """
2
+ Submódulo de DTOs base para la aplicación.
3
+ """
4
+
5
+ from .base import DTO
6
+
7
+ __all__ = ["DTO"]
@@ -0,0 +1,3 @@
1
+ from .base import DTO as DTO
2
+
3
+ __all__ = ['DTO']
@@ -0,0 +1,13 @@
1
+ from abc import ABC
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class DTO(BaseModel, ABC):
6
+ """
7
+ Clase base para todos los DTOs de la capa de aplicación.
8
+
9
+ Estos representan los datos que se desea exponer o recibir a través de la API,
10
+ evitando la exposición de detalles internos del dominio.
11
+ """
12
+
13
+ pass
@@ -0,0 +1,4 @@
1
+ from abc import ABC
2
+ from pydantic import BaseModel
3
+
4
+ class DTO(BaseModel, ABC): ...
@@ -0,0 +1,7 @@
1
+ """
2
+ Submódulo de casos de uso base para la aplicación.
3
+ """
4
+
5
+ from .base import UseCase
6
+
7
+ __all__ = ["UseCase"]
@@ -0,0 +1,3 @@
1
+ from .base import UseCase as UseCase
2
+
3
+ __all__ = ['UseCase']
@@ -0,0 +1,15 @@
1
+ import typing as t
2
+ from abc import ABC, abstractmethod
3
+ from hexcore.application.dtos.base import DTO
4
+
5
+ # Tipo del Input
6
+ T = t.TypeVar("T", bound=DTO)
7
+
8
+ # Tipo del Output(o resultado)
9
+ R = t.TypeVar("R", bound=DTO)
10
+
11
+
12
+ class UseCase(ABC, t.Generic[T, R]):
13
+ @abstractmethod
14
+ async def execute(self, command: T) -> R:
15
+ raise NotImplementedError("Subclasses must implement this method")
@@ -0,0 +1,11 @@
1
+ import abc
2
+ import typing as t
3
+ from abc import ABC, abstractmethod
4
+ from hexcore.application.dtos.base import DTO as DTO
5
+
6
+ T = t.TypeVar('T', bound=DTO)
7
+ R = t.TypeVar('R', bound=DTO)
8
+
9
+ class UseCase(ABC, t.Generic[T, R], metaclass=abc.ABCMeta):
10
+ @abstractmethod
11
+ async def execute(self, command: T) -> R: ...
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+ import importlib
3
+ import typing as t
4
+ from pydantic import BaseModel, ConfigDict
5
+ from pathlib import Path
6
+ from hexcore.infrastructure.cache import ICache
7
+ from hexcore.domain.events import IEventDispatcher
8
+
9
+
10
+ from hexcore.infrastructure.cache.cache_backends.memory import MemoryCache
11
+ from hexcore.infrastructure.events.events_backends.memory import InMemoryEventDispatcher
12
+
13
+
14
+ class ServerConfig(BaseModel):
15
+ # Project Config
16
+ base_dir: Path = Path(".")
17
+
18
+ # SERVER CONFIG
19
+ host: str = "localhost"
20
+ port: int = 8000
21
+ debug: bool = True
22
+
23
+ # DB CONFIG
24
+ sql_database_url: str = "sqlite:///./db.sqlite3"
25
+ async_sql_database_url: str = "sqlite+aiosqlite:///./db.sqlite3"
26
+
27
+ mongo_database_url: str = "mongodb://localhost:27017"
28
+ async_mongo_database_url: str = "mongodb+async://localhost:27017"
29
+ mongo_db_name: str = "euphoria_db"
30
+ mongo_uri: str = "mongodb://localhost:27017/euphoria_db"
31
+
32
+ redis_uri: str = "redis://localhost:6379/0"
33
+ redis_host: str = "localhost"
34
+ redis_port: int = 6379
35
+ redis_db: int = 0
36
+ redis_cache_duration: int = 300 # seconds
37
+
38
+ # Security
39
+ allow_origins: list[str] = [
40
+ "*" if debug else "http://localhost:{port}".format(port=port)
41
+ ]
42
+ allow_credentials: bool = True
43
+ allow_methods: list[str] = ["*"]
44
+ allow_headers: list[str] = ["*"]
45
+
46
+ # caching
47
+ cache_backend: ICache = (
48
+ MemoryCache()
49
+ ) # Debe ser una instancia de ICache(o subclase)
50
+
51
+ model_config = ConfigDict(arbitrary_types_allowed=True)
52
+
53
+ # Event Dispatcher
54
+ event_dispatcher: IEventDispatcher = InMemoryEventDispatcher()
55
+
56
+
57
+ class LazyConfig:
58
+ """
59
+ Loader de configuración flexible.
60
+ Busca una variable 'config' (instancia de ServerConfig) o una clase 'ServerConfig' en los módulos personalizados.
61
+ Si no la encuentra, usa la configuración base del kernel.
62
+
63
+ IMPORTANTE: La configuración personalizada debe estar en un módulo llamado 'config' en src.domain
64
+
65
+ """
66
+
67
+ _imported_config: t.Optional[ServerConfig] = None
68
+
69
+ @classmethod
70
+ def get_config(cls) -> ServerConfig:
71
+ if cls._imported_config is not None:
72
+ return cls._imported_config
73
+ # Intenta importar la config personalizada
74
+ for modpath in ("config", "src.domain.config"):
75
+ try:
76
+ mod = importlib.import_module(modpath)
77
+ config_instance = getattr(mod, "config", None)
78
+ if config_instance is not None:
79
+ # Si es clase, instanciar
80
+ if isinstance(config_instance, type) and issubclass(
81
+ config_instance, ServerConfig
82
+ ):
83
+ config_instance = config_instance()
84
+ if isinstance(config_instance, ServerConfig):
85
+ cls._imported_config = config_instance
86
+ return cls._imported_config
87
+ # Alternativamente, busca la clase ServerConfig
88
+ config_class = getattr(mod, "ServerConfig", None)
89
+ if isinstance(config_class, type) and issubclass(
90
+ config_class, ServerConfig
91
+ ):
92
+ config_instance = config_class()
93
+ cls._imported_config = config_instance
94
+ return cls._imported_config
95
+ except (ModuleNotFoundError, AttributeError):
96
+ continue
97
+ # Fallback: config base del kernel
98
+ cls._imported_config = ServerConfig()
99
+ return cls._imported_config
@@ -0,0 +1,35 @@
1
+ from _typeshed import Incomplete
2
+ from hexcore.domain.events import IEventDispatcher as IEventDispatcher
3
+ from hexcore.infrastructure.cache import ICache as ICache
4
+ from hexcore.infrastructure.cache.cache_backends.memory import MemoryCache as MemoryCache
5
+ from hexcore.infrastructure.events.events_backends.memory import InMemoryEventDispatcher as InMemoryEventDispatcher
6
+ from pathlib import Path
7
+ from pydantic import BaseModel
8
+
9
+ class ServerConfig(BaseModel):
10
+ base_dir: Path
11
+ host: str
12
+ port: int
13
+ debug: bool
14
+ sql_database_url: str
15
+ async_sql_database_url: str
16
+ mongo_database_url: str
17
+ async_mongo_database_url: str
18
+ mongo_db_name: str
19
+ mongo_uri: str
20
+ redis_uri: str
21
+ redis_host: str
22
+ redis_port: int
23
+ redis_db: int
24
+ redis_cache_duration: int
25
+ allow_origins: list[str]
26
+ allow_credentials: bool
27
+ allow_methods: list[str]
28
+ allow_headers: list[str]
29
+ cache_backend: ICache
30
+ model_config: Incomplete
31
+ event_dispatcher: IEventDispatcher
32
+
33
+ class LazyConfig:
34
+ @classmethod
35
+ def get_config(cls) -> ServerConfig: ...
File without changes
File without changes
@@ -0,0 +1,17 @@
1
+ """
2
+ Submódulo de autenticación y permisos del kernel.
3
+ """
4
+
5
+ from .permissions import (
6
+ PermissionsEnum,
7
+ get_all_permission_values,
8
+ get_owner_permissions,
9
+ )
10
+ from .value_objects import TokenClaims
11
+
12
+ __all__ = [
13
+ "PermissionsEnum",
14
+ "get_all_permission_values",
15
+ "get_owner_permissions",
16
+ "TokenClaims",
17
+ ]
@@ -0,0 +1,4 @@
1
+ from .permissions import PermissionsEnum as PermissionsEnum, get_all_permission_values as get_all_permission_values, get_owner_permissions as get_owner_permissions
2
+ from .value_objects import TokenClaims as TokenClaims
3
+
4
+ __all__ = ['PermissionsEnum', 'get_all_permission_values', 'get_owner_permissions', 'TokenClaims']
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+ from enum import Enum
3
+
4
+ # --- Prefijos de Permisos ---
5
+ _ROLES = "roles"
6
+ _USERS = "users"
7
+ _TENANTS = "tenants"
8
+
9
+ _LOGISTICS_INVENTORY = "logistics.inventory"
10
+ _LOGISTICS_PRODUCTS = "logistics.products"
11
+
12
+ # --- Sufijos Comunes ---
13
+
14
+ _VIEW = "view"
15
+ _CREATE = "create"
16
+ _EDIT = "edit"
17
+ _DELETE = "delete"
18
+
19
+
20
+ class PermissionsEnum(str, Enum):
21
+ """
22
+ Catálogo central de todos los permisos del sistema.
23
+ Esta es la ÚNICA fuente de la verdad para los permisos.
24
+ El valor (string) es lo que se almacena en la base de datos.
25
+ El formato es: <dominio>.<entidad>.<accion>
26
+ """
27
+
28
+ # -- SUPERUSER PERMISSION --
29
+
30
+ SUPERUSER = "__all__" # Permiso especial que otorga todos los permisos del sistema
31
+
32
+ # --- Dominio: IAM (Identidad y Acceso) ---
33
+ ROLES_VIEW = f"{_ROLES}.{_VIEW}"
34
+ ROLES_CREATE = f"{_ROLES}.{_CREATE}"
35
+ ROLES_EDIT = f"{_ROLES}.{_EDIT}"
36
+ ROLES_DELETE = f"{_ROLES}.{_DELETE}"
37
+
38
+ USERS_CREATE = f"{_USERS}.{_CREATE}"
39
+ USERS_VIEW = f"{_USERS}.{_VIEW}"
40
+ USERS_INVITE = f"{_USERS}.invite"
41
+ USERS_EDIT = f"{_USERS}.{_EDIT}"
42
+ USERS_DELETE = f"{_USERS}.{_DELETE}"
43
+ USERS_ADD_ROLE = f"{_USERS}.add.rol"
44
+
45
+ TENANTS_VIEW = f"{_TENANTS}.{_VIEW}"
46
+ TENANTS_EDIT = f"{_TENANTS}.{_EDIT}"
47
+
48
+ # --- Dominio: Logistics (Logística) ---
49
+ LOGISTICS_INVENTORY_VIEW = f"{_LOGISTICS_INVENTORY}.{_VIEW}"
50
+ LOGISTICS_INVENTORY_ADJUST = f"{_LOGISTICS_INVENTORY}.adjust"
51
+ LOGISTICS_PRODUCTS_MANAGE = f"{_LOGISTICS_PRODUCTS}.manage"
52
+
53
+
54
+ # Permisos que No puede tener un propietario
55
+ OWNER_EXCLUDED_PERMISSIONS = {
56
+ PermissionsEnum.TENANTS_EDIT,
57
+ PermissionsEnum.TENANTS_VIEW,
58
+ }
59
+
60
+
61
+ def get_all_permission_values() -> set[str]:
62
+ """
63
+ Devuelve un conjunto con todos los valores de los permisos definidos en PermissionsEnum.
64
+ Ideal para usar en el comando de sincronización.
65
+ """
66
+ return {p.value for p in PermissionsEnum}
67
+
68
+
69
+ def get_owner_permissions() -> set[str]:
70
+ """
71
+ Devuelve los permisos de un propietario.
72
+ """
73
+ return {p.value for p in PermissionsEnum} - OWNER_EXCLUDED_PERMISSIONS
@@ -0,0 +1,25 @@
1
+ from _typeshed import Incomplete
2
+ from enum import Enum
3
+
4
+ class PermissionsEnum(str, Enum):
5
+ SUPERUSER = '__all__'
6
+ ROLES_VIEW = ...
7
+ ROLES_CREATE = ...
8
+ ROLES_EDIT = ...
9
+ ROLES_DELETE = ...
10
+ USERS_CREATE = ...
11
+ USERS_VIEW = ...
12
+ USERS_INVITE = ...
13
+ USERS_EDIT = ...
14
+ USERS_DELETE = ...
15
+ USERS_ADD_ROLE = ...
16
+ TENANTS_VIEW = ...
17
+ TENANTS_EDIT = ...
18
+ LOGISTICS_INVENTORY_VIEW = ...
19
+ LOGISTICS_INVENTORY_ADJUST = ...
20
+ LOGISTICS_PRODUCTS_MANAGE = ...
21
+
22
+ OWNER_EXCLUDED_PERMISSIONS: Incomplete
23
+
24
+ def get_all_permission_values() -> set[str]: ...
25
+ def get_owner_permissions() -> set[str]: ...
@@ -0,0 +1,18 @@
1
+ import typing as t
2
+
3
+ from uuid import uuid4
4
+ from pydantic import Field, BaseModel
5
+ from .permissions import PermissionsEnum
6
+
7
+
8
+ class TokenClaims(BaseModel):
9
+ """Detalles sobre los claims de un token."""
10
+
11
+ iss: str # Identificador del token de acceso
12
+ sub: str # ID del usuario
13
+ exp: int # Tiempo de expiración
14
+ iat: int # Tiempo de emisión
15
+ jti: str = Field(default_factory=lambda: str(uuid4())) # ID del token
16
+ client_id: str # ID del cliente OAuth
17
+ scopes: t.List[PermissionsEnum] = [] # Permisos del Token
18
+ tenant_id: t.Optional[str] = None # ID del tenant (si aplica)
@@ -0,0 +1,12 @@
1
+ from .permissions import PermissionsEnum as PermissionsEnum
2
+ from pydantic import BaseModel
3
+
4
+ class TokenClaims(BaseModel):
5
+ iss: str
6
+ sub: str
7
+ exp: int
8
+ iat: int
9
+ jti: str
10
+ client_id: str
11
+ scopes: list[PermissionsEnum]
12
+ tenant_id: str | None
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+ import typing as t
3
+ import abc
4
+ from pydantic import BaseModel, Field, ConfigDict
5
+ from uuid import UUID, uuid4
6
+ from datetime import datetime, UTC
7
+
8
+ if t.TYPE_CHECKING:
9
+ from hexcore.domain.events import DomainEvent
10
+
11
+
12
+ class BaseEntity(BaseModel):
13
+ """
14
+ Clase base para todas las entidades del dominio.
15
+
16
+ Proporciona campos comunes y configuración de Pydantic para asegurar consistencia
17
+ y comportamiento predecible en todo el modelo.
18
+
19
+ Atributos:
20
+ id (UUID): Identificador único universal para la entidad.
21
+ created_at (datetime): Marca de tiempo de la creación de la entidad (UTC).
22
+ updated_at (datetime): Marca de tiempo de la última actualización (UTC).
23
+ is_active (bool): Indicador para borrado lógico (soft delete).
24
+ """
25
+
26
+ id: UUID = Field(default_factory=uuid4)
27
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
28
+ updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
29
+ is_active: t.Optional[bool] = True
30
+
31
+ _domain_events: t.List[DomainEvent] = []
32
+
33
+ model_config = ConfigDict(
34
+ from_attributes=True, # Permite crear modelos desde atributos de objetos (clave para ORMs).
35
+ validate_assignment=True, # Vuelve a validar la entidad cada vez que se modifica un campo.
36
+ # `frozen=True` hace que las entidades sean inmutables, lo cual es un ideal de DDD.
37
+ # Sin embargo, puede complicar el manejo de estado con un ORM como SQLAlchemy,
38
+ # donde los objetos a menudo se modifican y luego se guardan.
39
+ # Lo cambiamos a False para un enfoque más pragmático.
40
+ frozen=False,
41
+ )
42
+
43
+ def register_event(self, event: DomainEvent):
44
+ """Añade un evento a la lista de la entidad."""
45
+ self._domain_events.append(event)
46
+
47
+ def pull_domain_events(self) -> t.List[DomainEvent]:
48
+ """Entrega los eventos y limpia la lista."""
49
+ events = self._domain_events[:]
50
+ self._domain_events.clear()
51
+ return events
52
+
53
+ def clear_domain_events(self):
54
+ """Limpia la lista de eventos sin entregarlos."""
55
+ self._domain_events.clear()
56
+
57
+ async def deactivate(self):
58
+ """Desactiva la Entidad(Borrado Logico)"""
59
+ self.is_active = False
60
+
61
+
62
+ class AbstractModelMeta(BaseEntity, abc.ABC):
63
+ """
64
+ Metaclase para resolver un conflicto entre Pydantic y las clases abstractas de Python.
65
+
66
+ Problema:
67
+ - Pydantic (`BaseModel`) usa su propia metaclase para la validación de datos.
68
+ - Las clases abstractas de Python (`abc.ABC`) usan `abc.ABCMeta` para permitir `@abstractmethod`.
69
+ - Una clase no puede tener dos metaclases diferentes.
70
+
71
+ Solución:
72
+ Esta metaclase combina ambas, permitiendo crear clases que son a la vez
73
+ modelos de Pydantic y clases base abstractas.
74
+
75
+ Uso:
76
+ class MiClaseAbstracta(BaseEntity, abc.ABC, metaclass=AbstractModelMeta):
77
+ ...
78
+ """
79
+
80
+ pass
@@ -0,0 +1,19 @@
1
+ import abc
2
+ from _typeshed import Incomplete
3
+ from datetime import datetime
4
+ from hexcore.domain.events import DomainEvent as DomainEvent
5
+ from pydantic import BaseModel
6
+ from uuid import UUID
7
+
8
+ class BaseEntity(BaseModel):
9
+ id: UUID
10
+ created_at: datetime
11
+ updated_at: datetime
12
+ is_active: bool | None
13
+ model_config: Incomplete
14
+ def register_event(self, event: DomainEvent): ...
15
+ def pull_domain_events(self) -> list[DomainEvent]: ...
16
+ def clear_domain_events(self) -> None: ...
17
+ async def deactivate(self) -> None: ...
18
+
19
+ class AbstractModelMeta(BaseEntity, abc.ABC): ...