Encryptors 2.78__tar.gz → 2.81.0__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.78 → encryptors-2.81.0}/PKG-INFO +1 -2
- {encryptors-2.78 → encryptors-2.81.0}/setup.py +3 -4
- {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/PKG-INFO +1 -2
- encryptors-2.81.0/src/Encryptors.egg-info/SOURCES.txt +96 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/requires.txt +0 -1
- encryptors-2.81.0/src/Osdental/Cli/__init__.py +118 -0
- encryptors-2.81.0/src/Osdental/Database/BaseRepository.py +171 -0
- encryptors-2.81.0/src/Osdental/Database/SqlExceptionTranslator.py +85 -0
- encryptors-2.81.0/src/Osdental/Database/SqlTransientDetector.py +44 -0
- encryptors-2.81.0/src/Osdental/Database/StoredProcedureResultValidator.py +91 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Decorators/Grpc.py +1 -3
- encryptors-2.81.0/src/Osdental/Decorators/PublicResolver.py +3 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Decorators/Retry.py +1 -4
- encryptors-2.81.0/src/Osdental/Decorators/SecureResolver.py +59 -0
- encryptors-2.81.0/src/Osdental/Decorators/SqlDataNormalizer.py +60 -0
- encryptors-2.81.0/src/Osdental/Encryptor/Aes.py +85 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Bcrypt.py +1 -1
- encryptors-2.81.0/src/Osdental/Encryptor/Jwt.py +43 -0
- encryptors-2.81.0/src/Osdental/Encryptor/Rsa.py +58 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Sha512.py +3 -3
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Exception/ControlledException.py +60 -27
- encryptors-2.81.0/src/Osdental/Graphql/Extensions/AuditExtension.py +194 -0
- encryptors-2.81.0/src/Osdental/Graphql/Models/__init__.py +14 -0
- encryptors-2.81.0/src/Osdental/Graphql/_Exceptions/__init__.py +8 -0
- encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_AuditHelper.py +67 -0
- encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +11 -0
- encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +46 -0
- encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TokenService.py +44 -0
- encryptors-2.81.0/src/Osdental/Helpers/AuditDispatcher.py +172 -0
- encryptors-2.78/src/Osdental/Secrets/AzureKeyVaultProvider.py → encryptors-2.81.0/src/Osdental/Helpers/KeyVaultService.py +8 -1
- encryptors-2.78/src/Osdental/Helpers/CipherHelper.py → encryptors-2.81.0/src/Osdental/Helpers/ResponseDecryptor.py +11 -18
- {encryptors-2.78/src/Osdental/Services → encryptors-2.81.0/src/Osdental/Helpers}/WebsocketClient.py +3 -4
- encryptors-2.81.0/src/Osdental/Helpers/_Ports.py +11 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Http/APIClient.py +19 -53
- encryptors-2.81.0/src/Osdental/Http/_Helpers.py +122 -0
- encryptors-2.81.0/src/Osdental/Messaging/AzureServiceBus.py +213 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/__init__.py +6 -2
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/ApiResponse.py +3 -3
- encryptors-2.81.0/src/Osdental/Models/AuditConfig.py +9 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Response.py +5 -6
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Token.py +4 -0
- encryptors-2.81.0/src/Osdental/Models/_Audit.py +14 -0
- encryptors-2.81.0/src/Osdental/Rest/Context/RequestContext.py +19 -0
- encryptors-2.81.0/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +22 -0
- encryptors-2.81.0/src/Osdental/Rest/Middlewares/__init__.py +0 -0
- encryptors-2.81.0/src/Osdental/Rest/__init__.py +0 -0
- encryptors-2.81.0/src/Osdental/Shared/Enums/Code.py +26 -0
- encryptors-2.81.0/src/Osdental/Shared/Enums/Constant.py +10 -0
- encryptors-2.81.0/src/Osdental/Shared/Enums/FileType.py +9 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Enums/GrahpqlOperation.py +2 -2
- {encryptors-2.78/src/Osdental/Constants → encryptors-2.81.0/src/Osdental/Shared/Enums}/Message.py +10 -7
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Enums/Profile.py +1 -2
- encryptors-2.81.0/src/Osdental/Shared/Enums/__init__.py +0 -0
- encryptors-2.81.0/src/Osdental/Shared/Logger.py +17 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/FileMetaData.py +14 -14
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/Mapper.py +18 -1
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/PasswordGenerator.py +1 -1
- encryptors-2.81.0/src/Osdental/Shared/Utils/RsaUtils.py +30 -0
- encryptors-2.81.0/src/Osdental/Shared/Utils/__init__.py +0 -0
- encryptors-2.81.0/src/Osdental/Shared/__init__.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Storage/__init__.py +3 -15
- encryptors-2.81.0/src/Osdental/__init__.py +0 -0
- encryptors-2.78/src/Encryptors.egg-info/SOURCES.txt +0 -87
- encryptors-2.78/src/Osdental/Cli/__init__.py +0 -137
- encryptors-2.78/src/Osdental/Constants/Constant.py +0 -10
- encryptors-2.78/src/Osdental/Context/__init__.py +0 -5
- encryptors-2.78/src/Osdental/Database/BaseRepository.py +0 -192
- encryptors-2.78/src/Osdental/Decorators/SecureResolver.py +0 -177
- encryptors-2.78/src/Osdental/Encryptor/Aes.py +0 -83
- encryptors-2.78/src/Osdental/Encryptor/Jwt.py +0 -35
- encryptors-2.78/src/Osdental/Encryptor/Rsa.py +0 -54
- encryptors-2.78/src/Osdental/Enums/AuditType.py +0 -5
- encryptors-2.78/src/Osdental/Enums/CipherType.py +0 -6
- encryptors-2.78/src/Osdental/Enums/FileType.py +0 -9
- encryptors-2.78/src/Osdental/Enums/GrpcStatusCode.py +0 -25
- encryptors-2.78/src/Osdental/Enums/ResultType.py +0 -5
- encryptors-2.78/src/Osdental/Enums/Secret.py +0 -8
- encryptors-2.78/src/Osdental/Enums/StatusCode.py +0 -40
- encryptors-2.78/src/Osdental/Helpers/AuditHelper.py +0 -180
- encryptors-2.78/src/Osdental/Helpers/AuditQueue.py +0 -48
- encryptors-2.78/src/Osdental/Helpers/AuthTokenProcessor.py +0 -119
- encryptors-2.78/src/Osdental/Helpers/JwtTokenHelper.py +0 -172
- encryptors-2.78/src/Osdental/Messaging/AzureServiceBus.py +0 -146
- encryptors-2.78/src/Osdental/Models/AuditContext.py +0 -62
- encryptors-2.78/src/Osdental/Models/Graphql.py +0 -14
- encryptors-2.78/src/Osdental/Models/TokenClaims.py +0 -99
- encryptors-2.78/src/Osdental/Secrets/__init__.py +0 -7
- encryptors-2.78/src/Osdental/Services/JwtAuthTokenService.py +0 -147
- encryptors-2.78/src/Osdental/Services/ServiceBusAuditEmitter.py +0 -33
- encryptors-2.78/src/Osdental/Services/__init__.py +0 -62
- encryptors-2.78/src/Osdental/Utils/RsaUtils.py +0 -56
- {encryptors-2.78 → encryptors-2.81.0}/README.md +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/setup.cfg +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Cache/Redis.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Cache/__init__.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Database/Connection.py +0 -0
- {encryptors-2.78/src/Osdental/Constants → encryptors-2.81.0/src/Osdental/Database}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Database → encryptors-2.81.0/src/Osdental/Decorators}/__init__.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.78/src/Osdental/Decorators → encryptors-2.81.0/src/Osdental/Encryptor}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Encryptor → encryptors-2.81.0/src/Osdental/Exception}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Enums → encryptors-2.81.0/src/Osdental/Graphql/Extensions}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Exception → encryptors-2.81.0/src/Osdental/Graphql/_Helpers}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Helpers → encryptors-2.81.0/src/Osdental/Graphql}/__init__.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Helpers/AzureClassifier.py +0 -0
- {encryptors-2.78/src/Osdental/Services → encryptors-2.81.0/src/Osdental/Helpers}/GrpcConnection.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Helpers/Resilience.py +0 -0
- {encryptors-2.78/src/Osdental/Http → encryptors-2.81.0/src/Osdental/Helpers}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental/Models → encryptors-2.81.0/src/Osdental/Http}/__init__.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Notification.py +0 -0
- {encryptors-2.78/src/Osdental/Utils → encryptors-2.81.0/src/Osdental/Models}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Rest/Context}/__init__.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/CaseConverter.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/CodeGenerator.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DataNormalizer.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DataUtils.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DateUtils.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/HashValidator.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/QueryGenerator.py +0 -0
- {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/TextProcessor.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
- {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Storage/S3Storage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.81.0
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -57,7 +57,6 @@ Requires-Dist: bcrypt==4.3.0
|
|
|
57
57
|
Requires-Dist: azure-monitor-opentelemetry==1.8.1
|
|
58
58
|
Requires-Dist: uvicorn==0.37.0
|
|
59
59
|
Requires-Dist: gunicorn==23.0.0
|
|
60
|
-
Requires-Dist: joserfc==1.5.0
|
|
61
60
|
Dynamic: author
|
|
62
61
|
Dynamic: author-email
|
|
63
62
|
Dynamic: classifier
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
|
-
|
|
2
|
+
# ANDERSON REVISAR EL CACHE LOCAL DEL KEYVAULT PARA VALIDAR SI FUNCIONA
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.81.0",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -64,8 +64,7 @@ setup(
|
|
|
64
64
|
"bcrypt==4.3.0",
|
|
65
65
|
"azure-monitor-opentelemetry==1.8.1",
|
|
66
66
|
"uvicorn==0.37.0",
|
|
67
|
-
"gunicorn==23.0.0"
|
|
68
|
-
"joserfc==1.5.0",
|
|
67
|
+
"gunicorn==23.0.0"
|
|
69
68
|
],
|
|
70
69
|
entry_points={
|
|
71
70
|
'console_scripts': [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Encryptors
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.81.0
|
|
4
4
|
Summary: End-to-end algorithm library
|
|
5
5
|
Author: OSDental LLC
|
|
6
6
|
Author-email: support@osdental.ai
|
|
@@ -57,7 +57,6 @@ Requires-Dist: bcrypt==4.3.0
|
|
|
57
57
|
Requires-Dist: azure-monitor-opentelemetry==1.8.1
|
|
58
58
|
Requires-Dist: uvicorn==0.37.0
|
|
59
59
|
Requires-Dist: gunicorn==23.0.0
|
|
60
|
-
Requires-Dist: joserfc==1.5.0
|
|
61
60
|
Dynamic: author
|
|
62
61
|
Dynamic: author-email
|
|
63
62
|
Dynamic: classifier
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
src/Encryptors.egg-info/PKG-INFO
|
|
4
|
+
src/Encryptors.egg-info/SOURCES.txt
|
|
5
|
+
src/Encryptors.egg-info/dependency_links.txt
|
|
6
|
+
src/Encryptors.egg-info/entry_points.txt
|
|
7
|
+
src/Encryptors.egg-info/requires.txt
|
|
8
|
+
src/Encryptors.egg-info/top_level.txt
|
|
9
|
+
src/Osdental/__init__.py
|
|
10
|
+
src/Osdental/Cache/Redis.py
|
|
11
|
+
src/Osdental/Cache/__init__.py
|
|
12
|
+
src/Osdental/Cli/__init__.py
|
|
13
|
+
src/Osdental/Database/BaseRepository.py
|
|
14
|
+
src/Osdental/Database/Connection.py
|
|
15
|
+
src/Osdental/Database/SqlExceptionTranslator.py
|
|
16
|
+
src/Osdental/Database/SqlTransientDetector.py
|
|
17
|
+
src/Osdental/Database/StoredProcedureResultValidator.py
|
|
18
|
+
src/Osdental/Database/__init__.py
|
|
19
|
+
src/Osdental/Decorators/Grpc.py
|
|
20
|
+
src/Osdental/Decorators/PublicResolver.py
|
|
21
|
+
src/Osdental/Decorators/Retry.py
|
|
22
|
+
src/Osdental/Decorators/SecureResolver.py
|
|
23
|
+
src/Osdental/Decorators/SqlDataNormalizer.py
|
|
24
|
+
src/Osdental/Decorators/__init__.py
|
|
25
|
+
src/Osdental/Encryptor/Aes.py
|
|
26
|
+
src/Osdental/Encryptor/Argon2.py
|
|
27
|
+
src/Osdental/Encryptor/Bcrypt.py
|
|
28
|
+
src/Osdental/Encryptor/Jwt.py
|
|
29
|
+
src/Osdental/Encryptor/Rsa.py
|
|
30
|
+
src/Osdental/Encryptor/Sha512.py
|
|
31
|
+
src/Osdental/Encryptor/__init__.py
|
|
32
|
+
src/Osdental/Exception/ControlledException.py
|
|
33
|
+
src/Osdental/Exception/__init__.py
|
|
34
|
+
src/Osdental/Graphql/__init__.py
|
|
35
|
+
src/Osdental/Graphql/Extensions/AuditExtension.py
|
|
36
|
+
src/Osdental/Graphql/Extensions/__init__.py
|
|
37
|
+
src/Osdental/Graphql/Models/__init__.py
|
|
38
|
+
src/Osdental/Graphql/_Exceptions/__init__.py
|
|
39
|
+
src/Osdental/Graphql/_Helpers/_AuditHelper.py
|
|
40
|
+
src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py
|
|
41
|
+
src/Osdental/Graphql/_Helpers/_TenantPolicy.py
|
|
42
|
+
src/Osdental/Graphql/_Helpers/_TokenService.py
|
|
43
|
+
src/Osdental/Graphql/_Helpers/__init__.py
|
|
44
|
+
src/Osdental/Helpers/AuditDispatcher.py
|
|
45
|
+
src/Osdental/Helpers/AzureClassifier.py
|
|
46
|
+
src/Osdental/Helpers/GrpcConnection.py
|
|
47
|
+
src/Osdental/Helpers/KeyVaultService.py
|
|
48
|
+
src/Osdental/Helpers/Resilience.py
|
|
49
|
+
src/Osdental/Helpers/ResponseDecryptor.py
|
|
50
|
+
src/Osdental/Helpers/WebsocketClient.py
|
|
51
|
+
src/Osdental/Helpers/_Ports.py
|
|
52
|
+
src/Osdental/Helpers/__init__.py
|
|
53
|
+
src/Osdental/Http/APIClient.py
|
|
54
|
+
src/Osdental/Http/_Helpers.py
|
|
55
|
+
src/Osdental/Http/__init__.py
|
|
56
|
+
src/Osdental/Messaging/AzureServiceBus.py
|
|
57
|
+
src/Osdental/Messaging/Kafka.py
|
|
58
|
+
src/Osdental/Messaging/RabbitMQ.py
|
|
59
|
+
src/Osdental/Messaging/__init__.py
|
|
60
|
+
src/Osdental/Models/ApiResponse.py
|
|
61
|
+
src/Osdental/Models/AuditConfig.py
|
|
62
|
+
src/Osdental/Models/Notification.py
|
|
63
|
+
src/Osdental/Models/Response.py
|
|
64
|
+
src/Osdental/Models/Token.py
|
|
65
|
+
src/Osdental/Models/_Audit.py
|
|
66
|
+
src/Osdental/Models/__init__.py
|
|
67
|
+
src/Osdental/Rest/__init__.py
|
|
68
|
+
src/Osdental/Rest/Context/RequestContext.py
|
|
69
|
+
src/Osdental/Rest/Context/__init__.py
|
|
70
|
+
src/Osdental/Rest/Middlewares/RequestContextMiddleware.py
|
|
71
|
+
src/Osdental/Rest/Middlewares/__init__.py
|
|
72
|
+
src/Osdental/Shared/Logger.py
|
|
73
|
+
src/Osdental/Shared/__init__.py
|
|
74
|
+
src/Osdental/Shared/Enums/Code.py
|
|
75
|
+
src/Osdental/Shared/Enums/Constant.py
|
|
76
|
+
src/Osdental/Shared/Enums/FileType.py
|
|
77
|
+
src/Osdental/Shared/Enums/GrahpqlOperation.py
|
|
78
|
+
src/Osdental/Shared/Enums/Message.py
|
|
79
|
+
src/Osdental/Shared/Enums/Profile.py
|
|
80
|
+
src/Osdental/Shared/Enums/__init__.py
|
|
81
|
+
src/Osdental/Shared/Utils/CaseConverter.py
|
|
82
|
+
src/Osdental/Shared/Utils/CodeGenerator.py
|
|
83
|
+
src/Osdental/Shared/Utils/DataNormalizer.py
|
|
84
|
+
src/Osdental/Shared/Utils/DataUtils.py
|
|
85
|
+
src/Osdental/Shared/Utils/DateUtils.py
|
|
86
|
+
src/Osdental/Shared/Utils/FileMetaData.py
|
|
87
|
+
src/Osdental/Shared/Utils/HashValidator.py
|
|
88
|
+
src/Osdental/Shared/Utils/Mapper.py
|
|
89
|
+
src/Osdental/Shared/Utils/PasswordGenerator.py
|
|
90
|
+
src/Osdental/Shared/Utils/QueryGenerator.py
|
|
91
|
+
src/Osdental/Shared/Utils/RsaUtils.py
|
|
92
|
+
src/Osdental/Shared/Utils/TextProcessor.py
|
|
93
|
+
src/Osdental/Shared/Utils/__init__.py
|
|
94
|
+
src/Osdental/Storage/AzureBlobStorage.py
|
|
95
|
+
src/Osdental/Storage/S3Storage.py
|
|
96
|
+
src/Osdental/Storage/__init__.py
|
|
@@ -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.Cache.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()
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import text
|
|
5
|
+
from sqlalchemy.exc import ResourceClosedError
|
|
6
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
7
|
+
|
|
8
|
+
from Osdental.Exception.ControlledException import DatabaseException
|
|
9
|
+
from Osdental.Helpers.Resilience import (
|
|
10
|
+
AzureResiliencePolicy,
|
|
11
|
+
AzureTransientError,
|
|
12
|
+
)
|
|
13
|
+
from Osdental.Database.SqlExceptionTranslator import SqlExceptionTranslator
|
|
14
|
+
from Osdental.Database.SqlTransientDetector import SqlTransientDetector
|
|
15
|
+
from Osdental.Database.StoredProcedureResultValidator import (
|
|
16
|
+
StoredProcedureResultValidator,
|
|
17
|
+
)
|
|
18
|
+
from Osdental.Shared.Utils.DataNormalizer import normalize
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseRepository:
|
|
22
|
+
|
|
23
|
+
_policy = AzureResiliencePolicy(
|
|
24
|
+
"sql-database",
|
|
25
|
+
max_attempts=3,
|
|
26
|
+
base_delay=1,
|
|
27
|
+
failure_threshold=5,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
async_session: AsyncSession,
|
|
33
|
+
):
|
|
34
|
+
self.async_session = async_session
|
|
35
|
+
|
|
36
|
+
async def _safe_execute(
|
|
37
|
+
self,
|
|
38
|
+
query: str,
|
|
39
|
+
params: dict | None = None,
|
|
40
|
+
):
|
|
41
|
+
|
|
42
|
+
async def execute():
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
|
|
46
|
+
return await self.async_session.execute(
|
|
47
|
+
text(query),
|
|
48
|
+
params or {},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
except Exception as exc:
|
|
52
|
+
|
|
53
|
+
if SqlTransientDetector.is_transient(exc):
|
|
54
|
+
raise AzureTransientError(
|
|
55
|
+
str(exc)
|
|
56
|
+
) from exc
|
|
57
|
+
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
|
|
62
|
+
return await self._policy.execute(
|
|
63
|
+
execute
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
except Exception as exc:
|
|
67
|
+
|
|
68
|
+
raise SqlExceptionTranslator.translate(
|
|
69
|
+
exc
|
|
70
|
+
) from exc
|
|
71
|
+
|
|
72
|
+
async def _execute_query(
|
|
73
|
+
self,
|
|
74
|
+
query: str,
|
|
75
|
+
params: dict | None = None,
|
|
76
|
+
*,
|
|
77
|
+
validate: bool = False,
|
|
78
|
+
success_codes: str | int | Iterable[str | int] | None = None,
|
|
79
|
+
many: bool = False,
|
|
80
|
+
as_dict: bool = False,
|
|
81
|
+
) -> Any:
|
|
82
|
+
|
|
83
|
+
result = await self._safe_execute(
|
|
84
|
+
query,
|
|
85
|
+
params,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
rows = (
|
|
89
|
+
result.mappings().all()
|
|
90
|
+
if many
|
|
91
|
+
else result.mappings().first()
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if validate:
|
|
95
|
+
|
|
96
|
+
row = rows
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
many
|
|
100
|
+
and isinstance(rows, list)
|
|
101
|
+
and len(rows) == 1
|
|
102
|
+
):
|
|
103
|
+
row = rows[0]
|
|
104
|
+
|
|
105
|
+
StoredProcedureResultValidator.validate(
|
|
106
|
+
row,
|
|
107
|
+
success_codes,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if as_dict:
|
|
111
|
+
return normalize(rows)
|
|
112
|
+
|
|
113
|
+
return rows
|
|
114
|
+
|
|
115
|
+
async def _execute_command(
|
|
116
|
+
self,
|
|
117
|
+
query: str,
|
|
118
|
+
params: dict | None = None,
|
|
119
|
+
*,
|
|
120
|
+
validate: bool = True,
|
|
121
|
+
success_codes: str | int | Iterable[str | int] | None = None,
|
|
122
|
+
as_dict: bool = False,
|
|
123
|
+
commit_on_failure: bool = False,
|
|
124
|
+
) -> Any:
|
|
125
|
+
|
|
126
|
+
result = await self._safe_execute(
|
|
127
|
+
query,
|
|
128
|
+
params,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
|
|
133
|
+
row = result.mappings().first()
|
|
134
|
+
|
|
135
|
+
except ResourceClosedError:
|
|
136
|
+
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
if validate:
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
|
|
143
|
+
StoredProcedureResultValidator.validate(
|
|
144
|
+
row,
|
|
145
|
+
success_codes,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
except DatabaseException:
|
|
149
|
+
|
|
150
|
+
if commit_on_failure:
|
|
151
|
+
await self.async_session.commit()
|
|
152
|
+
|
|
153
|
+
raise
|
|
154
|
+
|
|
155
|
+
if as_dict:
|
|
156
|
+
return normalize(row)
|
|
157
|
+
|
|
158
|
+
return row
|
|
159
|
+
|
|
160
|
+
async def _execute_scalar(
|
|
161
|
+
self,
|
|
162
|
+
query: str,
|
|
163
|
+
params: dict | None = None,
|
|
164
|
+
) -> Any | None:
|
|
165
|
+
|
|
166
|
+
result = await self._safe_execute(
|
|
167
|
+
query,
|
|
168
|
+
params,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return result.scalar_one_or_none()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from sqlalchemy.exc import (
|
|
4
|
+
DBAPIError,
|
|
5
|
+
IntegrityError,
|
|
6
|
+
OperationalError,
|
|
7
|
+
ProgrammingError,
|
|
8
|
+
DataError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from Osdental.Exception.ControlledException import DatabaseException
|
|
12
|
+
from Osdental.Shared.Enums.Message import Message
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SqlExceptionTranslator:
|
|
18
|
+
|
|
19
|
+
_ERRORS = {
|
|
20
|
+
|
|
21
|
+
IntegrityError: (
|
|
22
|
+
"It was not possible to save the information.",
|
|
23
|
+
"DB_ERROR_INTEGRITY",
|
|
24
|
+
),
|
|
25
|
+
|
|
26
|
+
OperationalError: (
|
|
27
|
+
"The database is not available at this time.",
|
|
28
|
+
"DB_ERROR_UNAVAILABLE",
|
|
29
|
+
),
|
|
30
|
+
|
|
31
|
+
ProgrammingError: (
|
|
32
|
+
"An error occurred in the SQL query.",
|
|
33
|
+
"DB_ERROR_PROGRAMMING",
|
|
34
|
+
),
|
|
35
|
+
|
|
36
|
+
DataError: (
|
|
37
|
+
"The data sent to the database is invalid.",
|
|
38
|
+
"DB_ERROR_DATA",
|
|
39
|
+
),
|
|
40
|
+
|
|
41
|
+
DBAPIError: (
|
|
42
|
+
Message.UNEXPECTED_ERROR_MSG,
|
|
43
|
+
"DB_ERROR",
|
|
44
|
+
),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def translate(
|
|
49
|
+
cls,
|
|
50
|
+
exc: Exception,
|
|
51
|
+
) -> DatabaseException:
|
|
52
|
+
|
|
53
|
+
logger.exception(exc)
|
|
54
|
+
|
|
55
|
+
detail = cls._get_detail(exc)
|
|
56
|
+
|
|
57
|
+
for exception_type, (message, status_code) in cls._ERRORS.items():
|
|
58
|
+
|
|
59
|
+
if isinstance(exc, exception_type):
|
|
60
|
+
logger.error(f"Database error: {message} - Detail: {detail}")
|
|
61
|
+
return DatabaseException(
|
|
62
|
+
error=detail,
|
|
63
|
+
status_code=status_code,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return DatabaseException(
|
|
67
|
+
message=Message.UNEXPECTED_ERROR_MSG,
|
|
68
|
+
error=detail,
|
|
69
|
+
status_code="UNEXPECTED_ERROR",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _get_detail(
|
|
74
|
+
exc: Exception,
|
|
75
|
+
) -> str:
|
|
76
|
+
|
|
77
|
+
if isinstance(exc, DBAPIError):
|
|
78
|
+
|
|
79
|
+
original = getattr(exc, "orig", None)
|
|
80
|
+
|
|
81
|
+
if original is not None:
|
|
82
|
+
|
|
83
|
+
return str(original)
|
|
84
|
+
|
|
85
|
+
return str(exc)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from sqlalchemy.exc import DBAPIError
|
|
2
|
+
|
|
3
|
+
_SQLSTATE_TRANSIENT_ERRORS = frozenset({
|
|
4
|
+
"08S01", # Communication link failure
|
|
5
|
+
"40197", # Azure SQL transient error
|
|
6
|
+
"40501", # Service busy
|
|
7
|
+
"40613", # Database unavailable
|
|
8
|
+
"49918", # Not enough resources
|
|
9
|
+
"4060", # Cannot open database
|
|
10
|
+
"10928", # Resource limit
|
|
11
|
+
"10929", # Resource limit
|
|
12
|
+
"10053", # Connection aborted
|
|
13
|
+
"10054", # Connection reset
|
|
14
|
+
"10060", # Connection timeout
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SqlTransientDetector:
|
|
19
|
+
"""
|
|
20
|
+
Detecta si un DBAPIError corresponde a un error transitorio
|
|
21
|
+
susceptible de reintento.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def is_transient(exc: DBAPIError) -> bool:
|
|
26
|
+
|
|
27
|
+
original = getattr(exc, "orig", None)
|
|
28
|
+
|
|
29
|
+
if original is None:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
args = getattr(original, "args", ())
|
|
33
|
+
|
|
34
|
+
for arg in args:
|
|
35
|
+
|
|
36
|
+
if not isinstance(arg, str):
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
upper = arg.upper()
|
|
40
|
+
|
|
41
|
+
if any(code in upper for code in _SQLSTATE_TRANSIENT_ERRORS):
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
return False
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from Osdental.Exception.ControlledException import DatabaseException
|
|
5
|
+
from Osdental.Shared.Enums.Message import Message
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StoredProcedureResultValidator:
|
|
9
|
+
|
|
10
|
+
STATUS_CODE_KEYS = (
|
|
11
|
+
"statusCode",
|
|
12
|
+
"STATUS_CODE",
|
|
13
|
+
"status_code",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
STATUS_MESSAGE_KEYS = (
|
|
17
|
+
"statusMessage",
|
|
18
|
+
"STATUS_MESSAGE",
|
|
19
|
+
"status_message",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def validate(
|
|
24
|
+
cls,
|
|
25
|
+
row: dict[str, Any] | None,
|
|
26
|
+
success_codes: str | int | Iterable[str | int] | None,
|
|
27
|
+
) -> None:
|
|
28
|
+
|
|
29
|
+
if row is None:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
status_code = cls._get_value(
|
|
33
|
+
row,
|
|
34
|
+
cls.STATUS_CODE_KEYS,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
status_message = cls._get_value(
|
|
38
|
+
row,
|
|
39
|
+
cls.STATUS_MESSAGE_KEYS,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if success_codes is None:
|
|
43
|
+
|
|
44
|
+
if status_code is None:
|
|
45
|
+
|
|
46
|
+
raise DatabaseException(
|
|
47
|
+
message="Status code missing.",
|
|
48
|
+
error="Stored Procedure did not return a status code.",
|
|
49
|
+
status_code="DB_STATUS_CODE_MISSING",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
success_codes = cls._normalize(success_codes)
|
|
55
|
+
|
|
56
|
+
if status_code not in success_codes:
|
|
57
|
+
|
|
58
|
+
raise DatabaseException(
|
|
59
|
+
message=status_message or Message.UNEXPECTED_ERROR_MSG,
|
|
60
|
+
error=status_message or Message.UNEXPECTED_ERROR_MSG,
|
|
61
|
+
status_code=status_code,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _normalize(
|
|
66
|
+
success_codes: str | int | Iterable[str | int],
|
|
67
|
+
) -> tuple[str | int, ...]:
|
|
68
|
+
|
|
69
|
+
if isinstance(success_codes, tuple):
|
|
70
|
+
return success_codes
|
|
71
|
+
|
|
72
|
+
if isinstance(success_codes, Iterable) and not isinstance(
|
|
73
|
+
success_codes,
|
|
74
|
+
(str, bytes),
|
|
75
|
+
):
|
|
76
|
+
return tuple(success_codes)
|
|
77
|
+
|
|
78
|
+
return (success_codes,)
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _get_value(
|
|
82
|
+
row: dict[str, Any],
|
|
83
|
+
keys: tuple[str, ...],
|
|
84
|
+
) -> Any | None:
|
|
85
|
+
|
|
86
|
+
for key in keys:
|
|
87
|
+
|
|
88
|
+
if key in row:
|
|
89
|
+
return row[key]
|
|
90
|
+
|
|
91
|
+
return None
|