Encryptors 2.36__tar.gz → 2.38__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.
- {encryptors-2.36 → encryptors-2.38}/PKG-INFO +2 -1
- {encryptors-2.36 → encryptors-2.38}/setup.py +2 -1
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/PKG-INFO +2 -1
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/requires.txt +1 -0
- encryptors-2.38/src/Osdental/Cli/__init__.py +118 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Database/BaseRepository.py +34 -20
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/Extensions/AuditExtension.py +3 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +4 -1
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/Message.py +0 -1
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/FileMetaData.py +1 -1
- encryptors-2.36/src/Osdental/Cli/__init__.py +0 -505
- {encryptors-2.36 → encryptors-2.38}/README.md +0 -0
- {encryptors-2.36 → encryptors-2.38}/setup.cfg +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/SOURCES.txt +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Database/Connection.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Database/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Decorators/Grpc.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Decorators/Retry.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Decorators/SecureResolver.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Decorators/SqlDataNormalizer.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Decorators/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Aes.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Bcrypt.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Jwt.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Rsa.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/Sha512.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Encryptor/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Exception/ControlledException.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Exception/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/Extensions/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/Models/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Exceptions/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Helpers/_AuditHelper.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Helpers/_TokenService.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/_Helpers/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Graphql/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Helpers/AuditDispatcher.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Helpers/KeyVaultService.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Helpers/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Http/APIClient.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Http/_Exceptions.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Http/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Messaging/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/AuditConfig.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/Catalog.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/Legacy.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/Response.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/Token.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/_Audit.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Models/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/RedisCache/Redis.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/RedisCache/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/Context/RequestContext.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/Context/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/Middlewares/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/Code.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/Constant.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/FileType.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/Profile.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Enums/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Logger.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/Mapper.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/Utils/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Shared/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Storage/S3Storage.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/Storage/__init__.py +0 -0
- {encryptors-2.36 → encryptors-2.38}/src/Osdental/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.38
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -35,6 +35,7 @@ Requires-Dist: pydantic_core==2.41.4
|
|
|
35
35
|
Requires-Dist: PyJWT==2.10.1
|
|
36
36
|
Requires-Dist: aioodbc==0.5.0
|
|
37
37
|
Requires-Dist: python-dotenv==1.0.1
|
|
38
|
+
Requires-Dist: pydantic_settings==2.13.1
|
|
38
39
|
Requires-Dist: requests==2.32.3
|
|
39
40
|
Requires-Dist: six==1.17.0
|
|
40
41
|
Requires-Dist: sniffio==1.3.1
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.38",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -43,6 +43,7 @@ setup(
|
|
|
43
43
|
"PyJWT==2.10.1",
|
|
44
44
|
"aioodbc==0.5.0",
|
|
45
45
|
"python-dotenv==1.0.1",
|
|
46
|
+
"pydantic_settings==2.13.1",
|
|
46
47
|
"requests==2.32.3",
|
|
47
48
|
"six==1.17.0",
|
|
48
49
|
"sniffio==1.3.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.38
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -35,6 +35,7 @@ Requires-Dist: pydantic_core==2.41.4
|
|
|
35
35
|
Requires-Dist: PyJWT==2.10.1
|
|
36
36
|
Requires-Dist: aioodbc==0.5.0
|
|
37
37
|
Requires-Dist: python-dotenv==1.0.1
|
|
38
|
+
Requires-Dist: pydantic_settings==2.13.1
|
|
38
39
|
Requires-Dist: requests==2.32.3
|
|
39
40
|
Requires-Dist: six==1.17.0
|
|
40
41
|
Requires-Dist: sniffio==1.3.1
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
import platform
|
|
5
|
+
import click
|
|
6
|
+
from Osdental.Shared.Logger import logger
|
|
7
|
+
from Osdental.Shared.Enums.Message import Message
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def cli():
|
|
12
|
+
"""Custom commands to manage the project."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@cli.command()
|
|
16
|
+
def clean():
|
|
17
|
+
"""Borrar todos los __pycache__."""
|
|
18
|
+
if platform.system() == 'Windows':
|
|
19
|
+
subprocess.run('for /d /r . %d in (__pycache__) do @if exist "%d" rd /s/q "%d"', shell=True)
|
|
20
|
+
else:
|
|
21
|
+
subprocess.run("find . -name '__pycache__' -type d -exec rm -rf {} +", shell=True)
|
|
22
|
+
|
|
23
|
+
logger.info(Message.PYCACHE_CLEANUP_SUCCESS_MSG)
|
|
24
|
+
|
|
25
|
+
@cli.command()
|
|
26
|
+
@click.argument('port')
|
|
27
|
+
def start(port: int):
|
|
28
|
+
"""Start the FastAPI server.."""
|
|
29
|
+
try:
|
|
30
|
+
subprocess.run(['uvicorn', 'app:app', '--port', str(port), '--reload'], check=True)
|
|
31
|
+
except subprocess.CalledProcessError as e:
|
|
32
|
+
logger.error(f'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
|
|
33
|
+
|
|
34
|
+
@cli.command()
|
|
35
|
+
@click.argument('port')
|
|
36
|
+
def serve(port: int):
|
|
37
|
+
"""Set up the FastAPI server accessible from any machine."""
|
|
38
|
+
try:
|
|
39
|
+
subprocess.run(['uvicorn', 'app:app', '--host', '0.0.0.0', '--port', str(port), '--reload'], check=True)
|
|
40
|
+
except subprocess.CalledProcessError as e:
|
|
41
|
+
logger.error(f'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
|
|
42
|
+
|
|
43
|
+
@cli.command("clean-redis")
|
|
44
|
+
@click.argument('redis_env')
|
|
45
|
+
async def clean_redis(redis_env: str):
|
|
46
|
+
try:
|
|
47
|
+
from Osdental.RedisCache.Redis import RedisCacheAsync
|
|
48
|
+
redis_url = os.getenv(redis_env)
|
|
49
|
+
if not redis_url:
|
|
50
|
+
logger.warning(f'Environment variable not found: {redis_env}')
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
redis = RedisCacheAsync(redis_url=redis_url)
|
|
54
|
+
await redis.flush()
|
|
55
|
+
logger.info(Message.REDIS_CLEANUP_SUCCESS_MSG)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.error(f'{Message.REDIS_CLEANUP_ERROR_MSG}: {e}')
|
|
58
|
+
|
|
59
|
+
@cli.command(name='proto-files')
|
|
60
|
+
@click.argument('name')
|
|
61
|
+
def proto_files(name: str):
|
|
62
|
+
proto_dir = os.path.join('src', 'Infrastructure', 'Grpc', 'Proto').replace('\\', '/')
|
|
63
|
+
gen_dir = os.path.join('src', 'Infrastructure', 'Grpc', 'Generated').replace('\\', '/')
|
|
64
|
+
|
|
65
|
+
proto_path = os.path.join(proto_dir, f'{name}.proto').replace('\\', '/')
|
|
66
|
+
common_path = os.path.join(proto_dir, 'Common.proto').replace('\\', '/')
|
|
67
|
+
|
|
68
|
+
common_py = os.path.join(gen_dir, 'Common_pb2.py').replace('\\', '/')
|
|
69
|
+
common_grpc_py = os.path.join(gen_dir, 'Common_pb2_grpc.py').replace('\\', '/')
|
|
70
|
+
|
|
71
|
+
proto_files = [proto_path]
|
|
72
|
+
if not (os.path.exists(common_py) and os.path.exists(common_grpc_py)):
|
|
73
|
+
proto_files.append(common_path)
|
|
74
|
+
|
|
75
|
+
cmd = [
|
|
76
|
+
sys.executable, "-m", "grpc_tools.protoc",
|
|
77
|
+
f"-I={proto_dir}",
|
|
78
|
+
f"--python_out={gen_dir}",
|
|
79
|
+
f"--grpc_python_out={gen_dir}",
|
|
80
|
+
*proto_files
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
subprocess.run(cmd, shell=False, check=True)
|
|
84
|
+
logger.info(Message.PROTO_FILES_GENERATED_MSG)
|
|
85
|
+
|
|
86
|
+
@cli.command('run-server')
|
|
87
|
+
@click.option('--host', default="0.0.0.0", help='Host where to set up the server')
|
|
88
|
+
@click.option('--port', default=5000, help='Server port')
|
|
89
|
+
@click.option('--workers', default=4, help='Number of workers')
|
|
90
|
+
def run_server(host, port, workers):
|
|
91
|
+
"""
|
|
92
|
+
Launch the application with uvicorn on Windows or gunicorn on other systems
|
|
93
|
+
:host
|
|
94
|
+
:port
|
|
95
|
+
:workers
|
|
96
|
+
"""
|
|
97
|
+
if sys.platform.startswith('win'):
|
|
98
|
+
cmd = [
|
|
99
|
+
sys.executable, '-m', 'uvicorn',
|
|
100
|
+
'app:app',
|
|
101
|
+
'--host', host,
|
|
102
|
+
'--port', str(port),
|
|
103
|
+
'--workers', str(workers)
|
|
104
|
+
]
|
|
105
|
+
else:
|
|
106
|
+
cmd = [
|
|
107
|
+
sys.executable, '-m', 'gunicorn',
|
|
108
|
+
'app:app',
|
|
109
|
+
'-k', 'uvicorn.workers.UvicornWorker',
|
|
110
|
+
'--bind', f'{host}:{port}',
|
|
111
|
+
'--workers', str(workers)
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
logger.info(f'Running: {' '.join(cmd)}')
|
|
115
|
+
subprocess.run(cmd)
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
cli()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
from sqlalchemy import text
|
|
3
3
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
4
|
-
from Osdental.Exception.ControlledException import DatabaseException
|
|
4
|
+
from Osdental.Exception.ControlledException import DatabaseException, OSDException
|
|
5
5
|
from Osdental.Shared.Utils.DataNormalizer import normalize
|
|
6
6
|
|
|
7
7
|
class BaseRepository:
|
|
@@ -23,6 +23,7 @@ class BaseRepository:
|
|
|
23
23
|
many: bool = False,
|
|
24
24
|
as_dict: bool = False
|
|
25
25
|
) -> Any:
|
|
26
|
+
|
|
26
27
|
result = await self.async_session.execute(
|
|
27
28
|
text(query),
|
|
28
29
|
params or {}
|
|
@@ -34,13 +35,11 @@ class BaseRepository:
|
|
|
34
35
|
else result.mappings().first()
|
|
35
36
|
)
|
|
36
37
|
|
|
37
|
-
if
|
|
38
|
-
return data
|
|
39
|
-
|
|
40
|
-
if many:
|
|
38
|
+
if as_dict and data is not None:
|
|
41
39
|
return normalize(data)
|
|
42
40
|
|
|
43
|
-
return
|
|
41
|
+
return data
|
|
42
|
+
|
|
44
43
|
|
|
45
44
|
async def _execute_command(
|
|
46
45
|
self,
|
|
@@ -48,8 +47,10 @@ class BaseRepository:
|
|
|
48
47
|
params: dict | None = None,
|
|
49
48
|
*,
|
|
50
49
|
validate: bool = True,
|
|
51
|
-
success_codes: str | int | tuple[str | int, ...] | None = None
|
|
52
|
-
|
|
50
|
+
success_codes: str | int | tuple[str | int, ...] | None = None,
|
|
51
|
+
as_dict: bool = False
|
|
52
|
+
) -> Any:
|
|
53
|
+
|
|
53
54
|
result = await self.async_session.execute(
|
|
54
55
|
text(query),
|
|
55
56
|
params or {}
|
|
@@ -58,7 +59,8 @@ class BaseRepository:
|
|
|
58
59
|
row = result.mappings().first()
|
|
59
60
|
|
|
60
61
|
if not validate or not row:
|
|
61
|
-
return row
|
|
62
|
+
return normalize(row) if as_dict else row
|
|
63
|
+
|
|
62
64
|
|
|
63
65
|
status_code = self._get_status_value(
|
|
64
66
|
row,
|
|
@@ -82,17 +84,29 @@ class BaseRepository:
|
|
|
82
84
|
error="Status code missing",
|
|
83
85
|
status_code=500
|
|
84
86
|
)
|
|
85
|
-
return row
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
else:
|
|
89
|
+
|
|
90
|
+
if not isinstance(success_codes, tuple):
|
|
91
|
+
success_codes = (success_codes,)
|
|
92
|
+
|
|
93
|
+
if status_code not in success_codes:
|
|
94
|
+
raise DatabaseException(
|
|
95
|
+
message=status_message,
|
|
96
|
+
error=status_message,
|
|
97
|
+
status_code=status_code
|
|
98
|
+
)
|
|
90
99
|
|
|
91
|
-
if
|
|
92
|
-
|
|
93
|
-
message=status_message,
|
|
94
|
-
error=status_message,
|
|
95
|
-
status_code=status_code
|
|
96
|
-
)
|
|
100
|
+
return normalize(row) if as_dict else row
|
|
101
|
+
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
async def _execute_scalar(
|
|
104
|
+
self,
|
|
105
|
+
query: str,
|
|
106
|
+
params: dict | None = None
|
|
107
|
+
) -> Any | None:
|
|
108
|
+
result = await self.async_session.execute(
|
|
109
|
+
text(query),
|
|
110
|
+
params or {}
|
|
111
|
+
)
|
|
112
|
+
return result.scalar_one_or_none()
|
|
@@ -102,7 +102,10 @@ class AuditExtension(Extension):
|
|
|
102
102
|
self.errors = errors
|
|
103
103
|
|
|
104
104
|
def request_finished(self, context):
|
|
105
|
+
if not self.request_payload: return
|
|
106
|
+
|
|
105
107
|
query = self.request_payload.get("query", "")
|
|
108
|
+
|
|
106
109
|
if "__schema" in query or "__type" in query: return
|
|
107
110
|
|
|
108
111
|
if not hasattr(context, "audit_plain_response"): return
|
|
@@ -37,7 +37,10 @@ class TenantPolicy:
|
|
|
37
37
|
return token
|
|
38
38
|
|
|
39
39
|
# If it comes by request, it is taken as priority
|
|
40
|
-
external_enterprise_req =
|
|
40
|
+
external_enterprise_req = (
|
|
41
|
+
decrypted_payload.get("idExternalEnterprise")
|
|
42
|
+
if decrypted_payload else None
|
|
43
|
+
)
|
|
41
44
|
if external_enterprise_req and token:
|
|
42
45
|
token.id_external_enterprise = external_enterprise_req
|
|
43
46
|
|
|
@@ -21,7 +21,6 @@ class Message(StrEnum):
|
|
|
21
21
|
AES_KEY_USER_REQUIRED_MSG = 'AES key user cannot be empty.'
|
|
22
22
|
AES_KEY_AUTH_REQUIRED_MSG = 'AES key auth cannot be empty.'
|
|
23
23
|
MISSING_FIELD_ERROR_MSG = 'A required field is missing. Please review the data.'
|
|
24
|
-
ID_CDATA_INTEGRATION_REQUIRED = 'Cdata integration cannot be empty or null'
|
|
25
24
|
EXP_TIME_REQUIRED = 'Expiration time (cdata integration) cannot be empty or null'
|
|
26
25
|
KEY_PRIVATE_REQUIRED = 'Key private cannot be empty or null'
|
|
27
26
|
SUB_ACCOUNT_REQUIRED = 'Sub account parameter is required'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import mimetypes
|
|
3
3
|
from Osdental.Exception.ControlledException import ValidationDataException
|
|
4
|
-
from Osdental.Shared.Message import Message
|
|
4
|
+
from Osdental.Shared.Enums.Message import Message
|
|
5
5
|
from Osdental.Shared.Enums.FileType import FileType
|
|
6
6
|
|
|
7
7
|
class FileMetaData:
|
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import subprocess
|
|
4
|
-
import platform
|
|
5
|
-
import click
|
|
6
|
-
from Osdental.Shared.Logger import logger
|
|
7
|
-
from Osdental.Shared.Enums.Message import Message
|
|
8
|
-
from Osdental.Shared.Utils.CaseConverter import CaseConverter
|
|
9
|
-
|
|
10
|
-
SRC_PATH = 'src'
|
|
11
|
-
APP_PATH = os.path.join(SRC_PATH, 'Application')
|
|
12
|
-
DOMAIN_PATH = os.path.join(SRC_PATH, 'Domain')
|
|
13
|
-
INFRA_PATH = os.path.join(SRC_PATH, 'Infrastructure')
|
|
14
|
-
GRAPHQL_PATH = os.path.join(INFRA_PATH, 'Graphql')
|
|
15
|
-
GRPC_PATH = os.path.join(INFRA_PATH, 'Grpc')
|
|
16
|
-
RESOLVERS_PATH = os.path.join(GRAPHQL_PATH, 'Resolvers')
|
|
17
|
-
SCHEMAS_PATH = os.path.join(GRAPHQL_PATH, 'Schemas')
|
|
18
|
-
|
|
19
|
-
@click.group()
|
|
20
|
-
def cli():
|
|
21
|
-
"""Comandos personalizados para gestionar el proyecto."""
|
|
22
|
-
pass
|
|
23
|
-
|
|
24
|
-
@cli.command()
|
|
25
|
-
def clean():
|
|
26
|
-
"""Borrar todos los __pycache__."""
|
|
27
|
-
if platform.system() == 'Windows':
|
|
28
|
-
subprocess.run('for /d /r . %d in (__pycache__) do @if exist "%d" rd /s/q "%d"', shell=True)
|
|
29
|
-
else:
|
|
30
|
-
subprocess.run("find . -name '__pycache__' -type d -exec rm -rf {} +", shell=True)
|
|
31
|
-
|
|
32
|
-
logger.info(Message.PYCACHE_CLEANUP_SUCCESS_MSG)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@cli.command(name='start-app')
|
|
36
|
-
@click.argument('app')
|
|
37
|
-
def start_app(app: str):
|
|
38
|
-
"""Crear un servicio con estructura hexagonal y CRUD básico."""
|
|
39
|
-
app = CaseConverter.snake_to_pascal(app)
|
|
40
|
-
app_upper = app.upper()
|
|
41
|
-
if '-' in app:
|
|
42
|
-
part_one, part_two = tuple(app.split('-'))
|
|
43
|
-
app = part_one + part_two
|
|
44
|
-
app_upper = f'{part_one}_{part_two}'.upper()
|
|
45
|
-
|
|
46
|
-
name_method = CaseConverter.case_to_snake(app)
|
|
47
|
-
data = 'data: Dict[str, Any]'
|
|
48
|
-
token = 'token: AuthToken'
|
|
49
|
-
api_type_response = 'Response!'
|
|
50
|
-
|
|
51
|
-
directories = [
|
|
52
|
-
os.path.join(SRC_PATH),
|
|
53
|
-
os.path.join(APP_PATH, 'UseCases'),
|
|
54
|
-
os.path.join(APP_PATH, 'Interfaces'),
|
|
55
|
-
os.path.join(DOMAIN_PATH, 'Interfaces'),
|
|
56
|
-
os.path.join(DOMAIN_PATH, 'Models'),
|
|
57
|
-
os.path.join(RESOLVERS_PATH),
|
|
58
|
-
os.path.join(SCHEMAS_PATH),
|
|
59
|
-
os.path.join(SCHEMAS_PATH, app),
|
|
60
|
-
os.path.join(GRPC_PATH, 'Proto'),
|
|
61
|
-
os.path.join(GRPC_PATH, 'Generated'),
|
|
62
|
-
os.path.join(GRPC_PATH, 'Server'),
|
|
63
|
-
os.path.join(GRPC_PATH, 'Client'),
|
|
64
|
-
os.path.join(GRPC_PATH, 'Servicer'),
|
|
65
|
-
os.path.join(INFRA_PATH, 'Repositories', app)
|
|
66
|
-
]
|
|
67
|
-
|
|
68
|
-
for directory in directories:
|
|
69
|
-
os.makedirs(directory, exist_ok=True)
|
|
70
|
-
|
|
71
|
-
# Contenidos CRUD
|
|
72
|
-
use_case_interface_name = f'{app}UseCaseInterface'
|
|
73
|
-
use_case_interface_content = f'''
|
|
74
|
-
from abc import ABC, abstractmethod
|
|
75
|
-
from typing import Dict, Any
|
|
76
|
-
from Osdental.Models.Token import AuthToken
|
|
77
|
-
|
|
78
|
-
class {use_case_interface_name}(ABC):
|
|
79
|
-
|
|
80
|
-
@abstractmethod
|
|
81
|
-
async def get_all_{name_method}(self, {token}, {data}) -> str: ...
|
|
82
|
-
|
|
83
|
-
@abstractmethod
|
|
84
|
-
async def get_{name_method}_by_id(self, {token}, {data}) -> str: ...
|
|
85
|
-
|
|
86
|
-
@abstractmethod
|
|
87
|
-
async def create_{name_method}(self, {token}, {data}) -> str: ...
|
|
88
|
-
|
|
89
|
-
@abstractmethod
|
|
90
|
-
async def update_{name_method}(self, {token}, {data}) -> str: ...
|
|
91
|
-
|
|
92
|
-
@abstractmethod
|
|
93
|
-
async def delete_{name_method}(self, {token}, {data}) -> str: ...
|
|
94
|
-
'''
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
use_case_content = f'''
|
|
98
|
-
from typing import Dict, Any
|
|
99
|
-
from Osdental.Decorators.DecryptedData import process_encrypted_data
|
|
100
|
-
from Osdental.Models.Token import AuthToken
|
|
101
|
-
from Osdental.Database.UnitOfWork import UnitOfWork
|
|
102
|
-
from ..Interfaces.{use_case_interface_name} import {use_case_interface_name}
|
|
103
|
-
|
|
104
|
-
class {app}UseCase({use_case_interface_name}):
|
|
105
|
-
|
|
106
|
-
def __init__(self, unit_of_work: UnitOfWork):
|
|
107
|
-
self.unit_of_work = unit_of_work
|
|
108
|
-
|
|
109
|
-
@process_encrypted_data()
|
|
110
|
-
async def get_all_{name_method}(self, {token}, {data}) -> str:
|
|
111
|
-
async with self.unit_of_work() as uow:
|
|
112
|
-
...
|
|
113
|
-
|
|
114
|
-
@process_encrypted_data()
|
|
115
|
-
async def get_{name_method}_by_id(self, {token}, {data}) -> str:
|
|
116
|
-
async with self.unit_of_work() as uow:
|
|
117
|
-
...
|
|
118
|
-
|
|
119
|
-
@process_encrypted_data()
|
|
120
|
-
async def create_{name_method}(self, {token}, {data}) -> str:
|
|
121
|
-
async with self.unit_of_work() as uow:
|
|
122
|
-
...
|
|
123
|
-
|
|
124
|
-
@process_encrypted_data()
|
|
125
|
-
async def update_{name_method}(self, {token}, {data}) -> str:
|
|
126
|
-
async with self.unit_of_work() as uow:
|
|
127
|
-
...
|
|
128
|
-
|
|
129
|
-
@process_encrypted_data()
|
|
130
|
-
async def delete_{name_method}(self, {token}, {data}) -> str:
|
|
131
|
-
async with self.unit_of_work() as uow:
|
|
132
|
-
...
|
|
133
|
-
'''
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
repository_interface_name = f'{app}RepositoryInterface'
|
|
137
|
-
repository_interface_content = f'''
|
|
138
|
-
from abc import ABC, abstractmethod
|
|
139
|
-
from sqlalchemy import text, RowMapping
|
|
140
|
-
from typing import List, Dict, Any
|
|
141
|
-
|
|
142
|
-
class {repository_interface_name}(ABC):
|
|
143
|
-
|
|
144
|
-
@abstractmethod
|
|
145
|
-
async def get_all_{name_method}(self, {data}) -> List[RowMapping]: ...
|
|
146
|
-
|
|
147
|
-
@abstractmethod
|
|
148
|
-
async def get_{name_method}_by_id(self, id: str) -> RowMapping: ...
|
|
149
|
-
|
|
150
|
-
@abstractmethod
|
|
151
|
-
async def create_{name_method}(self, {data}) -> str: ...
|
|
152
|
-
|
|
153
|
-
@abstractmethod
|
|
154
|
-
async def update_{name_method}(self, id: str, {data}) -> str: ...
|
|
155
|
-
|
|
156
|
-
@abstractmethod
|
|
157
|
-
async def delete_{name_method}(self, id: str) -> str: ...
|
|
158
|
-
'''
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
repository_content = f'''
|
|
162
|
-
from typing import List, Dict, Any
|
|
163
|
-
from sqlalchemy import text, RowMapping
|
|
164
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
165
|
-
from src.Domain.Interfaces.{repository_interface_name} import {repository_interface_name}
|
|
166
|
-
|
|
167
|
-
class {app}Repository({repository_interface_name}):
|
|
168
|
-
|
|
169
|
-
def __init__(self, session: AsyncSession):
|
|
170
|
-
self.session = session
|
|
171
|
-
|
|
172
|
-
async def get_all_{name_method}(self, {data}) -> List[RowMapping]: ...
|
|
173
|
-
|
|
174
|
-
async def get_{name_method}_by_id(self, id: str) -> RowMapping: ...
|
|
175
|
-
|
|
176
|
-
async def create_{name_method}(self, {data}) -> str: ...
|
|
177
|
-
|
|
178
|
-
async def update_{name_method}(self, id: str, {data}) -> str: ...
|
|
179
|
-
|
|
180
|
-
async def delete_{name_method}(self, id: str) -> str: ...
|
|
181
|
-
'''
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
query_graphql = f'''type Query {{
|
|
185
|
-
getAll{app}(data: String!): {api_type_response}
|
|
186
|
-
get{app}ById(data: String!): {api_type_response}
|
|
187
|
-
}}
|
|
188
|
-
'''
|
|
189
|
-
|
|
190
|
-
mutation_graphql = f'''type Mutation {{
|
|
191
|
-
create{app}(data: String!): {api_type_response}
|
|
192
|
-
update{app}(data: String!): {api_type_response}
|
|
193
|
-
delete{app}(data: String!): {api_type_response}
|
|
194
|
-
}}
|
|
195
|
-
'''
|
|
196
|
-
|
|
197
|
-
resolver_content_init = f"""
|
|
198
|
-
from .{app}Resolver import {app}Resolver
|
|
199
|
-
|
|
200
|
-
{name_method}_query_resolvers = {{
|
|
201
|
-
'getAll{app}': {app}Resolver.resolve_get_all_{name_method},
|
|
202
|
-
'get{app}ById': {app}Resolver.resolve_get_{name_method}_by_id
|
|
203
|
-
}}
|
|
204
|
-
|
|
205
|
-
{name_method}_mutation_resolvers = {{
|
|
206
|
-
'create{app}': {app}Resolver.resolve_create_{name_method},
|
|
207
|
-
'update{app}': {app}Resolver.resolve_update_{name_method},
|
|
208
|
-
'delete{app}': {app}Resolver.resolve_delete_{name_method}
|
|
209
|
-
}}
|
|
210
|
-
"""
|
|
211
|
-
|
|
212
|
-
resolver_content = f'''
|
|
213
|
-
from Osdental.Decorators.AuditLog import handle_audit_and_exception
|
|
214
|
-
from Osdental.Database.Connection import Connection
|
|
215
|
-
from Osdental.Database.UnitOfWork import UnitOfWork
|
|
216
|
-
from src.Application.UseCases.{app}UseCase import {app}UseCase
|
|
217
|
-
from src.Infrastructure.Repositories.{app}.{app}Repository import {app}Repository
|
|
218
|
-
|
|
219
|
-
use_case = {app}UseCase({app}Repository)
|
|
220
|
-
|
|
221
|
-
connection = Connection(...) # Enter DB URL
|
|
222
|
-
unit_of_work = UnitOfWork(connection.get_session())
|
|
223
|
-
use_case = {app}UseCase(unit_of_work)
|
|
224
|
-
|
|
225
|
-
class {app}Resolver:
|
|
226
|
-
|
|
227
|
-
@staticmethod
|
|
228
|
-
@handle_audit_and_exception()
|
|
229
|
-
async def resolve_get_all_{name_method}(_, info, data):
|
|
230
|
-
return await use_case.get_all_{name_method}(info=info, aes_data=data)
|
|
231
|
-
|
|
232
|
-
@staticmethod
|
|
233
|
-
@handle_audit_and_exception()
|
|
234
|
-
async def resolve_get_{name_method}_by_id(_, info, data):
|
|
235
|
-
return await use_case.get_{name_method}_by_id(info=info, aes_data=data)
|
|
236
|
-
|
|
237
|
-
@staticmethod
|
|
238
|
-
@handle_audit_and_exception()
|
|
239
|
-
async def resolve_create_{name_method}(_, info, data):
|
|
240
|
-
return await use_case.create_{name_method}(info=info, aes_data=data)
|
|
241
|
-
|
|
242
|
-
@staticmethod
|
|
243
|
-
@handle_audit_and_exception()
|
|
244
|
-
async def resolve_update_{name_method}(_, info, data):
|
|
245
|
-
return await use_case.update_{name_method}(info=info, aes_data=data)
|
|
246
|
-
|
|
247
|
-
@staticmethod
|
|
248
|
-
@handle_audit_and_exception()
|
|
249
|
-
async def resolve_delete_{name_method}(_, info, data):
|
|
250
|
-
return await use_case.delete_{name_method}(info=info, aes_data=data)
|
|
251
|
-
'''
|
|
252
|
-
|
|
253
|
-
graphql_content_init = f'''
|
|
254
|
-
from ariadne import gql
|
|
255
|
-
from ariadne import QueryType, MutationType
|
|
256
|
-
from ariadne import make_executable_schema
|
|
257
|
-
from pathlib import Path
|
|
258
|
-
from ..Graphql.Resolvers.{app} import {name_method}_query_resolvers, {name_method}_mutation_resolvers
|
|
259
|
-
|
|
260
|
-
def load_schemas():
|
|
261
|
-
schema_dir = Path(__file__).parent / 'Schemas'
|
|
262
|
-
schemas = [schema.read_text() for schema in schema_dir.rglob('*.graphql')]
|
|
263
|
-
return gql('\\n'.join(schemas))
|
|
264
|
-
|
|
265
|
-
type_defs = load_schemas()
|
|
266
|
-
|
|
267
|
-
query_resolvers = {{
|
|
268
|
-
**{name_method}_query_resolvers,
|
|
269
|
-
}}
|
|
270
|
-
|
|
271
|
-
mutation_resolvers = {{
|
|
272
|
-
**{name_method}_mutation_resolvers,
|
|
273
|
-
}}
|
|
274
|
-
|
|
275
|
-
query = QueryType()
|
|
276
|
-
mutation = MutationType()
|
|
277
|
-
|
|
278
|
-
for field, resolver in query_resolvers.items():
|
|
279
|
-
query.set_field(field, resolver)
|
|
280
|
-
|
|
281
|
-
for field, resolver in mutation_resolvers.items():
|
|
282
|
-
mutation.set_field(field, resolver)
|
|
283
|
-
|
|
284
|
-
# Executable Schema
|
|
285
|
-
schema = make_executable_schema(type_defs, query, mutation)
|
|
286
|
-
'''
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
response_content = '''
|
|
290
|
-
type Response {
|
|
291
|
-
status: String
|
|
292
|
-
message: String
|
|
293
|
-
data: String
|
|
294
|
-
}
|
|
295
|
-
'''
|
|
296
|
-
|
|
297
|
-
init_file = '__init__.py'
|
|
298
|
-
|
|
299
|
-
repository_init_content = f"""
|
|
300
|
-
from enum import StrEnum
|
|
301
|
-
|
|
302
|
-
class {app}Query(StrEnum):
|
|
303
|
-
GET_ALL_{app_upper} = ''' EXEC '''
|
|
304
|
-
|
|
305
|
-
GET_{app_upper}_BY_ID = ''' EXEC '''
|
|
306
|
-
|
|
307
|
-
CREATE_{app_upper} = ''' EXEC '''
|
|
308
|
-
|
|
309
|
-
UPDATE_{app_upper} = ''' EXEC '''
|
|
310
|
-
|
|
311
|
-
DELETE_{app_upper} = ''' EXEC '''
|
|
312
|
-
"""
|
|
313
|
-
|
|
314
|
-
proto_app_content = f'''
|
|
315
|
-
syntax = "proto3";
|
|
316
|
-
|
|
317
|
-
package {app.lower()};
|
|
318
|
-
|
|
319
|
-
import "Common.proto";
|
|
320
|
-
|
|
321
|
-
// gRPC Service
|
|
322
|
-
service {app}{{
|
|
323
|
-
rpc Example (common.Request) returns (common.Response);
|
|
324
|
-
}}
|
|
325
|
-
'''
|
|
326
|
-
|
|
327
|
-
common_proto = '''
|
|
328
|
-
syntax = "proto3";
|
|
329
|
-
|
|
330
|
-
package common;
|
|
331
|
-
|
|
332
|
-
message Request { string data = 1; }
|
|
333
|
-
message Response { string status=1; string message=2; string data=3; }
|
|
334
|
-
message Empty {}
|
|
335
|
-
'''
|
|
336
|
-
server = '''
|
|
337
|
-
from grpc.experimental import aio
|
|
338
|
-
from grpc_reflection.v1alpha import reflection
|
|
339
|
-
|
|
340
|
-
async def serve():
|
|
341
|
-
server = aio.server()
|
|
342
|
-
|
|
343
|
-
# Register each service
|
|
344
|
-
ExampleService_pb2_grpc.add_ExampleServiceServicer_to_server(
|
|
345
|
-
ExampleServicer(), server
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
# Register reflection
|
|
349
|
-
SERVICE_NAMES = (
|
|
350
|
-
Example.DESCRIPTOR.services_by_name['ExampleService'].full_name,
|
|
351
|
-
reflection.SERVICE_NAME # List of services exposed via gRPC reflection
|
|
352
|
-
)
|
|
353
|
-
reflection.enable_server_reflection(SERVICE_NAMES, server)
|
|
354
|
-
# Single port exposed
|
|
355
|
-
server.add_insecure_port("[::]:50051") # Port must go in environment variables
|
|
356
|
-
|
|
357
|
-
await server.start()
|
|
358
|
-
await server.wait_for_termination()
|
|
359
|
-
'''
|
|
360
|
-
|
|
361
|
-
grpc_client = f'''
|
|
362
|
-
import grpc
|
|
363
|
-
from Osdental.Decorators.Grpc import with_grpc_metadata
|
|
364
|
-
from Osdental.Models.Response import Response
|
|
365
|
-
from src.Infrastructure.Grpc.Generated import Common_pb2
|
|
366
|
-
|
|
367
|
-
class {app}Client:
|
|
368
|
-
|
|
369
|
-
def __init__(self, host="localhost", port=50051):
|
|
370
|
-
# Connecting to the gRPC server
|
|
371
|
-
self.channel = grpc.aio.insecure_channel(f"{{host}}:{{port}}")
|
|
372
|
-
self.stub = ExampleService_pb2_grpc.ExampleServiceStub(self.channel)
|
|
373
|
-
|
|
374
|
-
# Implement your RPC methods to consume
|
|
375
|
-
@with_grpc_metadata
|
|
376
|
-
async def example(self, request, metadata) -> Response:
|
|
377
|
-
request = Common_pb2.Request(data=request)
|
|
378
|
-
return await self.stub.Example(request, metadata=metadata)
|
|
379
|
-
'''
|
|
380
|
-
files = {
|
|
381
|
-
os.path.join(APP_PATH, 'UseCases', f'{app}UseCase.py'): use_case_content,
|
|
382
|
-
os.path.join(APP_PATH, 'Interfaces', f'{app}UseCaseInterface.py'): use_case_interface_content,
|
|
383
|
-
os.path.join(DOMAIN_PATH, 'Interfaces', f'{app}RepositoryInterface.py'): repository_interface_content,
|
|
384
|
-
os.path.join(RESOLVERS_PATH, app, init_file): resolver_content_init,
|
|
385
|
-
os.path.join(RESOLVERS_PATH, app, f'{app}Resolver.py'): resolver_content,
|
|
386
|
-
os.path.join(GRAPHQL_PATH, init_file): graphql_content_init,
|
|
387
|
-
os.path.join(SCHEMAS_PATH, app, 'Query.graphql'): query_graphql,
|
|
388
|
-
os.path.join(SCHEMAS_PATH, app, 'Mutation.graphql'): mutation_graphql,
|
|
389
|
-
os.path.join(SCHEMAS_PATH, 'Response.graphql'): response_content,
|
|
390
|
-
os.path.join(INFRA_PATH, 'Repositories', app, init_file): repository_init_content,
|
|
391
|
-
os.path.join(INFRA_PATH, 'Repositories', app, f'{app}Repository.py'): repository_content,
|
|
392
|
-
os.path.join(GRPC_PATH, 'Proto', 'Common.proto'): common_proto,
|
|
393
|
-
os.path.join(GRPC_PATH, 'Proto', f'{app}.proto'): proto_app_content,
|
|
394
|
-
os.path.join(GRPC_PATH, 'Server', init_file): server,
|
|
395
|
-
os.path.join(GRPC_PATH, 'Client', f'{app}Client.py'): grpc_client
|
|
396
|
-
}
|
|
397
|
-
for file_path in files:
|
|
398
|
-
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
399
|
-
|
|
400
|
-
for file_path, content in files.items():
|
|
401
|
-
if not os.path.exists(file_path):
|
|
402
|
-
with open(file_path, 'w') as f:
|
|
403
|
-
f.write(content)
|
|
404
|
-
|
|
405
|
-
logger.info(Message.HEXAGONAL_SERVICE_CREATED_MSG)
|
|
406
|
-
|
|
407
|
-
@cli.command()
|
|
408
|
-
@click.argument('port')
|
|
409
|
-
def start(port: int):
|
|
410
|
-
"""Start the FastAPI server.."""
|
|
411
|
-
try:
|
|
412
|
-
subprocess.run(['uvicorn', 'app:app', '--port', str(port), '--reload'], check=True)
|
|
413
|
-
except subprocess.CalledProcessError as e:
|
|
414
|
-
logger.error(f'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@cli.command()
|
|
418
|
-
@click.argument('port')
|
|
419
|
-
def serve(port: int):
|
|
420
|
-
"""Set up the FastAPI server accessible from any machine."""
|
|
421
|
-
try:
|
|
422
|
-
subprocess.run(['uvicorn', 'app:app', '--host', '0.0.0.0', '--port', str(port), '--reload'], check=True)
|
|
423
|
-
except subprocess.CalledProcessError as e:
|
|
424
|
-
logger.error(f'{Message.SERVER_NETWORK_ACCESS_ERROR_MSG}: {e}')
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
@cli.command("clean-redis")
|
|
428
|
-
@click.argument('redis_env')
|
|
429
|
-
async def clean_redis(redis_env: str):
|
|
430
|
-
try:
|
|
431
|
-
from Osdental.RedisCache.Redis import RedisCacheAsync
|
|
432
|
-
redis_url = os.getenv(redis_env)
|
|
433
|
-
if not redis_url:
|
|
434
|
-
logger.warning(f'Environment variable not found: {redis_env}')
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
redis = RedisCacheAsync(redis_url=redis_url)
|
|
438
|
-
await redis.flush()
|
|
439
|
-
logger.info(Message.REDIS_CLEANUP_SUCCESS_MSG)
|
|
440
|
-
except Exception as e:
|
|
441
|
-
logger.error(f'{Message.REDIS_CLEANUP_ERROR_MSG}: {e}')
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
@cli.command(name='proto-files')
|
|
445
|
-
@click.argument('name')
|
|
446
|
-
def proto_files(name: str):
|
|
447
|
-
proto_dir = os.path.join('src', 'Infrastructure', 'Grpc', 'Proto').replace('\\', '/')
|
|
448
|
-
gen_dir = os.path.join('src', 'Infrastructure', 'Grpc', 'Generated').replace('\\', '/')
|
|
449
|
-
|
|
450
|
-
proto_path = os.path.join(proto_dir, f'{name}.proto').replace('\\', '/')
|
|
451
|
-
common_path = os.path.join(proto_dir, 'Common.proto').replace('\\', '/')
|
|
452
|
-
|
|
453
|
-
common_py = os.path.join(gen_dir, 'Common_pb2.py').replace('\\', '/')
|
|
454
|
-
common_grpc_py = os.path.join(gen_dir, 'Common_pb2_grpc.py').replace('\\', '/')
|
|
455
|
-
|
|
456
|
-
proto_files = [proto_path]
|
|
457
|
-
if not (os.path.exists(common_py) and os.path.exists(common_grpc_py)):
|
|
458
|
-
proto_files.append(common_path)
|
|
459
|
-
|
|
460
|
-
cmd = [
|
|
461
|
-
sys.executable, "-m", "grpc_tools.protoc",
|
|
462
|
-
f"-I={proto_dir}",
|
|
463
|
-
f"--python_out={gen_dir}",
|
|
464
|
-
f"--grpc_python_out={gen_dir}",
|
|
465
|
-
*proto_files
|
|
466
|
-
]
|
|
467
|
-
|
|
468
|
-
subprocess.run(cmd, shell=False, check=True)
|
|
469
|
-
logger.info(Message.PROTO_FILES_GENERATED_MSG)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
@cli.command('run-server')
|
|
474
|
-
@click.option('--host', default="0.0.0.0", help='Host where to set up the server')
|
|
475
|
-
@click.option('--port', default=5000, help='Server port')
|
|
476
|
-
@click.option('--workers', default=4, help='Number of workers')
|
|
477
|
-
def run_server(host, port, workers):
|
|
478
|
-
"""
|
|
479
|
-
Launch the application with uvicorn on Windows or gunicorn on other systems
|
|
480
|
-
:host
|
|
481
|
-
:port
|
|
482
|
-
:workers
|
|
483
|
-
"""
|
|
484
|
-
if sys.platform.startswith('win'):
|
|
485
|
-
cmd = [
|
|
486
|
-
sys.executable, '-m', 'uvicorn',
|
|
487
|
-
'app:app',
|
|
488
|
-
'--host', host,
|
|
489
|
-
'--port', str(port),
|
|
490
|
-
'--workers', str(workers)
|
|
491
|
-
]
|
|
492
|
-
else:
|
|
493
|
-
cmd = [
|
|
494
|
-
sys.executable, '-m', 'gunicorn',
|
|
495
|
-
'app:app',
|
|
496
|
-
'-k', 'uvicorn.workers.UvicornWorker',
|
|
497
|
-
'--bind', f'{host}:{port}',
|
|
498
|
-
'--workers', str(workers)
|
|
499
|
-
]
|
|
500
|
-
|
|
501
|
-
logger.info(f'Running: {' '.join(cmd)}')
|
|
502
|
-
subprocess.run(cmd)
|
|
503
|
-
|
|
504
|
-
if __name__ == "__main__":
|
|
505
|
-
cli()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{encryptors-2.36 → encryptors-2.38}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|