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.
Files changed (127) hide show
  1. {encryptors-2.78 → encryptors-2.81.0}/PKG-INFO +1 -2
  2. {encryptors-2.78 → encryptors-2.81.0}/setup.py +3 -4
  3. {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/PKG-INFO +1 -2
  4. encryptors-2.81.0/src/Encryptors.egg-info/SOURCES.txt +96 -0
  5. {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/requires.txt +0 -1
  6. encryptors-2.81.0/src/Osdental/Cli/__init__.py +118 -0
  7. encryptors-2.81.0/src/Osdental/Database/BaseRepository.py +171 -0
  8. encryptors-2.81.0/src/Osdental/Database/SqlExceptionTranslator.py +85 -0
  9. encryptors-2.81.0/src/Osdental/Database/SqlTransientDetector.py +44 -0
  10. encryptors-2.81.0/src/Osdental/Database/StoredProcedureResultValidator.py +91 -0
  11. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Decorators/Grpc.py +1 -3
  12. encryptors-2.81.0/src/Osdental/Decorators/PublicResolver.py +3 -0
  13. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Decorators/Retry.py +1 -4
  14. encryptors-2.81.0/src/Osdental/Decorators/SecureResolver.py +59 -0
  15. encryptors-2.81.0/src/Osdental/Decorators/SqlDataNormalizer.py +60 -0
  16. encryptors-2.81.0/src/Osdental/Encryptor/Aes.py +85 -0
  17. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Bcrypt.py +1 -1
  18. encryptors-2.81.0/src/Osdental/Encryptor/Jwt.py +43 -0
  19. encryptors-2.81.0/src/Osdental/Encryptor/Rsa.py +58 -0
  20. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Sha512.py +3 -3
  21. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Exception/ControlledException.py +60 -27
  22. encryptors-2.81.0/src/Osdental/Graphql/Extensions/AuditExtension.py +194 -0
  23. encryptors-2.81.0/src/Osdental/Graphql/Models/__init__.py +14 -0
  24. encryptors-2.81.0/src/Osdental/Graphql/_Exceptions/__init__.py +8 -0
  25. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_AuditHelper.py +67 -0
  26. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +11 -0
  27. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +46 -0
  28. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TokenService.py +44 -0
  29. encryptors-2.81.0/src/Osdental/Helpers/AuditDispatcher.py +172 -0
  30. encryptors-2.78/src/Osdental/Secrets/AzureKeyVaultProvider.py → encryptors-2.81.0/src/Osdental/Helpers/KeyVaultService.py +8 -1
  31. encryptors-2.78/src/Osdental/Helpers/CipherHelper.py → encryptors-2.81.0/src/Osdental/Helpers/ResponseDecryptor.py +11 -18
  32. {encryptors-2.78/src/Osdental/Services → encryptors-2.81.0/src/Osdental/Helpers}/WebsocketClient.py +3 -4
  33. encryptors-2.81.0/src/Osdental/Helpers/_Ports.py +11 -0
  34. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Http/APIClient.py +19 -53
  35. encryptors-2.81.0/src/Osdental/Http/_Helpers.py +122 -0
  36. encryptors-2.81.0/src/Osdental/Messaging/AzureServiceBus.py +213 -0
  37. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/__init__.py +6 -2
  38. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/ApiResponse.py +3 -3
  39. encryptors-2.81.0/src/Osdental/Models/AuditConfig.py +9 -0
  40. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Response.py +5 -6
  41. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Token.py +4 -0
  42. encryptors-2.81.0/src/Osdental/Models/_Audit.py +14 -0
  43. encryptors-2.81.0/src/Osdental/Rest/Context/RequestContext.py +19 -0
  44. encryptors-2.81.0/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +22 -0
  45. encryptors-2.81.0/src/Osdental/Rest/Middlewares/__init__.py +0 -0
  46. encryptors-2.81.0/src/Osdental/Rest/__init__.py +0 -0
  47. encryptors-2.81.0/src/Osdental/Shared/Enums/Code.py +26 -0
  48. encryptors-2.81.0/src/Osdental/Shared/Enums/Constant.py +10 -0
  49. encryptors-2.81.0/src/Osdental/Shared/Enums/FileType.py +9 -0
  50. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Enums/GrahpqlOperation.py +2 -2
  51. {encryptors-2.78/src/Osdental/Constants → encryptors-2.81.0/src/Osdental/Shared/Enums}/Message.py +10 -7
  52. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Enums/Profile.py +1 -2
  53. encryptors-2.81.0/src/Osdental/Shared/Enums/__init__.py +0 -0
  54. encryptors-2.81.0/src/Osdental/Shared/Logger.py +17 -0
  55. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/FileMetaData.py +14 -14
  56. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/Mapper.py +18 -1
  57. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/PasswordGenerator.py +1 -1
  58. encryptors-2.81.0/src/Osdental/Shared/Utils/RsaUtils.py +30 -0
  59. encryptors-2.81.0/src/Osdental/Shared/Utils/__init__.py +0 -0
  60. encryptors-2.81.0/src/Osdental/Shared/__init__.py +0 -0
  61. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Storage/__init__.py +3 -15
  62. encryptors-2.81.0/src/Osdental/__init__.py +0 -0
  63. encryptors-2.78/src/Encryptors.egg-info/SOURCES.txt +0 -87
  64. encryptors-2.78/src/Osdental/Cli/__init__.py +0 -137
  65. encryptors-2.78/src/Osdental/Constants/Constant.py +0 -10
  66. encryptors-2.78/src/Osdental/Context/__init__.py +0 -5
  67. encryptors-2.78/src/Osdental/Database/BaseRepository.py +0 -192
  68. encryptors-2.78/src/Osdental/Decorators/SecureResolver.py +0 -177
  69. encryptors-2.78/src/Osdental/Encryptor/Aes.py +0 -83
  70. encryptors-2.78/src/Osdental/Encryptor/Jwt.py +0 -35
  71. encryptors-2.78/src/Osdental/Encryptor/Rsa.py +0 -54
  72. encryptors-2.78/src/Osdental/Enums/AuditType.py +0 -5
  73. encryptors-2.78/src/Osdental/Enums/CipherType.py +0 -6
  74. encryptors-2.78/src/Osdental/Enums/FileType.py +0 -9
  75. encryptors-2.78/src/Osdental/Enums/GrpcStatusCode.py +0 -25
  76. encryptors-2.78/src/Osdental/Enums/ResultType.py +0 -5
  77. encryptors-2.78/src/Osdental/Enums/Secret.py +0 -8
  78. encryptors-2.78/src/Osdental/Enums/StatusCode.py +0 -40
  79. encryptors-2.78/src/Osdental/Helpers/AuditHelper.py +0 -180
  80. encryptors-2.78/src/Osdental/Helpers/AuditQueue.py +0 -48
  81. encryptors-2.78/src/Osdental/Helpers/AuthTokenProcessor.py +0 -119
  82. encryptors-2.78/src/Osdental/Helpers/JwtTokenHelper.py +0 -172
  83. encryptors-2.78/src/Osdental/Messaging/AzureServiceBus.py +0 -146
  84. encryptors-2.78/src/Osdental/Models/AuditContext.py +0 -62
  85. encryptors-2.78/src/Osdental/Models/Graphql.py +0 -14
  86. encryptors-2.78/src/Osdental/Models/TokenClaims.py +0 -99
  87. encryptors-2.78/src/Osdental/Secrets/__init__.py +0 -7
  88. encryptors-2.78/src/Osdental/Services/JwtAuthTokenService.py +0 -147
  89. encryptors-2.78/src/Osdental/Services/ServiceBusAuditEmitter.py +0 -33
  90. encryptors-2.78/src/Osdental/Services/__init__.py +0 -62
  91. encryptors-2.78/src/Osdental/Utils/RsaUtils.py +0 -56
  92. {encryptors-2.78 → encryptors-2.81.0}/README.md +0 -0
  93. {encryptors-2.78 → encryptors-2.81.0}/setup.cfg +0 -0
  94. {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  95. {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/entry_points.txt +0 -0
  96. {encryptors-2.78 → encryptors-2.81.0}/src/Encryptors.egg-info/top_level.txt +0 -0
  97. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Cache/Redis.py +0 -0
  98. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Cache/__init__.py +0 -0
  99. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Database/Connection.py +0 -0
  100. {encryptors-2.78/src/Osdental/Constants → encryptors-2.81.0/src/Osdental/Database}/__init__.py +0 -0
  101. {encryptors-2.78/src/Osdental/Database → encryptors-2.81.0/src/Osdental/Decorators}/__init__.py +0 -0
  102. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Encryptor/Argon2.py +0 -0
  103. {encryptors-2.78/src/Osdental/Decorators → encryptors-2.81.0/src/Osdental/Encryptor}/__init__.py +0 -0
  104. {encryptors-2.78/src/Osdental/Encryptor → encryptors-2.81.0/src/Osdental/Exception}/__init__.py +0 -0
  105. {encryptors-2.78/src/Osdental/Enums → encryptors-2.81.0/src/Osdental/Graphql/Extensions}/__init__.py +0 -0
  106. {encryptors-2.78/src/Osdental/Exception → encryptors-2.81.0/src/Osdental/Graphql/_Helpers}/__init__.py +0 -0
  107. {encryptors-2.78/src/Osdental/Helpers → encryptors-2.81.0/src/Osdental/Graphql}/__init__.py +0 -0
  108. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Helpers/AzureClassifier.py +0 -0
  109. {encryptors-2.78/src/Osdental/Services → encryptors-2.81.0/src/Osdental/Helpers}/GrpcConnection.py +0 -0
  110. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Helpers/Resilience.py +0 -0
  111. {encryptors-2.78/src/Osdental/Http → encryptors-2.81.0/src/Osdental/Helpers}/__init__.py +0 -0
  112. {encryptors-2.78/src/Osdental/Models → encryptors-2.81.0/src/Osdental/Http}/__init__.py +0 -0
  113. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/Kafka.py +0 -0
  114. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  115. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Models/Notification.py +0 -0
  116. {encryptors-2.78/src/Osdental/Utils → encryptors-2.81.0/src/Osdental/Models}/__init__.py +0 -0
  117. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Rest/Context}/__init__.py +0 -0
  118. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/CaseConverter.py +0 -0
  119. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/CodeGenerator.py +0 -0
  120. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DataNormalizer.py +0 -0
  121. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DataUtils.py +0 -0
  122. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/DateUtils.py +0 -0
  123. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/HashValidator.py +0 -0
  124. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/QueryGenerator.py +0 -0
  125. {encryptors-2.78/src/Osdental → encryptors-2.81.0/src/Osdental/Shared}/Utils/TextProcessor.py +0 -0
  126. {encryptors-2.78 → encryptors-2.81.0}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
  127. {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.78
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.78",
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.78
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
@@ -46,4 +46,3 @@ bcrypt==4.3.0
46
46
  azure-monitor-opentelemetry==1.8.1
47
47
  uvicorn==0.37.0
48
48
  gunicorn==23.0.0
49
- joserfc==1.5.0
@@ -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
@@ -1,8 +1,6 @@
1
- import logging
2
1
  import json
3
2
  from functools import wraps
4
-
5
- logger = logging.getLogger(__name__)
3
+ from Osdental.Shared.Logger import logger
6
4
 
7
5
  def parse_rpc_request_payload(func):
8
6
 
@@ -0,0 +1,3 @@
1
+ def public_resolver(resolver):
2
+ setattr(resolver, "_is_public", True)
3
+ return resolver
@@ -1,8 +1,5 @@
1
1
  from functools import wraps
2
- from tenacity import (
3
- retry, stop_after_attempt,
4
- wait_exponential, retry_if_exception_type
5
- )
2
+ from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
6
3
  import grpc
7
4
  import httpx
8
5