trovesuite 1.0.1__py3-none-any.whl → 1.0.31__py3-none-any.whl

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.
@@ -0,0 +1,21 @@
1
+ from .notification_write_dto import (
2
+ NotificationEmailControllerWriteDto,
3
+ NotificationSMSControllerWriteDto
4
+ )
5
+ from .notification_read_dto import (
6
+ NotificationEmailControllerReadDto,
7
+ NotificationSMSControllerReadDto
8
+ )
9
+ from .notification_service import NotificationService
10
+ from ..entities.sh_response import Respons
11
+ from fastapi import APIRouter
12
+
13
+ notification_router = APIRouter(tags=["Notification"])
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 .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 ..entities.sh_response import Respons
5
+ from .notification_read_dto import (
6
+ NotificationEmailServiceReadDto,
7
+ NotificationSMSServiceReadDto
8
+ )
9
+ from .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 .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
@@ -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
+ ]
@@ -0,0 +1,63 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class StorageConnectionBase(BaseModel):
6
+ """Base model for Azure Storage connection using Managed Identity"""
7
+ storage_account_url: str # e.g., https://<account-name>.blob.core.windows.net
8
+ container_name: str
9
+ managed_identity_client_id: Optional[str] = None # Optional: For user-assigned managed identity
10
+
11
+
12
+ class StorageFileUploadBase(BaseModel):
13
+ """Base model for file upload operations"""
14
+ storage_account_url: str
15
+ container_name: str
16
+ file_content: bytes
17
+ blob_name: str
18
+ directory_path: Optional[str] = None
19
+ content_type: Optional[str] = None
20
+ managed_identity_client_id: Optional[str] = None
21
+
22
+
23
+ class StorageFileUpdateBase(BaseModel):
24
+ """Base model for file update operations"""
25
+ storage_account_url: str
26
+ container_name: str
27
+ blob_name: str
28
+ file_content: bytes
29
+ content_type: Optional[str] = None
30
+ managed_identity_client_id: Optional[str] = None
31
+
32
+
33
+ class StorageFileDeleteBase(BaseModel):
34
+ """Base model for file delete operations"""
35
+ storage_account_url: str
36
+ container_name: str
37
+ blob_name: str
38
+ managed_identity_client_id: Optional[str] = None
39
+
40
+
41
+ class StorageFileDownloadBase(BaseModel):
42
+ """Base model for file download operations"""
43
+ storage_account_url: str
44
+ container_name: str
45
+ blob_name: str
46
+ managed_identity_client_id: Optional[str] = None
47
+
48
+
49
+ class StorageFileUrlBase(BaseModel):
50
+ """Base model for getting presigned URL"""
51
+ storage_account_url: str
52
+ container_name: str
53
+ blob_name: str
54
+ expiry_hours: Optional[int] = 1
55
+ managed_identity_client_id: Optional[str] = None
56
+
57
+
58
+ class StorageContainerCreateBase(BaseModel):
59
+ """Base model for creating a container"""
60
+ storage_account_url: str
61
+ container_name: str
62
+ public_access: Optional[str] = None
63
+ managed_identity_client_id: Optional[str] = None
@@ -0,0 +1,198 @@
1
+ from typing import List
2
+ from fastapi import APIRouter, File, UploadFile, Form
3
+ from fastapi.responses import StreamingResponse
4
+ from io import BytesIO
5
+ from .storage_write_dto import (
6
+ StorageContainerCreateControllerWriteDto,
7
+ StorageFileUploadControllerWriteDto,
8
+ StorageFileUpdateControllerWriteDto,
9
+ StorageFileDeleteControllerWriteDto,
10
+ StorageFileDownloadControllerWriteDto,
11
+ StorageFileUrlControllerWriteDto
12
+ )
13
+ from .storage_read_dto import (
14
+ StorageContainerCreateControllerReadDto,
15
+ StorageFileUploadControllerReadDto,
16
+ StorageFileUpdateControllerReadDto,
17
+ StorageFileDeleteControllerReadDto,
18
+ StorageFileDownloadControllerReadDto,
19
+ StorageFileUrlControllerReadDto
20
+ )
21
+ from .storage_service import StorageService
22
+ from ..entities.sh_response import Respons
23
+
24
+ storage_router = APIRouter(tags=["File Storage"])
25
+
26
+
27
+ @storage_router.post("/create-container", response_model=Respons[StorageContainerCreateControllerReadDto])
28
+ async def create_container(data: StorageContainerCreateControllerWriteDto):
29
+ """
30
+ Create a new Azure Storage container.
31
+
32
+ Example request body:
33
+ {
34
+ "storage_account_url": "https://myaccount.blob.core.windows.net",
35
+ "container_name": "my-container",
36
+ "public_access": null,
37
+ "managed_identity_client_id": "your-client-id" // optional
38
+ }
39
+ """
40
+ return StorageService.create_container(data=data)
41
+
42
+
43
+ @storage_router.post("/upload", response_model=Respons[StorageFileUploadControllerReadDto])
44
+ async def upload_file(
45
+ storage_account_url: str = Form(...),
46
+ container_name: str = Form(...),
47
+ blob_name: str = Form(...),
48
+ file: UploadFile = File(...),
49
+ directory_path: str = Form(None),
50
+ managed_identity_client_id: str = Form(None)
51
+ ):
52
+ """
53
+ Upload a file to Azure Storage.
54
+
55
+ Use form-data with the following fields:
56
+ - storage_account_url: Your Azure storage URL
57
+ - container_name: Container name
58
+ - blob_name: Name for the blob
59
+ - file: The file to upload
60
+ - directory_path: Optional directory path (e.g., "uploads/2024")
61
+ - managed_identity_client_id: Optional client ID for user-assigned managed identity
62
+ """
63
+ content = await file.read()
64
+
65
+ upload_data = StorageFileUploadControllerWriteDto(
66
+ storage_account_url=storage_account_url,
67
+ container_name=container_name,
68
+ file_content=content,
69
+ blob_name=blob_name,
70
+ directory_path=directory_path,
71
+ content_type=file.content_type,
72
+ managed_identity_client_id=managed_identity_client_id
73
+ )
74
+
75
+ return StorageService.upload_file(data=upload_data)
76
+
77
+
78
+ @storage_router.put("/update", response_model=Respons[StorageFileUpdateControllerReadDto])
79
+ async def update_file(
80
+ storage_account_url: str = Form(...),
81
+ container_name: str = Form(...),
82
+ blob_name: str = Form(...),
83
+ file: UploadFile = File(...),
84
+ managed_identity_client_id: str = Form(None)
85
+ ):
86
+ """
87
+ Update an existing file in Azure Storage.
88
+
89
+ Use form-data with the following fields:
90
+ - storage_account_url: Your Azure storage URL
91
+ - container_name: Container name
92
+ - blob_name: Full blob name including path (e.g., "uploads/2024/file.pdf")
93
+ - file: The new file content
94
+ - managed_identity_client_id: Optional client ID for user-assigned managed identity
95
+ """
96
+ content = await file.read()
97
+
98
+ update_data = StorageFileUpdateControllerWriteDto(
99
+ storage_account_url=storage_account_url,
100
+ container_name=container_name,
101
+ blob_name=blob_name,
102
+ file_content=content,
103
+ content_type=file.content_type,
104
+ managed_identity_client_id=managed_identity_client_id
105
+ )
106
+
107
+ return StorageService.update_file(data=update_data)
108
+
109
+
110
+ @storage_router.delete("/delete", response_model=Respons[StorageFileDeleteControllerReadDto])
111
+ async def delete_file(data: StorageFileDeleteControllerWriteDto):
112
+ """
113
+ Delete a file from Azure Storage.
114
+
115
+ Example request body:
116
+ {
117
+ "storage_account_url": "https://myaccount.blob.core.windows.net",
118
+ "container_name": "my-container",
119
+ "blob_name": "uploads/2024/file.pdf",
120
+ "managed_identity_client_id": "your-client-id" // optional
121
+ }
122
+ """
123
+ return StorageService.delete_file(data=data)
124
+
125
+
126
+ @storage_router.delete("/delete-multiple", response_model=Respons[StorageFileDeleteControllerReadDto])
127
+ async def delete_multiple_files(
128
+ storage_account_url: str,
129
+ container_name: str,
130
+ blob_names: List[str],
131
+ managed_identity_client_id: str = None
132
+ ):
133
+ """
134
+ Delete multiple files from Azure Storage.
135
+
136
+ Example request body:
137
+ {
138
+ "storage_account_url": "https://myaccount.blob.core.windows.net",
139
+ "container_name": "my-container",
140
+ "blob_names": ["file1.pdf", "file2.pdf", "folder/file3.jpg"],
141
+ "managed_identity_client_id": "your-client-id" // optional
142
+ }
143
+ """
144
+ return StorageService.delete_multiple_files(
145
+ storage_account_url=storage_account_url,
146
+ container_name=container_name,
147
+ blob_names=blob_names,
148
+ managed_identity_client_id=managed_identity_client_id
149
+ )
150
+
151
+
152
+ @storage_router.post("/download")
153
+ async def download_file(data: StorageFileDownloadControllerWriteDto):
154
+ """
155
+ Download a file from Azure Storage.
156
+
157
+ Returns the file as a streaming response.
158
+
159
+ Example request body:
160
+ {
161
+ "storage_account_url": "https://myaccount.blob.core.windows.net",
162
+ "container_name": "my-container",
163
+ "blob_name": "uploads/2024/file.pdf",
164
+ "managed_identity_client_id": "your-client-id" // optional
165
+ }
166
+ """
167
+ result = StorageService.download_file(data=data)
168
+
169
+ if not result.success:
170
+ return result
171
+
172
+ file_data = result.data[0]
173
+
174
+ # Return as streaming response
175
+ return StreamingResponse(
176
+ BytesIO(file_data.content),
177
+ media_type=file_data.content_type or "application/octet-stream",
178
+ headers={
179
+ "Content-Disposition": f"attachment; filename={data.blob_name.split('/')[-1]}"
180
+ }
181
+ )
182
+
183
+
184
+ @storage_router.post("/get-url", response_model=Respons[StorageFileUrlControllerReadDto])
185
+ async def get_file_url(data: StorageFileUrlControllerWriteDto):
186
+ """
187
+ Generate a presigned URL for a file.
188
+
189
+ Example request body:
190
+ {
191
+ "storage_account_url": "https://myaccount.blob.core.windows.net",
192
+ "container_name": "my-container",
193
+ "blob_name": "uploads/2024/file.pdf",
194
+ "expiry_hours": 2,
195
+ "managed_identity_client_id": "your-client-id" // optional
196
+ }
197
+ """
198
+ return StorageService.get_file_url(data=data)
@@ -0,0 +1,74 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+
4
+
5
+ # Container Creation
6
+
7
+ class StorageContainerCreateControllerReadDto(BaseModel):
8
+ container_name: str
9
+ container_url: Optional[str] = None
10
+
11
+
12
+ class StorageContainerCreateServiceReadDto(StorageContainerCreateControllerReadDto):
13
+ pass
14
+
15
+
16
+ # File Upload
17
+
18
+ class StorageFileUploadControllerReadDto(BaseModel):
19
+ blob_name: str
20
+ blob_url: str
21
+ content_type: Optional[str] = None
22
+ size: Optional[int] = None
23
+
24
+
25
+ class StorageFileUploadServiceReadDto(StorageFileUploadControllerReadDto):
26
+ pass
27
+
28
+
29
+ # File Update
30
+
31
+ class StorageFileUpdateControllerReadDto(BaseModel):
32
+ blob_name: str
33
+ blob_url: str
34
+ updated_at: Optional[str] = None
35
+
36
+
37
+ class StorageFileUpdateServiceReadDto(StorageFileUpdateControllerReadDto):
38
+ pass
39
+
40
+
41
+ # File Delete
42
+
43
+ class StorageFileDeleteControllerReadDto(BaseModel):
44
+ blob_name: str
45
+ deleted: bool
46
+
47
+
48
+ class StorageFileDeleteServiceReadDto(StorageFileDeleteControllerReadDto):
49
+ pass
50
+
51
+
52
+ # File Download
53
+
54
+ class StorageFileDownloadControllerReadDto(BaseModel):
55
+ blob_name: str
56
+ content: bytes
57
+ content_type: Optional[str] = None
58
+ size: Optional[int] = None
59
+
60
+
61
+ class StorageFileDownloadServiceReadDto(StorageFileDownloadControllerReadDto):
62
+ pass
63
+
64
+
65
+ # File URL
66
+
67
+ class StorageFileUrlControllerReadDto(BaseModel):
68
+ blob_name: str
69
+ presigned_url: str
70
+ expires_in_hours: int
71
+
72
+
73
+ class StorageFileUrlServiceReadDto(StorageFileUrlControllerReadDto):
74
+ pass