ipulse-shared-core-ftredge 19.0.1__py3-none-any.whl → 22.1.1__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.

Potentially problematic release.


This version of ipulse-shared-core-ftredge might be problematic. Click here for more details.

Files changed (47) hide show
  1. ipulse_shared_core_ftredge/cache/shared_cache.py +1 -2
  2. ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +4 -4
  3. ipulse_shared_core_ftredge/exceptions/base_exceptions.py +23 -0
  4. ipulse_shared_core_ftredge/models/__init__.py +3 -7
  5. ipulse_shared_core_ftredge/models/base_data_model.py +17 -19
  6. ipulse_shared_core_ftredge/models/catalog/__init__.py +10 -0
  7. ipulse_shared_core_ftredge/models/catalog/subscriptionplan.py +273 -0
  8. ipulse_shared_core_ftredge/models/catalog/usertype.py +170 -0
  9. ipulse_shared_core_ftredge/models/user/__init__.py +5 -0
  10. ipulse_shared_core_ftredge/models/user/user_permissions.py +66 -0
  11. ipulse_shared_core_ftredge/models/{subscription.py → user/user_subscription.py} +66 -20
  12. ipulse_shared_core_ftredge/models/{user_auth.py → user/userauth.py} +19 -10
  13. ipulse_shared_core_ftredge/models/{user_profile.py → user/userprofile.py} +53 -21
  14. ipulse_shared_core_ftredge/models/user/userstatus.py +430 -0
  15. ipulse_shared_core_ftredge/monitoring/__init__.py +2 -2
  16. ipulse_shared_core_ftredge/monitoring/tracemon.py +320 -0
  17. ipulse_shared_core_ftredge/services/__init__.py +11 -13
  18. ipulse_shared_core_ftredge/services/base/__init__.py +3 -1
  19. ipulse_shared_core_ftredge/services/base/base_firestore_service.py +73 -14
  20. ipulse_shared_core_ftredge/services/{cache_aware_firestore_service.py → base/cache_aware_firestore_service.py} +46 -32
  21. ipulse_shared_core_ftredge/services/catalog/__init__.py +14 -0
  22. ipulse_shared_core_ftredge/services/catalog/catalog_subscriptionplan_service.py +273 -0
  23. ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py +307 -0
  24. ipulse_shared_core_ftredge/services/charging_processors.py +25 -25
  25. ipulse_shared_core_ftredge/services/user/__init__.py +5 -25
  26. ipulse_shared_core_ftredge/services/user/firebase_auth_admin_helpers.py +160 -0
  27. ipulse_shared_core_ftredge/services/user/user_core_service.py +423 -515
  28. ipulse_shared_core_ftredge/services/user/user_multistep_operations.py +726 -0
  29. ipulse_shared_core_ftredge/services/user/user_permissions_operations.py +392 -0
  30. ipulse_shared_core_ftredge/services/user/user_subscription_operations.py +484 -0
  31. ipulse_shared_core_ftredge/services/user/userauth_operations.py +928 -0
  32. ipulse_shared_core_ftredge/services/user/userprofile_operations.py +166 -0
  33. ipulse_shared_core_ftredge/services/user/userstatus_operations.py +212 -0
  34. ipulse_shared_core_ftredge/services/{charging_service.py → user_charging_service.py} +9 -9
  35. {ipulse_shared_core_ftredge-19.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/METADATA +3 -4
  36. ipulse_shared_core_ftredge-22.1.1.dist-info/RECORD +51 -0
  37. ipulse_shared_core_ftredge/models/user_status.py +0 -495
  38. ipulse_shared_core_ftredge/monitoring/microservmon.py +0 -483
  39. ipulse_shared_core_ftredge/services/user/iam_management_operations.py +0 -326
  40. ipulse_shared_core_ftredge/services/user/subscription_management_operations.py +0 -384
  41. ipulse_shared_core_ftredge/services/user/user_account_operations.py +0 -479
  42. ipulse_shared_core_ftredge/services/user/user_auth_operations.py +0 -305
  43. ipulse_shared_core_ftredge/services/user/user_holistic_operations.py +0 -436
  44. ipulse_shared_core_ftredge-19.0.1.dist-info/RECORD +0 -41
  45. {ipulse_shared_core_ftredge-19.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/WHEEL +0 -0
  46. {ipulse_shared_core_ftredge-19.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/licenses/LICENCE +0 -0
  47. {ipulse_shared_core_ftredge-19.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,166 @@
1
+ """
2
+ User Profile Operations - CRUD operations for UserProfile
3
+ """
4
+ import os
5
+ import logging
6
+ from typing import Dict, Any, Optional
7
+ from google.cloud import firestore
8
+ from pydantic import ValidationError as PydanticValidationError
9
+
10
+ from ...models import UserProfile
11
+ from ...exceptions import ResourceNotFoundError, UserProfileError
12
+ from ..base import BaseFirestoreService
13
+
14
+
15
+ class UserprofileOperations:
16
+ """
17
+ Handles CRUD operations for UserProfile documents
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ firestore_client: firestore.Client,
23
+ logger: Optional[logging.Logger] = None,
24
+ timeout: float = 10.0,
25
+ profile_collection: Optional[str] = None,
26
+ ):
27
+ collection_name = profile_collection or UserProfile.get_collection_name()
28
+ self.db_service = BaseFirestoreService[UserProfile](
29
+ db=firestore_client,
30
+ collection_name=collection_name,
31
+ resource_type="UserProfile",
32
+ model_class=UserProfile,
33
+ logger=logger,
34
+ timeout=timeout
35
+ )
36
+ self.logger = logger or logging.getLogger(__name__)
37
+ self.profile_collection_name = collection_name
38
+
39
+ # Archival configuration
40
+ self.archive_profile_on_delete = os.getenv('ARCHIVE_PROFILE_ON_DELETE', 'true').lower() == 'true'
41
+ self.archive_profile_collection_name = os.getenv(
42
+ 'ARCHIVE_PROFILE_COLLECTION_NAME',
43
+ f"~archive_{self.profile_collection_name}"
44
+ )
45
+
46
+ async def get_userprofile(self, user_uid: str) -> Optional[UserProfile]:
47
+ """Fetches a user profile from Firestore."""
48
+ self.logger.info(f"Fetching user profile for UID: {user_uid}")
49
+ try:
50
+ profile_data = await self.db_service.get_document(f"{UserProfile.OBJ_REF}_{user_uid}", convert_to_model=True)
51
+ if profile_data and isinstance(profile_data, UserProfile):
52
+ return profile_data
53
+ return None
54
+ except ResourceNotFoundError:
55
+ self.logger.warning(f"UserProfile not found for UID: {user_uid}")
56
+ return None
57
+ except Exception as e:
58
+ self.logger.error("Error fetching user profile for %s: %s", user_uid, e, exc_info=True)
59
+ raise UserProfileError(
60
+ detail=f"Failed to fetch user profile: {str(e)}",
61
+ user_uid=user_uid,
62
+ operation="get_userprofile",
63
+ original_error=e
64
+ ) from e
65
+
66
+ async def create_userprofile(self, userprofile: UserProfile, creator_uid: Optional[str] = None) -> UserProfile:
67
+ """Creates a new user profile in Firestore."""
68
+ self.logger.info(f"Creating user profile for UID: {userprofile.user_uid}")
69
+ try:
70
+ doc_id = f"{UserProfile.OBJ_REF}_{userprofile.user_uid}"
71
+ effective_creator_uid = creator_uid or userprofile.user_uid
72
+ await self.db_service.create_document(doc_id, userprofile.model_dump(exclude_none=True), creator_uid=effective_creator_uid)
73
+ self.logger.info(f"Successfully created user profile for UID: {userprofile.user_uid}")
74
+ return userprofile
75
+ except Exception as e:
76
+ self.logger.error(f"Error creating user profile for {userprofile.user_uid}: {e}", exc_info=True)
77
+ raise UserProfileError(
78
+ detail=f"Failed to create user profile: {str(e)}",
79
+ user_uid=userprofile.user_uid,
80
+ operation="create_userprofile",
81
+ original_error=e
82
+ )
83
+
84
+ async def update_userprofile(self, user_uid: str, profile_data: Dict[str, Any], updater_uid: str) -> UserProfile:
85
+ """Updates an existing user profile in Firestore."""
86
+ self.logger.info(f"Updating user profile for UID: {user_uid}")
87
+ try:
88
+ doc_id = f"{UserProfile.OBJ_REF}_{user_uid}"
89
+ await self.db_service.update_document(doc_id, profile_data, updater_uid)
90
+ updated_profile = await self.get_userprofile(user_uid)
91
+ if not updated_profile:
92
+ raise ResourceNotFoundError(
93
+ resource_type="UserProfile",
94
+ resource_id=doc_id
95
+ )
96
+ self.logger.info(f"Successfully updated user profile for UID: {user_uid}")
97
+ return updated_profile
98
+ except ResourceNotFoundError as e:
99
+ self.logger.error(f"Cannot update non-existent user profile for {user_uid}")
100
+ raise e
101
+ except Exception as e:
102
+ self.logger.error(f"Error updating user profile for {user_uid}: {e}", exc_info=True)
103
+ raise UserProfileError(
104
+ detail=f"Failed to update user profile: {str(e)}",
105
+ user_uid=user_uid,
106
+ operation="update_userprofile",
107
+ original_error=e
108
+ )
109
+
110
+ async def delete_userprofile(self, user_uid: str, updater_uid: str = "system_deletion", archive: bool = True) -> bool:
111
+ """Delete (archive and delete) user profile"""
112
+ profile_doc_id = f"{UserProfile.OBJ_REF}_{user_uid}"
113
+ should_archive = archive if archive is not None else self.archive_profile_on_delete
114
+
115
+ try:
116
+ # Get profile data for archival
117
+ profile_data = await self.db_service.get_document(profile_doc_id, convert_to_model=False)
118
+
119
+ if profile_data:
120
+ # Ensure we have a dict for archival
121
+ profile_dict = profile_data if isinstance(profile_data, dict) else profile_data.__dict__
122
+
123
+ # Archive if enabled
124
+ if should_archive:
125
+ await self.db_service.archive_document(
126
+ document_data=profile_dict,
127
+ doc_id=profile_doc_id,
128
+ archive_collection=self.archive_profile_collection_name,
129
+ archived_by=updater_uid
130
+ )
131
+
132
+ # Delete the original document
133
+ await self.db_service.delete_document(profile_doc_id)
134
+ self.logger.info(f"Successfully deleted user profile: {profile_doc_id}")
135
+ return True
136
+ else:
137
+ self.logger.warning(f"User profile {profile_doc_id} not found for deletion")
138
+ return True # Consider non-existent as successfully deleted
139
+
140
+ except Exception as e:
141
+ self.logger.error(f"Failed to delete user profile {profile_doc_id}: {e}", exc_info=True)
142
+ raise UserProfileError(
143
+ detail=f"Failed to delete user profile: {str(e)}",
144
+ user_uid=user_uid,
145
+ operation="delete_userprofile",
146
+ original_error=e
147
+ )
148
+
149
+
150
+
151
+ async def userprofile_exists(self, user_uid: str) -> bool:
152
+ """Check if a user profile exists."""
153
+ return await self.db_service.document_exists(f"{UserProfile.OBJ_REF}_{user_uid}")
154
+
155
+ async def validate_userprofile_data(
156
+ self,
157
+ profile_data: Optional[Dict[str, Any]] = None,
158
+ ) -> tuple[bool, list[str]]:
159
+ """Validate user profile data without creating documents"""
160
+ errors = []
161
+ if profile_data:
162
+ try:
163
+ UserProfile(**profile_data)
164
+ except PydanticValidationError as e:
165
+ errors.append(f"Profile validation error: {str(e)}")
166
+ return len(errors) == 0, errors
@@ -0,0 +1,212 @@
1
+ """
2
+ Userstatus Operations - CRUD operations for Userstatus
3
+ """
4
+ import os
5
+ import logging
6
+ from typing import Dict, Any, Optional
7
+
8
+ from google.cloud import firestore
9
+ from pydantic import ValidationError as PydanticValidationError
10
+
11
+ from ...models import UserStatus
12
+ from ...exceptions import ResourceNotFoundError, UserStatusError
13
+ from ..base import BaseFirestoreService
14
+
15
+
16
+ class UserstatusOperations:
17
+ """
18
+ Handles CRUD operations for Userstatus documents
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ firestore_client: firestore.Client,
24
+ logger: Optional[logging.Logger] = None,
25
+ timeout: float = 10.0,
26
+ status_collection: Optional[str] = None
27
+ ):
28
+ self.db = firestore_client
29
+ self.logger = logger or logging.getLogger(__name__)
30
+ self.timeout = timeout
31
+
32
+ self.status_collection_name = status_collection or UserStatus.get_collection_name()
33
+
34
+ # Archival configuration
35
+ self.archive_userstatus_on_delete = os.getenv('ARCHIVE_USERSTATUS_ON_DELETE', 'true').lower() == 'true'
36
+ self.archive_userstatus_collection_name = os.getenv(
37
+ 'ARCHIVE_USERSTATUS_COLLECTION_NAME',
38
+ "~archive_core_user_userstatuss"
39
+ )
40
+
41
+ # Initialize DB service
42
+ self._status_db_service = BaseFirestoreService[UserStatus](
43
+ db=self.db,
44
+ collection_name=self.status_collection_name,
45
+ resource_type=UserStatus.OBJ_REF,
46
+ model_class=UserStatus,
47
+ logger=self.logger,
48
+ timeout=self.timeout
49
+ )
50
+
51
+ async def get_userstatus(self, user_uid: str, convert_to_model: bool = True) -> Optional[UserStatus]:
52
+ """Retrieve a user status by UID"""
53
+ userstatus_id = f"{UserStatus.OBJ_REF}_{user_uid}"
54
+
55
+ try:
56
+ userstatus = await self._status_db_service.get_document(
57
+ userstatus_id,
58
+ convert_to_model=convert_to_model
59
+ )
60
+ if userstatus:
61
+ self.logger.debug("Successfully retrieved user status for %s", user_uid)
62
+ # Always return a UserStatus model to match the return type
63
+ if isinstance(userstatus, dict):
64
+ return UserStatus(**userstatus)
65
+ return userstatus
66
+ else:
67
+ self.logger.debug("User status not found for %s", user_uid)
68
+ return None
69
+
70
+ except ResourceNotFoundError:
71
+ self.logger.debug("User status not found for %s", user_uid)
72
+ return None
73
+ except Exception as e:
74
+ self.logger.error("Failed to fetch user status for %s: %s", user_uid, str(e), exc_info=True)
75
+ raise UserStatusError(
76
+ detail=f"Failed to fetch user status: {str(e)}",
77
+ user_uid=user_uid,
78
+ operation="get_userstatus",
79
+ original_error=e
80
+ ) from e
81
+
82
+ async def create_userstatus(self, userstatus: UserStatus, creator_uid: Optional[str] = None) -> UserStatus:
83
+ """Create a new user status"""
84
+ self.logger.info(f"Creating user status for UID: {userstatus.user_uid}")
85
+ try:
86
+ doc_id = f"{UserStatus.OBJ_REF}_{userstatus.user_uid}"
87
+ effective_creator_uid = creator_uid or userstatus.user_uid
88
+ await self._status_db_service.create_document(doc_id, userstatus, effective_creator_uid)
89
+ self.logger.info("Successfully created user status for UID: %s", userstatus.user_uid)
90
+ return userstatus
91
+ except Exception as e:
92
+ self.logger.error("Error creating user status for %s: %s", userstatus.user_uid, e, exc_info=True)
93
+ raise UserStatusError(
94
+ detail=f"Failed to create user status: {str(e)}",
95
+ user_uid=userstatus.user_uid,
96
+ operation="create_userstatus",
97
+ original_error=e
98
+ ) from e
99
+
100
+ async def update_userstatus(self, user_uid: str, status_data: Dict[str, Any], updater_uid: str) -> UserStatus:
101
+ """Update a user status"""
102
+ userstatus_id = f"{UserStatus.OBJ_REF}_{user_uid}"
103
+
104
+ # Remove system fields that shouldn't be updated
105
+ update_data = status_data.copy()
106
+ update_data.pop('user_uid', None)
107
+ update_data.pop('id', None)
108
+ update_data.pop('created_at', None)
109
+ update_data.pop('created_by', None)
110
+
111
+ try:
112
+ updated_doc_dict = await self._status_db_service.update_document(
113
+ userstatus_id,
114
+ update_data,
115
+ updater_uid=updater_uid
116
+ )
117
+ self.logger.info("Userstatus for %s updated successfully by %s", user_uid, updater_uid)
118
+ return UserStatus(**updated_doc_dict)
119
+ except ResourceNotFoundError as exc:
120
+ raise UserStatusError(
121
+ detail="User status not found",
122
+ user_uid=user_uid,
123
+ operation="update_userstatus"
124
+ ) from exc
125
+ except Exception as e:
126
+ self.logger.error("Error updating Userstatus for %s: %s", user_uid, str(e), exc_info=True)
127
+ raise UserStatusError(
128
+ detail=f"Failed to update user status: {str(e)}",
129
+ user_uid=user_uid,
130
+ operation="update_userstatus",
131
+ original_error=e
132
+ ) from e
133
+
134
+ async def delete_userstatus(self, user_uid: str, updater_uid: str = "system_deletion", archive: Optional[bool] = True) -> bool:
135
+ """Delete (archive and delete) user status"""
136
+ status_doc_id = f"{UserStatus.OBJ_REF}_{user_uid}"
137
+ should_archive = archive if archive is not None else self.archive_userstatus_on_delete
138
+
139
+ try:
140
+ # Get status data for archival
141
+ status_data = await self._status_db_service.get_document(status_doc_id, convert_to_model=False)
142
+
143
+ if status_data:
144
+ # Ensure we have a dict for archival
145
+ status_dict = status_data if isinstance(status_data, dict) else status_data.__dict__
146
+
147
+ # Archive if enabled
148
+ if should_archive:
149
+ await self._status_db_service.archive_document(
150
+ document_data=status_dict,
151
+ doc_id=status_doc_id,
152
+ archive_collection=self.archive_userstatus_collection_name,
153
+ archived_by=updater_uid
154
+ )
155
+
156
+ # Delete the original document
157
+ await self._status_db_service.delete_document(status_doc_id)
158
+ self.logger.info("Successfully deleted user status: %s", status_doc_id)
159
+ return True
160
+ else:
161
+ self.logger.warning("User status %s not found for deletion", status_doc_id)
162
+ return True # Consider non-existent as successfully deleted
163
+
164
+ except ResourceNotFoundError:
165
+ self.logger.debug("User status %s not found for deletion (idempotent)", status_doc_id)
166
+ return True # Idempotent - already "deleted"
167
+ except Exception as e:
168
+ self.logger.error("Failed to delete user status %s: %s", status_doc_id, str(e), exc_info=True)
169
+ raise UserStatusError(
170
+ detail=f"Failed to delete user status: {str(e)}",
171
+ user_uid=user_uid,
172
+ operation="delete_userstatus",
173
+ original_error=e
174
+ ) from e
175
+
176
+ async def validate_userstatus_data(
177
+ self,
178
+ status_data: Optional[Dict[str, Any]] = None
179
+ ) -> tuple[bool, list[str]]:
180
+ """Validate user status data without creating documents"""
181
+ errors = []
182
+ if status_data:
183
+ try:
184
+ UserStatus(**status_data)
185
+ except PydanticValidationError as e:
186
+ errors.append(f"Status validation error: {str(e)}")
187
+ return len(errors) == 0, errors
188
+
189
+ async def validate_and_cleanup_user_permissions(
190
+ self, user_uid: str, updater_uid: str, delete_expired: bool = True
191
+ ) -> int:
192
+ """Validate and clean up expired IAM permissions for a user."""
193
+ userstatus = await self.get_userstatus(user_uid)
194
+ if not userstatus:
195
+ self.logger.warning("Userstatus not found for %s, cannot validate permissions.", user_uid)
196
+ return 0
197
+
198
+ removed_count = userstatus.cleanup_expired_permissions()
199
+
200
+ if removed_count > 0 and delete_expired:
201
+ await self.update_userstatus(
202
+ user_uid,
203
+ userstatus.model_dump(exclude_none=True),
204
+ updater_uid=updater_uid
205
+ )
206
+ self.logger.info("Removed %d expired permissions for user %s.", removed_count, user_uid)
207
+
208
+ return removed_count
209
+
210
+ async def userstatus_exists(self, user_uid: str) -> bool:
211
+ """Check if a user status exists."""
212
+ return await self._status_db_service.document_exists(f"{UserStatus.OBJ_REF}_{user_uid}")
@@ -4,12 +4,12 @@ from typing import Dict, Any, Optional, Tuple
4
4
  from datetime import datetime, timezone
5
5
  from google.cloud import firestore
6
6
  from ipulse_shared_core_ftredge.exceptions import ServiceError, ResourceNotFoundError, ValidationError
7
- from ipulse_shared_core_ftredge.models.user_status import UserStatus
7
+ from ipulse_shared_core_ftredge.models import UserStatus
8
8
 
9
9
  # Default Firestore timeout if not provided by the consuming application
10
10
  DEFAULT_FIRESTORE_TIMEOUT = 15.0
11
11
 
12
- class ChargingService:
12
+ class UserChargingService:
13
13
  """
14
14
  Service class for charging operations.
15
15
  Designed to be project-agnostic and directly uses UserStatus model constants.
@@ -32,13 +32,13 @@ class ChargingService:
32
32
  self.db = db
33
33
  # Use UserStatus constants directly
34
34
  self.users_status_collection_name = UserStatus.COLLECTION_NAME
35
- self.user_status_doc_prefix = f"{UserStatus.OBJ_REF}_" # Append underscore to OBJ_REF
35
+ self.userstatus_doc_prefix = f"{UserStatus.OBJ_REF}_" # Append underscore to OBJ_REF
36
36
  self.logger = logger or logging.getLogger(__name__)
37
37
  self.timeout = firestore_timeout
38
38
 
39
39
  self.logger.info(
40
40
  f"ChargingService initialized using UserStatus constants. Collection: {self.users_status_collection_name}, "
41
- f"Doc Prefix: {self.user_status_doc_prefix}, Timeout: {self.timeout}s"
41
+ f"Doc Prefix: {self.userstatus_doc_prefix}, Timeout: {self.timeout}s"
42
42
  )
43
43
 
44
44
  async def verify_credits(
@@ -57,7 +57,7 @@ class ChargingService:
57
57
  (keys: 'sbscrptn_based_insight_credits', 'extra_insight_credits')
58
58
 
59
59
  Returns:
60
- Tuple of (has_enough_credits, user_status_data) where user_status_data
60
+ Tuple of (has_enough_credits, userstatus_data) where userstatus_data
61
61
  will be a dict with keys 'sbscrptn_based_insight_credits' and 'extra_insight_credits'.
62
62
 
63
63
  Raises:
@@ -174,7 +174,7 @@ class ChargingService:
174
174
 
175
175
 
176
176
  try:
177
- userstatus_id = f"{self.user_status_doc_prefix}{user_uid}"
177
+ userstatus_id = f"{self.userstatus_doc_prefix}{user_uid}"
178
178
  user_ref = self.db.collection(self.users_status_collection_name).document(userstatus_id)
179
179
 
180
180
  transaction = self.db.transaction()
@@ -277,7 +277,7 @@ class ChargingService:
277
277
  async def _get_userstatus(self, user_uid: str) -> Dict[str, Any]:
278
278
  """Get a user's status document."""
279
279
  try:
280
- userstatus_id = f"{self.user_status_doc_prefix}{user_uid}"
280
+ userstatus_id = f"{self.userstatus_doc_prefix}{user_uid}"
281
281
  doc_ref = self.db.collection(self.users_status_collection_name).document(userstatus_id)
282
282
 
283
283
  # Using the timeout value set during initialization
@@ -285,7 +285,7 @@ class ChargingService:
285
285
 
286
286
  if not doc.exists:
287
287
  raise ResourceNotFoundError(
288
- resource_type="user_status", # Generic resource type
288
+ resource_type="userstatus", # Generic resource type
289
289
  resource_id=userstatus_id,
290
290
  additional_info={"collection": self.users_status_collection_name}
291
291
  )
@@ -299,7 +299,7 @@ class ChargingService:
299
299
  raise ServiceError(
300
300
  operation="getting user status",
301
301
  error=e,
302
- resource_type="user_status",
302
+ resource_type="userstatus",
303
303
  resource_id=user_uid,
304
304
  additional_info={"collection": self.users_status_collection_name}
305
305
  ) from e
@@ -1,19 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipulse_shared_core_ftredge
3
- Version: 19.0.1
3
+ Version: 22.1.1
4
4
  Summary: Shared Core models and Logger util for the Pulse platform project. Using AI for financial advisory and investment management.
5
5
  Home-page: https://github.com/TheFutureEdge/ipulse_shared_core
6
6
  Author: Russlan Ramdowar
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: License :: OSI Approved :: MIT License
9
9
  Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.11
10
+ Requires-Python: >=3.12
11
11
  License-File: LICENCE
12
12
  Requires-Dist: pydantic[email]~=2.5
13
13
  Requires-Dist: python-dateutil~=2.8
14
14
  Requires-Dist: fastapi~=0.115.8
15
- Requires-Dist: pytest
16
- Requires-Dist: ipulse_shared_base_ftredge==7.2.0
15
+ Requires-Dist: ipulse_shared_base_ftredge==10.2.1
17
16
  Dynamic: author
18
17
  Dynamic: classifier
19
18
  Dynamic: home-page
@@ -0,0 +1,51 @@
1
+ ipulse_shared_core_ftredge/__init__.py,sha256=-KbdF_YW8pgf7pVv9qh_cA1xrNm_B9zigHYDo7ZA4eU,42
2
+ ipulse_shared_core_ftredge/cache/__init__.py,sha256=i2fPojmZiBwAoY5ovnnnME9USl4bi8MRPYkAgEfACfI,136
3
+ ipulse_shared_core_ftredge/cache/shared_cache.py,sha256=BDJtkTsdfmVjKaUkbBXOhJ2Oib7Li0UCsPjWX7FLIPU,12940
4
+ ipulse_shared_core_ftredge/dependencies/__init__.py,sha256=HGsR8HUguKTfjz_BorCILS4izX8CAjG-apE0kIPE0Yo,68
5
+ ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py,sha256=EFWyhoVOI0tGYOWqN5St4JNIy4cMwpxeBhKdjOwEfbg,1888
6
+ ipulse_shared_core_ftredge/dependencies/auth_protected_router.py,sha256=em5D5tE7OkgZmuCtYCKuUAnIZCgRJhCF8Ye5QmtGWlk,1807
7
+ ipulse_shared_core_ftredge/dependencies/authz_for_apis.py,sha256=TnJ2ubGUZRxRa6v-OLC2uCLzaoCuX1GkXSaFJMaYg0s,15793
8
+ ipulse_shared_core_ftredge/dependencies/firestore_client.py,sha256=VbTb121nsc9EZPd1RDEsHBLW5pIiVw6Wdo2JFL4afMg,714
9
+ ipulse_shared_core_ftredge/exceptions/__init__.py,sha256=Cb_RsIie4DbT_NLwFVwjw4riDKsNNRQEuAvHvYa-Zco,1038
10
+ ipulse_shared_core_ftredge/exceptions/base_exceptions.py,sha256=RuihEpgXdUIR2XEXaP6aJnepePbvZ2RZYQwcKfatGPw,4981
11
+ ipulse_shared_core_ftredge/exceptions/user_exceptions.py,sha256=I-nm21MKrUYEoybpRODeYNzc184HfgHvRZQm_xux4VY,6824
12
+ ipulse_shared_core_ftredge/models/__init__.py,sha256=oaBL_BZWd7hIDu2K7yxtVKxtkOD9UF9r9_V2ZIPQ8Yk,350
13
+ ipulse_shared_core_ftredge/models/base_api_response.py,sha256=OwuWI2PsMSLDkFt643u35ZhW5AHFEMMAGnGprmUO0fA,2380
14
+ ipulse_shared_core_ftredge/models/base_data_model.py,sha256=GZ7KTT5FanHTgvmaHHTxawzAJtuixkbyb-SuL-mjWys,2193
15
+ ipulse_shared_core_ftredge/models/catalog/__init__.py,sha256=9oKJ74_mTtmj-0iDnRBiPI8m8QJ2J9wvx4ZWaZw3zRk,208
16
+ ipulse_shared_core_ftredge/models/catalog/subscriptionplan.py,sha256=5q1yUwy_755UNt7FZ4Ez-1njF0APSyzmiLbhGP1f2g0,9069
17
+ ipulse_shared_core_ftredge/models/catalog/usertype.py,sha256=W42bis9l6_FL5Q3HY8pqxs1b0gkcIGgGYpLTOU6fYxE,6319
18
+ ipulse_shared_core_ftredge/models/user/__init__.py,sha256=TheOLldY6v-OK9i-A5mQNIxjHhBFpuOJ43mi-swcN_o,196
19
+ ipulse_shared_core_ftredge/models/user/user_permissions.py,sha256=CUWDBPLPmKN3o43BTZAt0zJvm_ekjJA46iV6rVNp-oc,2411
20
+ ipulse_shared_core_ftredge/models/user/user_subscription.py,sha256=f0EFPtIQU_KKB3LO1MKZYraaxSdJp-FqeZDPKs2VP6E,8587
21
+ ipulse_shared_core_ftredge/models/user/userauth.py,sha256=PbS-XSLxDl1feskI0iCziGvmMiLuF8o_ZTspAx1B0j0,3679
22
+ ipulse_shared_core_ftredge/models/user/userprofile.py,sha256=7VbE4qiKpDxZsNTk-IJKA32QxW0JOo8KWPkj8h9J2-Y,6945
23
+ ipulse_shared_core_ftredge/models/user/userstatus.py,sha256=NpSQWlbCnFRQ_Gnx7WLkyt3dKgRDf4-MRrJU5VYF7DA,16748
24
+ ipulse_shared_core_ftredge/monitoring/__init__.py,sha256=gUoJjT0wj-cQYnMWheWbh1mmRHmaeojmnBZTj7KPNus,61
25
+ ipulse_shared_core_ftredge/monitoring/tracemon.py,sha256=Trku0qrwWvEcvKsBWiYokd_G3fcH-5uP2wRVgcgIz_k,11596
26
+ ipulse_shared_core_ftredge/services/__init__.py,sha256=9AkMLCHNswhuNbQuJZaEVz4zt4F84PxfJLyU_bYk4Js,565
27
+ ipulse_shared_core_ftredge/services/charging_processors.py,sha256=9Re24dyXdjKYbqwx6uNLu3JBzIaw87TAV7Oe__M1QnA,16308
28
+ ipulse_shared_core_ftredge/services/user_charging_service.py,sha256=C3wMfgBXOz4RM1RLc7up2_pIPAnZv8ZYu-lLrkofTmc,14625
29
+ ipulse_shared_core_ftredge/services/base/__init__.py,sha256=zhyrHQMM0cLJr4spk2b6VsgJXuWBy7hUBzhrq_Seg9k,389
30
+ ipulse_shared_core_ftredge/services/base/base_firestore_service.py,sha256=xXmPfDEgBa4trWMJz1UBJRDLPxi8TtG2jDFbo_B28pw,19313
31
+ ipulse_shared_core_ftredge/services/base/cache_aware_firestore_service.py,sha256=ya5Asff9BQodYnJVAw6M_Pm8WtVRPpEK7izFlZ2MyjA,10016
32
+ ipulse_shared_core_ftredge/services/catalog/__init__.py,sha256=ctc2nDGwsW_Ji4lk9pys3oyNwR_V-gHSbSHawym5fKQ,385
33
+ ipulse_shared_core_ftredge/services/catalog/catalog_subscriptionplan_service.py,sha256=mbwCC1RnIZEav5i5sRGT15nxQJF0qMVA2LT8BAjmJuY,9264
34
+ ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py,sha256=pZdsRt9V96NcYTRremTf76fDL126lAiraxGs936i0aU,9959
35
+ ipulse_shared_core_ftredge/services/user/__init__.py,sha256=jmkD5XzAmaD8QV2UsgB5xynGcfsXliWtRtN2pt6kzbA,884
36
+ ipulse_shared_core_ftredge/services/user/firebase_auth_admin_helpers.py,sha256=7N-MWpCwzWCjIuIFXGv1sIjJJcU9M92nWOfRyMEuHbE,5430
37
+ ipulse_shared_core_ftredge/services/user/user_core_service.py,sha256=9nhFgFVPp188JMiZYINLwyHGtUYqtbHZhIR6cQMhj00,23648
38
+ ipulse_shared_core_ftredge/services/user/user_multistep_operations.py,sha256=hQ1-BygC2Cxin6AUsEVgEWfZg035v5yn__sAoPzy0Us,35543
39
+ ipulse_shared_core_ftredge/services/user/user_permissions_operations.py,sha256=FByszIWo-qooLVXFTw0tGLWksIJEqHUPc_ZGwue0_pM,15753
40
+ ipulse_shared_core_ftredge/services/user/user_subscription_operations.py,sha256=h5jz4jvnwocDIwHa5VUX3_u4Qzrkt5A1s06aL5Bafm0,21266
41
+ ipulse_shared_core_ftredge/services/user/userauth_operations.py,sha256=9l2uBAcAxbUnilK8MZ7IlHzaGiaPuqx7nIC51mAyR9w,36120
42
+ ipulse_shared_core_ftredge/services/user/userprofile_operations.py,sha256=_qyIEAQYCTV-subgP-5naMs_26apCpauomE6qmCCVWs,7333
43
+ ipulse_shared_core_ftredge/services/user/userstatus_operations.py,sha256=Gz2P4toJNs3ZMpGBvmN0HfiFqonRv6G3jTKkDz7mjuA,8927
44
+ ipulse_shared_core_ftredge/utils/__init__.py,sha256=JnxUb8I2MRjJC7rBPXSrpwBIQDEOku5O9JsiTi3oun8,56
45
+ ipulse_shared_core_ftredge/utils/custom_json_encoder.py,sha256=DblQLD0KOSNDyQ58wQRogBrShIXzPIZUw_oGOBATnJY,1366
46
+ ipulse_shared_core_ftredge/utils/json_encoder.py,sha256=QkcaFneVv3-q-s__Dz4OiUWYnM6jgHDJrDMdPv09RCA,2093
47
+ ipulse_shared_core_ftredge-22.1.1.dist-info/licenses/LICENCE,sha256=YBtYAXNqCCOo9Mr2hfkbSPAM9CeAr2j1VZBSwQTrNwE,1060
48
+ ipulse_shared_core_ftredge-22.1.1.dist-info/METADATA,sha256=Q-CLpF1xpkRoIMZ5ab17rGskMnkPdA6M04e0qj8yqHI,782
49
+ ipulse_shared_core_ftredge-22.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
+ ipulse_shared_core_ftredge-22.1.1.dist-info/top_level.txt,sha256=8sgYrptpexkA_6_HyGvho26cVFH9kmtGvaK8tHbsGHk,27
51
+ ipulse_shared_core_ftredge-22.1.1.dist-info/RECORD,,