trovesuite 1.0.5__tar.gz → 1.0.7__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 (43) hide show
  1. {trovesuite-1.0.5/src/trovesuite.egg-info → trovesuite-1.0.7}/PKG-INFO +57 -3
  2. {trovesuite-1.0.5 → trovesuite-1.0.7}/README.md +52 -0
  3. {trovesuite-1.0.5 → trovesuite-1.0.7}/pyproject.toml +11 -7
  4. {trovesuite-1.0.5 → trovesuite-1.0.7}/setup.py +1 -1
  5. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/__init__.py +7 -4
  6. trovesuite-1.0.7/src/trovesuite/auth/auth_controller.py +11 -0
  7. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/configs/database.py +104 -22
  8. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/entities/health.py +4 -4
  9. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/notification_controller.py +5 -5
  10. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/notification_read_dto.py +1 -1
  11. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/notification_service.py +3 -3
  12. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/notification_write_dto.py +1 -1
  13. trovesuite-1.0.7/src/trovesuite/storage/__init__.py +42 -0
  14. trovesuite-1.0.7/src/trovesuite/storage/storage_base.py +63 -0
  15. trovesuite-1.0.7/src/trovesuite/storage/storage_controller.py +198 -0
  16. trovesuite-1.0.7/src/trovesuite/storage/storage_read_dto.py +74 -0
  17. trovesuite-1.0.7/src/trovesuite/storage/storage_service.py +529 -0
  18. trovesuite-1.0.7/src/trovesuite/storage/storage_write_dto.py +70 -0
  19. {trovesuite-1.0.5 → trovesuite-1.0.7/src/trovesuite.egg-info}/PKG-INFO +57 -3
  20. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite.egg-info/SOURCES.txt +6 -0
  21. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite.egg-info/requires.txt +2 -0
  22. trovesuite-1.0.5/src/trovesuite/auth/auth_controller.py +0 -11
  23. {trovesuite-1.0.5 → trovesuite-1.0.7}/LICENSE +0 -0
  24. {trovesuite-1.0.5 → trovesuite-1.0.7}/MANIFEST.in +0 -0
  25. {trovesuite-1.0.5 → trovesuite-1.0.7}/requirements.txt +0 -0
  26. {trovesuite-1.0.5 → trovesuite-1.0.7}/setup.cfg +0 -0
  27. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/auth/__init__.py +0 -0
  28. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/auth/auth_base.py +0 -0
  29. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/auth/auth_read_dto.py +0 -0
  30. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/auth/auth_service.py +0 -0
  31. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/auth/auth_write_dto.py +0 -0
  32. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/configs/__init__.py +0 -0
  33. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/configs/logging.py +0 -0
  34. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/configs/settings.py +0 -0
  35. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/entities/__init__.py +0 -0
  36. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/entities/sh_response.py +0 -0
  37. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/__init__.py +0 -0
  38. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/notification/notification_base.py +0 -0
  39. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/utils/__init__.py +0 -0
  40. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite/utils/helper.py +0 -0
  41. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite.egg-info/dependency_links.txt +0 -0
  42. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite.egg-info/not-zip-safe +0 -0
  43. {trovesuite-1.0.5 → trovesuite-1.0.7}/src/trovesuite.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trovesuite
3
- Version: 1.0.5
4
- Summary: TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications
3
+ Version: 1.0.7
4
+ Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications
5
5
  Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
6
6
  Author: Bright Debrah Owusu
7
7
  Author-email: Bright Debrah Owusu <owusu.debrah@deladetech.com>
@@ -11,7 +11,7 @@ Project-URL: Homepage, https://dev.azure.com/brightgclt/trovesuite/_git/packages
11
11
  Project-URL: Repository, https://dev.azure.com/brightgclt/trovesuite/_git/packages
12
12
  Project-URL: Documentation, https://dev.azure.com/brightgclt/trovesuite/_git/packages
13
13
  Project-URL: Bug Tracker, https://dev.azure.com/brightgclt/trovesuite/_workitems/create
14
- Keywords: authentication,authorization,notifications,jwt,trovesuite,fastapi,security,tenant,permissions,enterprise,services
14
+ Keywords: authentication,authorization,notifications,jwt,trovesuite,fastapi,security,tenant,permissions,enterprise,services,azure,storage,blob,cloud-storage
15
15
  Classifier: Development Status :: 5 - Production/Stable
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: License :: OSI Approved :: MIT License
@@ -36,6 +36,8 @@ Requires-Dist: passlib[bcrypt]>=1.7.4
36
36
  Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
37
37
  Requires-Dist: uvicorn<0.39.0,>=0.38.0
38
38
  Requires-Dist: pyjwt<3.0.0,>=2.10.1
39
+ Requires-Dist: azure-storage-blob>=12.19.0
40
+ Requires-Dist: azure-identity>=1.15.0
39
41
  Provides-Extra: dev
40
42
  Requires-Dist: pytest>=8.4.2; extra == "dev"
41
43
  Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
@@ -119,6 +121,8 @@ poetry install --with dev
119
121
 
120
122
  ## Quick Start
121
123
 
124
+ > **✅ Package Status**: All import issues have been resolved in version 1.0.5. The package now works correctly when installed from PyPI or wheel files.
125
+
122
126
  ### Import Patterns
123
127
 
124
128
  The package provides clean, simplified import patterns:
@@ -673,12 +677,62 @@ poetry install
673
677
 
674
678
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
675
679
 
680
+ ## Troubleshooting
681
+
682
+ ### Import Issues
683
+
684
+ If you encounter import errors, make sure you're using the correct import patterns:
685
+
686
+ ```python
687
+ # ✅ Correct imports
688
+ from trovesuite import AuthService, NotificationService
689
+ from trovesuite.auth import AuthServiceWriteDto
690
+ from trovesuite.notification import NotificationEmailServiceWriteDto
691
+
692
+ # ❌ Incorrect imports (will fail)
693
+ from trovesuite.auth.auth_write_dto import AuthServiceWriteDto # Too specific
694
+ from trovesuite.notification.notification_write_dto import NotificationEmailServiceWriteDto # Too specific
695
+ ```
696
+
697
+ ### Package Installation
698
+
699
+ If you're having issues with the package installation:
700
+
701
+ 1. **Make sure you have the latest version**:
702
+ ```bash
703
+ pip install --upgrade trovesuite
704
+ ```
705
+
706
+ 2. **Force reinstall if needed**:
707
+ ```bash
708
+ pip install --force-reinstall trovesuite
709
+ ```
710
+
711
+ 3. **Check your Python environment**:
712
+ ```bash
713
+ python -c "import trovesuite; print('Package installed successfully')"
714
+ ```
715
+
716
+ ### Common Issues
717
+
718
+ - **ImportError: No module named 'src'**: This was fixed in version 1.0.5. Update to the latest version.
719
+ - **AttributeError: module has no attribute 'AuthServiceWriteDto'**: Use `from trovesuite.auth import AuthServiceWriteDto` instead of importing from the main package.
720
+
676
721
  ## Support
677
722
 
678
723
  For support, email brightgclt@gmail.com or create a work item in the [Azure DevOps repository](https://dev.azure.com/brightgclt/trovesuite/_workitems/create).
679
724
 
680
725
  ## Changelog
681
726
 
727
+ ### 1.0.5
728
+ - Fixed all import issues across auth, notification, and entities modules
729
+ - Changed absolute imports (`from src.trovesuite.`) to relative imports (`from .` and `from ..`)
730
+ - Ensured package works correctly when installed from PyPI or wheel
731
+ - Added service write DTOs to module exports for easier usage
732
+ - Updated documentation with simplified import patterns
733
+ - All services and DTOs now import correctly in clean environments
734
+ - Package builds and installs without import errors
735
+
682
736
  ### 1.0.8
683
737
  - Restructured package for direct service imports
684
738
  - Added comprehensive notification services with email support
@@ -66,6 +66,8 @@ poetry install --with dev
66
66
 
67
67
  ## Quick Start
68
68
 
69
+ > **✅ Package Status**: All import issues have been resolved in version 1.0.5. The package now works correctly when installed from PyPI or wheel files.
70
+
69
71
  ### Import Patterns
70
72
 
71
73
  The package provides clean, simplified import patterns:
@@ -620,12 +622,62 @@ poetry install
620
622
 
621
623
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
622
624
 
625
+ ## Troubleshooting
626
+
627
+ ### Import Issues
628
+
629
+ If you encounter import errors, make sure you're using the correct import patterns:
630
+
631
+ ```python
632
+ # ✅ Correct imports
633
+ from trovesuite import AuthService, NotificationService
634
+ from trovesuite.auth import AuthServiceWriteDto
635
+ from trovesuite.notification import NotificationEmailServiceWriteDto
636
+
637
+ # ❌ Incorrect imports (will fail)
638
+ from trovesuite.auth.auth_write_dto import AuthServiceWriteDto # Too specific
639
+ from trovesuite.notification.notification_write_dto import NotificationEmailServiceWriteDto # Too specific
640
+ ```
641
+
642
+ ### Package Installation
643
+
644
+ If you're having issues with the package installation:
645
+
646
+ 1. **Make sure you have the latest version**:
647
+ ```bash
648
+ pip install --upgrade trovesuite
649
+ ```
650
+
651
+ 2. **Force reinstall if needed**:
652
+ ```bash
653
+ pip install --force-reinstall trovesuite
654
+ ```
655
+
656
+ 3. **Check your Python environment**:
657
+ ```bash
658
+ python -c "import trovesuite; print('Package installed successfully')"
659
+ ```
660
+
661
+ ### Common Issues
662
+
663
+ - **ImportError: No module named 'src'**: This was fixed in version 1.0.5. Update to the latest version.
664
+ - **AttributeError: module has no attribute 'AuthServiceWriteDto'**: Use `from trovesuite.auth import AuthServiceWriteDto` instead of importing from the main package.
665
+
623
666
  ## Support
624
667
 
625
668
  For support, email brightgclt@gmail.com or create a work item in the [Azure DevOps repository](https://dev.azure.com/brightgclt/trovesuite/_workitems/create).
626
669
 
627
670
  ## Changelog
628
671
 
672
+ ### 1.0.5
673
+ - Fixed all import issues across auth, notification, and entities modules
674
+ - Changed absolute imports (`from src.trovesuite.`) to relative imports (`from .` and `from ..`)
675
+ - Ensured package works correctly when installed from PyPI or wheel
676
+ - Added service write DTOs to module exports for easier usage
677
+ - Updated documentation with simplified import patterns
678
+ - All services and DTOs now import correctly in clean environments
679
+ - Package builds and installs without import errors
680
+
629
681
  ### 1.0.8
630
682
  - Restructured package for direct service imports
631
683
  - Added comprehensive notification services with email support
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "trovesuite"
7
- version = "1.0.5"
8
- description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
7
+ version = "1.0.7"
8
+ description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
9
9
  authors = ["brightgclt <brightgclt@gmail.com>"]
10
10
  license = "MIT"
11
11
  readme = "README.md"
12
12
  homepage = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
13
13
  repository = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
14
14
  documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
15
- keywords = ["authentication", "authorization", "notifications", "jwt", "trovesuite", "fastapi", "security", "tenant", "permissions", "enterprise", "services"]
15
+ keywords = ["authentication", "authorization", "notifications", "jwt", "trovesuite", "fastapi", "security", "tenant", "permissions", "enterprise", "services", "azure", "storage", "blob", "cloud-storage"]
16
16
  classifiers = [
17
17
  "Development Status :: 5 - Production/Stable",
18
18
  "Intended Audience :: Developers",
@@ -36,6 +36,8 @@ python-dotenv = "^1.0.0"
36
36
  python-multipart = "^0.0.6"
37
37
  python-jose = {extras = ["cryptography"], version = "^3.3.0"}
38
38
  passlib = {extras = ["bcrypt"], version = "^1.7.4"}
39
+ azure-storage-blob = "^12.19.0"
40
+ azure-identity = "^1.15.0"
39
41
 
40
42
  [tool.poetry.group.dev.dependencies]
41
43
  pytest = "^8.4.2"
@@ -56,8 +58,8 @@ Documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
56
58
 
57
59
  [project]
58
60
  name = "trovesuite"
59
- version = "1.0.5"
60
- description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
61
+ version = "1.0.7"
62
+ description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
61
63
  readme = "README.md"
62
64
  license = {text = "MIT"}
63
65
  authors = [
@@ -66,7 +68,7 @@ authors = [
66
68
  maintainers = [
67
69
  {name = "Bright Debrah Owusu", email = "owusu.debrah@deladetech.com"}
68
70
  ]
69
- keywords = ["authentication", "authorization", "notifications", "jwt", "trovesuite", "fastapi", "security", "tenant", "permissions", "enterprise", "services"]
71
+ keywords = ["authentication", "authorization", "notifications", "jwt", "trovesuite", "fastapi", "security", "tenant", "permissions", "enterprise", "services", "azure", "storage", "blob", "cloud-storage"]
70
72
  classifiers = [
71
73
  "Development Status :: 5 - Production/Stable",
72
74
  "Intended Audience :: Developers",
@@ -91,7 +93,9 @@ dependencies = [
91
93
  "passlib[bcrypt]>=1.7.4",
92
94
  "passlib[argon2] (>=1.7.4,<2.0.0)",
93
95
  "uvicorn (>=0.38.0,<0.39.0)",
94
- "pyjwt (>=2.10.1,<3.0.0)"
96
+ "pyjwt (>=2.10.1,<3.0.0)",
97
+ "azure-storage-blob>=12.19.0",
98
+ "azure-identity>=1.15.0"
95
99
 
96
100
  ]
97
101
 
@@ -15,7 +15,7 @@ with open("pyproject.toml", "r", encoding="utf-8") as fh:
15
15
 
16
16
  setup(
17
17
  name="trovesuite",
18
- version="1.0.5",
18
+ version="1.0.7",
19
19
  author="Bright Debrah Owusu",
20
20
  author_email="owusu.debrah@deladetech.com",
21
21
  description="TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications",
@@ -1,18 +1,21 @@
1
1
  """
2
2
  TroveSuite Package
3
3
 
4
- A comprehensive authentication, authorization, and notification service for ERP systems.
5
- Provides JWT token validation, user authorization, permission checking, and notification capabilities.
4
+ A comprehensive authentication, authorization, notification, and storage service for ERP systems.
5
+ Provides JWT token validation, user authorization, permission checking, notification capabilities,
6
+ and Azure Storage blob management.
6
7
  """
7
8
 
8
9
  from .auth import AuthService
9
10
  from .notification import NotificationService
11
+ from .storage import StorageService
10
12
 
11
- __version__ = "1.0.8"
13
+ __version__ = "1.0.7"
12
14
  __author__ = "Bright Debrah Owusu"
13
15
  __email__ = "owusu.debrah@deladetech.com"
14
16
 
15
17
  __all__ = [
16
18
  "AuthService",
17
- "NotificationService"
19
+ "NotificationService",
20
+ "StorageService"
18
21
  ]
@@ -0,0 +1,11 @@
1
+ from fastapi import APIRouter
2
+ from .auth_write_dto import AuthControllerWriteDto
3
+ from .auth_read_dto import AuthControllerReadDto
4
+ from .auth_service import AuthService
5
+ from ..entities.sh_response import Respons
6
+
7
+ auth_router = APIRouter(tags=["Auth"])
8
+
9
+ @auth_router.post("/auth", response_model=Respons[AuthControllerReadDto])
10
+ async def authorize(data: AuthControllerWriteDto):
11
+ return AuthService.authorize(data=data)
@@ -17,20 +17,25 @@ _connection_pool: Optional[psycopg2.pool.ThreadedConnectionPool] = None
17
17
 
18
18
  class DatabaseConfig:
19
19
  """Database configuration and connection management"""
20
-
20
+
21
21
  def __init__(self):
22
22
  self.settings = db_settings
23
23
  self.database_url = self.settings.database_url
24
24
  self.pool_size = 5
25
25
  self.max_overflow = 10
26
-
26
+
27
27
  def get_connection_params(self) -> dict:
28
28
  """Get database connection parameters"""
29
29
  if self.settings.DATABASE_URL:
30
30
  # Use full DATABASE_URL if available
31
31
  return {
32
32
  "dsn": self.settings.DATABASE_URL,
33
- "cursor_factory": RealDictCursor
33
+ "cursor_factory": RealDictCursor,
34
+ "keepalives": 1,
35
+ "keepalives_idle": 30,
36
+ "keepalives_interval": 10,
37
+ "keepalives_count": 5,
38
+ "connect_timeout": 10
34
39
  }
35
40
 
36
41
  # fallback to individual DB_* variables
@@ -41,9 +46,14 @@ class DatabaseConfig:
41
46
  "user": self.settings.DB_USER,
42
47
  "password": self.settings.DB_PASSWORD,
43
48
  "cursor_factory": RealDictCursor,
44
- "application_name": f"{self.settings.APP_NAME}_{self.settings.ENVIRONMENT}"
49
+ "application_name": f"{self.settings.APP_NAME}_{self.settings.ENVIRONMENT}",
50
+ "keepalives": 1,
51
+ "keepalives_idle": 30,
52
+ "keepalives_interval": 10,
53
+ "keepalives_count": 5,
54
+ "connect_timeout": 10
45
55
  }
46
-
56
+
47
57
  def create_connection_pool(self) -> psycopg2.pool.ThreadedConnectionPool:
48
58
  """Create a connection pool for psycopg2"""
49
59
  try:
@@ -57,7 +67,7 @@ class DatabaseConfig:
57
67
  except Exception as e:
58
68
  logger.error(f"Failed to create database connection pool: {str(e)}")
59
69
  raise
60
-
70
+
61
71
  def test_connection(self) -> bool:
62
72
  """Test database connection"""
63
73
  try:
@@ -81,17 +91,17 @@ db_config = DatabaseConfig()
81
91
  def initialize_database():
82
92
  """Initialize database connections and pool"""
83
93
  global _connection_pool
84
-
94
+
85
95
  try:
86
96
  # Test connection first
87
97
  if not db_config.test_connection():
88
98
  raise Exception("Database connection test failed")
89
-
99
+
90
100
  # Create connection pool
91
101
  _connection_pool = db_config.create_connection_pool()
92
-
102
+
93
103
  logger.info("Database initialization completed successfully")
94
-
104
+
95
105
  except Exception as e:
96
106
  logger.error(f"Database initialization failed: {str(e)}")
97
107
  raise
@@ -114,6 +124,17 @@ def get_connection_pool() -> psycopg2.pool.ThreadedConnectionPool:
114
124
  return _connection_pool
115
125
 
116
126
 
127
+ def _validate_connection(conn) -> bool:
128
+ """Validate if a connection is still alive"""
129
+ try:
130
+ # Test if connection is alive with a simple query
131
+ with conn.cursor() as cursor:
132
+ cursor.execute("SELECT 1")
133
+ return True
134
+ except (psycopg2.OperationalError, psycopg2.InterfaceError):
135
+ return False
136
+
137
+
117
138
  @contextmanager
118
139
  def get_db_connection():
119
140
  """Get a database connection from the pool (context manager)"""
@@ -121,52 +142,79 @@ def get_db_connection():
121
142
  conn = None
122
143
  try:
123
144
  conn = pool.getconn()
145
+
146
+ # Validate connection before using it
147
+ if not _validate_connection(conn):
148
+ logger.warning("Stale connection detected, getting new connection")
149
+ pool.putconn(conn, close=True)
150
+ conn = pool.getconn()
151
+
124
152
  logger.debug("Database connection acquired from pool")
125
153
  yield conn
126
154
  except Exception as e:
127
155
  logger.error(f"Database connection error: {str(e)}")
128
156
  if conn:
129
- conn.rollback()
157
+ try:
158
+ # Only rollback if connection is still open
159
+ if not conn.closed:
160
+ conn.rollback()
161
+ except (psycopg2.OperationalError, psycopg2.InterfaceError) as rollback_error:
162
+ logger.warning(f"Could not rollback closed connection: {str(rollback_error)}")
130
163
  raise
131
164
  finally:
132
165
  if conn:
133
- pool.putconn(conn)
134
- logger.debug("Database connection returned to pool")
166
+ try:
167
+ # If connection is broken, close it instead of returning to pool
168
+ if conn.closed:
169
+ pool.putconn(conn, close=True)
170
+ else:
171
+ pool.putconn(conn)
172
+ logger.debug("Database connection returned to pool")
173
+ except Exception as put_error:
174
+ logger.error(f"Error returning connection to pool: {str(put_error)}")
135
175
 
136
176
 
137
177
  @contextmanager
138
178
  def get_db_cursor():
139
179
  """Get a database cursor (context manager)"""
140
180
  with get_db_connection() as conn:
141
- cursor = conn.cursor()
181
+ cursor = conn.cursor(cursor_factory=RealDictCursor)
142
182
  try:
143
183
  yield cursor
144
- conn.commit()
184
+ if not conn.closed:
185
+ conn.commit()
145
186
  except Exception as e:
146
- conn.rollback()
187
+ if not conn.closed:
188
+ try:
189
+ conn.rollback()
190
+ except (psycopg2.OperationalError, psycopg2.InterfaceError) as rollback_error:
191
+ logger.warning(f"Could not rollback transaction on closed connection: {str(rollback_error)}")
147
192
  logger.error(f"Database cursor error: {str(e)}")
148
193
  raise
149
194
  finally:
150
- cursor.close()
195
+ try:
196
+ cursor.close()
197
+ except Exception as close_error:
198
+ logger.warning(f"Error closing cursor: {str(close_error)}")
151
199
 
152
200
 
153
201
  class DatabaseManager:
154
202
  """Database manager for common operations"""
155
-
203
+
156
204
  @staticmethod
157
205
  def execute_query(query: str, params: tuple = None) -> list:
158
206
  """Execute a SELECT query and return results"""
159
207
  with get_db_cursor() as cursor:
160
208
  cursor.execute(query, params)
161
209
  return cursor.fetchall()
162
-
210
+
163
211
  @staticmethod
164
212
  def execute_update(query: str, params: tuple = None) -> int:
165
213
  """Execute an INSERT/UPDATE/DELETE query and return affected rows"""
166
214
  with get_db_cursor() as cursor:
167
215
  cursor.execute(query, params)
168
216
  return cursor.rowcount
169
-
217
+
170
218
  @staticmethod
171
219
  def execute_scalar(query: str, params: tuple = None):
172
220
  """Execute a query and return a single value"""
@@ -182,7 +230,41 @@ class DatabaseManager:
182
230
  # Handle tuple result
183
231
  return result[0] if len(result) > 0 else None
184
232
  return None
185
-
233
+
234
+ @staticmethod
235
+ @contextmanager
236
+ def transaction():
237
+ """
238
+ Context manager for database transactions.
239
+ Wraps multiple operations in a single transaction.
240
+
241
+ Usage:
242
+ with DatabaseManager.transaction() as cursor:
243
+ cursor.execute("INSERT INTO table1 ...")
244
+ cursor.execute("INSERT INTO table2 ...")
245
+ # Auto-commits on success, auto-rollbacks on exception
246
+ """
247
+ with get_db_connection() as conn:
248
+ cursor = conn.cursor(cursor_factory=RealDictCursor)
249
+ try:
250
+ yield cursor
251
+ if not conn.closed:
252
+ conn.commit()
253
+ logger.debug("Transaction committed successfully")
254
+ except Exception as e:
255
+ if not conn.closed:
256
+ try:
257
+ conn.rollback()
258
+ logger.warning(f"Transaction rolled back due to error: {str(e)}")
259
+ except (psycopg2.OperationalError, psycopg2.InterfaceError) as rollback_error:
260
+ logger.error(f"Could not rollback transaction: {str(rollback_error)}")
261
+ raise
262
+ finally:
263
+ try:
264
+ cursor.close()
265
+ except Exception as close_error:
266
+ logger.warning(f"Error closing transaction cursor: {str(close_error)}")
267
+
186
268
  @staticmethod
187
269
  def health_check() -> dict:
188
270
  """Perform database health check"""
@@ -190,7 +272,7 @@ class DatabaseManager:
190
272
  with get_db_cursor() as cursor:
191
273
  cursor.execute("SELECT version(), current_database(), current_user")
192
274
  result = cursor.fetchone()
193
-
275
+
194
276
  if result:
195
277
  # Handle RealDictRow (dictionary-like) result
196
278
  if hasattr(result, 'get'):
@@ -1,8 +1,8 @@
1
1
  from fastapi import APIRouter
2
- from src.entities.shared.shared_response import Respons
3
- from src.configs.settings import db_settings
4
- from src.configs.database import DatabaseManager
5
- from src.configs.logging import get_logger
2
+ from .sh_response import Respons
3
+ from ..configs.settings import db_settings
4
+ from ..configs.database import DatabaseManager
5
+ from ..configs.logging import get_logger
6
6
 
7
7
  health_check_router = APIRouter(tags=["Health Path"])
8
8
  logger = get_logger("health")
@@ -1,16 +1,16 @@
1
- from src.trovesuite.notification.notification_write_dto import (
1
+ from .notification_write_dto import (
2
2
  NotificationEmailControllerWriteDto,
3
3
  NotificationSMSControllerWriteDto
4
4
  )
5
- from src.trovesuite.notification.notification_read_dto import (
5
+ from .notification_read_dto import (
6
6
  NotificationEmailControllerReadDto,
7
7
  NotificationSMSControllerReadDto
8
8
  )
9
- from src.trovesuite.notification.notification_service import NotificationService
10
- from src.trovesuite.entities.sh_response import Respons
9
+ from .notification_service import NotificationService
10
+ from ..entities.sh_response import Respons
11
11
  from fastapi import APIRouter
12
12
 
13
- notification_router = APIRouter()
13
+ notification_router = APIRouter(tags=["Notification"])
14
14
 
15
15
  @notification_router.post("/send_email", response_model=Respons[NotificationEmailControllerReadDto])
16
16
  async def send_email(data: NotificationEmailControllerWriteDto):
@@ -1,4 +1,4 @@
1
- from src.trovesuite.notification.notification_base import (
1
+ from .notification_base import (
2
2
  NotificationEmailBase,
3
3
  NotificationSMSBase
4
4
  )
@@ -1,12 +1,12 @@
1
1
  import smtplib
2
2
  from email.mime.text import MIMEText
3
3
  from email.mime.multipart import MIMEMultipart
4
- from src.trovesuite.entities.sh_response import Respons
5
- from src.trovesuite.notification.notification_read_dto import (
4
+ from ..entities.sh_response import Respons
5
+ from .notification_read_dto import (
6
6
  NotificationEmailServiceReadDto,
7
7
  NotificationSMSServiceReadDto
8
8
  )
9
- from src.trovesuite.notification.notification_write_dto import (
9
+ from .notification_write_dto import (
10
10
  NotificationEmailServiceWriteDto,
11
11
  NotificationSMSServiceWriteDto
12
12
  )
@@ -1,5 +1,5 @@
1
1
  from pydantic import BaseModel
2
- from src.trovesuite.notification.notification_base import (
2
+ from .notification_base import (
3
3
  NotificationEmailBase,
4
4
  NotificationSMSBase
5
5
  )
@@ -0,0 +1,42 @@
1
+ """
2
+ TroveSuite Storage Service
3
+
4
+ Provides Azure Storage blob management capabilities for TroveSuite applications.
5
+ Includes container creation, file upload/download/update/delete, and presigned URL generation.
6
+ """
7
+
8
+ from .storage_service import StorageService
9
+ from .storage_write_dto import (
10
+ StorageContainerCreateServiceWriteDto,
11
+ StorageFileUploadServiceWriteDto,
12
+ StorageFileUpdateServiceWriteDto,
13
+ StorageFileDeleteServiceWriteDto,
14
+ StorageFileDownloadServiceWriteDto,
15
+ StorageFileUrlServiceWriteDto
16
+ )
17
+ from .storage_read_dto import (
18
+ StorageContainerCreateServiceReadDto,
19
+ StorageFileUploadServiceReadDto,
20
+ StorageFileUpdateServiceReadDto,
21
+ StorageFileDeleteServiceReadDto,
22
+ StorageFileDownloadServiceReadDto,
23
+ StorageFileUrlServiceReadDto
24
+ )
25
+
26
+ __all__ = [
27
+ "StorageService",
28
+ # Write DTOs
29
+ "StorageContainerCreateServiceWriteDto",
30
+ "StorageFileUploadServiceWriteDto",
31
+ "StorageFileUpdateServiceWriteDto",
32
+ "StorageFileDeleteServiceWriteDto",
33
+ "StorageFileDownloadServiceWriteDto",
34
+ "StorageFileUrlServiceWriteDto",
35
+ # Read DTOs
36
+ "StorageContainerCreateServiceReadDto",
37
+ "StorageFileUploadServiceReadDto",
38
+ "StorageFileUpdateServiceReadDto",
39
+ "StorageFileDeleteServiceReadDto",
40
+ "StorageFileDownloadServiceReadDto",
41
+ "StorageFileUrlServiceReadDto",
42
+ ]