sharedkernel 2.4.1__tar.gz → 2.5.2__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 (46) hide show
  1. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/PKG-INFO +8 -1
  2. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/README.md +6 -0
  3. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/setup.py +3 -1
  4. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/jwt_service.py +3 -0
  5. sharedkernel-2.5.2/sharedkernel/logger/log_decorator.py +64 -0
  6. sharedkernel-2.5.2/sharedkernel/logger/log_dto.py +23 -0
  7. sharedkernel-2.5.2/sharedkernel/logger/log_enums.py +12 -0
  8. sharedkernel-2.5.2/sharedkernel/logger/log_info.py +5 -0
  9. sharedkernel-2.5.2/sharedkernel/logger/log_middlewares.py +38 -0
  10. sharedkernel-2.5.2/sharedkernel/logger/logger_service.py +155 -0
  11. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel.egg-info/PKG-INFO +8 -1
  12. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel.egg-info/SOURCES.txt +6 -0
  13. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel.egg-info/requires.txt +1 -0
  14. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/setup.cfg +0 -0
  15. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/common.py +0 -0
  16. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/data_format_converter.py +0 -0
  17. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/database/__init__.py +0 -0
  18. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/database/audit_model.py +0 -0
  19. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/database/mongo_generic_audit_repository.py +0 -0
  20. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/database/mongo_generic_repository.py +0 -0
  21. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/database/pagination_response_dto.py +0 -0
  22. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/date_converter.py +0 -0
  23. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/diff_utils.py +0 -0
  24. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/enum/__init__.py +0 -0
  25. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/enum/error_code.py +0 -0
  26. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/enum/sort_order.py +0 -0
  27. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/exception/__init__.py +0 -0
  28. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/exception/exception.py +0 -0
  29. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/exception/exception_handlers.py +0 -0
  30. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/file_validation.py +0 -0
  31. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/multipart_upload.py +0 -0
  32. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/normalizer/__init__.py +0 -0
  33. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/normalizer/number_normalizer.py +0 -0
  34. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/normalizer/phone_number_normalizer.py +0 -0
  35. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/normalizer/string_normalizer.py +0 -0
  36. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/__init__.py +0 -0
  37. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/base_document.py +0 -0
  38. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/json_string_model.py +0 -0
  39. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/jwt_model.py +0 -0
  40. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/result.py +0 -0
  41. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/objects/user_info.py +0 -0
  42. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/regex_masking.py +0 -0
  43. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/s3_uploader.py +0 -0
  44. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel/string_extentions.py +0 -0
  45. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel.egg-info/dependency_links.txt +0 -0
  46. {sharedkernel-2.4.1 → sharedkernel-2.5.2}/sharedkernel.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharedkernel
3
- Version: 2.4.1
3
+ Version: 2.5.2
4
4
  Summary: sharekernel is a shared package between all python projects
5
5
  Author: Smilinno
6
6
  Description-Content-Type: text/markdown
@@ -19,6 +19,7 @@ Requires-Dist: mammoth
19
19
  Requires-Dist: markdown
20
20
  Requires-Dist: beautifulsoup4
21
21
  Requires-Dist: deepdiff
22
+ Requires-Dist: kombu
22
23
  Dynamic: author
23
24
  Dynamic: description
24
25
  Dynamic: description-content-type
@@ -29,6 +30,12 @@ Dynamic: summary
29
30
  this is a shared kernel package
30
31
 
31
32
  # Change Log
33
+ ### Version 2.5.2
34
+ - Minor Fix
35
+ ### Version 2.5.1
36
+ - Apply log on jwt service
37
+ ### Version 2.5.0
38
+ - Implement logger
32
39
  ### Version 2.4.1
33
40
  - Fix file validation
34
41
  ### Version 2.4.0
@@ -2,6 +2,12 @@
2
2
  this is a shared kernel package
3
3
 
4
4
  # Change Log
5
+ ### Version 2.5.2
6
+ - Minor Fix
7
+ ### Version 2.5.1
8
+ - Apply log on jwt service
9
+ ### Version 2.5.0
10
+ - Implement logger
5
11
  ### Version 2.4.1
6
12
  - Fix file validation
7
13
  ### Version 2.4.0
@@ -17,6 +17,7 @@ setup(
17
17
  "sharedkernel.exception",
18
18
  "sharedkernel.objects",
19
19
  "sharedkernel.normalizer",
20
+ "sharedkernel.logger",
20
21
  ],
21
22
  # Needed for dependencies
22
23
  install_requires=[
@@ -35,9 +36,10 @@ setup(
35
36
  "markdown",
36
37
  "beautifulsoup4",
37
38
  "deepdiff",
39
+ "kombu",
38
40
  ],
39
41
  # *strongly* suggested for sharing
40
- version="2.4.1",
42
+ version="2.5.2",
41
43
  description="sharekernel is a shared package between all python projects",
42
44
  long_description=long_description,
43
45
  long_description_content_type="text/markdown",
@@ -5,6 +5,7 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
5
5
  from sharedkernel.exception.exception import UnAuthorizedException
6
6
  from sharedkernel.objects import JwtModel
7
7
  from sharedkernel.objects.user_info import current_user_info, UserInfo
8
+ from sharedkernel.logger.log_decorator import unified_logger
8
9
 
9
10
 
10
11
  class JWTBearer(HTTPBearer):
@@ -13,6 +14,7 @@ class JWTBearer(HTTPBearer):
13
14
  self.jwt_config = jwt_config
14
15
  super(JWTBearer, self).__init__(auto_error=auto_error)
15
16
 
17
+ @unified_logger(class_name="JWTBearer")
16
18
  async def __call__(self, request: Request):
17
19
  try:
18
20
  if current_user_info.get(None):
@@ -33,6 +35,7 @@ class JWTBearer(HTTPBearer):
33
35
  except:
34
36
  raise UnAuthorizedException()
35
37
 
38
+ @unified_logger(class_name="JWTBearer")
36
39
  def verify(self, token: str) -> dict:
37
40
  decoded_token = jwt.decode(
38
41
  jwt=token.replace("Bearer", "").strip(),
@@ -0,0 +1,64 @@
1
+ import inspect
2
+ from functools import wraps
3
+ from typing import Callable, Type
4
+ from asyncio import iscoroutinefunction
5
+ import os
6
+
7
+ from sharedkernel.logger.logger_service import LoggerService
8
+
9
+ LOG_ENABLE = True if os.getenv("LOG_ENABLE", "True").lower() == "true" else False
10
+ print("Log Enable" if LOG_ENABLE else "Log Disable")
11
+
12
+ logger = LoggerService()
13
+
14
+ def decorate_class_methods(cls: Type) -> Type:
15
+ for name, attr in cls.__dict__.items():
16
+ if name.startswith("_"):
17
+ continue
18
+
19
+ if isinstance(attr, staticmethod):
20
+ original = attr.__func__
21
+ wrapped = unified_logger(class_name=cls.__name__)(original)
22
+ setattr(cls, name, staticmethod(wrapped))
23
+
24
+ elif isinstance(attr, classmethod):
25
+ original = attr.__func__
26
+ wrapped = unified_logger(class_name=cls.__name__)(original)
27
+ setattr(cls, name, classmethod(wrapped))
28
+
29
+ elif callable(attr):
30
+ wrapped = unified_logger(class_name=cls.__name__)(attr)
31
+ setattr(cls, name, wrapped)
32
+
33
+ return cls
34
+
35
+
36
+
37
+ def unified_logger(endpoint_name: str = None, class_name: str = None) -> Callable:
38
+ def decorator(target: Callable | Type):
39
+
40
+ if not LOG_ENABLE:
41
+ return target
42
+
43
+ if inspect.isclass(target):
44
+ return decorate_class_methods(target)
45
+
46
+ method_name = f"{class_name}.{target.__name__}" if class_name else target.__name__
47
+
48
+ if iscoroutinefunction(target):
49
+
50
+ @wraps(target)
51
+ async def async_wrapper(*args, **kwargs):
52
+ return await logger.execute_async(target, args, kwargs, method_name, endpoint_name)
53
+
54
+ return async_wrapper
55
+
56
+ else:
57
+
58
+ @wraps(target)
59
+ def sync_wrapper(*args, **kwargs):
60
+ return logger.execute_sync(target, args, kwargs, method_name, endpoint_name)
61
+
62
+ return sync_wrapper
63
+
64
+ return decorator
@@ -0,0 +1,23 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+ import datetime
4
+
5
+ from sharedkernel.logger.log_enums import LogEventTypeEnum, LogLevelEnum
6
+
7
+
8
+ class LogDTO(BaseModel):
9
+ EventType : Optional[LogEventTypeEnum] = None
10
+ Description: Optional[str] = None
11
+ MethodName: Optional[str] = None
12
+ IsSuccess : Optional[bool] = None
13
+ UserId : Optional[str] = None
14
+ UserName : Optional[str] = None
15
+ FailureReason : Optional[str] = None
16
+ UserAgent : Optional[str] = None
17
+ IpAddress : Optional[str] = None
18
+ RequestPath : Optional[str] = None
19
+ ServiceName : Optional[str] = None
20
+ HttpMethod : Optional[str] = None
21
+ CorrelationId : Optional[str] = None
22
+ Level : Optional[LogLevelEnum] = None
23
+ CreatedOn : datetime.datetime = None
@@ -0,0 +1,12 @@
1
+ from enum import IntEnum
2
+
3
+ class LogEventTypeEnum(IntEnum):
4
+ START = 1
5
+ END = 2
6
+
7
+ class LogLevelEnum(IntEnum):
8
+ INFORMATION = 1
9
+ WARNING = 2
10
+ CRITICAL = 3
11
+ SECURITY = 4
12
+ ERROR = 5
@@ -0,0 +1,5 @@
1
+ from contextvars import ContextVar
2
+
3
+ from sharedkernel.logger.log_dto import LogDTO
4
+
5
+ log_info: ContextVar[LogDTO] = ContextVar("log_info")
@@ -0,0 +1,38 @@
1
+ import uuid
2
+ from typing import Callable
3
+ import os
4
+ from fastapi import Request, Response
5
+ from starlette.middleware.base import BaseHTTPMiddleware
6
+
7
+
8
+ from sharedkernel.logger.log_info import log_info
9
+ from sharedkernel.logger.log_dto import LogDTO
10
+
11
+ IP_HEADER_NAME = os.getenv("IP_HEADER_NAME")
12
+ LOG_ENABLE = True if os.getenv("LOG_ENABLE", "True").lower() == "true" else False
13
+
14
+ class LogMiddleware(BaseHTTPMiddleware):
15
+
16
+ async def dispatch(self, request: Request, call_next: Callable) -> Response:
17
+
18
+ if not LOG_ENABLE:
19
+ return await call_next(request)
20
+
21
+ correlation_id = request.headers.get("x-request-id") or uuid.uuid4().hex
22
+ ip_address = request.headers.get(IP_HEADER_NAME)
23
+ log_obj = LogDTO(
24
+ CorrelationId=correlation_id,
25
+ RequestPath=request.url.path,
26
+ HttpMethod=request.method,
27
+ UserAgent=request.headers.get("user-agent"),
28
+ IpAddress=ip_address,
29
+ )
30
+
31
+ log_info.set(log_obj)
32
+
33
+ response = await call_next(request)
34
+
35
+ return response
36
+
37
+
38
+
@@ -0,0 +1,155 @@
1
+ from fastapi import HTTPException, WebSocket
2
+ from kombu import Connection, Producer, Exchange
3
+ from pydantic import BaseModel
4
+ from typing import Optional
5
+ import datetime
6
+ import uuid
7
+ import os
8
+
9
+ from sharedkernel.logger.log_info import log_info
10
+ from sharedkernel.enum import ErrorCode
11
+ from sharedkernel.objects.user_info import current_user_info
12
+ from sharedkernel.exception.exception import BusinessException
13
+ from sharedkernel.logger.log_dto import LogDTO
14
+ from sharedkernel.logger.log_enums import LogEventTypeEnum, LogLevelEnum
15
+
16
+ RABBITMQ_USER = os.getenv("RABBITMQ_USER")
17
+ RABBITMQ_PASSWORD = os.getenv("RABBITMQ_PASSWORD")
18
+ RABBITMQ_HOST = os.getenv("RABBITMQ_HOST")
19
+ RABBITMQ_VHOST = os.getenv("RABBITMQ_VHOST")
20
+ LOG_EXCHANGE = os.getenv("LOG_EXCHANGE")
21
+ LOG_REQUESTS_QUEUE = os.getenv("LOG_REQUESTS_QUEUE")
22
+ SERVICE_NAME = os.getenv("SERVICE_NAME")
23
+ IP_HEADER_NAME = os.getenv("IP_HEADER_NAME")
24
+
25
+ rabbitmq_url = (
26
+ f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASSWORD}"
27
+ f"@{RABBITMQ_HOST}/{RABBITMQ_VHOST}"
28
+ )
29
+
30
+ class LoggerService:
31
+ def __init__(self):
32
+ self._connection: Optional[Connection] = None
33
+ self._producer: Optional[Producer] = None
34
+ self._exchange = Exchange(LOG_EXCHANGE, type="topic")
35
+
36
+
37
+ def _ensure_connection(self):
38
+ if not self._connection or not self._connection.connected:
39
+ self._connection = Connection(rabbitmq_url)
40
+ self._connection.connect()
41
+ self._producer = Producer(self._connection)
42
+
43
+
44
+ def publish(self, log: BaseModel):
45
+ self._ensure_connection()
46
+
47
+ self._producer.publish(
48
+ body=log.model_dump_json(),
49
+ exchange=self._exchange,
50
+ routing_key=LOG_REQUESTS_QUEUE,
51
+ headers={
52
+ "cap-msg-id": str(uuid.uuid4()),
53
+ "cap-msg-name": LOG_REQUESTS_QUEUE,
54
+ },
55
+ content_type="application/json",
56
+ retry=True
57
+ )
58
+
59
+
60
+ def build(
61
+ self,
62
+ method_name: str,
63
+ event_type: LogEventTypeEnum,
64
+ endpoint_name: str = None,
65
+ is_success: bool = True,
66
+ failure_reason: Optional[str] = None,
67
+ ) -> LogDTO:
68
+
69
+ log = log_info.get()
70
+ user = current_user_info.get(None)
71
+ if user:
72
+ log.UserId = user.nameid
73
+ log.ServiceName = SERVICE_NAME
74
+ log.MethodName = method_name
75
+ log.Description = endpoint_name
76
+ log.IsSuccess = is_success
77
+ log.Level = LogLevelEnum.INFORMATION if is_success else LogLevelEnum.ERROR
78
+ log.EventType = event_type
79
+ log.FailureReason = failure_reason
80
+ log.CreatedOn = datetime.datetime.now(datetime.timezone.utc)
81
+
82
+ return log
83
+
84
+
85
+ def map_exception(self, exc: Exception):
86
+ if isinstance(exc, BusinessException):
87
+ return str(exc.detail)
88
+ if isinstance(exc, HTTPException):
89
+ return str(exc.detail)
90
+ return ErrorCode.Internal_Server
91
+
92
+
93
+ def emit(self, log: LogDTO):
94
+ self.publish(log)
95
+
96
+
97
+ async def execute_async(self, target, args, kwargs, method_name, endpoint_name):
98
+ self.emit(self.build(method_name, LogEventTypeEnum.START, endpoint_name))
99
+
100
+ try:
101
+ result = await target(*args, **kwargs)
102
+
103
+ except Exception as exc:
104
+ self.emit(
105
+ self.build(
106
+ method_name,
107
+ LogEventTypeEnum.END,
108
+ endpoint_name,
109
+ is_success=False,
110
+ failure_reason=self.map_exception(exc),
111
+ )
112
+ )
113
+ raise
114
+
115
+ else:
116
+ self.emit(self.build(method_name, LogEventTypeEnum.END, endpoint_name))
117
+ return result
118
+
119
+
120
+ def execute_sync(self, target, args, kwargs, method_name, endpoint_name):
121
+ self.emit(self.build(method_name, LogEventTypeEnum.START, endpoint_name))
122
+
123
+ try:
124
+ result = target(*args, **kwargs)
125
+
126
+ except Exception as exc:
127
+ self.emit(
128
+ self.build(
129
+ method_name,
130
+ LogEventTypeEnum.END,
131
+ endpoint_name,
132
+ is_success=False,
133
+ failure_reason=self.map_exception(exc),
134
+ )
135
+ )
136
+ raise
137
+
138
+ else:
139
+ self.emit(self.build(method_name, LogEventTypeEnum.END, endpoint_name))
140
+ return result
141
+
142
+ @staticmethod
143
+ def websocket_handler(websocket: WebSocket):
144
+ correlation_id = websocket.headers.get("x-request-id") or uuid.uuid4().hex
145
+ ip_address = websocket.headers.get(IP_HEADER_NAME)
146
+
147
+ log_obj = LogDTO(
148
+ CorrelationId=correlation_id,
149
+ RequestPath=websocket.url.path,
150
+ HttpMethod="ws",
151
+ UserAgent=websocket.headers.get("user-agent"),
152
+ IpAddress=ip_address,
153
+ )
154
+
155
+ log_info.set(log_obj)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharedkernel
3
- Version: 2.4.1
3
+ Version: 2.5.2
4
4
  Summary: sharekernel is a shared package between all python projects
5
5
  Author: Smilinno
6
6
  Description-Content-Type: text/markdown
@@ -19,6 +19,7 @@ Requires-Dist: mammoth
19
19
  Requires-Dist: markdown
20
20
  Requires-Dist: beautifulsoup4
21
21
  Requires-Dist: deepdiff
22
+ Requires-Dist: kombu
22
23
  Dynamic: author
23
24
  Dynamic: description
24
25
  Dynamic: description-content-type
@@ -29,6 +30,12 @@ Dynamic: summary
29
30
  this is a shared kernel package
30
31
 
31
32
  # Change Log
33
+ ### Version 2.5.2
34
+ - Minor Fix
35
+ ### Version 2.5.1
36
+ - Apply log on jwt service
37
+ ### Version 2.5.0
38
+ - Implement logger
32
39
  ### Version 2.4.1
33
40
  - Fix file validation
34
41
  ### Version 2.4.0
@@ -26,6 +26,12 @@ sharedkernel/enum/sort_order.py
26
26
  sharedkernel/exception/__init__.py
27
27
  sharedkernel/exception/exception.py
28
28
  sharedkernel/exception/exception_handlers.py
29
+ sharedkernel/logger/log_decorator.py
30
+ sharedkernel/logger/log_dto.py
31
+ sharedkernel/logger/log_enums.py
32
+ sharedkernel/logger/log_info.py
33
+ sharedkernel/logger/log_middlewares.py
34
+ sharedkernel/logger/logger_service.py
29
35
  sharedkernel/normalizer/__init__.py
30
36
  sharedkernel/normalizer/number_normalizer.py
31
37
  sharedkernel/normalizer/phone_number_normalizer.py
@@ -13,3 +13,4 @@ mammoth
13
13
  markdown
14
14
  beautifulsoup4
15
15
  deepdiff
16
+ kombu
File without changes