trovesuite 1.0.3__tar.gz → 1.0.5__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 (38) hide show
  1. {trovesuite-1.0.3/src/trovesuite.egg-info → trovesuite-1.0.5}/PKG-INFO +126 -5
  2. {trovesuite-1.0.3 → trovesuite-1.0.5}/README.md +123 -4
  3. {trovesuite-1.0.3 → trovesuite-1.0.5}/pyproject.toml +5 -3
  4. {trovesuite-1.0.3 → trovesuite-1.0.5}/setup.py +1 -1
  5. trovesuite-1.0.5/src/trovesuite/__init__.py +18 -0
  6. trovesuite-1.0.5/src/trovesuite/auth/__init__.py +14 -0
  7. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/auth/auth_controller.py +2 -1
  8. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/auth/auth_read_dto.py +0 -1
  9. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/auth/auth_service.py +3 -3
  10. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/auth/auth_write_dto.py +1 -1
  11. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/configs/database.py +29 -17
  12. trovesuite-1.0.5/src/trovesuite/notification/__init__.py +14 -0
  13. trovesuite-1.0.5/src/trovesuite/notification/notification_base.py +13 -0
  14. trovesuite-1.0.5/src/trovesuite/notification/notification_controller.py +21 -0
  15. trovesuite-1.0.5/src/trovesuite/notification/notification_read_dto.py +21 -0
  16. trovesuite-1.0.5/src/trovesuite/notification/notification_service.py +73 -0
  17. trovesuite-1.0.5/src/trovesuite/notification/notification_write_dto.py +21 -0
  18. {trovesuite-1.0.3 → trovesuite-1.0.5/src/trovesuite.egg-info}/PKG-INFO +126 -5
  19. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite.egg-info/SOURCES.txt +6 -0
  20. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite.egg-info/requires.txt +2 -0
  21. trovesuite-1.0.3/src/trovesuite/__init__.py +0 -16
  22. trovesuite-1.0.3/src/trovesuite/auth/__init__.py +0 -17
  23. {trovesuite-1.0.3 → trovesuite-1.0.5}/LICENSE +0 -0
  24. {trovesuite-1.0.3 → trovesuite-1.0.5}/MANIFEST.in +0 -0
  25. {trovesuite-1.0.3 → trovesuite-1.0.5}/requirements.txt +0 -0
  26. {trovesuite-1.0.3 → trovesuite-1.0.5}/setup.cfg +0 -0
  27. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/auth/auth_base.py +0 -0
  28. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/configs/__init__.py +0 -0
  29. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/configs/logging.py +0 -0
  30. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/configs/settings.py +0 -0
  31. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/entities/__init__.py +0 -0
  32. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/entities/health.py +0 -0
  33. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/entities/sh_response.py +0 -0
  34. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/utils/__init__.py +0 -0
  35. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite/utils/helper.py +0 -0
  36. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite.egg-info/dependency_links.txt +0 -0
  37. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite.egg-info/not-zip-safe +0 -0
  38. {trovesuite-1.0.3 → trovesuite-1.0.5}/src/trovesuite.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trovesuite
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: TroveSuite services package providing authentication, authorization, notifications, 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
@@ -34,6 +34,8 @@ Requires-Dist: python-multipart>=0.0.6
34
34
  Requires-Dist: python-jose[cryptography]>=3.3.0
35
35
  Requires-Dist: passlib[bcrypt]>=1.7.4
36
36
  Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
37
+ Requires-Dist: uvicorn<0.39.0,>=0.38.0
38
+ Requires-Dist: pyjwt<3.0.0,>=2.10.1
37
39
  Provides-Extra: dev
38
40
  Requires-Dist: pytest>=8.4.2; extra == "dev"
39
41
  Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
@@ -117,10 +119,26 @@ poetry install --with dev
117
119
 
118
120
  ## Quick Start
119
121
 
122
+ ### Import Patterns
123
+
124
+ The package provides clean, simplified import patterns:
125
+
126
+ ```python
127
+ # Import services and DTOs from main package
128
+ from trovesuite import AuthService, NotificationService
129
+ from trovesuite.auth import AuthServiceWriteDto
130
+ from trovesuite.notification import NotificationEmailServiceWriteDto, NotificationSMSServiceWriteDto
131
+
132
+ # Or import everything you need in one line
133
+ from trovesuite import AuthService, NotificationService
134
+ from trovesuite.auth import AuthServiceWriteDto
135
+ from trovesuite.notification import NotificationEmailServiceWriteDto
136
+ ```
137
+
120
138
  ### Basic Usage
121
139
 
122
140
  ```python
123
- from trovesuite import AuthService
141
+ from trovesuite import AuthService, NotificationService
124
142
  from trovesuite.configs.settings import db_settings
125
143
 
126
144
  # Configure your database settings
@@ -135,7 +153,7 @@ db_settings.SECRET_KEY = "your-secret-key"
135
153
  auth_service = AuthService()
136
154
 
137
155
  # Authorize a user
138
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
156
+ from trovesuite.auth import AuthServiceWriteDto
139
157
  auth_data = AuthServiceWriteDto(user_id="user123", tenant="tenant456")
140
158
  result = AuthService.authorize(auth_data)
141
159
 
@@ -147,6 +165,30 @@ else:
147
165
  print(f"Authorization failed: {result.detail}")
148
166
  ```
149
167
 
168
+ ### Notification Service Usage
169
+
170
+ ```python
171
+ from trovesuite import NotificationService
172
+ from trovesuite.notification import NotificationEmailServiceWriteDto
173
+
174
+ # Send an email notification
175
+ email_data = NotificationEmailServiceWriteDto(
176
+ sender_email="your-email@gmail.com",
177
+ receiver_email=["recipient1@example.com", "recipient2@example.com"],
178
+ password="your-app-password", # Gmail app password
179
+ subject="Test Notification",
180
+ text_message="This is a plain text message",
181
+ html_message="<h1>This is an HTML message</h1><p>With rich formatting!</p>"
182
+ )
183
+
184
+ result = NotificationService.send_email(email_data)
185
+
186
+ if result.success:
187
+ print(f"Email sent successfully: {result.detail}")
188
+ else:
189
+ print(f"Email failed: {result.error}")
190
+ ```
191
+
150
192
  ### JWT Token Decoding
151
193
 
152
194
  ```python
@@ -164,7 +206,7 @@ async def protected_route(token: str = Depends(oauth2_scheme)):
164
206
  tenant_id = user_data["tenant_id"]
165
207
 
166
208
  # Authorize user
167
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
209
+ from trovesuite.auth import AuthServiceWriteDto
168
210
  auth_data = AuthServiceWriteDto(user_id=user_id, tenant=tenant_id)
169
211
  auth_result = AuthService.authorize(auth_data)
170
212
  return auth_result
@@ -393,8 +435,84 @@ Check if user has all of the required permissions.
393
435
  **Returns:**
394
436
  - `bool`: True if user has all of the required permissions
395
437
 
438
+ ### NotificationService
439
+
440
+ #### `send_email(data: NotificationEmailServiceWriteDto) -> Respons[NotificationEmailServiceReadDto]`
441
+
442
+ Sends an email notification via Gmail SMTP. Supports both plain text and HTML email bodies.
443
+
444
+ **Parameters:**
445
+ - `data`: Email notification data including sender, recipients, subject, and message content
446
+
447
+ **Returns:**
448
+ - `Respons[NotificationEmailServiceReadDto]`: Email sending result with success status and details
449
+
450
+ **Example:**
451
+ ```python
452
+ from trovesuite import NotificationService
453
+ from trovesuite.notification import NotificationEmailServiceWriteDto
454
+
455
+ email_data = NotificationEmailServiceWriteDto(
456
+ sender_email="sender@gmail.com",
457
+ receiver_email=["user1@example.com", "user2@example.com"],
458
+ password="your-gmail-app-password",
459
+ subject="Welcome to TroveSuite",
460
+ text_message="Welcome! This is a plain text message.",
461
+ html_message="<h1>Welcome!</h1><p>This is an <strong>HTML</strong> message.</p>"
462
+ )
463
+
464
+ result = NotificationService.send_email(email_data)
465
+ if result.success:
466
+ print("Email sent successfully!")
467
+ else:
468
+ print(f"Failed to send email: {result.error}")
469
+ ```
470
+
471
+ #### `send_sms(data: NotificationSMSServiceWriteDto) -> Respons[NotificationSMSServiceReadDto]`
472
+
473
+ Sends an SMS notification (currently not implemented).
474
+
475
+ **Parameters:**
476
+ - `data`: SMS notification data
477
+
478
+ **Returns:**
479
+ - `Respons[NotificationSMSServiceReadDto]`: SMS sending result
480
+
396
481
  ### Data Models
397
482
 
483
+ #### `NotificationEmailServiceWriteDto`
484
+
485
+ ```python
486
+ class NotificationEmailServiceWriteDto(BaseModel):
487
+ sender_email: str
488
+ receiver_email: Union[str, List[str]] # Single email or list of emails
489
+ password: str # Gmail app password
490
+ subject: str
491
+ text_message: str
492
+ html_message: Optional[str] = None # Optional HTML content
493
+ ```
494
+
495
+ #### `NotificationEmailServiceReadDto`
496
+
497
+ ```python
498
+ class NotificationEmailServiceReadDto(BaseModel):
499
+ pass # Empty response model for email service
500
+ ```
501
+
502
+ #### `NotificationSMSServiceWriteDto`
503
+
504
+ ```python
505
+ class NotificationSMSServiceWriteDto(BaseModel):
506
+ pass # To be implemented for SMS functionality
507
+ ```
508
+
509
+ #### `NotificationSMSServiceReadDto`
510
+
511
+ ```python
512
+ class NotificationSMSServiceReadDto(BaseModel):
513
+ pass # To be implemented for SMS functionality
514
+ ```
515
+
398
516
  #### `AuthServiceReadDto`
399
517
 
400
518
  ```python
@@ -563,7 +681,7 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
563
681
 
564
682
  ### 1.0.8
565
683
  - Restructured package for direct service imports
566
- - Added notification services
684
+ - Added comprehensive notification services with email support
567
685
  - Excluded controllers from package build
568
686
  - Updated import paths for better usability
569
687
  - JWT token validation
@@ -572,3 +690,6 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
572
690
  - PostgreSQL database integration
573
691
  - Comprehensive logging
574
692
  - Azure integration support
693
+ - Email notification service with Gmail SMTP support
694
+ - Support for both plain text and HTML email content
695
+ - Multiple recipient support for email notifications
@@ -66,10 +66,26 @@ poetry install --with dev
66
66
 
67
67
  ## Quick Start
68
68
 
69
+ ### Import Patterns
70
+
71
+ The package provides clean, simplified import patterns:
72
+
73
+ ```python
74
+ # Import services and DTOs from main package
75
+ from trovesuite import AuthService, NotificationService
76
+ from trovesuite.auth import AuthServiceWriteDto
77
+ from trovesuite.notification import NotificationEmailServiceWriteDto, NotificationSMSServiceWriteDto
78
+
79
+ # Or import everything you need in one line
80
+ from trovesuite import AuthService, NotificationService
81
+ from trovesuite.auth import AuthServiceWriteDto
82
+ from trovesuite.notification import NotificationEmailServiceWriteDto
83
+ ```
84
+
69
85
  ### Basic Usage
70
86
 
71
87
  ```python
72
- from trovesuite import AuthService
88
+ from trovesuite import AuthService, NotificationService
73
89
  from trovesuite.configs.settings import db_settings
74
90
 
75
91
  # Configure your database settings
@@ -84,7 +100,7 @@ db_settings.SECRET_KEY = "your-secret-key"
84
100
  auth_service = AuthService()
85
101
 
86
102
  # Authorize a user
87
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
103
+ from trovesuite.auth import AuthServiceWriteDto
88
104
  auth_data = AuthServiceWriteDto(user_id="user123", tenant="tenant456")
89
105
  result = AuthService.authorize(auth_data)
90
106
 
@@ -96,6 +112,30 @@ else:
96
112
  print(f"Authorization failed: {result.detail}")
97
113
  ```
98
114
 
115
+ ### Notification Service Usage
116
+
117
+ ```python
118
+ from trovesuite import NotificationService
119
+ from trovesuite.notification import NotificationEmailServiceWriteDto
120
+
121
+ # Send an email notification
122
+ email_data = NotificationEmailServiceWriteDto(
123
+ sender_email="your-email@gmail.com",
124
+ receiver_email=["recipient1@example.com", "recipient2@example.com"],
125
+ password="your-app-password", # Gmail app password
126
+ subject="Test Notification",
127
+ text_message="This is a plain text message",
128
+ html_message="<h1>This is an HTML message</h1><p>With rich formatting!</p>"
129
+ )
130
+
131
+ result = NotificationService.send_email(email_data)
132
+
133
+ if result.success:
134
+ print(f"Email sent successfully: {result.detail}")
135
+ else:
136
+ print(f"Email failed: {result.error}")
137
+ ```
138
+
99
139
  ### JWT Token Decoding
100
140
 
101
141
  ```python
@@ -113,7 +153,7 @@ async def protected_route(token: str = Depends(oauth2_scheme)):
113
153
  tenant_id = user_data["tenant_id"]
114
154
 
115
155
  # Authorize user
116
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
156
+ from trovesuite.auth import AuthServiceWriteDto
117
157
  auth_data = AuthServiceWriteDto(user_id=user_id, tenant=tenant_id)
118
158
  auth_result = AuthService.authorize(auth_data)
119
159
  return auth_result
@@ -342,8 +382,84 @@ Check if user has all of the required permissions.
342
382
  **Returns:**
343
383
  - `bool`: True if user has all of the required permissions
344
384
 
385
+ ### NotificationService
386
+
387
+ #### `send_email(data: NotificationEmailServiceWriteDto) -> Respons[NotificationEmailServiceReadDto]`
388
+
389
+ Sends an email notification via Gmail SMTP. Supports both plain text and HTML email bodies.
390
+
391
+ **Parameters:**
392
+ - `data`: Email notification data including sender, recipients, subject, and message content
393
+
394
+ **Returns:**
395
+ - `Respons[NotificationEmailServiceReadDto]`: Email sending result with success status and details
396
+
397
+ **Example:**
398
+ ```python
399
+ from trovesuite import NotificationService
400
+ from trovesuite.notification import NotificationEmailServiceWriteDto
401
+
402
+ email_data = NotificationEmailServiceWriteDto(
403
+ sender_email="sender@gmail.com",
404
+ receiver_email=["user1@example.com", "user2@example.com"],
405
+ password="your-gmail-app-password",
406
+ subject="Welcome to TroveSuite",
407
+ text_message="Welcome! This is a plain text message.",
408
+ html_message="<h1>Welcome!</h1><p>This is an <strong>HTML</strong> message.</p>"
409
+ )
410
+
411
+ result = NotificationService.send_email(email_data)
412
+ if result.success:
413
+ print("Email sent successfully!")
414
+ else:
415
+ print(f"Failed to send email: {result.error}")
416
+ ```
417
+
418
+ #### `send_sms(data: NotificationSMSServiceWriteDto) -> Respons[NotificationSMSServiceReadDto]`
419
+
420
+ Sends an SMS notification (currently not implemented).
421
+
422
+ **Parameters:**
423
+ - `data`: SMS notification data
424
+
425
+ **Returns:**
426
+ - `Respons[NotificationSMSServiceReadDto]`: SMS sending result
427
+
345
428
  ### Data Models
346
429
 
430
+ #### `NotificationEmailServiceWriteDto`
431
+
432
+ ```python
433
+ class NotificationEmailServiceWriteDto(BaseModel):
434
+ sender_email: str
435
+ receiver_email: Union[str, List[str]] # Single email or list of emails
436
+ password: str # Gmail app password
437
+ subject: str
438
+ text_message: str
439
+ html_message: Optional[str] = None # Optional HTML content
440
+ ```
441
+
442
+ #### `NotificationEmailServiceReadDto`
443
+
444
+ ```python
445
+ class NotificationEmailServiceReadDto(BaseModel):
446
+ pass # Empty response model for email service
447
+ ```
448
+
449
+ #### `NotificationSMSServiceWriteDto`
450
+
451
+ ```python
452
+ class NotificationSMSServiceWriteDto(BaseModel):
453
+ pass # To be implemented for SMS functionality
454
+ ```
455
+
456
+ #### `NotificationSMSServiceReadDto`
457
+
458
+ ```python
459
+ class NotificationSMSServiceReadDto(BaseModel):
460
+ pass # To be implemented for SMS functionality
461
+ ```
462
+
347
463
  #### `AuthServiceReadDto`
348
464
 
349
465
  ```python
@@ -512,7 +628,7 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
512
628
 
513
629
  ### 1.0.8
514
630
  - Restructured package for direct service imports
515
- - Added notification services
631
+ - Added comprehensive notification services with email support
516
632
  - Excluded controllers from package build
517
633
  - Updated import paths for better usability
518
634
  - JWT token validation
@@ -521,3 +637,6 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
521
637
  - PostgreSQL database integration
522
638
  - Comprehensive logging
523
639
  - Azure integration support
640
+ - Email notification service with Gmail SMTP support
641
+ - Support for both plain text and HTML email content
642
+ - Multiple recipient support for email notifications
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "trovesuite"
7
- version = "1.0.3"
7
+ version = "1.0.5"
8
8
  description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
9
9
  authors = ["brightgclt <brightgclt@gmail.com>"]
10
10
  license = "MIT"
@@ -56,7 +56,7 @@ Documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
56
56
 
57
57
  [project]
58
58
  name = "trovesuite"
59
- version = "1.0.3"
59
+ version = "1.0.5"
60
60
  description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
61
61
  readme = "README.md"
62
62
  license = {text = "MIT"}
@@ -89,7 +89,9 @@ dependencies = [
89
89
  "python-multipart>=0.0.6",
90
90
  "python-jose[cryptography]>=3.3.0",
91
91
  "passlib[bcrypt]>=1.7.4",
92
- "passlib[argon2] (>=1.7.4,<2.0.0)"
92
+ "passlib[argon2] (>=1.7.4,<2.0.0)",
93
+ "uvicorn (>=0.38.0,<0.39.0)",
94
+ "pyjwt (>=2.10.1,<3.0.0)"
93
95
 
94
96
  ]
95
97
 
@@ -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.3",
18
+ version="1.0.5",
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",
@@ -0,0 +1,18 @@
1
+ """
2
+ TroveSuite Package
3
+
4
+ A comprehensive authentication, authorization, and notification service for ERP systems.
5
+ Provides JWT token validation, user authorization, permission checking, and notification capabilities.
6
+ """
7
+
8
+ from .auth import AuthService
9
+ from .notification import NotificationService
10
+
11
+ __version__ = "1.0.8"
12
+ __author__ = "Bright Debrah Owusu"
13
+ __email__ = "owusu.debrah@deladetech.com"
14
+
15
+ __all__ = [
16
+ "AuthService",
17
+ "NotificationService"
18
+ ]
@@ -0,0 +1,14 @@
1
+ """
2
+ TroveSuite Auth Module
3
+
4
+ Authentication and authorization services for ERP systems.
5
+ """
6
+
7
+ from .auth_service import AuthService
8
+ from .auth_write_dto import AuthServiceWriteDto
9
+
10
+ __all__ = [
11
+ "AuthService",
12
+ "AuthServiceWriteDto"
13
+ ]
14
+
@@ -2,9 +2,10 @@ from fastapi import APIRouter
2
2
  from src.trovesuite.auth.auth_write_dto import AuthControllerWriteDto
3
3
  from src.trovesuite.auth.auth_read_dto import AuthControllerReadDto
4
4
  from src.trovesuite.auth.auth_service import AuthService
5
+ from src.trovesuite.entities.sh_response import Respons
5
6
 
6
7
  auth_router = APIRouter()
7
8
 
8
- @auth_router.post("/auth", response_model=AuthControllerReadDto)
9
+ @auth_router.post("/auth", response_model=Respons[AuthControllerReadDto])
9
10
  async def authorize(data: AuthControllerWriteDto):
10
11
  return AuthService.authorize(data=data)
@@ -11,7 +11,6 @@ class AuthControllerReadDto(BaseModel):
11
11
  role_id: Optional[str] = None
12
12
  tenant_id: Optional[str] = None
13
13
  permissions: Optional[List[str]] = None
14
- shared_resource_id: Optional[str] = None
15
14
  resource_id: Optional[str] = None
16
15
 
17
16
  class AuthServiceReadDto(AuthControllerReadDto):
@@ -48,7 +48,7 @@ class AuthService:
48
48
 
49
49
  user_id: str = data.user_id
50
50
  tenant_id: str = data.tenant_id
51
-
51
+
52
52
  """Check if a user is authorized based on login settings and roles"""
53
53
  # Input validation
54
54
  if not user_id or not isinstance(user_id, str):
@@ -68,14 +68,14 @@ class AuthService:
68
68
  status_code=400,
69
69
  error="INVALID_TENANT_ID"
70
70
  )
71
-
71
+
72
72
  try:
73
73
 
74
74
  is_tenant_verified = DatabaseManager.execute_query(
75
75
  f"SELECT is_verified FROM {db_settings.MAIN_TENANTS_TABLE} WHERE delete_status = 'NOT_DELETED' AND id = %s",
76
76
  (tenant_id,),
77
77
  )
78
-
78
+
79
79
  if not is_tenant_verified or len(is_tenant_verified) == 0:
80
80
  logger.warning("Login failed - tenant not found: %s", tenant_id)
81
81
  return Respons[AuthServiceReadDto](
@@ -4,7 +4,7 @@ from pydantic import BaseModel
4
4
 
5
5
  class AuthControllerWriteDto(BaseModel):
6
6
  user_id: Optional[str] = None
7
- tenant: Optional[str] = None
7
+ tenant_id: Optional[str] = None
8
8
 
9
9
  class AuthServiceWriteDto(AuthControllerWriteDto):
10
10
  pass
@@ -13,7 +13,6 @@ logger = get_logger("database")
13
13
 
14
14
  # Database connection pool
15
15
  _connection_pool: Optional[psycopg2.pool.ThreadedConnectionPool] = None
16
- _sqlmodel_engine = None
17
16
 
18
17
 
19
18
  class DatabaseConfig:
@@ -21,16 +20,20 @@ class DatabaseConfig:
21
20
 
22
21
  def __init__(self):
23
22
  self.settings = db_settings
23
+ self.database_url = self.settings.database_url
24
24
  self.pool_size = 5
25
25
  self.max_overflow = 10
26
-
27
- @property
28
- def database_url(self):
29
- """Get database URL (lazy evaluation)"""
30
- return self.settings.database_url
31
26
 
32
27
  def get_connection_params(self) -> dict:
33
28
  """Get database connection parameters"""
29
+ if self.settings.DATABASE_URL:
30
+ # Use full DATABASE_URL if available
31
+ return {
32
+ "dsn": self.settings.DATABASE_URL,
33
+ "cursor_factory": RealDictCursor
34
+ }
35
+
36
+ # fallback to individual DB_* variables
34
37
  return {
35
38
  "host": self.settings.DB_HOST,
36
39
  "port": self.settings.DB_PORT,
@@ -98,18 +101,19 @@ def get_connection_pool() -> psycopg2.pool.ThreadedConnectionPool:
98
101
  """Get the database connection pool"""
99
102
  global _connection_pool
100
103
  if _connection_pool is None:
101
- raise Exception("Database not initialized. Call initialize_database() first.")
104
+ error_msg = (
105
+ "Database not initialized. This usually means:\n"
106
+ "1. Missing or incorrect .env file in app/ directory\n"
107
+ "2. Database credentials are wrong\n"
108
+ "3. Database container is not running\n"
109
+ "4. Database initialization failed during startup\n"
110
+ "Please check the startup logs for more details."
111
+ )
112
+ logger.error(error_msg)
113
+ raise Exception(error_msg)
102
114
  return _connection_pool
103
115
 
104
116
 
105
- def get_sqlmodel_engine():
106
- """Get the SQLModel engine"""
107
- global _sqlmodel_engine
108
- if _sqlmodel_engine is None:
109
- raise Exception("Database not initialized. Call initialize_database() first.")
110
- return _sqlmodel_engine
111
-
112
-
113
117
  @contextmanager
114
118
  def get_db_connection():
115
119
  """Get a database connection from the pool (context manager)"""
@@ -217,5 +221,13 @@ class DatabaseManager:
217
221
  }
218
222
 
219
223
 
220
- # Database initialization is done lazily when needed
221
- # Call initialize_database() explicitly when you need to use the database
224
+ # Database initialization on module import
225
+ try:
226
+ initialize_database()
227
+ except Exception as e:
228
+ logger.error(f"Failed to initialize database on startup: {str(e)}")
229
+ logger.error("⚠️ CRITICAL: Application started without database connection!")
230
+ logger.error("⚠️ Please check your .env file and database configuration")
231
+ logger.error("⚠️ The application will not function properly without a database connection")
232
+ # Don't raise here to allow the app to start even if DB is unavailable
233
+ # But log it clearly so it's obvious what's wrong
@@ -0,0 +1,14 @@
1
+ """
2
+ TroveSuite Notification Service
3
+
4
+ Provides email and SMS notification capabilities for TroveSuite applications.
5
+ """
6
+
7
+ from .notification_service import NotificationService
8
+ from .notification_write_dto import NotificationEmailServiceWriteDto, NotificationSMSServiceWriteDto
9
+
10
+ __all__ = [
11
+ "NotificationService",
12
+ "NotificationEmailServiceWriteDto",
13
+ "NotificationSMSServiceWriteDto"
14
+ ]
@@ -0,0 +1,13 @@
1
+ from typing import List, Optional, Union
2
+ from pydantic import BaseModel
3
+
4
+ class NotificationEmailBase(BaseModel):
5
+ sender_email: str
6
+ receiver_email: Union[str, List[str]]
7
+ password: str
8
+ subject: str
9
+ text_message: str
10
+ html_message: Optional[str] = None
11
+
12
+ class NotificationSMSBase(BaseModel):
13
+ pass
@@ -0,0 +1,21 @@
1
+ from src.trovesuite.notification.notification_write_dto import (
2
+ NotificationEmailControllerWriteDto,
3
+ NotificationSMSControllerWriteDto
4
+ )
5
+ from src.trovesuite.notification.notification_read_dto import (
6
+ NotificationEmailControllerReadDto,
7
+ NotificationSMSControllerReadDto
8
+ )
9
+ from src.trovesuite.notification.notification_service import NotificationService
10
+ from src.trovesuite.entities.sh_response import Respons
11
+ from fastapi import APIRouter
12
+
13
+ notification_router = APIRouter()
14
+
15
+ @notification_router.post("/send_email", response_model=Respons[NotificationEmailControllerReadDto])
16
+ async def send_email(data: NotificationEmailControllerWriteDto):
17
+ return NotificationService.send_email(data=data)
18
+
19
+ @notification_router.post("/send_sms", response_model=Respons[NotificationSMSControllerReadDto])
20
+ async def send_sms(data: NotificationSMSControllerWriteDto):
21
+ return NotificationService.send_sms(data=data)
@@ -0,0 +1,21 @@
1
+ from src.trovesuite.notification.notification_base import (
2
+ NotificationEmailBase,
3
+ NotificationSMSBase
4
+ )
5
+ from pydantic import BaseModel
6
+
7
+ # EMAIL Notification
8
+
9
+ class NotificationEmailControllerReadDto(NotificationEmailBase):
10
+ pass
11
+
12
+ class NotificationEmailServiceReadDto(BaseModel):
13
+ pass
14
+
15
+ # SMS Notification
16
+
17
+ class NotificationSMSControllerReadDto(NotificationSMSBase):
18
+ pass
19
+
20
+ class NotificationSMSServiceReadDto(BaseModel):
21
+ pass
@@ -0,0 +1,73 @@
1
+ import smtplib
2
+ from email.mime.text import MIMEText
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 (
6
+ NotificationEmailServiceReadDto,
7
+ NotificationSMSServiceReadDto
8
+ )
9
+ from src.trovesuite.notification.notification_write_dto import (
10
+ NotificationEmailServiceWriteDto,
11
+ NotificationSMSServiceWriteDto
12
+ )
13
+
14
+ class NotificationService:
15
+
16
+ @staticmethod
17
+ def send_email(data: NotificationEmailServiceWriteDto) -> Respons[NotificationEmailServiceReadDto]:
18
+ """
19
+ Send an email (single or multiple recipients) via Gmail SMTP.
20
+ Supports both plain text and HTML email bodies.
21
+ """
22
+
23
+ # Extract input data
24
+ receiver_email = data.receiver_email
25
+ text_message = data.text_message
26
+ html_message = getattr(data, "html_message", None)
27
+ sender_email = data.sender_email
28
+ password = data.password
29
+ subject = data.subject
30
+
31
+ # Allow single email or list
32
+ if isinstance(receiver_email, str):
33
+ receiver_email = [receiver_email]
34
+
35
+ # Create the email container
36
+ message = MIMEMultipart("alternative")
37
+ message["From"] = sender_email
38
+ message["To"] = ", ".join(receiver_email)
39
+ message["Subject"] = subject
40
+
41
+ # Attach plain text
42
+ message.attach(MIMEText(text_message, "plain"))
43
+
44
+ # Attach HTML if provided
45
+ if html_message:
46
+ message.attach(MIMEText(html_message, "html"))
47
+
48
+ try:
49
+ with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
50
+ server.login(sender_email, password)
51
+ server.sendmail(sender_email, receiver_email, message.as_string())
52
+
53
+ return Respons[NotificationEmailServiceReadDto](
54
+ detail=f"Email successfully sent to {len(receiver_email)} recipient(s)",
55
+ error=None,
56
+ data=[],
57
+ status_code=200,
58
+ success=True,
59
+ )
60
+
61
+ except Exception as e:
62
+ print(e)
63
+ return Respons[NotificationEmailServiceReadDto](
64
+ detail="An error occurred while sending the email",
65
+ error=str(e),
66
+ data=[],
67
+ status_code=500,
68
+ success=False,
69
+ )
70
+
71
+ @staticmethod
72
+ def send_sms(data: NotificationSMSServiceWriteDto) -> Respons[NotificationSMSServiceReadDto]:
73
+ pass
@@ -0,0 +1,21 @@
1
+ from pydantic import BaseModel
2
+ from src.trovesuite.notification.notification_base import (
3
+ NotificationEmailBase,
4
+ NotificationSMSBase
5
+ )
6
+
7
+ # Email Notification
8
+
9
+ class NotificationEmailControllerWriteDto(NotificationEmailBase):
10
+ pass
11
+
12
+ class NotificationEmailServiceWriteDto(NotificationEmailControllerWriteDto):
13
+ pass
14
+
15
+ # SMS Notification
16
+
17
+ class NotificationSMSControllerWriteDto(NotificationSMSBase):
18
+ pass
19
+
20
+ class NotificationSMSServiceWriteDto(BaseModel):
21
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trovesuite
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: TroveSuite services package providing authentication, authorization, notifications, 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
@@ -34,6 +34,8 @@ Requires-Dist: python-multipart>=0.0.6
34
34
  Requires-Dist: python-jose[cryptography]>=3.3.0
35
35
  Requires-Dist: passlib[bcrypt]>=1.7.4
36
36
  Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
37
+ Requires-Dist: uvicorn<0.39.0,>=0.38.0
38
+ Requires-Dist: pyjwt<3.0.0,>=2.10.1
37
39
  Provides-Extra: dev
38
40
  Requires-Dist: pytest>=8.4.2; extra == "dev"
39
41
  Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
@@ -117,10 +119,26 @@ poetry install --with dev
117
119
 
118
120
  ## Quick Start
119
121
 
122
+ ### Import Patterns
123
+
124
+ The package provides clean, simplified import patterns:
125
+
126
+ ```python
127
+ # Import services and DTOs from main package
128
+ from trovesuite import AuthService, NotificationService
129
+ from trovesuite.auth import AuthServiceWriteDto
130
+ from trovesuite.notification import NotificationEmailServiceWriteDto, NotificationSMSServiceWriteDto
131
+
132
+ # Or import everything you need in one line
133
+ from trovesuite import AuthService, NotificationService
134
+ from trovesuite.auth import AuthServiceWriteDto
135
+ from trovesuite.notification import NotificationEmailServiceWriteDto
136
+ ```
137
+
120
138
  ### Basic Usage
121
139
 
122
140
  ```python
123
- from trovesuite import AuthService
141
+ from trovesuite import AuthService, NotificationService
124
142
  from trovesuite.configs.settings import db_settings
125
143
 
126
144
  # Configure your database settings
@@ -135,7 +153,7 @@ db_settings.SECRET_KEY = "your-secret-key"
135
153
  auth_service = AuthService()
136
154
 
137
155
  # Authorize a user
138
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
156
+ from trovesuite.auth import AuthServiceWriteDto
139
157
  auth_data = AuthServiceWriteDto(user_id="user123", tenant="tenant456")
140
158
  result = AuthService.authorize(auth_data)
141
159
 
@@ -147,6 +165,30 @@ else:
147
165
  print(f"Authorization failed: {result.detail}")
148
166
  ```
149
167
 
168
+ ### Notification Service Usage
169
+
170
+ ```python
171
+ from trovesuite import NotificationService
172
+ from trovesuite.notification import NotificationEmailServiceWriteDto
173
+
174
+ # Send an email notification
175
+ email_data = NotificationEmailServiceWriteDto(
176
+ sender_email="your-email@gmail.com",
177
+ receiver_email=["recipient1@example.com", "recipient2@example.com"],
178
+ password="your-app-password", # Gmail app password
179
+ subject="Test Notification",
180
+ text_message="This is a plain text message",
181
+ html_message="<h1>This is an HTML message</h1><p>With rich formatting!</p>"
182
+ )
183
+
184
+ result = NotificationService.send_email(email_data)
185
+
186
+ if result.success:
187
+ print(f"Email sent successfully: {result.detail}")
188
+ else:
189
+ print(f"Email failed: {result.error}")
190
+ ```
191
+
150
192
  ### JWT Token Decoding
151
193
 
152
194
  ```python
@@ -164,7 +206,7 @@ async def protected_route(token: str = Depends(oauth2_scheme)):
164
206
  tenant_id = user_data["tenant_id"]
165
207
 
166
208
  # Authorize user
167
- from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
209
+ from trovesuite.auth import AuthServiceWriteDto
168
210
  auth_data = AuthServiceWriteDto(user_id=user_id, tenant=tenant_id)
169
211
  auth_result = AuthService.authorize(auth_data)
170
212
  return auth_result
@@ -393,8 +435,84 @@ Check if user has all of the required permissions.
393
435
  **Returns:**
394
436
  - `bool`: True if user has all of the required permissions
395
437
 
438
+ ### NotificationService
439
+
440
+ #### `send_email(data: NotificationEmailServiceWriteDto) -> Respons[NotificationEmailServiceReadDto]`
441
+
442
+ Sends an email notification via Gmail SMTP. Supports both plain text and HTML email bodies.
443
+
444
+ **Parameters:**
445
+ - `data`: Email notification data including sender, recipients, subject, and message content
446
+
447
+ **Returns:**
448
+ - `Respons[NotificationEmailServiceReadDto]`: Email sending result with success status and details
449
+
450
+ **Example:**
451
+ ```python
452
+ from trovesuite import NotificationService
453
+ from trovesuite.notification import NotificationEmailServiceWriteDto
454
+
455
+ email_data = NotificationEmailServiceWriteDto(
456
+ sender_email="sender@gmail.com",
457
+ receiver_email=["user1@example.com", "user2@example.com"],
458
+ password="your-gmail-app-password",
459
+ subject="Welcome to TroveSuite",
460
+ text_message="Welcome! This is a plain text message.",
461
+ html_message="<h1>Welcome!</h1><p>This is an <strong>HTML</strong> message.</p>"
462
+ )
463
+
464
+ result = NotificationService.send_email(email_data)
465
+ if result.success:
466
+ print("Email sent successfully!")
467
+ else:
468
+ print(f"Failed to send email: {result.error}")
469
+ ```
470
+
471
+ #### `send_sms(data: NotificationSMSServiceWriteDto) -> Respons[NotificationSMSServiceReadDto]`
472
+
473
+ Sends an SMS notification (currently not implemented).
474
+
475
+ **Parameters:**
476
+ - `data`: SMS notification data
477
+
478
+ **Returns:**
479
+ - `Respons[NotificationSMSServiceReadDto]`: SMS sending result
480
+
396
481
  ### Data Models
397
482
 
483
+ #### `NotificationEmailServiceWriteDto`
484
+
485
+ ```python
486
+ class NotificationEmailServiceWriteDto(BaseModel):
487
+ sender_email: str
488
+ receiver_email: Union[str, List[str]] # Single email or list of emails
489
+ password: str # Gmail app password
490
+ subject: str
491
+ text_message: str
492
+ html_message: Optional[str] = None # Optional HTML content
493
+ ```
494
+
495
+ #### `NotificationEmailServiceReadDto`
496
+
497
+ ```python
498
+ class NotificationEmailServiceReadDto(BaseModel):
499
+ pass # Empty response model for email service
500
+ ```
501
+
502
+ #### `NotificationSMSServiceWriteDto`
503
+
504
+ ```python
505
+ class NotificationSMSServiceWriteDto(BaseModel):
506
+ pass # To be implemented for SMS functionality
507
+ ```
508
+
509
+ #### `NotificationSMSServiceReadDto`
510
+
511
+ ```python
512
+ class NotificationSMSServiceReadDto(BaseModel):
513
+ pass # To be implemented for SMS functionality
514
+ ```
515
+
398
516
  #### `AuthServiceReadDto`
399
517
 
400
518
  ```python
@@ -563,7 +681,7 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
563
681
 
564
682
  ### 1.0.8
565
683
  - Restructured package for direct service imports
566
- - Added notification services
684
+ - Added comprehensive notification services with email support
567
685
  - Excluded controllers from package build
568
686
  - Updated import paths for better usability
569
687
  - JWT token validation
@@ -572,3 +690,6 @@ For support, email brightgclt@gmail.com or create a work item in the [Azure DevO
572
690
  - PostgreSQL database integration
573
691
  - Comprehensive logging
574
692
  - Azure integration support
693
+ - Email notification service with Gmail SMTP support
694
+ - Support for both plain text and HTML email content
695
+ - Multiple recipient support for email notifications
@@ -24,5 +24,11 @@ src/trovesuite/configs/settings.py
24
24
  src/trovesuite/entities/__init__.py
25
25
  src/trovesuite/entities/health.py
26
26
  src/trovesuite/entities/sh_response.py
27
+ src/trovesuite/notification/__init__.py
28
+ src/trovesuite/notification/notification_base.py
29
+ src/trovesuite/notification/notification_controller.py
30
+ src/trovesuite/notification/notification_read_dto.py
31
+ src/trovesuite/notification/notification_service.py
32
+ src/trovesuite/notification/notification_write_dto.py
27
33
  src/trovesuite/utils/__init__.py
28
34
  src/trovesuite/utils/helper.py
@@ -6,6 +6,8 @@ python-multipart>=0.0.6
6
6
  python-jose[cryptography]>=3.3.0
7
7
  passlib[bcrypt]>=1.7.4
8
8
  passlib[argon2]<2.0.0,>=1.7.4
9
+ uvicorn<0.39.0,>=0.38.0
10
+ pyjwt<3.0.0,>=2.10.1
9
11
 
10
12
  [dev]
11
13
  pytest>=8.4.2
@@ -1,16 +0,0 @@
1
- """
2
- TroveSuite Auth Package
3
-
4
- A comprehensive authentication and authorization service for ERP systems.
5
- Provides JWT token validation, user authorization, and permission checking.
6
- """
7
-
8
- from .auth import AuthService
9
-
10
- __version__ = "1.0.8"
11
- __author__ = "Bright Debrah Owusu"
12
- __email__ = "owusu.debrah@deladetech.com"
13
-
14
- __all__ = [
15
- "AuthService"
16
- ]
@@ -1,17 +0,0 @@
1
- """
2
- TroveSuite Auth Module
3
-
4
- Authentication and authorization services for ERP systems.
5
- """
6
-
7
- from .auth_service import AuthService
8
- from .auth_base import AuthBase
9
- from .auth_read_dto import AuthServiceReadDto, AuthControllerReadDto
10
-
11
- __all__ = [
12
- "AuthService",
13
- "AuthBase",
14
- "AuthServiceReadDto",
15
- "AuthControllerReadDto"
16
- ]
17
-
File without changes
File without changes
File without changes
File without changes