airless-google-cloud-core 0.0.3.dev2__tar.gz → 0.0.4__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.
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/PKG-INFO +4 -2
- airless_google_cloud_core-0.0.4/airless/google/cloud/core/operator/__init__.py +12 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/base.py +6 -2
- airless_google_cloud_core-0.0.4/airless/google/cloud/core/operator/delay.py +11 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/error.py +49 -7
- airless_google_cloud_core-0.0.4/airless/google/cloud/pubsub/hook/__init__.py +5 -0
- airless_google_cloud_core-0.0.4/airless/google/cloud/pubsub/hook/pubsub.py +40 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless_google_cloud_core.egg-info/PKG-INFO +4 -2
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless_google_cloud_core.egg-info/SOURCES.txt +4 -1
- airless_google_cloud_core-0.0.4/airless_google_cloud_core.egg-info/requires.txt +2 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/pyproject.toml +22 -11
- airless_google_cloud_core-0.0.4/requirements.txt +3 -0
- airless_google_cloud_core-0.0.4/tests/test_fake.py +3 -0
- airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/core/operator/__init__.py +0 -4
- airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/core/operator/delay.py +0 -18
- airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/pubsub/hook/__init__.py +0 -1
- airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/pubsub/hook/pubsub.py +0 -30
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/README.md +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/__init__.py +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/redirect.py +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/pubsub/__init__.py +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless_google_cloud_core.egg-info/dependency_links.txt +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless_google_cloud_core.egg-info/top_level.txt +0 -0
- {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/setup.cfg +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: airless-google-cloud-core
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Airless package with the main components to use airless in google cloud
|
|
5
5
|
License: MIT License
|
|
6
6
|
Project-URL: Homepage, https://github.com/astercapital/airless
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/astercapital/airless/issues
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: google-cloud-pubsub<3.0.0,>=2.13.11
|
|
11
|
+
Requires-Dist: airless-core<0.2.0,>=0.1.1.dev6
|
|
10
12
|
|
|
11
13
|
# airless-google-cloud-core
|
|
12
14
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .base import (GoogleBaseEventOperator, GoogleBaseFileOperator)
|
|
2
|
+
from .delay import (GoogleDelayOperator)
|
|
3
|
+
from .error import (GoogleErrorReprocessOperator)
|
|
4
|
+
from .redirect import (GoogleRedirectOperator)
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'GoogleBaseEventOperator',
|
|
8
|
+
'GoogleBaseFileOperator',
|
|
9
|
+
'GoogleDelayOperator',
|
|
10
|
+
'GoogleErrorReprocessOperator',
|
|
11
|
+
'GoogleRedirectOperator'
|
|
12
|
+
]
|
|
@@ -5,14 +5,18 @@ from airless.google.cloud.pubsub.hook import GooglePubsubHook
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class GoogleBaseFileOperator(BaseFileOperator):
|
|
8
|
+
"""Base operator for file operations in Google Cloud."""
|
|
8
9
|
|
|
9
|
-
def __init__(self):
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
"""Initializes the GoogleBaseFileOperator."""
|
|
10
12
|
super().__init__()
|
|
11
13
|
self.queue_hook = GooglePubsubHook() # Have to redefine this attribute for each vendor
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class GoogleBaseEventOperator(BaseEventOperator):
|
|
17
|
+
"""Base operator for event operations in Google Cloud."""
|
|
15
18
|
|
|
16
|
-
def __init__(self):
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
"""Initializes the GoogleBaseEventOperator."""
|
|
17
21
|
super().__init__()
|
|
18
22
|
self.queue_hook = GooglePubsubHook() # Have to redefine this attribute for each vendor
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
from airless.core.operator import DelayOperator
|
|
3
|
+
from airless.google.cloud.core.operator import GoogleBaseEventOperator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GoogleDelayOperator(GoogleBaseEventOperator, DelayOperator):
|
|
7
|
+
"""Operator that adds a delay to the pipeline."""
|
|
8
|
+
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
"""Initializes the GoogleDelayOperator."""
|
|
11
|
+
super().__init__()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
4
5
|
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
|
|
@@ -10,12 +11,19 @@ from airless.google.cloud.core.operator import GoogleBaseEventOperator
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
14
|
+
"""Operator for reprocessing errors in Google Cloud."""
|
|
13
15
|
|
|
14
|
-
def __init__(self):
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
"""Initializes the GoogleErrorReprocessOperator."""
|
|
15
18
|
super().__init__()
|
|
16
19
|
|
|
17
|
-
def execute(self, data, topic):
|
|
20
|
+
def execute(self, data: Dict[str, Any], topic: str) -> None:
|
|
21
|
+
"""Executes the error reprocessing logic.
|
|
18
22
|
|
|
23
|
+
Args:
|
|
24
|
+
data (Dict[str, Any]): The input data containing error information.
|
|
25
|
+
topic (str): The Pub/Sub topic to publish messages to.
|
|
26
|
+
"""
|
|
19
27
|
input_type = data['input_type']
|
|
20
28
|
origin = data.get('origin', 'undefined')
|
|
21
29
|
message_id = data.get('event_id')
|
|
@@ -25,7 +33,7 @@ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
|
25
33
|
retry_interval = metadata.get('retry_interval', 5)
|
|
26
34
|
retries = metadata.get('retries', 0)
|
|
27
35
|
max_retries = metadata.get('max_retries', 2)
|
|
28
|
-
max_interval = metadata.get('max_interval', 480)
|
|
36
|
+
max_interval = metadata.get('max_interval', 480)
|
|
29
37
|
|
|
30
38
|
if (input_type == 'event') and (retries < max_retries):
|
|
31
39
|
time.sleep(min(retry_interval ** retries, max_interval))
|
|
@@ -60,7 +68,14 @@ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
|
60
68
|
if slack_send_topic and (origin != slack_send_topic):
|
|
61
69
|
self.notify_slack(origin, message_id, data)
|
|
62
70
|
|
|
63
|
-
def notify_email(self, origin, message_id, data):
|
|
71
|
+
def notify_email(self, origin: str, message_id: str, data: Dict[str, Any]) -> None:
|
|
72
|
+
"""Sends a notification email.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
origin (str): The origin of the error.
|
|
76
|
+
message_id (str): The ID of the message.
|
|
77
|
+
data (Dict[str, Any]): The data related to the error.
|
|
78
|
+
"""
|
|
64
79
|
email_message = {
|
|
65
80
|
'sender': get_config('EMAIL_SENDER_ERROR'),
|
|
66
81
|
'recipients': eval(get_config('EMAIL_RECIPIENTS_ERROR')),
|
|
@@ -72,7 +87,14 @@ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
|
72
87
|
topic=get_config('PUBSUB_TOPIC_EMAIL_SEND'),
|
|
73
88
|
data=email_message)
|
|
74
89
|
|
|
75
|
-
def notify_slack(self, origin, message_id, data):
|
|
90
|
+
def notify_slack(self, origin: str, message_id: str, data: Dict[str, Any]) -> None:
|
|
91
|
+
"""Sends a notification to Slack.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
origin (str): The origin of the error.
|
|
95
|
+
message_id (str): The ID of the message.
|
|
96
|
+
data (Dict[str, Any]): The data related to the error.
|
|
97
|
+
"""
|
|
76
98
|
slack_message = {
|
|
77
99
|
'channels': eval(get_config('SLACK_CHANNELS_ERROR')),
|
|
78
100
|
'message': f'{origin} | {message_id}\n\n{json.dumps(data["data"])}\n\n{data["error"]}'
|
|
@@ -82,7 +104,17 @@ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
|
82
104
|
topic=get_config('PUBSUB_TOPIC_SLACK_SEND'),
|
|
83
105
|
data=slack_message)
|
|
84
106
|
|
|
85
|
-
def prepare_row(self, row, message_id, origin):
|
|
107
|
+
def prepare_row(self, row: Dict[str, Any], message_id: Optional[str], origin: Optional[str]) -> Dict[str, Any]:
|
|
108
|
+
"""Prepares a row for insertion.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
row (Dict[str, Any]): The row data.
|
|
112
|
+
message_id (Optional[str]): The message ID.
|
|
113
|
+
origin (Optional[str]): The origin of the message.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dict[str, Any]: The prepared row.
|
|
117
|
+
"""
|
|
86
118
|
return {
|
|
87
119
|
'_event_id': message_id or 1234,
|
|
88
120
|
'_resource': origin or 'local',
|
|
@@ -90,6 +122,16 @@ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
|
|
|
90
122
|
'_created_at': str(datetime.now())
|
|
91
123
|
}
|
|
92
124
|
|
|
93
|
-
def prepare_rows(self, data, message_id, origin):
|
|
125
|
+
def prepare_rows(self, data: Any, message_id: Optional[str], origin: Optional[str]) -> List[Dict[str, Any]]:
|
|
126
|
+
"""Prepares multiple rows for insertion.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
data (Any): The data to prepare.
|
|
130
|
+
message_id (Optional[str]): The message ID.
|
|
131
|
+
origin (Optional[str]): The origin of the message.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List[Dict[str, Any]]: The list of prepared rows.
|
|
135
|
+
"""
|
|
94
136
|
prepared_rows = data if isinstance(data, list) else [data]
|
|
95
137
|
return [self.prepare_row(row, message_id, origin) for row in prepared_rows]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from google.cloud import pubsub_v1
|
|
6
|
+
|
|
7
|
+
from airless.core.hook import QueueHook
|
|
8
|
+
from airless.core.utils import get_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GooglePubsubHook(QueueHook):
|
|
12
|
+
"""Hook for interacting with Google Pub/Sub."""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
"""Initializes the GooglePubsubHook."""
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.publisher = pubsub_v1.PublisherClient()
|
|
18
|
+
|
|
19
|
+
def publish(self, project: str, topic: str, data: Any) -> str:
|
|
20
|
+
"""Publishes a message to a specified Pub/Sub topic.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
project (str): The GCP project ID.
|
|
24
|
+
topic (str): The Pub/Sub topic name.
|
|
25
|
+
data (Any): The data to publish.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: A confirmation message.
|
|
29
|
+
"""
|
|
30
|
+
if get_config('ENV') == 'prod':
|
|
31
|
+
topic_path = self.publisher.topic_path(project or get_config('GCP_PROJECT'), topic)
|
|
32
|
+
|
|
33
|
+
message_bytes = json.dumps(data).encode('utf-8')
|
|
34
|
+
|
|
35
|
+
publish_future = self.publisher.publish(topic_path, data=message_bytes)
|
|
36
|
+
publish_future.result(timeout=10)
|
|
37
|
+
self.logger.info(f'published to {project or get_config("GCP_PROJECT")}.{topic}')
|
|
38
|
+
return 'Message published.'
|
|
39
|
+
else:
|
|
40
|
+
self.logger.debug(f'[DEV] Message published to Project {project or get_config("GCP_PROJECT")}, Topic {topic}: {data}')
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: airless-google-cloud-core
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Airless package with the main components to use airless in google cloud
|
|
5
5
|
License: MIT License
|
|
6
6
|
Project-URL: Homepage, https://github.com/astercapital/airless
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/astercapital/airless/issues
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: google-cloud-pubsub<3.0.0,>=2.13.11
|
|
11
|
+
Requires-Dist: airless-core<0.2.0,>=0.1.1.dev6
|
|
10
12
|
|
|
11
13
|
# airless-google-cloud-core
|
|
12
14
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
pyproject.toml
|
|
3
|
+
requirements.txt
|
|
3
4
|
airless/google/cloud/core/__init__.py
|
|
4
5
|
airless/google/cloud/core/operator/__init__.py
|
|
5
6
|
airless/google/cloud/core/operator/base.py
|
|
@@ -12,4 +13,6 @@ airless/google/cloud/pubsub/hook/pubsub.py
|
|
|
12
13
|
airless_google_cloud_core.egg-info/PKG-INFO
|
|
13
14
|
airless_google_cloud_core.egg-info/SOURCES.txt
|
|
14
15
|
airless_google_cloud_core.egg-info/dependency_links.txt
|
|
15
|
-
airless_google_cloud_core.egg-info/
|
|
16
|
+
airless_google_cloud_core.egg-info/requires.txt
|
|
17
|
+
airless_google_cloud_core.egg-info/top_level.txt
|
|
18
|
+
tests/test_fake.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "airless-google-cloud-core"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4"
|
|
4
4
|
description = "Airless package with the main components to use airless in google cloud"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9"
|
|
@@ -20,11 +20,13 @@ minversion = "6.0"
|
|
|
20
20
|
addopts = "--capture=sys --cache-clear --disable-warnings --junitxml=pytest.xml --cov-report=xml:cov.xml --cov-report=term-missing:skip-covered --cov=airless"
|
|
21
21
|
testpaths = ["tests"]
|
|
22
22
|
|
|
23
|
+
[tool.setuptools.dynamic]
|
|
24
|
+
dependencies = {file = ["requirements.txt"]}
|
|
25
|
+
|
|
23
26
|
[tool.setuptools.packages.find]
|
|
24
27
|
include = ["airless*"]
|
|
25
28
|
|
|
26
|
-
[tool.
|
|
27
|
-
# List of patterns to ignore
|
|
29
|
+
[tool.ruff]
|
|
28
30
|
exclude = [
|
|
29
31
|
".git",
|
|
30
32
|
"__pycache__",
|
|
@@ -33,14 +35,23 @@ exclude = [
|
|
|
33
35
|
"*.pyc",
|
|
34
36
|
"*.egg-info",
|
|
35
37
|
"*.egg",
|
|
36
|
-
"temp"
|
|
38
|
+
"temp",
|
|
39
|
+
"venv"
|
|
37
40
|
]
|
|
41
|
+
line-length = 88
|
|
42
|
+
indent-width = 4
|
|
43
|
+
target-version = "py39"
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
[tool.ruff.lint]
|
|
40
46
|
ignore = ["E501"]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
fixable = ["ALL"]
|
|
48
|
+
unfixable = []
|
|
49
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
50
|
+
|
|
51
|
+
[tool.ruff.format]
|
|
52
|
+
quote-style = "single"
|
|
53
|
+
indent-style = "space"
|
|
54
|
+
skip-magic-trailing-comma = false
|
|
55
|
+
line-ending = "lf"
|
|
56
|
+
docstring-code-format = false
|
|
57
|
+
docstring-code-line-length = "dynamic"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from airless.core.operator import DelayOperator
|
|
3
|
-
from airless.google.cloud.core.operator import GoogleBaseEventOperator
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class GoogleDelayOperator(GoogleBaseEventOperator, DelayOperator):
|
|
8
|
-
|
|
9
|
-
"""
|
|
10
|
-
Operator that adds a delay to the pipeline. The maximum delay is 500 due
|
|
11
|
-
to Cloud Functions time constraint of 540 of execution time
|
|
12
|
-
|
|
13
|
-
It can receive 1 parameter:
|
|
14
|
-
seconds: number of seconds to wait
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
def __init__(self):
|
|
18
|
-
super().__init__()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .pubsub import (GooglePubsubHook)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
from google.cloud import pubsub_v1
|
|
5
|
-
|
|
6
|
-
from airless.core.hook import QueueHook
|
|
7
|
-
from airless.core.utils import get_config
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class GooglePubsubHook(QueueHook):
|
|
11
|
-
|
|
12
|
-
def __init__(self):
|
|
13
|
-
super().__init__()
|
|
14
|
-
self.publisher = pubsub_v1.PublisherClient()
|
|
15
|
-
|
|
16
|
-
def publish(self, project, topic, data):
|
|
17
|
-
|
|
18
|
-
if get_config('ENV') == 'prod':
|
|
19
|
-
topic_path = self.publisher.topic_path(project, topic)
|
|
20
|
-
|
|
21
|
-
message_bytes = json \
|
|
22
|
-
.dumps(data) \
|
|
23
|
-
.encode('utf-8')
|
|
24
|
-
|
|
25
|
-
publish_future = self.publisher.publish(topic_path, data=message_bytes)
|
|
26
|
-
publish_future.result(timeout=10)
|
|
27
|
-
self.logger.info(f'published to {project}.{topic}')
|
|
28
|
-
return 'Message published.'
|
|
29
|
-
else:
|
|
30
|
-
self.logger.debug(f'[DEV] Message published to Project {project}, Topic {topic}: {data}')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|