atk-common 2.2.0__tar.gz → 3.1.0__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 (79) hide show
  1. {atk_common-2.2.0 → atk_common-3.1.0}/PKG-INFO +1 -1
  2. {atk_common-2.2.0 → atk_common-3.1.0}/setup.py +1 -1
  3. atk_common-3.1.0/src/atk_common/__init__.py +1 -0
  4. atk_common-3.1.0/src/atk_common/classes/__init__.py +16 -0
  5. atk_common-3.1.0/src/atk_common/classes/bo_logger.py +62 -0
  6. atk_common-3.1.0/src/atk_common/classes/docker_handler.py +76 -0
  7. atk_common-3.1.0/src/atk_common/classes/env_handler.py +38 -0
  8. atk_common-3.1.0/src/atk_common/classes/error_handler.py +81 -0
  9. atk_common-3.1.0/src/atk_common/classes/http_response_handler.py +42 -0
  10. atk_common-3.1.0/src/atk_common/classes/rabbitmq_consumer.py +72 -0
  11. atk_common-3.1.0/src/atk_common/interfaces/__init__.py +14 -0
  12. atk_common-3.1.0/src/atk_common/interfaces/docker_handler_interface.py +9 -0
  13. atk_common-3.1.0/src/atk_common/interfaces/env_handler_interface.py +8 -0
  14. atk_common-3.1.0/src/atk_common/interfaces/error_handler_interface.py +11 -0
  15. atk_common-3.1.0/src/atk_common/interfaces/http_response_handler_interface.py +5 -0
  16. atk_common-3.1.0/src/atk_common/interfaces/logger_interface.py +9 -0
  17. atk_common-3.1.0/src/atk_common/utils/__init__.py +47 -0
  18. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/consumer_retry_handler.py +21 -15
  19. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/default_should_retry.py +1 -1
  20. atk_common-3.1.0/src/atk_common/utils/error_utils.py +5 -0
  21. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/http_utils.py +1 -1
  22. atk_common-3.1.0/src/atk_common/utils/mq_utils.py +30 -0
  23. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common.egg-info/PKG-INFO +1 -1
  24. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common.egg-info/SOURCES.txt +24 -16
  25. {atk_common-2.2.0 → atk_common-3.1.0}/tests/test_docker_utils.py +1 -1
  26. {atk_common-2.2.0 → atk_common-3.1.0}/tests/test_error_utils.py +1 -1
  27. {atk_common-2.2.0 → atk_common-3.1.0}/tests/test_http_utils.py +1 -1
  28. {atk_common-2.2.0 → atk_common-3.1.0}/tests/test_log_utils.py +1 -1
  29. atk_common-2.2.0/src/atk_common/__init__.py +0 -67
  30. atk_common-2.2.0/src/atk_common/bo_logger.py +0 -46
  31. atk_common-2.2.0/src/atk_common/docker_utils.py +0 -70
  32. atk_common-2.2.0/src/atk_common/env_utils.py +0 -27
  33. atk_common-2.2.0/src/atk_common/error_utils.py +0 -73
  34. atk_common-2.2.0/src/atk_common/http_response_utils.py +0 -37
  35. atk_common-2.2.0/src/atk_common/log_utils.py +0 -16
  36. atk_common-2.2.0/src/atk_common/mq_utils.py +0 -27
  37. atk_common-2.2.0/src/atk_common/rabbitmq_consumer.py +0 -51
  38. {atk_common-2.2.0 → atk_common-3.1.0}/README.md +0 -0
  39. {atk_common-2.2.0 → atk_common-3.1.0}/license.txt +0 -0
  40. {atk_common-2.2.0 → atk_common-3.1.0}/pyproject.toml +0 -0
  41. {atk_common-2.2.0 → atk_common-3.1.0}/setup.cfg +0 -0
  42. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/__init__.py +0 -0
  43. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/api_error_type_enum.py +0 -0
  44. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/camera_cabinet_type_enum.py +0 -0
  45. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/camera_role_enum.py +0 -0
  46. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/certificate_issuer_enum.py +0 -0
  47. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/command_status_type_enum.py +0 -0
  48. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/command_type_enum.py +0 -0
  49. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/config_reply_status_type_enum.py +0 -0
  50. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/detection_status_type_enum.py +0 -0
  51. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/encryption_type_enum.py +0 -0
  52. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/file_exists_enum.py +0 -0
  53. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/history_status_type_enum.py +0 -0
  54. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/image_encoding_type_enum.py +0 -0
  55. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/image_part_category_enum.py +0 -0
  56. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/image_part_type_enum.py +0 -0
  57. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/image_shelf_type_enum.py +0 -0
  58. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/log_level_enum.py +0 -0
  59. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/metering_direction_enum.py +0 -0
  60. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/multimotor_status_type_enum.py +0 -0
  61. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/piezo_vehicle_type_enum.py +0 -0
  62. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/process_status_type_enum.py +0 -0
  63. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/response_status_type_enum.py +0 -0
  64. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/section_role_enum.py +0 -0
  65. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/sensor_order_enum.py +0 -0
  66. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/sensor_type_enum.py +0 -0
  67. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/speed_control_status_type_enum.py +0 -0
  68. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/speed_control_stop_reason.py +0 -0
  69. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/test_image_type_enum.py +0 -0
  70. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common/enums/violation_type_enum.py +0 -0
  71. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/datetime_utils.py +0 -0
  72. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/db_utils.py +0 -0
  73. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/file_utils.py +0 -0
  74. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/hash_utils.py +0 -0
  75. {atk_common-2.2.0/src/atk_common → atk_common-3.1.0/src/atk_common/utils}/internal_response_utils.py +0 -0
  76. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common.egg-info/dependency_links.txt +0 -0
  77. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common.egg-info/requires.txt +0 -0
  78. {atk_common-2.2.0 → atk_common-3.1.0}/src/atk_common.egg-info/top_level.txt +0 -0
  79. {atk_common-2.2.0 → atk_common-3.1.0}/src/shared_python_atk_enforcement/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atk_common
3
- Version: 2.2.0
3
+ Version: 3.1.0
4
4
  Summary: ATK common methods
5
5
  Home-page: https://github.com/pypa/atk_common
6
6
  Author: Roger
@@ -36,7 +36,7 @@ setup(
36
36
  # For a discussion on single-sourcing the version across setup.py and the
37
37
  # project code, see
38
38
  # https://packaging.python.org/guides/single-sourcing-package-version/
39
- version="2.2.0", # Required
39
+ version="3.1.0", # Required
40
40
  # This is a one-line description or tagline of what your project does. This
41
41
  # corresponds to the "Summary" metadata field:
42
42
  # https://packaging.python.org/specifications/core-metadata/#summary
@@ -0,0 +1 @@
1
+ # __init__.py
@@ -0,0 +1,16 @@
1
+ # __init__.py
2
+ from atk_common.classes.bo_logger import BoLogger
3
+ from atk_common.classes.docker_handler import DockerHandler
4
+ from atk_common.classes.env_handler import EnvHandler
5
+ from atk_common.classes.error_handler import ErrorHandler
6
+ from atk_common.classes.http_response_handler import HttpResponseHandler
7
+ from atk_common.classes.rabbitmq_consumer import RabbitMQConsumer
8
+
9
+ __all__ = [
10
+ 'BoLogger',
11
+ 'DockerHandler',
12
+ 'EnvHandler',
13
+ 'ErrorHandler',
14
+ 'HttpResponseHandler',
15
+ 'RabbitMQConsumer',
16
+ ]
@@ -0,0 +1,62 @@
1
+ import json
2
+ from typing import Optional
3
+ from opentelemetry.trace import get_current_span
4
+ from atk_common.interfaces import ILogger
5
+ from atk_common.enums import LogLevel
6
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
7
+ from atk_common.enums.log_level_enum import LogLevel
8
+
9
+ class BoLogger(ILogger):
10
+ def __init__(self, log_level: LogLevel, component, version):
11
+ if log_level is None:
12
+ self.log_level = LogLevel.INFO
13
+ else:
14
+ self.log_level = log_level
15
+ self.component = component
16
+ self.version = version
17
+
18
+ def _get_trace_context(self):
19
+ span = get_current_span()
20
+ ctx = span.get_span_context()
21
+ if ctx.is_valid:
22
+ return {
23
+ "trace_id": f"{ctx.trace_id:032x}",
24
+ "span_id": f"{ctx.span_id:016x}",
25
+ }
26
+ return {"trace_id": None, "span_id": None}
27
+
28
+ def _create_log_json(self, timestamp, level: LogLevel, message: str):
29
+ log_entry = {
30
+ "timestamp": timestamp,
31
+ "severity": level.name,
32
+ "message": message,
33
+ "component": self.component,
34
+ "version": self.version,
35
+ }
36
+ log_entry.update(self._get_trace_context())
37
+ return log_entry
38
+
39
+ def _log(self, level: LogLevel, message: str):
40
+ if level.value >= self.log_level.value:
41
+ timestamp = get_utc_date_time_str()
42
+ log_json = self._create_log_json(timestamp, level, message)
43
+ print(json.dumps(log_json))
44
+
45
+ def debug(self, message: str):
46
+ self._log(LogLevel.DEBUG, message)
47
+
48
+ def info(self, message: str):
49
+ self._log(LogLevel.INFO, message)
50
+
51
+ def warning(self, message: str):
52
+ self._log(LogLevel.WARNING, message)
53
+
54
+ def error(self, message: str):
55
+ self._log(LogLevel.ERROR, message)
56
+
57
+ def critical(self, message: str):
58
+ self._log(LogLevel.CRITICAL, message)
59
+
60
+ def set_level(self, log_level: LogLevel):
61
+ self.log_level = log_level
62
+
@@ -0,0 +1,76 @@
1
+ import docker
2
+ import socket
3
+ from atk_common.interfaces import IDockerHandler
4
+ from atk_common.interfaces import ILogger
5
+
6
+ class DockerHandler(IDockerHandler):
7
+ def __init__(self, logger: ILogger, image_name, image_version):
8
+ self.logger = logger
9
+ self.image_name = image_name
10
+ self.image_version = image_version
11
+ self.set_container_metadata()
12
+
13
+ def get_image_name_and_version(self, tags):
14
+ if tags:
15
+ # Use the first tag (usually only one)
16
+ full_tag = tags[0] # e.g., 'bo-crypto-wrapper-api:latest'
17
+
18
+ if ":" in full_tag:
19
+ image_name, image_version = full_tag.split(":", 1)
20
+ else:
21
+ image_name = full_tag
22
+ image_version = "<none>"
23
+ return image_name, image_version
24
+ else:
25
+ return None, None
26
+
27
+ def create_port_item(self, port, binding):
28
+ data = {}
29
+ data['port'] = port
30
+ data['binding'] = binding
31
+ return data
32
+
33
+ def create_container_log(self, container_data):
34
+ image_name = container_data.get('imageName') or 'Unknown'
35
+ image_version = container_data.get('imageVersion') or 'Unknown'
36
+
37
+ log_str = f'Image name: {image_name}, image version: {image_version}'
38
+ self.logger.info(log_str)
39
+
40
+ def get_current_container_info(self):
41
+ try:
42
+ data = {}
43
+ client = docker.from_env()
44
+
45
+ # Get current container's hostname (usually the container ID)
46
+ container_id = socket.gethostname()
47
+
48
+ # Fetch container object using partial ID
49
+ container = client.containers.get(container_id)
50
+
51
+ tags_info = self.get_image_name_and_version(container.image.tags)
52
+ data['imageName'] = tags_info[0]
53
+ data['imageVersion'] = tags_info[1]
54
+ data['containerName'] = container.name
55
+ ports = container.attrs['NetworkSettings']['Ports']
56
+ data['ports'] = []
57
+ if ports:
58
+ for container_port, host_bindings in ports.items():
59
+ if host_bindings:
60
+ for binding in host_bindings:
61
+ data['ports'].append(self.create_port_item(container_port, f"{binding['HostIp']}:{binding['HostPort']}"))
62
+ else:
63
+ data['ports'].append(self.create_port_item(container_port, None))
64
+ self.create_container_log(data)
65
+ return data
66
+ except Exception as e:
67
+ self.logger.error("Error getting container data:" + str(e))
68
+ return None
69
+
70
+ def set_container_metadata(self):
71
+ data = {}
72
+ data['imageName'] = self.image_name
73
+ data['imageVersion'] = self.image_version
74
+ data['containerName'] = self.image_name
75
+ self.create_container_log(data)
76
+ return data
@@ -0,0 +1,38 @@
1
+ import os
2
+ from typing import Optional
3
+ from atk_common.interfaces import IEnvHandler
4
+ from atk_common.interfaces import ILogger
5
+
6
+ class EnvHandler(IEnvHandler):
7
+ def __init__(self, logger: Optional[ILogger]): # type: ignore
8
+ self.logger = logger
9
+
10
+ def val_str(self, value):
11
+ if value is None:
12
+ return '<Empty>'
13
+ if isinstance(value, str):
14
+ if value.strip() == '' or value.lower() == 'null':
15
+ return '<Null>'
16
+ return value
17
+ return str(value)
18
+
19
+ def is_value_null_or_empty(self, value):
20
+ if isinstance(value, str):
21
+ return value.strip() == '' or value.lower() == 'null'
22
+ return False
23
+
24
+ def get_env_value(self, key, abort_on_error=True):
25
+ val = os.environ.get(key)
26
+ if self.logger:
27
+ self.logger.info(key + ':' + self.val_str(val))
28
+ if val is None and abort_on_error:
29
+ err_msg = f"Environment variable '{key}' is not set."
30
+ if self.logger:
31
+ self.logger.critical(err_msg)
32
+ raise ValueError(err_msg)
33
+ if self.is_value_null_or_empty(val):
34
+ return None
35
+ return val
36
+
37
+ def set_logger(self, logger: ILogger):
38
+ self.logger = logger
@@ -0,0 +1,81 @@
1
+ from http import HTTPStatus
2
+ from flask import Response
3
+ from enum import Enum
4
+ import json
5
+ from typing import Optional
6
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
7
+ from atk_common.enums.api_error_type_enum import ApiErrorType
8
+ from atk_common.utils.http_utils import is_http_status_internal
9
+ from atk_common.utils.internal_response_utils import create_response
10
+ from atk_common.interfaces import IErrorHandler
11
+ from atk_common.interfaces import ILogger
12
+ from atk_common.interfaces import IDockerHandler
13
+
14
+ class ErrorHandler(IErrorHandler):
15
+ def __init__(self, logger: ILogger, docker_handler: IDockerHandler):
16
+ self.logger = logger
17
+ self.docker_handler = docker_handler
18
+
19
+ def get_message(self, error):
20
+ if hasattr(error, 'message'):
21
+ return str(error.message)
22
+ else:
23
+ return str(error)
24
+
25
+ def create_error_log(self, data):
26
+ err_str = data['message'] + ', statusCode: ' + str(data['statusCode']) + ', method: ' + data['method']
27
+ if data['containerInfo'] is not None:
28
+ err_str += ', imageName: ' + data['containerInfo']['imageName']
29
+ err_str += ', imageVersion: ' + data['containerInfo']['imageVersion']
30
+ err_str += ', containerName: ' + data['containerInfo']['containerName']
31
+ else:
32
+ err_str += ', imageName: <none>'
33
+ err_str += ', imageVersion: <none>'
34
+ err_str += ', containerName: <none>'
35
+ self.logger.critical(err_str)
36
+
37
+ def get_error_entity(self, error, method, error_type, status_code, container_info):
38
+ data = {}
39
+ data['statusCode'] = status_code
40
+ data['exceptionType'] = str(type(error))
41
+ data['errorType'] = error_type.value if isinstance(error_type, Enum) else error_type
42
+ data['message'] = self.get_message(error)
43
+ data['method'] = method
44
+ data['timestamp'] = get_utc_date_time_str()
45
+ data['containerInfo'] = container_info
46
+ self.create_error_log(data)
47
+ return Response(
48
+ response=json.dumps(data),
49
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
50
+ mimetype='application/json'
51
+ )
52
+
53
+ def resend_error_entity(self, error_entity):
54
+ self.create_error_log(error_entity)
55
+ return Response(
56
+ response=json.dumps(error_entity),
57
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
58
+ mimetype='application/json'
59
+ )
60
+
61
+ def handle_error(self, resp, status):
62
+ if is_http_status_internal(resp.status_code):
63
+ self.logger.info(resp.json().get('message'))
64
+ return create_response(status, resp.status_code, resp.json())
65
+ else:
66
+ self.logger.info(resp.text)
67
+ return create_response(status, resp.status_code, resp.text)
68
+
69
+ def get_response_error(self, resp):
70
+ if is_http_status_internal(resp.status_code):
71
+ return resp.json()
72
+ else:
73
+ return resp.text
74
+
75
+ # Return values:
76
+ # 1 - Connection error
77
+ # 2 - Internal error
78
+ def get_error_type(self, conn):
79
+ if conn is None:
80
+ return ApiErrorType.CONNECTION
81
+ return ApiErrorType.INTERNAL
@@ -0,0 +1,42 @@
1
+ import json
2
+ from flask import Response
3
+ from http import HTTPStatus
4
+ from atk_common.enums.api_error_type_enum import ApiErrorType
5
+ from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
6
+ from atk_common.utils.internal_response_utils import is_response_http
7
+ from atk_common.interfaces import IErrorHandler
8
+ from atk_common.interfaces import IHttpResponseHandler
9
+
10
+ class HttpResponseHandler(IHttpResponseHandler):
11
+ def __init__(self, error_handler: IErrorHandler):
12
+ self.error_handler = error_handler
13
+
14
+ def _convert_response_data(self, data):
15
+ if isinstance(data, dict):
16
+ return json.dumps(data)
17
+ elif isinstance(data, list):
18
+ return json.dumps(data)
19
+ elif isinstance(data, str):
20
+ json_data = json.loads(data)
21
+ return json.dumps(json_data)
22
+ else:
23
+ return data
24
+
25
+ # If response['status'] == 0 (OK, http status = 200): create Response and return response['responseMsg']
26
+ # If http status == 500:
27
+ # If response['status'] == 1 (HTTP): resend received error entity
28
+ # If response['status'] == 2 (INTERNAL): create new error entity and return as response
29
+ # If http status other value: create new error entity and return as response
30
+ def http_response(self, method, response, container_info):
31
+ if is_http_status_ok(response['statusCode']):
32
+ return Response(
33
+ response=self._convert_response_data(response['responseMsg']),
34
+ status=HTTPStatus.OK,
35
+ mimetype=response['contentType'],
36
+ headers=response['httpHeaders']
37
+ )
38
+ elif is_http_status_internal(response['statusCode']):
39
+ if is_response_http(response):
40
+ return self.error_handler.resend_error_entity(response['responseMsg'])
41
+ return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.INTERNAL, response['statusCode'], container_info)
42
+ return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.CONNECTION, response['statusCode'], container_info)
@@ -0,0 +1,72 @@
1
+ from kombu import Connection, Queue, Consumer
2
+ from kombu.exceptions import ConnectionError, ChannelError # Import relevant exceptions
3
+ import socket
4
+ import time
5
+ from atk_common.interfaces import ILogger
6
+ CONNECTION_ERRORS = (
7
+ ConnectionError,
8
+ ChannelError,
9
+ OSError,
10
+ )
11
+
12
+ class RabbitMQConsumer:
13
+ def __init__(self, queue_name, user, pwd, host, vhost, dlx, dlq, content_type, message_handler, logger: ILogger):
14
+ self.logger = logger
15
+
16
+ rabbit_url = 'amqp://' + user + ':' + pwd + '@' + host + '/' + vhost
17
+ self.logger.info(f"Initializing connection to RabbitMQ {queue_name} at {host}")
18
+
19
+ self.connection = Connection(rabbit_url, heartbeat=10)
20
+ if dlx is not None and dlq is not None:
21
+ queue = Queue(name=queue_name,
22
+ queue_arguments={
23
+ 'x-dead-letter-exchange': dlx,
24
+ 'x-dead-letter-routing-key': dlq},
25
+ )
26
+ else:
27
+ queue = Queue(name=queue_name)
28
+ self.queue = queue
29
+ self.content_type = content_type # Optional content type for message handling
30
+ self.message_handler = message_handler # Custom message handler
31
+
32
+ def consume(self):
33
+ conn = self.establish_connection()
34
+ self.logger.info("Begin consuming messages...")
35
+
36
+ while True:
37
+ try:
38
+ conn.drain_events(timeout=2)
39
+ self.logger.debug("Drained event or heartbeat.")
40
+ except socket.timeout:
41
+ self.logger.debug("Socket timeout, checking heartbeat...")
42
+ conn.heartbeat_check()
43
+ except CONNECTION_ERRORS as e:
44
+ self.logger.critical(f"Connection lost: {e}. Reconnecting...")
45
+ return # break loop and re-establish connection
46
+ except Exception as e:
47
+ self.logger.critical(f"Top-level exception in consume loop: {e}. Restarting after delay...")
48
+ return
49
+
50
+ def establish_connection(self):
51
+ revived_connection = self.connection.clone()
52
+ revived_connection.ensure_connection(max_retries=3)
53
+ channel = revived_connection.channel()
54
+
55
+ consumer = Consumer(
56
+ revived_connection,
57
+ queues=self.queue,
58
+ callbacks=[self.message_handler],
59
+ accept=[self.content_type] if self.content_type else None)
60
+ consumer.revive(channel)
61
+ consumer.consume()
62
+ self.logger.info("Connection revived!")
63
+ return revived_connection
64
+
65
+ def run(self):
66
+ self.logger.info("Starting RabbitMQ consumer run loop...")
67
+ while True:
68
+ try:
69
+ self.consume()
70
+ except Exception as e:
71
+ self.logger.critical(f"Top-level exception in run loop: {e}. Restarting after delay...")
72
+ time.sleep(5)
@@ -0,0 +1,14 @@
1
+ # __init__.py
2
+ from atk_common.interfaces.docker_handler_interface import IDockerHandler
3
+ from atk_common.interfaces.env_handler_interface import IEnvHandler
4
+ from atk_common.interfaces.error_handler_interface import IErrorHandler
5
+ from atk_common.interfaces.http_response_handler_interface import IHttpResponseHandler
6
+ from atk_common.interfaces.logger_interface import ILogger
7
+
8
+ __all__ = [
9
+ 'IDockerHandler',
10
+ 'IEnvHandler',
11
+ 'IErrorHandler',
12
+ 'ILogger',
13
+ 'IHttpResponseHandler',
14
+ ]
@@ -0,0 +1,9 @@
1
+ # interfaces/logger_interface.py
2
+ from typing import Protocol
3
+
4
+ class IDockerHandler(Protocol):
5
+ def get_image_name_and_version(self, tags) -> None: ...
6
+ def create_port_item(port, binding) -> None: ...
7
+ def create_container_log(container_data) -> None: ...
8
+ def get_current_container_info() -> None: ...
9
+ def set_container_metadata(image_name_env_key, image_version_env_key) -> None: ...
@@ -0,0 +1,8 @@
1
+ # interfaces/env_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IEnvHandler(Protocol):
5
+ def val_str(self, value) -> None: ...
6
+ def is_value_null_or_empty(self, value) -> None: ...
7
+ def get_env_value(self, msg: str) -> None: ...
8
+ def set_logger(self, logger) -> None: ...
@@ -0,0 +1,11 @@
1
+ # interfaces/error_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IErrorHandler(Protocol):
5
+ def get_message(self, error) -> None: ...
6
+ def create_error_log(self, data) -> None: ...
7
+ def get_error_entity(self, error, method, error_type, status_code, container_info) -> None: ...
8
+ def resend_error_entity(self, error_entity) -> None: ...
9
+ def handle_error(self, resp, status) -> None: ...
10
+ def get_response_error(self, resp) -> None: ...
11
+ def get_error_type(self, conn) -> None: ...
@@ -0,0 +1,5 @@
1
+ # interfaces/env_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IHttpResponseHandler(Protocol):
5
+ def http_response(self, method, response, container_info) -> None: ...
@@ -0,0 +1,9 @@
1
+ # interfaces/logger_interface.py
2
+ from typing import Protocol
3
+
4
+ class ILogger(Protocol):
5
+ def debug(self, msg: str) -> None: ...
6
+ def info(self, msg: str) -> None: ...
7
+ def warning(self, msg: str) -> None: ...
8
+ def error(self, msg: str) -> None: ...
9
+ def critical(self, msg: str) -> None: ...
@@ -0,0 +1,47 @@
1
+ # __init__.py
2
+ from atk_common.utils.consumer_retry_handler import create_retry_handler
3
+ from atk_common.utils.datetime_utils import \
4
+ get_utc_date_time, \
5
+ get_utc_date_time_str, \
6
+ get_utc_date_time_str_with_z, \
7
+ seconds_to_utc_timestamp, \
8
+ get_utc_date_from_iso, \
9
+ adjust_millisescond, \
10
+ convert_to_utc, \
11
+ convert_to_utc_image_dt
12
+ from atk_common.utils.db_utils import sql, sql_with_record, convert_none_to_null, date_time_utc_column
13
+ from atk_common.utils.default_should_retry import default_should_retry
14
+ from atk_common.utils.error_utils import get_message
15
+ from atk_common.utils.hash_utils import create_enforcement_hash
16
+ from atk_common.utils.http_utils import is_http_status_ok, is_http_status_internal, get_test_response
17
+ from atk_common.utils.internal_response_utils import create_response, is_response_ok, is_response_http, is_response_internal
18
+ from atk_common.utils.file_utils import get_image_file_type
19
+ from atk_common.utils.mq_utils import decode_message
20
+
21
+ __all__ = [
22
+ 'create_retry_handler',
23
+ 'get_utc_date_time',
24
+ 'get_utc_date_time_str',
25
+ 'get_utc_date_time_str_with_z',
26
+ 'seconds_to_utc_timestamp',
27
+ 'get_utc_date_from_iso',
28
+ 'adjust_millisescond',
29
+ 'convert_to_utc',
30
+ 'convert_to_utc_image_dt',
31
+ 'sql',
32
+ 'sql_with_record',
33
+ 'convert_none_to_null',
34
+ 'date_time_utc_column',
35
+ 'default_should_retry',
36
+ 'get_message',
37
+ 'create_enforcement_hash',
38
+ 'is_http_status_ok',
39
+ 'is_http_status_internal',
40
+ 'get_test_response',
41
+ 'create_response',
42
+ 'is_response_ok',
43
+ 'is_response_http',
44
+ 'is_response_internal',
45
+ 'get_image_file_type',
46
+ 'decode_message',
47
+ ]
@@ -1,13 +1,17 @@
1
1
  from kombu import Producer
2
- from atk_common.internal_response_utils import is_response_ok
2
+ from atk_common.classes.bo_logger import BoLogger
3
+ from atk_common.utils.internal_response_utils import is_response_ok
3
4
 
4
- def create_retry_handler(process_func, connection, exchange, routing_key, should_retry, log, declare=None):
5
- """
6
- process_func: your original handler function (body, message)
7
- retry_queue: kombu.Queue instance to republish to
8
- should_retry: function(message_status) -> bool
9
- log: function for logging
10
- """
5
+ """
6
+ process_func: your original handler function (body, message)
7
+ connection: kombu.Connection instance to use for retrying (nullable)
8
+ exchange: kombu.Exchange instance to publish to (nullable)
9
+ routing_key: routing key for the exchange (nullable)
10
+ should_retry: function(message_status) -> bool
11
+ bo_logger: BoLogger instance for logging
12
+ declare: optional kombu.DeclarativeExchange or Queue to declare before publishing
13
+ """
14
+ def create_retry_handler(process_func, connection, exchange, routing_key, should_retry, bo_logger: BoLogger, declare=None):
11
15
 
12
16
  def handler(body, message):
13
17
  try:
@@ -18,7 +22,7 @@ def create_retry_handler(process_func, connection, exchange, routing_key, should
18
22
  if connection is not None:
19
23
  # Use retry queue
20
24
  if should_retry(process_response):
21
- log("Retrying after delay...")
25
+ bo_logger.info("Retrying after delay...")
22
26
  with connection.Producer() as producer:
23
27
  producer.publish(
24
28
  message.body,
@@ -33,17 +37,19 @@ def create_retry_handler(process_func, connection, exchange, routing_key, should
33
37
  )
34
38
  message.ack()
35
39
  else:
36
- log("Sending to DLQ...")
40
+ bo_logger.critical("Sending to DLQ...")
37
41
  message.reject(requeue=False)
38
42
  else:
39
43
  if should_retry(process_response):
40
- log("Requing...")
41
- message.requeue()
42
- else:
43
- log("Sending to DLQ...")
44
+ bo_logger.critical("Sending to DLQ...")
44
45
  message.reject(requeue=False)
46
+ # bo_logger.critical("Requing...")
47
+ # message.requeue()
48
+ else:
49
+ bo_logger.critical("Discarding message...")
50
+ message.ack()
45
51
  except Exception as e:
46
- log(f"Error during processing: {e}")
52
+ bo_logger.critical(f"Error during processing: {e}, sending to DLQ...")
47
53
  message.reject(requeue=False)
48
54
 
49
55
  return handler
@@ -1,6 +1,6 @@
1
1
  from atk_common.enums.api_error_type_enum import ApiErrorType
2
2
  from atk_common.enums.response_status_type_enum import ResponseStatusType
3
- from atk_common.http_utils import is_http_status_internal, is_http_status_ok
3
+ from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
4
4
 
5
5
  def default_should_retry(message_status):
6
6
  if message_status['status'] == ResponseStatusType.HTTP:
@@ -0,0 +1,5 @@
1
+ def get_message(error):
2
+ if hasattr(error, 'message'):
3
+ return str(error.message)
4
+ else:
5
+ return str(error)
@@ -1,5 +1,5 @@
1
1
  from http import HTTPStatus
2
- from atk_common.datetime_utils import get_utc_date_time_str
2
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
3
3
 
4
4
  def is_http_status_ok(status_code):
5
5
  return status_code >= HTTPStatus.OK.value and status_code < HTTPStatus.MULTIPLE_CHOICES.value
@@ -0,0 +1,30 @@
1
+ import json
2
+ from http import HTTPStatus
3
+ from atk_common.classes.bo_logger import BoLogger
4
+ from atk_common.enums.response_status_type_enum import ResponseStatusType
5
+ from atk_common.utils.error_utils import get_message
6
+ from atk_common.utils.internal_response_utils import create_response
7
+
8
+ def decode_message(body, message, bo_logger: BoLogger):
9
+ import gzip
10
+ import msgpack
11
+ try:
12
+ content_encoding = message.headers.get('content_encoding')
13
+ if content_encoding is not None and content_encoding == 'gzip':
14
+ body = gzip.decompress(body)
15
+ if message.content_type is None or message.content_type == '':
16
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
17
+ elif message.content_type == 'application/json':
18
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
19
+ elif message.content_type == 'application/octet-stream':
20
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
21
+ elif message.content_type == 'application/x-msgpack' or message.content_type == 'application/msgpack':
22
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, msgpack.unpackb(body, raw=False))
23
+ elif message.content_type.startswith('text/'):
24
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body.decode('utf-8'))
25
+ else:
26
+ bo_logger.critical(f"Unknown message content type {message.content_type}. Cannot decode message.")
27
+ return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
28
+ except Exception as error:
29
+ bo_logger.critical('Error decoding message: ' + get_message(error))
30
+ return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atk_common
3
- Version: 2.2.0
3
+ Version: 3.1.0
4
4
  Summary: ATK common methods
5
5
  Home-page: https://github.com/pypa/atk_common
6
6
  Author: Roger