Encryptors 2.48__tar.gz → 2.50__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 (112) hide show
  1. {encryptors-2.48 → encryptors-2.50}/PKG-INFO +6 -4
  2. {encryptors-2.48 → encryptors-2.50}/setup.py +8 -6
  3. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/PKG-INFO +6 -4
  4. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/SOURCES.txt +13 -3
  5. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/requires.txt +5 -3
  6. encryptors-2.50/src/Osdental/Cache/Redis.py +325 -0
  7. encryptors-2.50/src/Osdental/Cache/__init__.py +53 -0
  8. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Cli/__init__.py +1 -1
  9. encryptors-2.50/src/Osdental/Database/BaseRepository.py +195 -0
  10. encryptors-2.50/src/Osdental/Database/Connection.py +188 -0
  11. encryptors-2.50/src/Osdental/Decorators/Grpc.py +32 -0
  12. encryptors-2.50/src/Osdental/Decorators/SecureResolver.py +134 -0
  13. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Exception/ControlledException.py +24 -19
  14. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/Extensions/AuditExtension.py +5 -3
  15. encryptors-2.50/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +46 -0
  16. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/_Helpers/_TokenService.py +3 -3
  17. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Helpers/AuditDispatcher.py +36 -7
  18. encryptors-2.50/src/Osdental/Helpers/AzureClassifier.py +74 -0
  19. encryptors-2.50/src/Osdental/Helpers/GrpcConnection.py +24 -0
  20. encryptors-2.50/src/Osdental/Helpers/JwtAuthTokenService.py +147 -0
  21. encryptors-2.50/src/Osdental/Helpers/JwtTokenHelper.py +183 -0
  22. encryptors-2.50/src/Osdental/Helpers/Resilience.py +232 -0
  23. encryptors-2.50/src/Osdental/Helpers/WebsocketClient.py +44 -0
  24. encryptors-2.50/src/Osdental/Helpers/_AuthTokenProcessor.py +99 -0
  25. encryptors-2.50/src/Osdental/Helpers/_Ports.py +47 -0
  26. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Http/APIClient.py +2 -2
  27. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Http/_Helpers.py +5 -5
  28. encryptors-2.50/src/Osdental/Messaging/AzureServiceBus.py +146 -0
  29. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/ApiResponse.py +2 -2
  30. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/AuditConfig.py +3 -1
  31. encryptors-2.50/src/Osdental/Models/Notification.py +10 -0
  32. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/Response.py +2 -2
  33. encryptors-2.50/src/Osdental/Models/TokenClaims.py +47 -0
  34. encryptors-2.50/src/Osdental/Shared/Enums/Profile.py +11 -0
  35. encryptors-2.50/src/Osdental/Shared/Enums/StatusCode.py +40 -0
  36. encryptors-2.50/src/Osdental/Shared/Utils/RsaUtils.py +56 -0
  37. encryptors-2.50/src/Osdental/Storage/AzureBlobStorage.py +134 -0
  38. encryptors-2.50/src/Osdental/Storage/__init__.py +48 -0
  39. encryptors-2.48/src/Osdental/Database/BaseRepository.py +0 -112
  40. encryptors-2.48/src/Osdental/Database/Connection.py +0 -66
  41. encryptors-2.48/src/Osdental/Decorators/Grpc.py +0 -55
  42. encryptors-2.48/src/Osdental/Decorators/SecureResolver.py +0 -59
  43. encryptors-2.48/src/Osdental/Graphql/_Helpers/_TenantPolicy.py +0 -45
  44. encryptors-2.48/src/Osdental/Messaging/AzureServiceBus.py +0 -51
  45. encryptors-2.48/src/Osdental/RedisCache/Redis.py +0 -108
  46. encryptors-2.48/src/Osdental/Shared/Enums/Code.py +0 -26
  47. encryptors-2.48/src/Osdental/Shared/Enums/Profile.py +0 -10
  48. encryptors-2.48/src/Osdental/Shared/Utils/RsaUtils.py +0 -30
  49. encryptors-2.48/src/Osdental/Storage/AzureBlobStorage.py +0 -87
  50. encryptors-2.48/src/Osdental/Storage/__init__.py +0 -20
  51. encryptors-2.48/src/Osdental/__init__.py +0 -0
  52. {encryptors-2.48 → encryptors-2.50}/README.md +0 -0
  53. {encryptors-2.48 → encryptors-2.50}/setup.cfg +0 -0
  54. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/dependency_links.txt +0 -0
  55. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/entry_points.txt +0 -0
  56. {encryptors-2.48 → encryptors-2.50}/src/Encryptors.egg-info/top_level.txt +0 -0
  57. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Database/__init__.py +0 -0
  58. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Decorators/PublicResolver.py +0 -0
  59. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Decorators/Retry.py +0 -0
  60. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Decorators/SqlDataNormalizer.py +0 -0
  61. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Decorators/__init__.py +0 -0
  62. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Aes.py +0 -0
  63. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Argon2.py +0 -0
  64. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Bcrypt.py +0 -0
  65. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Jwt.py +0 -0
  66. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Rsa.py +0 -0
  67. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/Sha512.py +0 -0
  68. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Encryptor/__init__.py +0 -0
  69. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Exception/__init__.py +0 -0
  70. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/Extensions/__init__.py +0 -0
  71. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/Models/__init__.py +0 -0
  72. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/_Exceptions/__init__.py +0 -0
  73. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/_Helpers/_AuditHelper.py +0 -0
  74. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/_Helpers/_ExtractAuthToken.py +0 -0
  75. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/_Helpers/__init__.py +0 -0
  76. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Graphql/__init__.py +0 -0
  77. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Helpers/KeyVaultService.py +0 -0
  78. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Helpers/ResponseDecryptor.py +0 -0
  79. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Helpers/__init__.py +0 -0
  80. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Http/__init__.py +0 -0
  81. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Messaging/Kafka.py +0 -0
  82. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Messaging/RabbitMQ.py +0 -0
  83. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Messaging/__init__.py +0 -0
  84. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/Token.py +0 -0
  85. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/_Audit.py +0 -0
  86. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Models/__init__.py +0 -0
  87. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Rest/Context/RequestContext.py +0 -0
  88. {encryptors-2.48/src/Osdental/RedisCache → encryptors-2.50/src/Osdental/Rest/Context}/__init__.py +0 -0
  89. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Rest/Middlewares/RequestContextMiddleware.py +0 -0
  90. {encryptors-2.48/src/Osdental/Rest/Context → encryptors-2.50/src/Osdental/Rest/Middlewares}/__init__.py +0 -0
  91. {encryptors-2.48/src/Osdental/Rest/Middlewares → encryptors-2.50/src/Osdental/Rest}/__init__.py +0 -0
  92. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Enums/Constant.py +0 -0
  93. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Enums/FileType.py +0 -0
  94. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Enums/GrahpqlOperation.py +0 -0
  95. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Enums/Message.py +0 -0
  96. {encryptors-2.48/src/Osdental/Rest → encryptors-2.50/src/Osdental/Shared/Enums}/__init__.py +0 -0
  97. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Logger.py +0 -0
  98. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/CaseConverter.py +0 -0
  99. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/CodeGenerator.py +0 -0
  100. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/DataNormalizer.py +0 -0
  101. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/DataUtils.py +0 -0
  102. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/DateUtils.py +0 -0
  103. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/FileMetaData.py +0 -0
  104. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/HashValidator.py +0 -0
  105. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/Mapper.py +0 -0
  106. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/PasswordGenerator.py +0 -0
  107. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/QueryGenerator.py +0 -0
  108. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Shared/Utils/TextProcessor.py +0 -0
  109. {encryptors-2.48/src/Osdental/Shared/Enums → encryptors-2.50/src/Osdental/Shared/Utils}/__init__.py +0 -0
  110. {encryptors-2.48/src/Osdental/Shared/Utils → encryptors-2.50/src/Osdental/Shared}/__init__.py +0 -0
  111. {encryptors-2.48 → encryptors-2.50}/src/Osdental/Storage/S3Storage.py +0 -0
  112. {encryptors-2.48/src/Osdental/Shared → encryptors-2.50/src/Osdental}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.48
3
+ Version: 2.50
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -16,7 +16,7 @@ Requires-Dist: ariadne==0.26.2
16
16
  Requires-Dist: azure-core==1.32.0
17
17
  Requires-Dist: azure-servicebus==7.13.0
18
18
  Requires-Dist: azure-storage-blob==12.24.0
19
- Requires-Dist: azure-identity==1.25.1
19
+ Requires-Dist: azure-identity<2,>=1.24
20
20
  Requires-Dist: azure-keyvault-secrets==4.10.0
21
21
  Requires-Dist: certifi==2024.12.14
22
22
  Requires-Dist: cffi==1.17.1
@@ -32,7 +32,7 @@ Requires-Dist: isodate==0.7.2
32
32
  Requires-Dist: pycparser==2.22
33
33
  Requires-Dist: pydantic==2.12.2
34
34
  Requires-Dist: pydantic_core==2.41.4
35
- Requires-Dist: PyJWT==2.10.1
35
+ Requires-Dist: PyJWT~=2.12.0
36
36
  Requires-Dist: aioodbc==0.5.0
37
37
  Requires-Dist: python-dotenv==1.0.1
38
38
  Requires-Dist: pydantic_settings==2.13.1
@@ -45,7 +45,8 @@ Requires-Dist: tenacity==9.1.2
45
45
  Requires-Dist: typing_extensions==4.15.0
46
46
  Requires-Dist: tzlocal==5.2
47
47
  Requires-Dist: urllib3==2.3.0
48
- Requires-Dist: redis==5.2.1
48
+ Requires-Dist: redis<8,>=5
49
+ Requires-Dist: redis-entraid==1.1.2
49
50
  Requires-Dist: colorlog==6.9.0
50
51
  Requires-Dist: click==8.2.0
51
52
  Requires-Dist: grpcio==1.75.0
@@ -56,6 +57,7 @@ Requires-Dist: bcrypt==4.3.0
56
57
  Requires-Dist: azure-monitor-opentelemetry==1.8.1
57
58
  Requires-Dist: uvicorn==0.37.0
58
59
  Requires-Dist: gunicorn==23.0.0
60
+ Requires-Dist: jwcrypto==1.5.7
59
61
  Dynamic: author
60
62
  Dynamic: author-email
61
63
  Dynamic: classifier
@@ -1,8 +1,8 @@
1
1
  from setuptools import setup, find_packages
2
- # ANDERSON ESTO YA SE SUBIO Y ESTA ESTABLE, AUN TE QUEDA PENDIENTE LA AUDITORIA CON RSA Y AES DE ACCESSTOKEN
2
+ # ANDERSON REVISAR EL CACHE LOCAL DEL KEYVAULT PARA VALIDAR SI FUNCIONA
3
3
  setup(
4
4
  name="Encryptors",
5
- version="2.48",
5
+ version="2.50",
6
6
  author="OSDental LLC",
7
7
  author_email="support@osdental.ai",
8
8
  description="End-to-end algorithm library",
@@ -24,7 +24,7 @@ setup(
24
24
  "azure-core==1.32.0",
25
25
  "azure-servicebus==7.13.0",
26
26
  "azure-storage-blob==12.24.0",
27
- "azure-identity==1.25.1",
27
+ "azure-identity>=1.24,<2",
28
28
  "azure-keyvault-secrets==4.10.0",
29
29
  "certifi==2024.12.14",
30
30
  "cffi==1.17.1",
@@ -40,7 +40,7 @@ setup(
40
40
  "pycparser==2.22",
41
41
  "pydantic==2.12.2",
42
42
  "pydantic_core==2.41.4",
43
- "PyJWT==2.10.1",
43
+ "PyJWT~=2.12.0",
44
44
  "aioodbc==0.5.0",
45
45
  "python-dotenv==1.0.1",
46
46
  "pydantic_settings==2.13.1",
@@ -53,7 +53,8 @@ setup(
53
53
  "typing_extensions==4.15.0",
54
54
  "tzlocal==5.2",
55
55
  "urllib3==2.3.0",
56
- "redis==5.2.1",
56
+ "redis>=5,<8",
57
+ "redis-entraid==1.1.2",
57
58
  "colorlog==6.9.0",
58
59
  "click==8.2.0",
59
60
  "grpcio==1.75.0",
@@ -63,7 +64,8 @@ setup(
63
64
  "bcrypt==4.3.0",
64
65
  "azure-monitor-opentelemetry==1.8.1",
65
66
  "uvicorn==0.37.0",
66
- "gunicorn==23.0.0"
67
+ "gunicorn==23.0.0",
68
+ "jwcrypto==1.5.7",
67
69
  ],
68
70
  entry_points={
69
71
  'console_scripts': [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Encryptors
3
- Version: 2.48
3
+ Version: 2.50
4
4
  Summary: End-to-end algorithm library
5
5
  Author: OSDental LLC
6
6
  Author-email: support@osdental.ai
@@ -16,7 +16,7 @@ Requires-Dist: ariadne==0.26.2
16
16
  Requires-Dist: azure-core==1.32.0
17
17
  Requires-Dist: azure-servicebus==7.13.0
18
18
  Requires-Dist: azure-storage-blob==12.24.0
19
- Requires-Dist: azure-identity==1.25.1
19
+ Requires-Dist: azure-identity<2,>=1.24
20
20
  Requires-Dist: azure-keyvault-secrets==4.10.0
21
21
  Requires-Dist: certifi==2024.12.14
22
22
  Requires-Dist: cffi==1.17.1
@@ -32,7 +32,7 @@ Requires-Dist: isodate==0.7.2
32
32
  Requires-Dist: pycparser==2.22
33
33
  Requires-Dist: pydantic==2.12.2
34
34
  Requires-Dist: pydantic_core==2.41.4
35
- Requires-Dist: PyJWT==2.10.1
35
+ Requires-Dist: PyJWT~=2.12.0
36
36
  Requires-Dist: aioodbc==0.5.0
37
37
  Requires-Dist: python-dotenv==1.0.1
38
38
  Requires-Dist: pydantic_settings==2.13.1
@@ -45,7 +45,8 @@ Requires-Dist: tenacity==9.1.2
45
45
  Requires-Dist: typing_extensions==4.15.0
46
46
  Requires-Dist: tzlocal==5.2
47
47
  Requires-Dist: urllib3==2.3.0
48
- Requires-Dist: redis==5.2.1
48
+ Requires-Dist: redis<8,>=5
49
+ Requires-Dist: redis-entraid==1.1.2
49
50
  Requires-Dist: colorlog==6.9.0
50
51
  Requires-Dist: click==8.2.0
51
52
  Requires-Dist: grpcio==1.75.0
@@ -56,6 +57,7 @@ Requires-Dist: bcrypt==4.3.0
56
57
  Requires-Dist: azure-monitor-opentelemetry==1.8.1
57
58
  Requires-Dist: uvicorn==0.37.0
58
59
  Requires-Dist: gunicorn==23.0.0
60
+ Requires-Dist: jwcrypto==1.5.7
59
61
  Dynamic: author
60
62
  Dynamic: author-email
61
63
  Dynamic: classifier
@@ -7,6 +7,8 @@ src/Encryptors.egg-info/entry_points.txt
7
7
  src/Encryptors.egg-info/requires.txt
8
8
  src/Encryptors.egg-info/top_level.txt
9
9
  src/Osdental/__init__.py
10
+ src/Osdental/Cache/Redis.py
11
+ src/Osdental/Cache/__init__.py
10
12
  src/Osdental/Cli/__init__.py
11
13
  src/Osdental/Database/BaseRepository.py
12
14
  src/Osdental/Database/Connection.py
@@ -37,8 +39,16 @@ src/Osdental/Graphql/_Helpers/_TenantPolicy.py
37
39
  src/Osdental/Graphql/_Helpers/_TokenService.py
38
40
  src/Osdental/Graphql/_Helpers/__init__.py
39
41
  src/Osdental/Helpers/AuditDispatcher.py
42
+ src/Osdental/Helpers/AzureClassifier.py
43
+ src/Osdental/Helpers/GrpcConnection.py
44
+ src/Osdental/Helpers/JwtAuthTokenService.py
45
+ src/Osdental/Helpers/JwtTokenHelper.py
40
46
  src/Osdental/Helpers/KeyVaultService.py
47
+ src/Osdental/Helpers/Resilience.py
41
48
  src/Osdental/Helpers/ResponseDecryptor.py
49
+ src/Osdental/Helpers/WebsocketClient.py
50
+ src/Osdental/Helpers/_AuthTokenProcessor.py
51
+ src/Osdental/Helpers/_Ports.py
42
52
  src/Osdental/Helpers/__init__.py
43
53
  src/Osdental/Http/APIClient.py
44
54
  src/Osdental/Http/_Helpers.py
@@ -49,12 +59,12 @@ src/Osdental/Messaging/RabbitMQ.py
49
59
  src/Osdental/Messaging/__init__.py
50
60
  src/Osdental/Models/ApiResponse.py
51
61
  src/Osdental/Models/AuditConfig.py
62
+ src/Osdental/Models/Notification.py
52
63
  src/Osdental/Models/Response.py
53
64
  src/Osdental/Models/Token.py
65
+ src/Osdental/Models/TokenClaims.py
54
66
  src/Osdental/Models/_Audit.py
55
67
  src/Osdental/Models/__init__.py
56
- src/Osdental/RedisCache/Redis.py
57
- src/Osdental/RedisCache/__init__.py
58
68
  src/Osdental/Rest/__init__.py
59
69
  src/Osdental/Rest/Context/RequestContext.py
60
70
  src/Osdental/Rest/Context/__init__.py
@@ -62,12 +72,12 @@ src/Osdental/Rest/Middlewares/RequestContextMiddleware.py
62
72
  src/Osdental/Rest/Middlewares/__init__.py
63
73
  src/Osdental/Shared/Logger.py
64
74
  src/Osdental/Shared/__init__.py
65
- src/Osdental/Shared/Enums/Code.py
66
75
  src/Osdental/Shared/Enums/Constant.py
67
76
  src/Osdental/Shared/Enums/FileType.py
68
77
  src/Osdental/Shared/Enums/GrahpqlOperation.py
69
78
  src/Osdental/Shared/Enums/Message.py
70
79
  src/Osdental/Shared/Enums/Profile.py
80
+ src/Osdental/Shared/Enums/StatusCode.py
71
81
  src/Osdental/Shared/Enums/__init__.py
72
82
  src/Osdental/Shared/Utils/CaseConverter.py
73
83
  src/Osdental/Shared/Utils/CodeGenerator.py
@@ -5,7 +5,7 @@ ariadne==0.26.2
5
5
  azure-core==1.32.0
6
6
  azure-servicebus==7.13.0
7
7
  azure-storage-blob==12.24.0
8
- azure-identity==1.25.1
8
+ azure-identity<2,>=1.24
9
9
  azure-keyvault-secrets==4.10.0
10
10
  certifi==2024.12.14
11
11
  cffi==1.17.1
@@ -21,7 +21,7 @@ isodate==0.7.2
21
21
  pycparser==2.22
22
22
  pydantic==2.12.2
23
23
  pydantic_core==2.41.4
24
- PyJWT==2.10.1
24
+ PyJWT~=2.12.0
25
25
  aioodbc==0.5.0
26
26
  python-dotenv==1.0.1
27
27
  pydantic_settings==2.13.1
@@ -34,7 +34,8 @@ tenacity==9.1.2
34
34
  typing_extensions==4.15.0
35
35
  tzlocal==5.2
36
36
  urllib3==2.3.0
37
- redis==5.2.1
37
+ redis<8,>=5
38
+ redis-entraid==1.1.2
38
39
  colorlog==6.9.0
39
40
  click==8.2.0
40
41
  grpcio==1.75.0
@@ -45,3 +46,4 @@ bcrypt==4.3.0
45
46
  azure-monitor-opentelemetry==1.8.1
46
47
  uvicorn==0.37.0
47
48
  gunicorn==23.0.0
49
+ jwcrypto==1.5.7
@@ -0,0 +1,325 @@
1
+ import asyncio
2
+ import json
3
+ from typing import Dict, List, Optional, Any
4
+ import redis.asyncio as redis
5
+ from redis_entraid.cred_provider import (
6
+ create_from_default_azure_credential
7
+ )
8
+ from Osdental.Cache import ICacheService
9
+ from Osdental.Exception.ControlledException import RedisException
10
+ from Osdental.Helpers.Resilience import AzureResiliencePolicy, AzureTransientError
11
+ from Osdental.Helpers.AzureClassifier import classify_redis
12
+ from Osdental.Shared.Logger import logger
13
+ from Osdental.Shared.Enums.Message import Message
14
+
15
+
16
+ class RedisCacheAsync(ICacheService):
17
+
18
+ _instances: Dict[str, "RedisCacheAsync"] = {}
19
+ _lock = asyncio.Lock()
20
+
21
+
22
+ def __new__(cls, redis_host: str, redis_port: int = 6380):
23
+
24
+ key = f"{redis_host}:{redis_port}"
25
+
26
+ if key not in cls._instances:
27
+ cls._instances[key] = super().__new__(cls)
28
+
29
+ return cls._instances[key]
30
+
31
+ def __init__(
32
+ self,
33
+ redis_host: str,
34
+ redis_port: int = 6380
35
+ ):
36
+
37
+ if hasattr(self, "_initialized"):
38
+ return
39
+
40
+ self.redis_host = redis_host
41
+ self.redis_port = redis_port
42
+
43
+ self.client = None
44
+
45
+ self._policy = AzureResiliencePolicy(
46
+ "redis-cache",
47
+ max_attempts=3,
48
+ base_delay=0.5,
49
+ failure_threshold=5,
50
+ )
51
+
52
+ self._initialized = True
53
+ self._reconnect_lock = asyncio.Lock()
54
+
55
+
56
+ async def _call(self, func, *args, **kwargs):
57
+ """
58
+ Ejecuta una operación de Redis a través de la policy.
59
+ Clasifica el error y deja que la policy decida si reintenta.
60
+ Si se agota o el circuito está abierto, convierte a RedisException.
61
+ """
62
+ await self._ensure_connection()
63
+
64
+ async def _inner():
65
+ try:
66
+ return await func(*args, **kwargs)
67
+ except Exception as exc:
68
+ raise classify_redis(exc)
69
+
70
+ try:
71
+ return await self._policy.execute(_inner)
72
+ except AzureTransientError as exc:
73
+ try:
74
+ await self._reconnect()
75
+
76
+ except Exception as reconnect_exc:
77
+ logger.error(
78
+ "redis.reconnect.failed error=%s",
79
+ reconnect_exc
80
+ )
81
+
82
+ logger.error(
83
+ "redis.exhausted error=%s",
84
+ exc
85
+ )
86
+
87
+ raise RedisException(
88
+ message=Message.UNEXPECTED_ERROR_MSG,
89
+ error=str(exc)
90
+ )
91
+ except Exception as exc:
92
+ raise RedisException(message=Message.UNEXPECTED_ERROR_MSG, error=str(exc))
93
+
94
+
95
+ async def connect(self):
96
+ if self.client:
97
+ return
98
+
99
+ async with self._lock:
100
+
101
+ if self.client:
102
+ return
103
+
104
+ try:
105
+ credential_provider = (
106
+ create_from_default_azure_credential(
107
+ ("https://redis.azure.com/.default",)
108
+ )
109
+ )
110
+
111
+ self.client = redis.Redis(
112
+ host=self.redis_host,
113
+ port=self.redis_port,
114
+ ssl=True,
115
+ credential_provider=credential_provider,
116
+ decode_responses=True,
117
+ health_check_interval=30,
118
+ retry_on_timeout=True,
119
+ socket_connect_timeout=5,
120
+ socket_timeout=5,
121
+ max_connections=100,
122
+ socket_keepalive=True
123
+ )
124
+
125
+ await self.client.ping()
126
+
127
+ logger.info(
128
+ "Redis connected with Managed Identity"
129
+ )
130
+
131
+ except Exception as e:
132
+ self.client = None
133
+
134
+ logger.error(
135
+ f"Redis connection error: {str(e)}"
136
+ )
137
+
138
+ raise RedisException(
139
+ message=Message.UNEXPECTED_ERROR_MSG,
140
+ error=str(e)
141
+ )
142
+
143
+ async def _ensure_connection(self):
144
+
145
+ if not self.client:
146
+ await self.connect()
147
+
148
+ async def set_dict(
149
+ self,
150
+ key: str,
151
+ value: Dict[str, Any],
152
+ ttl: Optional[int] = None
153
+ ):
154
+
155
+ await self._call(
156
+ self.client.set,
157
+ key,
158
+ json.dumps(value),
159
+ ex=ttl
160
+ )
161
+
162
+
163
+ async def set_str(
164
+ self,
165
+ key: str,
166
+ value: str,
167
+ ttl: Optional[int] = None
168
+ ):
169
+
170
+ await self._call(
171
+ self.client.set,
172
+ key,
173
+ value,
174
+ ex=ttl
175
+ )
176
+
177
+ async def get_dict(
178
+ self,
179
+ key: str
180
+ ) -> Optional[Dict[str, Any]]:
181
+
182
+ value = await self._call(
183
+ self.client.get,
184
+ key
185
+ )
186
+
187
+ try:
188
+ return json.loads(value) if value else None
189
+
190
+ except json.JSONDecodeError:
191
+ logger.error(
192
+ "redis.invalid_json key=%s",
193
+ key
194
+ )
195
+ return None
196
+
197
+
198
+ async def get_str(
199
+ self,
200
+ key: str
201
+ ) -> Optional[str]:
202
+
203
+ return await self._call(
204
+ self.client.get,
205
+ key
206
+ )
207
+
208
+
209
+ async def delete(self, key: str) -> bool:
210
+
211
+ result = await self._call(
212
+ self.client.delete,
213
+ key
214
+ )
215
+
216
+ return result > 0
217
+
218
+
219
+ async def exists(self, key: str) -> bool:
220
+
221
+ result = await self._call(
222
+ self.client.exists,
223
+ key
224
+ )
225
+
226
+ return result > 0
227
+
228
+
229
+ async def flush(self):
230
+
231
+ await self._call(
232
+ self.client.flushdb
233
+ )
234
+
235
+
236
+ async def flush_all(self):
237
+
238
+ await self._call(
239
+ self.client.flushall
240
+ )
241
+
242
+
243
+ async def mget(
244
+ self,
245
+ keys: List[str]
246
+ ) -> List[Optional[Any]]:
247
+
248
+ values = await self._call(
249
+ self.client.mget,
250
+ keys
251
+ )
252
+
253
+ return [
254
+ json.loads(value)
255
+ if value else None
256
+ for value in values
257
+ ]
258
+
259
+
260
+ async def clear_cache(self, prefix: str):
261
+
262
+ await self._ensure_connection()
263
+
264
+ async for key in self._scan_keys(
265
+ match=f"{prefix}*"
266
+ ):
267
+
268
+ await self._call(
269
+ self.client.delete,
270
+ key
271
+ )
272
+
273
+
274
+ async def _scan_keys(self, match: str = "*"):
275
+
276
+ async for key in self.client.scan_iter(
277
+ match=match
278
+ ):
279
+ yield key
280
+
281
+
282
+ async def _reconnect(self):
283
+
284
+ async with self._reconnect_lock:
285
+
286
+ try:
287
+
288
+ if self.client:
289
+ await self.client.ping()
290
+ return
291
+
292
+ except Exception:
293
+ pass
294
+
295
+ try:
296
+
297
+ if self.client:
298
+ await self.client.aclose()
299
+
300
+ except Exception:
301
+ pass
302
+
303
+ self.client = None
304
+
305
+ await self.connect()
306
+
307
+ async def close(self):
308
+
309
+ try:
310
+
311
+ if self.client:
312
+
313
+ await self.client.aclose()
314
+
315
+ self.client = None
316
+
317
+ logger.info(
318
+ "Redis connection closed"
319
+ )
320
+
321
+ except Exception as e:
322
+
323
+ logger.error(
324
+ f"Redis close error: {str(e)}"
325
+ )
@@ -0,0 +1,53 @@
1
+ from typing import Protocol, Optional, Dict, List, Any
2
+
3
+
4
+ class ICacheService(Protocol):
5
+
6
+ async def set_dict(self, key: str, value: Dict[str, Any], ttl: Optional[int] = None):
7
+ """Set a JSON value in the cache."""
8
+ pass
9
+
10
+ async def set_str(self, key: str, value: str, ttl: Optional[int] = None):
11
+ """Set a string value in the cache."""
12
+ pass
13
+
14
+ async def get_dict(self, key: str) -> Optional[Dict[str, Any]]:
15
+ """Get a JSON value from the cache and convert it back to a Python object."""
16
+ pass
17
+
18
+
19
+ async def get_str(self, key: str) -> Optional[str]:
20
+ """Get a string value from the cache."""
21
+ pass
22
+
23
+ async def delete(self, key: str) -> bool:
24
+ """Delete a value from the cache."""
25
+ pass
26
+
27
+ async def exists(self, key: str) -> bool:
28
+ """Check if a key exists in the cache."""
29
+ pass
30
+
31
+ async def flush(self):
32
+ """Flush all keys in the cache."""
33
+ pass
34
+
35
+ async def flush_all(self):
36
+ """Flush all keys in all Redis databases."""
37
+ pass
38
+
39
+ async def mget(self, keys:List[str]) -> List[Optional[str]]:
40
+ """Get multiple values from the cache."""
41
+ pass
42
+
43
+ async def clear_cache(self, prefix: str):
44
+ """Delete all keys matching the given prefix."""
45
+ pass
46
+
47
+ async def _scan_keys(self, match: str = "*"):
48
+ """Asynchronous generator to scan keys matching a pattern."""
49
+ pass
50
+
51
+ async def close(self):
52
+ """Close the connection pool and Redis client."""
53
+ pass
@@ -44,7 +44,7 @@ def serve(port: int):
44
44
  @click.argument('redis_env')
45
45
  async def clean_redis(redis_env: str):
46
46
  try:
47
- from Osdental.RedisCache.Redis import RedisCacheAsync
47
+ from Osdental.Cache.Redis import RedisCacheAsync
48
48
  redis_url = os.getenv(redis_env)
49
49
  if not redis_url:
50
50
  logger.warning(f'Environment variable not found: {redis_env}')