airless-google-cloud-core 0.0.1__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 (21) hide show
  1. airless_google_cloud_core-0.0.1/PKG-INFO +16 -0
  2. airless_google_cloud_core-0.0.1/README.md +5 -0
  3. airless_google_cloud_core-0.0.1/airless/__init__.py +1 -0
  4. airless_google_cloud_core-0.0.1/airless/google/__init__.py +0 -0
  5. airless_google_cloud_core-0.0.1/airless/google/cloud/__init__.py +0 -0
  6. airless_google_cloud_core-0.0.1/airless/google/cloud/core/__init__.py +0 -0
  7. airless_google_cloud_core-0.0.1/airless/google/cloud/core/operator/__init__.py +4 -0
  8. airless_google_cloud_core-0.0.1/airless/google/cloud/core/operator/base.py +18 -0
  9. airless_google_cloud_core-0.0.1/airless/google/cloud/core/operator/delay.py +18 -0
  10. airless_google_cloud_core-0.0.1/airless/google/cloud/core/operator/error.py +95 -0
  11. airless_google_cloud_core-0.0.1/airless/google/cloud/core/operator/redirect.py +22 -0
  12. airless_google_cloud_core-0.0.1/airless/google/cloud/pubsub/__init__.py +0 -0
  13. airless_google_cloud_core-0.0.1/airless/google/cloud/pubsub/hook/__init__.py +1 -0
  14. airless_google_cloud_core-0.0.1/airless/google/cloud/pubsub/hook/pubsub.py +30 -0
  15. airless_google_cloud_core-0.0.1/airless_google_cloud_core.egg-info/PKG-INFO +16 -0
  16. airless_google_cloud_core-0.0.1/airless_google_cloud_core.egg-info/SOURCES.txt +20 -0
  17. airless_google_cloud_core-0.0.1/airless_google_cloud_core.egg-info/dependency_links.txt +1 -0
  18. airless_google_cloud_core-0.0.1/airless_google_cloud_core.egg-info/requires.txt +2 -0
  19. airless_google_cloud_core-0.0.1/airless_google_cloud_core.egg-info/top_level.txt +1 -0
  20. airless_google_cloud_core-0.0.1/pyproject.toml +19 -0
  21. airless_google_cloud_core-0.0.1/setup.cfg +32 -0
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: airless-google-cloud-core
3
+ Version: 0.0.1
4
+ Summary: Airless package with the main components to use airless in google cloud
5
+ Project-URL: Homepage, https://github.com/astercapital/airless
6
+ Project-URL: Bug Tracker, https://github.com/astercapital/airless/issues
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: google-cloud-pubsub==2.13.11
10
+ Requires-Dist: airless-core>=0.1.0.dev1
11
+
12
+ # airless-google-cloud-core
13
+
14
+ [![PyPI version](https://badge.fury.io/py/airless-google-cloud-core.svg)](https://badge.fury.io/py/airless-google-cloud-core)
15
+
16
+ airless-google-cloud-core [Airless](https://github.com/astercapital/airless) package with the main components to use airless in google cloud.
@@ -0,0 +1,5 @@
1
+ # airless-google-cloud-core
2
+
3
+ [![PyPI version](https://badge.fury.io/py/airless-google-cloud-core.svg)](https://badge.fury.io/py/airless-google-cloud-core)
4
+
5
+ airless-google-cloud-core [Airless](https://github.com/astercapital/airless) package with the main components to use airless in google cloud.
@@ -0,0 +1 @@
1
+ __path__ = __import__('pkgutil').extend_path(__path__, __name__)
@@ -0,0 +1,4 @@
1
+ from .base import (GoogleBaseEventOperator, GoogleBaseFileOperator)
2
+ from .delay import (GoogleDelayOperator)
3
+ from .error import (GoogleErrorReprocessOperator)
4
+ from .redirect import (GoogleRedirectOperator)
@@ -0,0 +1,18 @@
1
+
2
+ from airless.core.operator import BaseFileOperator, BaseEventOperator
3
+
4
+ from airless.google.cloud.pubsub.hook import GooglePubsubHook
5
+
6
+
7
+ class GoogleBaseFileOperator(BaseFileOperator):
8
+
9
+ def __init__(self):
10
+ super().__init__()
11
+ self.queue_hook = GooglePubsubHook() # Have to redefine this attribute for each vendor
12
+
13
+
14
+ class GoogleBaseEventOperator(BaseEventOperator):
15
+
16
+ def __init__(self):
17
+ super().__init__()
18
+ self.queue_hook = GooglePubsubHook() # Have to redefine this attribute for each vendor
@@ -0,0 +1,18 @@
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__()
@@ -0,0 +1,95 @@
1
+
2
+ import json
3
+ import time
4
+
5
+ from datetime import datetime
6
+
7
+ from airless.core.config import get_config
8
+ from airless.core.dto import BaseDto
9
+ from airless.google.cloud.core.operator import GoogleBaseEventOperator
10
+
11
+
12
+ class GoogleErrorReprocessOperator(GoogleBaseEventOperator):
13
+
14
+ def __init__(self):
15
+ super().__init__()
16
+
17
+ def execute(self, data, topic):
18
+
19
+ input_type = data['input_type']
20
+ origin = data.get('origin', 'undefined')
21
+ message_id = data.get('event_id')
22
+ original_data = data['data']
23
+ metadata = original_data.get('metadata', {})
24
+
25
+ retry_interval = metadata.get('retry_interval', 5)
26
+ retries = metadata.get('retries', 0)
27
+ 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)
29
+
30
+ if (input_type == 'event') and (retries < max_retries):
31
+ time.sleep(min(retry_interval ** retries, max_interval))
32
+ original_data.setdefault('metadata', {})['retries'] = retries + 1
33
+ self.queue_hook.publish(
34
+ project=get_config('GCP_PROJECT'),
35
+ topic=origin,
36
+ data=original_data)
37
+
38
+ else:
39
+ dto = BaseDto(
40
+ event_id=message_id,
41
+ resource=origin,
42
+ to_project=get_config('GCP_PROJECT'),
43
+ to_dataset=get_config('BIGQUERY_DATASET_ERROR'),
44
+ to_table=get_config('BIGQUERY_TABLE_ERROR'),
45
+ to_schema=None,
46
+ to_partition_column='_created_at',
47
+ to_extract_to_cols=False,
48
+ to_keys_format=None,
49
+ data=data)
50
+ self.queue_hook.publish(
51
+ project=get_config('GCP_PROJECT'),
52
+ topic=get_config('PUBSUB_TOPIC_PUBSUB_TO_BQ'),
53
+ data=dto.as_dict())
54
+
55
+ email_send_topic = get_config('PUBSUB_TOPIC_EMAIL_SEND', False)
56
+ if email_send_topic and (origin != email_send_topic):
57
+ self.notify_email(origin, message_id, data)
58
+
59
+ slack_send_topic = get_config('PUBSUB_TOPIC_SLACK_SEND', False)
60
+ if slack_send_topic and (origin != slack_send_topic):
61
+ self.notify_slack(origin, message_id, data)
62
+
63
+ def notify_email(self, origin, message_id, data):
64
+ email_message = {
65
+ 'sender': get_config('EMAIL_SENDER_ERROR'),
66
+ 'recipients': eval(get_config('EMAIL_RECIPIENTS_ERROR')),
67
+ 'subject': f'{origin} | {message_id}',
68
+ 'content': f'Input Type: {data["input_type"]} Origin: {origin}\nMessage ID: {message_id}\n\n {json.dumps(data["data"])}\n\n{data["error"]}'
69
+ }
70
+ self.queue_hook.publish(
71
+ project=get_config('GCP_PROJECT'),
72
+ topic=get_config('PUBSUB_TOPIC_EMAIL_SEND'),
73
+ data=email_message)
74
+
75
+ def notify_slack(self, origin, message_id, data):
76
+ slack_message = {
77
+ 'channels': eval(get_config('SLACK_CHANNELS_ERROR')),
78
+ 'message': f'{origin} | {message_id}\n\n{json.dumps(data["data"])}\n\n{data["error"]}'
79
+ }
80
+ self.queue_hook.publish(
81
+ project=get_config('GCP_PROJECT'),
82
+ topic=get_config('PUBSUB_TOPIC_SLACK_SEND'),
83
+ data=slack_message)
84
+
85
+ def prepare_row(self, row, message_id, origin):
86
+ return {
87
+ '_event_id': message_id or 1234,
88
+ '_resource': origin or 'local',
89
+ '_json': json.dumps(row),
90
+ '_created_at': str(datetime.now())
91
+ }
92
+
93
+ def prepare_rows(self, data, message_id, origin):
94
+ prepared_rows = data if isinstance(data, list) else [data]
95
+ return [self.prepare_row(row, message_id, origin) for row in prepared_rows]
@@ -0,0 +1,22 @@
1
+
2
+ from airless.core.operator import RedirectOperator
3
+ from airless.google.cloud.core.operator import GoogleBaseEventOperator
4
+
5
+
6
+ class GoogleRedirectOperator(GoogleBaseEventOperator, RedirectOperator):
7
+
8
+ """
9
+ Operator that receives one event from a pubsub topic
10
+ and publish multiple messages to another topic.
11
+
12
+ It can receive 4 parameters:
13
+ project: the project where the destination pubsub is hosted
14
+ topic: the pubsub topic it must publish the newly generated messages
15
+ messages: a list of messages to publish the topic
16
+ params: a list of dicts containing a key and a list of values
17
+
18
+ The output messages will be the product of messages and every param values list
19
+ """
20
+
21
+ def __init__(self):
22
+ super().__init__()
@@ -0,0 +1 @@
1
+ from .pubsub import (GooglePubsubHook)
@@ -0,0 +1,30 @@
1
+
2
+ import json
3
+
4
+ from google.cloud import pubsub_v1
5
+
6
+ from airless.core.hook import QueueHook
7
+ from airless.core.config 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}')
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: airless-google-cloud-core
3
+ Version: 0.0.1
4
+ Summary: Airless package with the main components to use airless in google cloud
5
+ Project-URL: Homepage, https://github.com/astercapital/airless
6
+ Project-URL: Bug Tracker, https://github.com/astercapital/airless/issues
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: google-cloud-pubsub==2.13.11
10
+ Requires-Dist: airless-core>=0.1.0.dev1
11
+
12
+ # airless-google-cloud-core
13
+
14
+ [![PyPI version](https://badge.fury.io/py/airless-google-cloud-core.svg)](https://badge.fury.io/py/airless-google-cloud-core)
15
+
16
+ airless-google-cloud-core [Airless](https://github.com/astercapital/airless) package with the main components to use airless in google cloud.
@@ -0,0 +1,20 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.cfg
4
+ airless/__init__.py
5
+ airless/google/__init__.py
6
+ airless/google/cloud/__init__.py
7
+ airless/google/cloud/core/__init__.py
8
+ airless/google/cloud/core/operator/__init__.py
9
+ airless/google/cloud/core/operator/base.py
10
+ airless/google/cloud/core/operator/delay.py
11
+ airless/google/cloud/core/operator/error.py
12
+ airless/google/cloud/core/operator/redirect.py
13
+ airless/google/cloud/pubsub/__init__.py
14
+ airless/google/cloud/pubsub/hook/__init__.py
15
+ airless/google/cloud/pubsub/hook/pubsub.py
16
+ airless_google_cloud_core.egg-info/PKG-INFO
17
+ airless_google_cloud_core.egg-info/SOURCES.txt
18
+ airless_google_cloud_core.egg-info/dependency_links.txt
19
+ airless_google_cloud_core.egg-info/requires.txt
20
+ airless_google_cloud_core.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ google-cloud-pubsub==2.13.11
2
+ airless-core>=0.1.0.dev1
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "airless-google-cloud-core"
3
+ version = "0.0.1"
4
+ description = "Airless package with the main components to use airless in google cloud"
5
+ readme = "README.md"
6
+ dynamic = ["dependencies", "requires-python"]
7
+
8
+ [build-system]
9
+ requires = ["setuptools", "wheel"]
10
+ build-backend = "setuptools.build_meta"
11
+
12
+ [project.urls]
13
+ "Homepage" = "https://github.com/astercapital/airless"
14
+ "Bug Tracker" = "https://github.com/astercapital/airless/issues"
15
+
16
+ [tool.pytest.ini_options]
17
+ minversion = "6.0"
18
+ addopts = "--capture=sys --cache-clear --disable-warnings --junitxml=pytest.xml --cov-report=xml:cov.xml --cov-report=term-missing:skip-covered --cov=airless"
19
+ testpaths = ["tests"]
@@ -0,0 +1,32 @@
1
+ [metadata]
2
+ name = airless-google-cloud-core
3
+ version = 0.0.1
4
+
5
+ [options]
6
+ python_requires = >=3.9
7
+ packages = find_namespace:
8
+ install_requires =
9
+ google-cloud-pubsub==2.13.11
10
+ airless-core>=0.1.0.dev1
11
+
12
+ [options.packages.find]
13
+ include = airless*
14
+
15
+ [flake8]
16
+ exclude =
17
+ .git,
18
+ __pycache__,
19
+ build,
20
+ dist,
21
+ *.pyc,
22
+ *.egg-info,
23
+ *.egg,
24
+ temp
25
+ ignore = E501
26
+ max-line-length = 120
27
+ max-complexity = 15
28
+
29
+ [egg_info]
30
+ tag_build =
31
+ tag_date = 0
32
+