Encryptors 2.81.0__tar.gz → 2.81.2__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 (108) hide show
  1. {encryptors-2.81.0 → encryptors-2.81.2}/PKG-INFO +1 -1
  2. {encryptors-2.81.0 → encryptors-2.81.2}/setup.py +1 -1
  3. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/PKG-INFO +1 -1
  4. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/SOURCES.txt +11 -26
  5. encryptors-2.81.2/src/Osdental/Decorators/SecureResolver.py +119 -0
  6. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Exception/ControlledException.py +4 -10
  7. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TokenService.py → encryptors-2.81.2/src/Osdental/Helpers/token_validation_service.py +13 -8
  8. encryptors-2.81.2/src/Osdental/Http/Constants.py +5 -0
  9. encryptors-2.81.2/src/Osdental/Http/Exceptions.py +25 -0
  10. encryptors-2.81.0/src/Osdental/Http/APIClient.py → encryptors-2.81.2/src/Osdental/Http/HttpxClient.py +39 -40
  11. encryptors-2.81.2/src/Osdental/Http/Messages.py +1 -0
  12. encryptors-2.81.2/src/Osdental/Http/Types.py +3 -0
  13. encryptors-2.81.2/src/Osdental/Http/__init__.py +30 -0
  14. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Messaging/AzureServiceBus.py +6 -0
  15. encryptors-2.81.0/src/Osdental/Messaging/__init__.py → encryptors-2.81.2/src/Osdental/Messaging/Kafka.py +6 -7
  16. encryptors-2.81.2/src/Osdental/Messaging/RabbitMQ.py +15 -0
  17. encryptors-2.81.2/src/Osdental/Messaging/__init__.py +37 -0
  18. encryptors-2.81.0/src/Osdental/Models/ApiResponse.py → encryptors-2.81.2/src/Osdental/Models/Response.py +5 -9
  19. encryptors-2.81.0/src/Osdental/Graphql/Models/__init__.py → encryptors-2.81.2/src/Osdental/Models/base_graphql_context.py +1 -4
  20. {encryptors-2.81.0/src/Osdental/Helpers → encryptors-2.81.2/src/Osdental/Secrets}/KeyVaultService.py +36 -11
  21. encryptors-2.81.2/src/Osdental/Secrets/__init__.py +83 -0
  22. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/Mapper.py +18 -16
  23. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Storage/AzureBlobStorage.py +9 -9
  24. encryptors-2.81.2/src/Osdental/Storage/__init__.py +118 -0
  25. encryptors-2.81.0/src/Osdental/Decorators/SecureResolver.py +0 -59
  26. encryptors-2.81.0/src/Osdental/Decorators/SqlDataNormalizer.py +0 -60
  27. encryptors-2.81.0/src/Osdental/Graphql/Extensions/AuditExtension.py +0 -194
  28. encryptors-2.81.0/src/Osdental/Graphql/_Exceptions/__init__.py +0 -8
  29. encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_AuditHelper.py +0 -67
  30. encryptors-2.81.0/src/Osdental/Helpers/AuditDispatcher.py +0 -172
  31. encryptors-2.81.0/src/Osdental/Helpers/WebsocketClient.py +0 -44
  32. encryptors-2.81.0/src/Osdental/Helpers/_Ports.py +0 -11
  33. encryptors-2.81.0/src/Osdental/Http/_Helpers.py +0 -122
  34. encryptors-2.81.0/src/Osdental/Messaging/Kafka.py +0 -11
  35. encryptors-2.81.0/src/Osdental/Messaging/RabbitMQ.py +0 -11
  36. encryptors-2.81.0/src/Osdental/Models/AuditConfig.py +0 -9
  37. encryptors-2.81.0/src/Osdental/Models/Notification.py +0 -10
  38. encryptors-2.81.0/src/Osdental/Models/Response.py +0 -27
  39. encryptors-2.81.0/src/Osdental/Models/_Audit.py +0 -14
  40. encryptors-2.81.0/src/Osdental/Rest/Context/RequestContext.py +0 -19
  41. encryptors-2.81.0/src/Osdental/Rest/Context/__init__.py +0 -0
  42. encryptors-2.81.0/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -22
  43. encryptors-2.81.0/src/Osdental/Rest/Middlewares/__init__.py +0 -0
  44. encryptors-2.81.0/src/Osdental/Rest/__init__.py +0 -0
  45. encryptors-2.81.0/src/Osdental/Shared/Enums/__init__.py +0 -0
  46. encryptors-2.81.0/src/Osdental/Shared/Utils/__init__.py +0 -0
  47. encryptors-2.81.0/src/Osdental/Shared/__init__.py +0 -0
  48. encryptors-2.81.0/src/Osdental/Storage/__init__.py +0 -36
  49. encryptors-2.81.0/src/Osdental/__init__.py +0 -0
  50. {encryptors-2.81.0 → encryptors-2.81.2}/README.md +0 -0
  51. {encryptors-2.81.0 → encryptors-2.81.2}/setup.cfg +0 -0
  52. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  53. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/entry_points.txt +0 -0
  54. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/requires.txt +0 -0
  55. {encryptors-2.81.0 → encryptors-2.81.2}/src/Encryptors.egg-info/top_level.txt +0 -0
  56. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Cache/Redis.py +0 -0
  57. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Cache/__init__.py +0 -0
  58. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Cli/__init__.py +0 -0
  59. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/BaseRepository.py +0 -0
  60. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/Connection.py +0 -0
  61. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/SqlExceptionTranslator.py +0 -0
  62. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/SqlTransientDetector.py +0 -0
  63. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/StoredProcedureResultValidator.py +0 -0
  64. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Database/__init__.py +0 -0
  65. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Decorators/Grpc.py +0 -0
  66. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Decorators/PublicResolver.py +0 -0
  67. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Decorators/Retry.py +0 -0
  68. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Decorators/__init__.py +0 -0
  69. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Aes.py +0 -0
  70. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Argon2.py +0 -0
  71. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Bcrypt.py +0 -0
  72. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Jwt.py +0 -0
  73. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Rsa.py +0 -0
  74. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/Sha512.py +0 -0
  75. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Encryptor/__init__.py +0 -0
  76. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Exception/__init__.py +0 -0
  77. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Helpers/AzureClassifier.py +0 -0
  78. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Helpers/GrpcConnection.py +0 -0
  79. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Helpers/Resilience.py +0 -0
  80. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Helpers/ResponseDecryptor.py +0 -0
  81. {encryptors-2.81.0/src/Osdental/Graphql/Extensions → encryptors-2.81.2/src/Osdental/Helpers}/__init__.py +0 -0
  82. /encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py → /encryptors-2.81.2/src/Osdental/Helpers/extract_auth_token.py +0 -0
  83. /encryptors-2.81.0/src/Osdental/Graphql/_Helpers/_TenantPolicy.py → /encryptors-2.81.2/src/Osdental/Helpers/tenant_policy.py +0 -0
  84. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Models/Token.py +0 -0
  85. {encryptors-2.81.0/src/Osdental/Graphql/_Helpers → encryptors-2.81.2/src/Osdental/Models}/__init__.py +0 -0
  86. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/Code.py +0 -0
  87. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/Constant.py +0 -0
  88. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/FileType.py +0 -0
  89. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
  90. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/Message.py +0 -0
  91. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Enums/Profile.py +0 -0
  92. {encryptors-2.81.0/src/Osdental/Graphql → encryptors-2.81.2/src/Osdental/Shared/Enums}/__init__.py +0 -0
  93. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Logger.py +0 -0
  94. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
  95. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
  96. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
  97. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
  98. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
  99. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/FileMetaData.py +0 -0
  100. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
  101. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
  102. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
  103. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/RsaUtils.py +0 -0
  104. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
  105. {encryptors-2.81.0/src/Osdental/Helpers → encryptors-2.81.2/src/Osdental/Shared/Utils}/__init__.py +0 -0
  106. {encryptors-2.81.0/src/Osdental/Http → encryptors-2.81.2/src/Osdental/Shared}/__init__.py +0 -0
  107. {encryptors-2.81.0 → encryptors-2.81.2}/src/Osdental/Storage/S3Storage.py +0 -0
  108. {encryptors-2.81.0/src/Osdental/Models → encryptors-2.81.2/src/Osdental}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.81.0
3
+ Version: 2.81.2
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -2,7 +2,7 @@ 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.81.0",
5
+ version="2.81.2",
6
6
  author="OSDental LLC",
7
7
  author_email="support@osdental.ai",
8
8
  description="End-to-end algorithm library",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.81.0
3
+ Version: 2.81.2
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -20,7 +20,6 @@ src/Osdental/Decorators/Grpc.py
20
20
  src/Osdental/Decorators/PublicResolver.py
21
21
  src/Osdental/Decorators/Retry.py
22
22
  src/Osdental/Decorators/SecureResolver.py
23
- src/Osdental/Decorators/SqlDataNormalizer.py
24
23
  src/Osdental/Decorators/__init__.py
25
24
  src/Osdental/Encryptor/Aes.py
26
25
  src/Osdental/Encryptor/Argon2.py
@@ -31,44 +30,30 @@ src/Osdental/Encryptor/Sha512.py
31
30
  src/Osdental/Encryptor/__init__.py
32
31
  src/Osdental/Exception/ControlledException.py
33
32
  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
33
  src/Osdental/Helpers/AzureClassifier.py
46
34
  src/Osdental/Helpers/GrpcConnection.py
47
- src/Osdental/Helpers/KeyVaultService.py
48
35
  src/Osdental/Helpers/Resilience.py
49
36
  src/Osdental/Helpers/ResponseDecryptor.py
50
- src/Osdental/Helpers/WebsocketClient.py
51
- src/Osdental/Helpers/_Ports.py
52
37
  src/Osdental/Helpers/__init__.py
53
- src/Osdental/Http/APIClient.py
54
- src/Osdental/Http/_Helpers.py
38
+ src/Osdental/Helpers/extract_auth_token.py
39
+ src/Osdental/Helpers/tenant_policy.py
40
+ src/Osdental/Helpers/token_validation_service.py
41
+ src/Osdental/Http/Constants.py
42
+ src/Osdental/Http/Exceptions.py
43
+ src/Osdental/Http/HttpxClient.py
44
+ src/Osdental/Http/Messages.py
45
+ src/Osdental/Http/Types.py
55
46
  src/Osdental/Http/__init__.py
56
47
  src/Osdental/Messaging/AzureServiceBus.py
57
48
  src/Osdental/Messaging/Kafka.py
58
49
  src/Osdental/Messaging/RabbitMQ.py
59
50
  src/Osdental/Messaging/__init__.py
60
- src/Osdental/Models/ApiResponse.py
61
- src/Osdental/Models/AuditConfig.py
62
- src/Osdental/Models/Notification.py
63
51
  src/Osdental/Models/Response.py
64
52
  src/Osdental/Models/Token.py
65
- src/Osdental/Models/_Audit.py
66
53
  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
54
+ src/Osdental/Models/base_graphql_context.py
55
+ src/Osdental/Secrets/KeyVaultService.py
56
+ src/Osdental/Secrets/__init__.py
72
57
  src/Osdental/Shared/Logger.py
73
58
  src/Osdental/Shared/__init__.py
74
59
  src/Osdental/Shared/Enums/Code.py
@@ -0,0 +1,119 @@
1
+ import json
2
+ from functools import wraps
3
+ from typing import Callable
4
+ from Osdental.Models.Response import Response
5
+ from Osdental.Shared.Enums.Profile import Profile
6
+ from Osdental.Exception.ControlledException import (
7
+ OSDException, AccessDeniedException, UnauthorizedException
8
+ )
9
+ from Osdental.Encryptor.Aes import AES
10
+ from Osdental.Models.base_graphql_context import BaseGraphQLContext
11
+ from Osdental.Helpers.tenant_policy import TenantPolicy
12
+ from Osdental.Secrets import ISecretProvider
13
+ from Osdental.Shared.Logger import logger
14
+
15
+ def resolver(action=None):
16
+
17
+ def decorator(func: Callable):
18
+
19
+ @wraps(func)
20
+ async def wrapper(obj, info, **kwargs):
21
+ try:
22
+ context: BaseGraphQLContext = info.context
23
+ request = context.request
24
+ token_service = context.container.token_service
25
+
26
+ if action:
27
+ if Profile(token.abbreviation) not in action.allowed_roles:
28
+ raise AccessDeniedException(
29
+ error="User not allowed to perform this action"
30
+ )
31
+
32
+ # Get secrets
33
+ secret_service: ISecretProvider = context.container.secret_service
34
+ keys = await secret_service.get_many(
35
+ ["AES-KEY-AUTH", "AES-KEY-USER", "AUTH-JWT-USER-KEY"]
36
+ )
37
+ #
38
+
39
+ encrypted_payload = kwargs.get("data")
40
+
41
+ decrypted_payload = None
42
+
43
+ if encrypted_payload is not None:
44
+
45
+ if not isinstance(encrypted_payload, str):
46
+ raise ValueError("Encrypted payload must be a string")
47
+
48
+ if not context.aes_auth:
49
+ raise ValueError("Missing AES configuration")
50
+
51
+ try:
52
+ decrypted = AES.decrypt(context.aes_auth, encrypted_payload)
53
+ except Exception:
54
+ raise ValueError("Invalid encrypted payload")
55
+
56
+ if isinstance(decrypted, dict):
57
+ decrypted_payload = decrypted
58
+
59
+ elif isinstance(decrypted, str):
60
+ try:
61
+ decrypted_payload = json.loads(decrypted)
62
+ except Exception:
63
+ raise ValueError("Decrypted payload is not valid JSON")
64
+
65
+ else:
66
+ raise ValueError("Unsupported decrypted payload type")
67
+
68
+ if decrypted_payload is not None:
69
+ kwargs["data"] = decrypted_payload
70
+
71
+ # token
72
+ try:
73
+ original_token = await token_service.authenticate(
74
+ request.headers,
75
+ keys["AUTH-JWT-USER-KEY"],
76
+ keys["AES-KEY-USER"],
77
+ )
78
+ except Exception:
79
+ original_token = None
80
+
81
+ token = TenantPolicy.resolve(
82
+ token=original_token,
83
+ headers=request.headers,
84
+ decrypted_payload=decrypted_payload,
85
+ operation_type=info.operation.operation.value
86
+ )
87
+
88
+ if not token:
89
+ raise UnauthorizedException(
90
+ error="Authorization required"
91
+ )
92
+ # token
93
+
94
+ result = await func(obj, info, **kwargs)
95
+
96
+ if not isinstance(result, Response):
97
+ raise TypeError("Resolver must return a Response instance")
98
+
99
+ return result
100
+
101
+ except OSDException as e:
102
+ logger.warning(f"Business error: {str(e)}")
103
+
104
+ return Response(
105
+ status=e.status_code,
106
+ message=e.message,
107
+ )
108
+
109
+ except Exception as e:
110
+ logger.exception(f"Unexpected error: {str(e)}")
111
+
112
+ return Response(
113
+ status="DB_ERROR_UNEXPECTED",
114
+ message="Could not process request.",
115
+ )
116
+
117
+ return wrapper
118
+
119
+ return decorator
@@ -8,7 +8,7 @@ class OSDException(Exception):
8
8
  self,
9
9
  message: str = Message.UNEXPECTED_ERROR_MSG,
10
10
  error: str = None,
11
- status_code: Any = Code.APP_ERROR_CODE
11
+ status_code: Any = Code.APP_ERROR_CODE,
12
12
  ):
13
13
  super().__init__(error)
14
14
  self.message = message
@@ -19,7 +19,9 @@ class OSDException(Exception):
19
19
  def __str__(self):
20
20
  error_detail = self.error if self.error else "None"
21
21
  return f"[{self.status_code}] {self.message} | {error_detail}"
22
-
22
+
23
+
24
+
23
25
  class UnauthorizedException(OSDException):
24
26
  def __init__(
25
27
  self,
@@ -155,11 +157,3 @@ class InvalidFormatException(OSDException):
155
157
  ):
156
158
  super().__init__(message=message, error=error, status_code=status_code)
157
159
 
158
- class HttpClientException(OSDException):
159
- def __init__(
160
- self,
161
- message: str = Message.UNEXPECTED_ERROR_MSG,
162
- error: str = None,
163
- status_code: str = Code.HTTP_ERROR_CODE
164
- ):
165
- super().__init__(message=message, error=error, status_code=status_code)
@@ -2,19 +2,24 @@ from starlette.datastructures import Headers
2
2
  from Osdental.Encryptor.Aes import AES
3
3
  from Osdental.Encryptor.Jwt import JWT
4
4
  from Osdental.Models.Token import AuthToken
5
- from Osdental.Graphql._Helpers._ExtractAuthToken import ExtractAuthToken
5
+ from Osdental.Helpers.extract_auth_token import ExtractAuthToken
6
6
 
7
- class TokenService:
7
+ class TokenValidationService:
8
8
 
9
- def __init__(self, jwt_user_key: str, auth_validator):
10
- self.jwt_user_key = jwt_user_key
9
+ def __init__(self, auth_validator):
11
10
  self.auth_validator = auth_validator
12
11
 
13
- async def authenticate(self, headers: Headers, aes_user: str) -> AuthToken:
12
+ async def authenticate(
13
+ self,
14
+ headers: Headers,
15
+ jwt_user_key: str,
16
+ aes_user: str
17
+ ) -> AuthToken:
18
+
14
19
  encrypted_token = ExtractAuthToken.get_auth_token(headers)
15
20
 
16
21
  user_token = AES.decrypt(aes_user, encrypted_token)
17
- payload = JWT.extract_payload(user_token, self.jwt_user_key)
22
+ payload = JWT.extract_payload(user_token, jwt_user_key)
18
23
 
19
24
  token = AuthToken(
20
25
  id_token=payload.get("idToken"),
@@ -33,11 +38,11 @@ class TokenService:
33
38
  )
34
39
 
35
40
  # Validate via RPC
36
- paylod = {
41
+ payload = {
37
42
  "idToken": token.id_token,
38
43
  "idUser": token.id_user
39
44
  }
40
- is_valid = await self.auth_validator.validate_auth_token(paylod)
45
+ is_valid = await self.auth_validator.validate_auth_token(payload)
41
46
  if not int(is_valid):
42
47
  raise ValueError("You are not authorized to access this portal.")
43
48
 
@@ -0,0 +1,5 @@
1
+
2
+ HTTP_ERROR_CODE = "DB_HTTP_ERROR"
3
+ HTTP_TIMEOUT = 408
4
+ MAX_CONNECTIONS = 100
5
+ MAX_KEEPALIVE_CONNECTIONS = 20
@@ -0,0 +1,25 @@
1
+ from typing import Any
2
+
3
+ from Osdental.Exception.ControlledException import OSDException
4
+ from Osdental.Http.Constants import (
5
+ INTERNAL_SERVER_ERROR_MSG,
6
+ HTTP_ERROR_CODE,
7
+ )
8
+
9
+ class HttpClientException(OSDException):
10
+ """For failures in calls to external APIs."""
11
+
12
+ def __init__(
13
+ self,
14
+ message: str = INTERNAL_SERVER_ERROR_MSG,
15
+ error: str = None,
16
+ status_code: Any = HTTP_ERROR_CODE,
17
+ http_status_code: int | None = None,
18
+ ):
19
+ super().__init__(message=message, error=error, status_code=status_code)
20
+ self.http_status_code = http_status_code
21
+
22
+
23
+ def __str__(self):
24
+ base = super().__str__()
25
+ return f"{base} | http={self.http_status_code} code={self.error_code}"
@@ -1,34 +1,35 @@
1
1
  import httpx
2
- from typing import Optional, Dict, Any
2
+ from typing import Any
3
+
4
+ from Osdental.Http import APIClient
3
5
  from Osdental.Decorators.Retry import rest_retry
4
- from Osdental.Http._Helpers import (
5
- audit_success, audit_http_error, audit_exception_error,
6
- audit_unknown_error, audit_graphql_error
6
+ from Osdental.Http.Exceptions import HttpClientException
7
+ from Osdental.Http.Types import HTTPMethod
8
+ from Osdental.Http.Constants import (
9
+ HTTP_TIMEOUT,
10
+ MAX_CONNECTIONS,
11
+ MAX_KEEPALIVE_CONNECTIONS,
7
12
  )
8
- from Osdental.Exception.ControlledException import HttpClientException
9
13
  from Osdental.Shared.Logger import logger
10
14
 
11
- class APIClient:
15
+ class HttpxClient(APIClient):
12
16
 
13
17
  def __init__(
14
18
  self,
15
- timeout: Optional[httpx.Timeout] = None,
16
- limits: Optional[httpx.Limits] = None
19
+ timeout: httpx.Timeout | None = None,
20
+ limits: httpx.Limits | None = None
17
21
  ):
18
22
  self._client = httpx.AsyncClient(
19
23
  follow_redirects=True,
20
24
  timeout=timeout or httpx.Timeout(10.0, read=20.0),
21
25
  limits=limits or httpx.Limits(
22
- max_connections=100,
23
- max_keepalive_connections=20
26
+ max_connections=MAX_CONNECTIONS,
27
+ max_keepalive_connections=MAX_KEEPALIVE_CONNECTIONS,
24
28
  )
25
29
  )
26
30
 
27
- async def close(self):
28
- await self._client.aclose()
29
-
30
31
  @rest_retry
31
- async def _request(self, method: str, url: str, **kwargs) -> httpx.Response:
32
+ async def _request(self, method: HTTPMethod, url: str, **kwargs) -> httpx.Response:
32
33
 
33
34
  try:
34
35
 
@@ -40,35 +41,30 @@ class APIClient:
40
41
 
41
42
  response.raise_for_status()
42
43
 
43
- audit_success(response, method, url, kwargs)
44
-
45
44
  return response
46
-
47
- except httpx.HTTPStatusError as exc:
48
-
49
- audit_http_error(exc.response, method, url, kwargs)
50
- raise HttpClientException(
51
- status_code=exc.response.status_code,
52
- error=exc.response.text
53
- ) from exc
54
45
 
55
- except httpx.RequestError as exc:
56
-
57
- audit_exception_error(exc, method, url, kwargs)
46
+ except httpx.TimeoutException as e:
58
47
  raise HttpClientException(
59
- status_code=0,
60
- error=str(exc)
61
- ) from exc
62
-
48
+ error=str(e),
49
+ http_status_code=HTTP_TIMEOUT
50
+ )
51
+ except httpx.HTTPStatusError as e:
52
+ raise HttpClientException(
53
+ error=str(e),
54
+ http_status_code=e.response.status_code
55
+ )
56
+ except httpx.RequestError as e:
57
+ raise HttpClientException(
58
+ error=str(e),
59
+ )
63
60
  except Exception as exc:
64
-
65
- audit_unknown_error(exc, method, url, kwargs)
66
-
61
+ logger.error(f"Unexpected error in APIClient: {exc}")
67
62
  raise
68
63
 
64
+
69
65
  async def request(
70
66
  self,
71
- method: str,
67
+ method: HTTPMethod,
72
68
  url: str,
73
69
  **kwargs
74
70
  ) -> Any:
@@ -96,9 +92,9 @@ class APIClient:
96
92
  self,
97
93
  url: str,
98
94
  query: str,
99
- variables: Dict[str, Any],
100
- headers: Optional[Dict[str, str]] = None
101
- ) -> Dict[str, Any]:
95
+ variables: dict[str, Any],
96
+ headers: dict[str, str] | None = None
97
+ ) -> dict[str, Any]:
102
98
 
103
99
  payload = {
104
100
  "query": query,
@@ -120,10 +116,13 @@ class APIClient:
120
116
  f"GraphQL error: {data['errors']}"
121
117
  )
122
118
 
123
- audit_graphql_error(data["errors"], url, payload)
124
119
  raise HttpClientException(
125
120
  message="GraphQL execution failed",
126
121
  error=str(data["errors"])
127
122
  )
128
123
 
129
- return data["data"]
124
+ return data["data"]
125
+
126
+
127
+ async def close(self):
128
+ await self._client.aclose()
@@ -0,0 +1 @@
1
+ INTERNAL_SERVER_ERROR_MSG = "Your request could not be processed. Please try again later."
@@ -0,0 +1,3 @@
1
+ from typing import Literal
2
+
3
+ HTTPMethod = Literal["GET", "POST", "PUT", "DELETE"]
@@ -0,0 +1,30 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+ from Osdental.Http.Types import HTTPMethod
4
+
5
+ class APIClient(ABC):
6
+
7
+ @abstractmethod
8
+ async def request(
9
+ self,
10
+ method: HTTPMethod,
11
+ url: str,
12
+ **kwargs
13
+ ) -> Any:
14
+ pass
15
+
16
+
17
+ @abstractmethod
18
+ async def execute(
19
+ self,
20
+ url: str,
21
+ query: str,
22
+ variables: dict[str, Any],
23
+ headers: dict[str, str] | None = None
24
+ ) -> dict[str, Any]:
25
+ pass
26
+
27
+
28
+ @abstractmethod
29
+ async def close(self) -> None:
30
+ pass
@@ -28,6 +28,12 @@ class SenderContext:
28
28
 
29
29
  class AzureServiceBus(IMessageBus):
30
30
 
31
+ """Azure Service Bus implementation of IMessageBus.
32
+
33
+ This implementation maintains one sender per queue and
34
+ automatically recreates senders when transient failures occur.
35
+ """
36
+
31
37
  def __init__(
32
38
  self,
33
39
  namespace: str,
@@ -1,16 +1,15 @@
1
- from abc import ABC, abstractmethod
2
1
  from typing import Any
2
+ from Osdental.Messaging import IMessageBus
3
3
 
4
- class IMessageBus(ABC):
4
+ class KafkaQueue(IMessageBus):
5
5
 
6
- @abstractmethod
7
6
  async def publish(
8
7
  self,
9
8
  queue: str,
10
9
  message: dict[str, Any] | str,
11
10
  ) -> None:
12
- pass
13
-
14
- @abstractmethod
11
+ print(f'[Kafka] Queuing message: {message}')
12
+
13
+
15
14
  async def close(self) -> None:
16
- pass
15
+ print("Closing...")
@@ -0,0 +1,15 @@
1
+ from typing import Any
2
+ from Osdental.Messaging import IMessageBus
3
+
4
+ class RabbitMqQueue(IMessageBus):
5
+
6
+ async def publish(
7
+ self,
8
+ queue: str,
9
+ message: dict[str, Any] | str,
10
+ ) -> None:
11
+ print(f'[RabbitMQ] Queuing message: {message}')
12
+
13
+
14
+ async def close(self) -> None:
15
+ print("Closing...")
@@ -0,0 +1,37 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ class IMessageBus(ABC):
5
+
6
+ """Provides message publishing capabilities.
7
+
8
+ Implementations may publish messages using providers such as
9
+ Azure Service Bus, RabbitMQ, Kafka, or other messaging systems.
10
+ """
11
+
12
+ @abstractmethod
13
+ async def publish(
14
+ self,
15
+ queue: str,
16
+ message: dict[str, Any] | str,
17
+ ) -> None:
18
+ """Publishes a message to the specified queue.
19
+
20
+ Args:
21
+ queue:
22
+ Name of the destination queue.
23
+
24
+ message:
25
+ Message payload to publish. Implementations may support
26
+ plain strings, dictionaries, or other serializable types.
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ async def close(self) -> None:
32
+ """Releases any resources held by the messaging provider.
33
+
34
+ Implementations should close network connections, authentication
35
+ credentials, and any other allocated resources.
36
+ """
37
+ pass
@@ -1,17 +1,13 @@
1
1
  from dataclasses import dataclass, field, asdict
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
  from Osdental.Shared.Enums.Code import Code
4
4
  from Osdental.Shared.Enums.Message import Message
5
5
 
6
6
  @dataclass
7
- class ApiResponse:
7
+ class Response:
8
8
  status: Any = field(default=Code.PROCESS_SUCCESS_CODE)
9
9
  message: str = field(default=Message.PROCESS_SUCCESS_MSG)
10
- data: Optional[Any] = None
11
- error: Optional[str] = None
10
+ data: Any | None = None
12
11
 
13
- def send(self):
14
- api_response = asdict(self)
15
-
16
- if api_response["error"] is None:
17
- api_response.pop("error")
12
+ def send(self) -> dict[str, Any]:
13
+ return asdict(self)
@@ -7,8 +7,5 @@ from Osdental.Models.Token import AuthToken
7
7
  class BaseGraphQLContext:
8
8
  request: Request
9
9
  container: Any
10
- # valores agregados durante ejecución (AuditExtention)
11
10
  token: Optional[AuthToken] = None
12
- decrypted_payload: Optional[dict] = None
13
- aes_auth: Optional[str] = None
14
- audit_plain_response: Optional[Any] = None
11
+ aes_auth: Optional[str] = None