maleo-foundation 0.3.71__py3-none-any.whl → 0.3.74__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.
Files changed (42) hide show
  1. maleo_foundation/authentication.py +2 -48
  2. maleo_foundation/client/manager.py +9 -3
  3. maleo_foundation/constants.py +2 -0
  4. maleo_foundation/controller_types.py +25 -0
  5. maleo_foundation/enums.py +7 -1
  6. maleo_foundation/managers/client/base.py +37 -5
  7. maleo_foundation/managers/client/google/base.py +7 -3
  8. maleo_foundation/managers/client/google/secret.py +12 -3
  9. maleo_foundation/managers/client/google/storage.py +11 -2
  10. maleo_foundation/managers/client/google/subscription.py +40 -26
  11. maleo_foundation/managers/client/maleo.py +3 -5
  12. maleo_foundation/managers/credential.py +6 -2
  13. maleo_foundation/managers/middleware.py +8 -8
  14. maleo_foundation/managers/service.py +33 -17
  15. maleo_foundation/middlewares/authentication.py +3 -2
  16. maleo_foundation/middlewares/base.py +312 -197
  17. maleo_foundation/models/schemas/general.py +1 -127
  18. maleo_foundation/models/transfers/general/authentication.py +35 -0
  19. maleo_foundation/{authorization.py → models/transfers/general/authorization.py} +0 -3
  20. maleo_foundation/models/transfers/general/configurations/__init__.py +2 -0
  21. maleo_foundation/models/transfers/general/configurations/client/maleo.py +1 -1
  22. maleo_foundation/models/transfers/general/configurations/middleware.py +6 -7
  23. maleo_foundation/models/transfers/general/configurations/pubsub/subscription.py +16 -0
  24. maleo_foundation/models/transfers/general/configurations/service.py +2 -1
  25. maleo_foundation/models/transfers/general/operation.py +192 -30
  26. maleo_foundation/models/transfers/general/request.py +13 -19
  27. maleo_foundation/models/transfers/general/response.py +14 -0
  28. maleo_foundation/models/transfers/general/service.py +9 -0
  29. maleo_foundation/models/transfers/general/settings.py +1 -1
  30. maleo_foundation/models/transfers/general/user_agent.py +34 -0
  31. maleo_foundation/utils/exceptions/client.py +26 -2
  32. maleo_foundation/utils/exceptions/service.py +26 -2
  33. maleo_foundation/utils/extractor.py +49 -19
  34. maleo_foundation/utils/logging.py +90 -21
  35. maleo_foundation/utils/parser.py +7 -0
  36. {maleo_foundation-0.3.71.dist-info → maleo_foundation-0.3.74.dist-info}/METADATA +3 -1
  37. {maleo_foundation-0.3.71.dist-info → maleo_foundation-0.3.74.dist-info}/RECORD +39 -35
  38. maleo_foundation/utils/dependencies/__init__.py +0 -6
  39. maleo_foundation/utils/dependencies/auth.py +0 -17
  40. maleo_foundation/utils/dependencies/context.py +0 -8
  41. {maleo_foundation-0.3.71.dist-info → maleo_foundation-0.3.74.dist-info}/WHEEL +0 -0
  42. {maleo_foundation-0.3.71.dist-info → maleo_foundation-0.3.74.dist-info}/top_level.txt +0 -0
@@ -3,10 +3,12 @@ from functools import wraps
3
3
  from httpx import RequestError
4
4
  from pydantic import ValidationError
5
5
  from typing import Optional
6
+ from uuid import UUID
6
7
  from maleo_foundation.enums import BaseEnums
7
8
  from maleo_foundation.models.transfers.results.client.service import (
8
9
  BaseClientServiceResultsTransfers,
9
10
  )
11
+ from maleo_foundation.types import BaseTypes
10
12
  from maleo_foundation.utils.logging import BaseLogger
11
13
 
12
14
 
@@ -30,10 +32,14 @@ class BaseClientExceptions:
30
32
  fail_result_class: type[
31
33
  BaseClientServiceResultsTransfers.Fail
32
34
  ] = BaseClientServiceResultsTransfers.Fail,
35
+ operation_id: BaseTypes.OptionalUUID = None,
33
36
  ) -> BaseClientServiceResultsTransfers.Fail:
34
- if logger:
37
+ if logger is not None:
38
+ log_string = f"{category} occurred while {summary}: '{str(e)}'"
39
+ if operation_id is not None:
40
+ log_string = f"{operation_id} - {log_string}"
35
41
  logger.error(
36
- f"{category} occurred while {summary}: '{str(e)}'",
42
+ log_string,
37
43
  exc_info=True,
38
44
  )
39
45
  return fail_result_class(
@@ -77,6 +83,12 @@ class BaseClientExceptions:
77
83
  def decorator(func):
78
84
  @wraps(func)
79
85
  async def wrapper(*args, **kwargs):
86
+ # Search for operation_id in args and kwargs
87
+ operation_id: BaseTypes.OptionalUUID = None
88
+ for k, v in kwargs.items():
89
+ if k == "operation_id":
90
+ operation_id = UUID(v)
91
+ break
80
92
  try:
81
93
  return await func(*args, **kwargs)
82
94
  except ValidationError as e:
@@ -96,6 +108,7 @@ class BaseClientExceptions:
96
108
  status_update_type=status_update_type,
97
109
  logger=logger,
98
110
  fail_result_class=fail_result_class,
111
+ operation_id=operation_id,
99
112
  )
100
113
  except RequestError as e:
101
114
  return BaseClientExceptions._exception_handler(
@@ -114,6 +127,7 @@ class BaseClientExceptions:
114
127
  status_update_type=status_update_type,
115
128
  logger=logger,
116
129
  fail_result_class=fail_result_class,
130
+ operation_id=operation_id,
117
131
  )
118
132
  except Exception as e:
119
133
  return BaseClientExceptions._exception_handler(
@@ -132,6 +146,7 @@ class BaseClientExceptions:
132
146
  status_update_type=status_update_type,
133
147
  logger=logger,
134
148
  fail_result_class=fail_result_class,
149
+ operation_id=operation_id,
135
150
  )
136
151
 
137
152
  return wrapper
@@ -159,6 +174,12 @@ class BaseClientExceptions:
159
174
  def decorator(func):
160
175
  @wraps(func)
161
176
  def wrapper(*args, **kwargs):
177
+ # Search for operation_id in args and kwargs
178
+ operation_id: BaseTypes.OptionalUUID = None
179
+ for k, v in kwargs.items():
180
+ if k == "operation_id":
181
+ operation_id = UUID(v)
182
+ break
162
183
  try:
163
184
  return func(*args, **kwargs)
164
185
  except ValidationError as e:
@@ -178,6 +199,7 @@ class BaseClientExceptions:
178
199
  status_update_type=status_update_type,
179
200
  logger=logger,
180
201
  fail_result_class=fail_result_class,
202
+ operation_id=operation_id,
181
203
  )
182
204
  except RequestError as e:
183
205
  return BaseClientExceptions._exception_handler(
@@ -196,6 +218,7 @@ class BaseClientExceptions:
196
218
  status_update_type=status_update_type,
197
219
  logger=logger,
198
220
  fail_result_class=fail_result_class,
221
+ operation_id=operation_id,
199
222
  )
200
223
  except Exception as e:
201
224
  return BaseClientExceptions._exception_handler(
@@ -214,6 +237,7 @@ class BaseClientExceptions:
214
237
  status_update_type=status_update_type,
215
238
  logger=logger,
216
239
  fail_result_class=fail_result_class,
240
+ operation_id=operation_id,
217
241
  )
218
242
 
219
243
  return wrapper
@@ -3,10 +3,12 @@ from functools import wraps
3
3
  from pydantic import ValidationError
4
4
  from sqlalchemy.exc import SQLAlchemyError
5
5
  from typing import Optional
6
+ from uuid import UUID
6
7
  from maleo_foundation.enums import BaseEnums
7
8
  from maleo_foundation.models.transfers.results.service.general import (
8
9
  BaseServiceGeneralResultsTransfers,
9
10
  )
11
+ from maleo_foundation.types import BaseTypes
10
12
  from maleo_foundation.utils.logging import BaseLogger
11
13
 
12
14
 
@@ -30,10 +32,14 @@ class BaseServiceExceptions:
30
32
  fail_result_class: type[
31
33
  BaseServiceGeneralResultsTransfers.Fail
32
34
  ] = BaseServiceGeneralResultsTransfers.Fail,
35
+ operation_id: BaseTypes.OptionalUUID = None,
33
36
  ) -> BaseServiceGeneralResultsTransfers.Fail:
34
- if logger:
37
+ if logger is not None:
38
+ log_string = f"{category} occurred while {summary}: '{str(e)}'"
39
+ if operation_id is not None:
40
+ log_string = f"{operation_id} - {log_string}"
35
41
  logger.error(
36
- f"{category} occurred while {summary}: '{str(e)}'",
42
+ log_string,
37
43
  exc_info=True,
38
44
  )
39
45
  return fail_result_class(
@@ -77,6 +83,12 @@ class BaseServiceExceptions:
77
83
  def decorator(func):
78
84
  @wraps(func)
79
85
  async def wrapper(*args, **kwargs):
86
+ # Search for operation_id in args and kwargs
87
+ operation_id: BaseTypes.OptionalUUID = None
88
+ for k, v in kwargs.items():
89
+ if k == "operation_id":
90
+ operation_id = UUID(v)
91
+ break
80
92
  try:
81
93
  return await func(*args, **kwargs)
82
94
  except ValidationError as e:
@@ -96,6 +108,7 @@ class BaseServiceExceptions:
96
108
  status_update_type=status_update_type,
97
109
  logger=logger,
98
110
  fail_result_class=fail_result_class,
111
+ operation_id=operation_id,
99
112
  )
100
113
  except SQLAlchemyError as e:
101
114
  return BaseServiceExceptions._exception_handler(
@@ -114,6 +127,7 @@ class BaseServiceExceptions:
114
127
  status_update_type=status_update_type,
115
128
  logger=logger,
116
129
  fail_result_class=fail_result_class,
130
+ operation_id=operation_id,
117
131
  )
118
132
  except Exception as e:
119
133
  return BaseServiceExceptions._exception_handler(
@@ -132,6 +146,7 @@ class BaseServiceExceptions:
132
146
  status_update_type=status_update_type,
133
147
  logger=logger,
134
148
  fail_result_class=fail_result_class,
149
+ operation_id=operation_id,
135
150
  )
136
151
 
137
152
  return wrapper
@@ -159,6 +174,12 @@ class BaseServiceExceptions:
159
174
  def decorator(func):
160
175
  @wraps(func)
161
176
  def wrapper(*args, **kwargs):
177
+ # Search for operation_id in args and kwargs
178
+ operation_id: BaseTypes.OptionalUUID = None
179
+ for k, v in kwargs.items():
180
+ if k == "operation_id":
181
+ operation_id = UUID(v)
182
+ break
162
183
  try:
163
184
  return func(*args, **kwargs)
164
185
  except ValidationError as e:
@@ -178,6 +199,7 @@ class BaseServiceExceptions:
178
199
  status_update_type=status_update_type,
179
200
  logger=logger,
180
201
  fail_result_class=fail_result_class,
202
+ operation_id=operation_id,
181
203
  )
182
204
  except SQLAlchemyError as e:
183
205
  return BaseServiceExceptions._exception_handler(
@@ -196,6 +218,7 @@ class BaseServiceExceptions:
196
218
  status_update_type=status_update_type,
197
219
  logger=logger,
198
220
  fail_result_class=fail_result_class,
221
+ operation_id=operation_id,
199
222
  )
200
223
  except Exception as e:
201
224
  return BaseServiceExceptions._exception_handler(
@@ -214,6 +237,7 @@ class BaseServiceExceptions:
214
237
  status_update_type=status_update_type,
215
238
  logger=logger,
216
239
  fail_result_class=fail_result_class,
240
+ operation_id=operation_id,
217
241
  )
218
242
 
219
243
  return wrapper
@@ -1,8 +1,40 @@
1
- from datetime import datetime, timezone
2
- from fastapi import Request
1
+ from fastapi import Request, Security
2
+ from fastapi.security import HTTPAuthorizationCredentials
3
3
  from starlette.requests import HTTPConnection
4
- from uuid import UUID, uuid4
4
+ from uuid import UUID
5
+ from maleo_foundation.constants import TOKEN_SCHEME
6
+ from maleo_foundation.authentication import (
7
+ Credentials as RequestCredentials,
8
+ User as RequestUser,
9
+ )
10
+ from maleo_foundation.models.transfers.general.authentication import (
11
+ Authentication,
12
+ Credentials,
13
+ User,
14
+ )
15
+ from maleo_foundation.models.transfers.general.authorization import Authorization
5
16
  from maleo_foundation.models.transfers.general.request import RequestContext
17
+ from .parser import parse_user_agent
18
+
19
+
20
+ def extract_authentication(request: Request) -> Authentication:
21
+ # validate credentials
22
+ request_credentials = request.auth
23
+ if not isinstance(request_credentials, RequestCredentials):
24
+ raise TypeError("'credentials' is not of type 'RequestCredentials'")
25
+ credentials = Credentials.model_validate(request_credentials, from_attributes=True)
26
+ # validate user
27
+ request_user = request.user
28
+ if not isinstance(request_user, RequestUser):
29
+ raise TypeError("'user' is not of type 'RequestUser'")
30
+ user = User.model_validate(request_user, from_attributes=True)
31
+ return Authentication(credentials=credentials, user=user)
32
+
33
+
34
+ def extract_authorization(
35
+ token: HTTPAuthorizationCredentials = Security(TOKEN_SCHEME),
36
+ ) -> Authorization:
37
+ return Authorization(scheme=token.scheme, credentials=token.credentials)
6
38
 
7
39
 
8
40
  def extract_client_ip(conn: HTTPConnection) -> str:
@@ -23,28 +55,26 @@ def extract_client_ip(conn: HTTPConnection) -> str:
23
55
  return conn.client.host if conn.client else "unknown"
24
56
 
25
57
 
58
+ def extract_operation_id(request: Request) -> UUID:
59
+ return request.state.operation_id
60
+
61
+
26
62
  def extract_request_context(request: Request) -> RequestContext:
27
- headers = request.headers
63
+ request_id = request.state.request_id
64
+ requested_at = request.state.requested_at
28
65
 
29
- request_id = headers.get("x-request-id")
30
- if request_id is None:
31
- request_id = uuid4()
32
- else:
33
- request_id = UUID(request_id)
66
+ headers = request.headers
34
67
 
35
68
  ip_address = extract_client_ip(request)
36
69
 
37
- ua_browser = headers.get("sec-ch-ua", "")
38
- if ua_browser:
39
- ua_browser = ua_browser.replace('"', "").split(",")[0].strip()
70
+ user_agent_string = headers.get("user-agent", "")
71
+ user_agent = parse_user_agent(user_agent_string=user_agent_string)
40
72
 
41
73
  return RequestContext(
42
74
  request_id=request_id,
43
- requested_at=datetime.now(tz=timezone.utc),
75
+ requested_at=requested_at,
44
76
  method=request.method,
45
77
  url=request.url.path,
46
- path_params=None if not request.path_params else request.path_params,
47
- query_params=None if not request.query_params else str(request.query_params),
48
78
  ip_address=ip_address,
49
79
  is_internal=(
50
80
  None
@@ -55,10 +85,10 @@ def extract_request_context(request: Request) -> RequestContext:
55
85
  or ip_address.startswith("172.")
56
86
  )
57
87
  ),
58
- user_agent=headers.get("user-agent"),
59
- ua_browser=ua_browser,
60
- ua_mobile=headers.get("sec-ch-ua-mobile"),
61
- platform=headers.get("sec-ch-ua-platform"),
88
+ headers=headers.items(),
89
+ path_params=None if not request.path_params else request.path_params,
90
+ query_params=None if not request.query_params else str(request.query_params),
91
+ user_agent=user_agent,
62
92
  referer=headers.get("referer"),
63
93
  origin=headers.get("origin"),
64
94
  host=headers.get("host"),
@@ -57,21 +57,33 @@ class SimpleConfig(BaseModel):
57
57
  class BaseLogger(logging.Logger):
58
58
  def __init__(
59
59
  self,
60
- dir: str,
61
60
  type: BaseEnums.LoggerType,
62
- service_key: BaseTypes.OptionalString = None,
61
+ dir: str,
62
+ environment: Optional[BaseEnums.EnvironmentType] = None,
63
+ service_key: Optional[BaseEnums.Service] = None,
63
64
  client_key: BaseTypes.OptionalString = None,
64
65
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
65
66
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
66
67
  ):
67
68
  self._type = type # * Declare logger type
68
69
 
69
- # * Ensure service_key exists
70
- self._service_key = service_key or os.getenv("SERVICE_KEY")
71
- if self._service_key is None:
70
+ # Ensure environment exists
71
+ actual_environment = environment or os.getenv("ENVIRONMENT")
72
+ if actual_environment is None:
73
+ raise ValueError(
74
+ "ENVIRONMENT environment variable must be set if 'environment' is set to None"
75
+ )
76
+ else:
77
+ self._environment = BaseEnums.EnvironmentType(actual_environment)
78
+
79
+ # Ensure service_key exists
80
+ actual_service_key = service_key or os.getenv("SERVICE_KEY")
81
+ if actual_service_key is None:
72
82
  raise ValueError(
73
83
  "SERVICE_KEY environment variable must be set if 'service_key' is set to None"
74
84
  )
85
+ else:
86
+ self._service_key = BaseEnums.Service(actual_service_key)
75
87
 
76
88
  self._client_key = client_key # * Declare client key
77
89
 
@@ -82,10 +94,11 @@ class BaseLogger(logging.Logger):
82
94
  )
83
95
 
84
96
  # * Define logger name
97
+ base_name = f"{self._environment} - {self._service_key} - {self._type}"
85
98
  if self._type == BaseEnums.LoggerType.CLIENT:
86
- self._name = f"{self._service_key} - {self._type} - {self._client_key}"
99
+ self._name = f"{base_name} - {self._client_key}"
87
100
  else:
88
- self._name = f"{self._service_key} - {self._type}"
101
+ self._name = base_name
89
102
 
90
103
  super().__init__(self._name, level) # * Init the superclass's logger
91
104
 
@@ -111,7 +124,9 @@ class BaseLogger(logging.Logger):
111
124
  )
112
125
  self.addHandler(cloud_logging_handler)
113
126
  else:
114
- self.info("Cloud logging is not configured.")
127
+ self.warning(
128
+ "Cloud logging is not configured. Will not add cloud loggin handler"
129
+ )
115
130
 
116
131
  # * Define log directory
117
132
  if self._type == BaseEnums.LoggerType.CLIENT:
@@ -164,13 +179,15 @@ class ApplicationLogger(BaseLogger):
164
179
  def __init__(
165
180
  self,
166
181
  dir: str,
167
- service_key: BaseTypes.OptionalString = None,
182
+ environment: Optional[BaseEnums.EnvironmentType] = None,
183
+ service_key: Optional[BaseEnums.Service] = None,
168
184
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
169
185
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
170
186
  ):
171
187
  super().__init__(
172
- dir=dir,
173
188
  type=BaseEnums.LoggerType.APPLICATION,
189
+ dir=dir,
190
+ environment=environment,
174
191
  service_key=service_key,
175
192
  client_key=None,
176
193
  level=level,
@@ -182,13 +199,15 @@ class CacheLogger(BaseLogger):
182
199
  def __init__(
183
200
  self,
184
201
  dir: str,
185
- service_key: BaseTypes.OptionalString = None,
202
+ environment: Optional[BaseEnums.EnvironmentType] = None,
203
+ service_key: Optional[BaseEnums.Service] = None,
186
204
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
187
205
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
188
206
  ):
189
207
  super().__init__(
190
208
  dir=dir,
191
209
  type=BaseEnums.LoggerType.CACHE,
210
+ environment=environment,
192
211
  service_key=service_key,
193
212
  client_key=None,
194
213
  level=level,
@@ -201,13 +220,15 @@ class ClientLogger(BaseLogger):
201
220
  self,
202
221
  dir: str,
203
222
  client_key: str,
204
- service_key: BaseTypes.OptionalString = None,
223
+ environment: Optional[BaseEnums.EnvironmentType] = None,
224
+ service_key: Optional[BaseEnums.Service] = None,
205
225
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
206
226
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
207
227
  ):
208
228
  super().__init__(
209
- dir=dir,
210
229
  type=BaseEnums.LoggerType.CLIENT,
230
+ dir=dir,
231
+ environment=environment,
211
232
  service_key=service_key,
212
233
  client_key=client_key,
213
234
  level=level,
@@ -215,17 +236,39 @@ class ClientLogger(BaseLogger):
215
236
  )
216
237
 
217
238
 
239
+ class ControllerLogger(BaseLogger):
240
+ def __init__(
241
+ self,
242
+ dir: str,
243
+ environment: Optional[BaseEnums.EnvironmentType] = None,
244
+ service_key: Optional[BaseEnums.Service] = None,
245
+ level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
246
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
247
+ ):
248
+ super().__init__(
249
+ dir=dir,
250
+ type=BaseEnums.LoggerType.CONTROLLER,
251
+ environment=environment,
252
+ service_key=service_key,
253
+ client_key=None,
254
+ level=level,
255
+ google_cloud_logging=google_cloud_logging,
256
+ )
257
+
258
+
218
259
  class DatabaseLogger(BaseLogger):
219
260
  def __init__(
220
261
  self,
221
262
  dir: str,
222
- service_key: BaseTypes.OptionalString = None,
263
+ environment: Optional[BaseEnums.EnvironmentType] = None,
264
+ service_key: Optional[BaseEnums.Service] = None,
223
265
  level=BaseEnums.LoggerLevel.INFO,
224
266
  google_cloud_logging=None,
225
267
  ):
226
268
  super().__init__(
227
- dir=dir,
228
269
  type=BaseEnums.LoggerType.DATABASE,
270
+ dir=dir,
271
+ environment=environment,
229
272
  service_key=service_key,
230
273
  client_key=None,
231
274
  level=level,
@@ -237,13 +280,15 @@ class MiddlewareLogger(BaseLogger):
237
280
  def __init__(
238
281
  self,
239
282
  dir: str,
240
- service_key: BaseTypes.OptionalString = None,
283
+ environment: Optional[BaseEnums.EnvironmentType] = None,
284
+ service_key: Optional[BaseEnums.Service] = None,
241
285
  level=BaseEnums.LoggerLevel.INFO,
242
286
  google_cloud_logging=None,
243
287
  ):
244
288
  super().__init__(
245
- dir=dir,
246
289
  type=BaseEnums.LoggerType.MIDDLEWARE,
290
+ dir=dir,
291
+ environment=environment,
247
292
  service_key=service_key,
248
293
  client_key=None,
249
294
  level=level,
@@ -251,17 +296,39 @@ class MiddlewareLogger(BaseLogger):
251
296
  )
252
297
 
253
298
 
254
- class RepositoryLogger(BaseLogger):
299
+ class RouterLogger(BaseLogger):
255
300
  def __init__(
256
301
  self,
257
302
  dir: str,
258
- service_key: BaseTypes.OptionalString = None,
303
+ environment: Optional[BaseEnums.EnvironmentType] = None,
304
+ service_key: Optional[BaseEnums.Service] = None,
259
305
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
260
306
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
261
307
  ):
262
308
  super().__init__(
263
309
  dir=dir,
310
+ type=BaseEnums.LoggerType.ROUTER,
311
+ environment=environment,
312
+ service_key=service_key,
313
+ client_key=None,
314
+ level=level,
315
+ google_cloud_logging=google_cloud_logging,
316
+ )
317
+
318
+
319
+ class RepositoryLogger(BaseLogger):
320
+ def __init__(
321
+ self,
322
+ dir: str,
323
+ environment: Optional[BaseEnums.EnvironmentType] = None,
324
+ service_key: Optional[BaseEnums.Service] = None,
325
+ level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
326
+ google_cloud_logging: Optional[GoogleCloudLogging] = None,
327
+ ):
328
+ super().__init__(
264
329
  type=BaseEnums.LoggerType.REPOSITORY,
330
+ dir=dir,
331
+ environment=environment,
265
332
  service_key=service_key,
266
333
  client_key=None,
267
334
  level=level,
@@ -273,13 +340,15 @@ class ServiceLogger(BaseLogger):
273
340
  def __init__(
274
341
  self,
275
342
  dir: str,
276
- service_key: BaseTypes.OptionalString = None,
343
+ environment: Optional[BaseEnums.EnvironmentType] = None,
344
+ service_key: Optional[BaseEnums.Service] = None,
277
345
  level: BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
278
346
  google_cloud_logging: Optional[GoogleCloudLogging] = None,
279
347
  ):
280
348
  super().__init__(
281
- dir=dir,
282
349
  type=BaseEnums.LoggerType.SERVICE,
350
+ dir=dir,
351
+ environment=environment,
283
352
  service_key=service_key,
284
353
  client_key=None,
285
354
  level=level,
@@ -0,0 +1,7 @@
1
+ from user_agents.parsers import parse
2
+ from maleo_foundation.models.transfers.general.user_agent import UserAgent
3
+
4
+
5
+ def parse_user_agent(user_agent_string: str) -> UserAgent:
6
+ parsed_user_agent = parse(user_agent_string)
7
+ return UserAgent.model_validate(parsed_user_agent, from_attributes=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maleo_foundation
3
- Version: 0.3.71
3
+ Version: 0.3.74
4
4
  Summary: Foundation package for Maleo
5
5
  Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
6
  License: MIT
@@ -98,6 +98,8 @@ Requires-Dist: starlette>=0.46.2
98
98
  Requires-Dist: twine>=6.1.0
99
99
  Requires-Dist: typing-inspection>=0.4.0
100
100
  Requires-Dist: typing_extensions>=4.13.2
101
+ Requires-Dist: ua-parser>=1.0.1
102
+ Requires-Dist: ua-parser-builtins>=0.18.0.post1
101
103
  Requires-Dist: urllib3>=2.4.0
102
104
  Requires-Dist: uvicorn>=0.34.2
103
105
  Requires-Dist: virtualenv>=20.31.2