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.
Files changed (24) hide show
  1. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/PKG-INFO +4 -2
  2. airless_google_cloud_core-0.0.4/airless/google/cloud/core/operator/__init__.py +12 -0
  3. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/base.py +6 -2
  4. airless_google_cloud_core-0.0.4/airless/google/cloud/core/operator/delay.py +11 -0
  5. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/error.py +49 -7
  6. airless_google_cloud_core-0.0.4/airless/google/cloud/pubsub/hook/__init__.py +5 -0
  7. airless_google_cloud_core-0.0.4/airless/google/cloud/pubsub/hook/pubsub.py +40 -0
  8. {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
  9. {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
  10. airless_google_cloud_core-0.0.4/airless_google_cloud_core.egg-info/requires.txt +2 -0
  11. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/pyproject.toml +22 -11
  12. airless_google_cloud_core-0.0.4/requirements.txt +3 -0
  13. airless_google_cloud_core-0.0.4/tests/test_fake.py +3 -0
  14. airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/core/operator/__init__.py +0 -4
  15. airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/core/operator/delay.py +0 -18
  16. airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/pubsub/hook/__init__.py +0 -1
  17. airless_google_cloud_core-0.0.3.dev2/airless/google/cloud/pubsub/hook/pubsub.py +0 -30
  18. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/README.md +0 -0
  19. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/__init__.py +0 -0
  20. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/core/operator/redirect.py +0 -0
  21. {airless_google_cloud_core-0.0.3.dev2 → airless_google_cloud_core-0.0.4}/airless/google/cloud/pubsub/__init__.py +0 -0
  22. {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
  23. {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
  24. {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
1
+ Metadata-Version: 2.2
2
2
  Name: airless-google-cloud-core
3
- Version: 0.0.3.dev2
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) # Cloud function max execution time is 540s (9 min), so set it to wait max 480s (8 min)
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,5 @@
1
+ from .pubsub import (GooglePubsubHook)
2
+
3
+ __all__ = [
4
+ 'GooglePubsubHook'
5
+ ]
@@ -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
1
+ Metadata-Version: 2.2
2
2
  Name: airless-google-cloud-core
3
- Version: 0.0.3.dev2
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/top_level.txt
16
+ airless_google_cloud_core.egg-info/requires.txt
17
+ airless_google_cloud_core.egg-info/top_level.txt
18
+ tests/test_fake.py
@@ -0,0 +1,2 @@
1
+ google-cloud-pubsub<3.0.0,>=2.13.11
2
+ airless-core<0.2.0,>=0.1.1.dev6
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "airless-google-cloud-core"
3
- version = "0.0.3-dev2"
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.flake8]
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
- # Ignore error max line length
45
+ [tool.ruff.lint]
40
46
  ignore = ["E501"]
41
-
42
- # Maximum allowed line length
43
- max-line-length = 120
44
-
45
- # Cyclomatic complexity threshold
46
- max-complexity = 15
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"
@@ -0,0 +1,3 @@
1
+ google-cloud-pubsub>=2.13.11,<3.0.0
2
+
3
+ airless-core>=0.1.1.dev6,<0.2.0
@@ -0,0 +1,3 @@
1
+
2
+ def test_fake():
3
+ assert 1 == 1
@@ -1,4 +0,0 @@
1
- from .base import (GoogleBaseEventOperator, GoogleBaseFileOperator)
2
- from .delay import (GoogleDelayOperator)
3
- from .error import (GoogleErrorReprocessOperator)
4
- from .redirect import (GoogleRedirectOperator)
@@ -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}')