port-ocean 0.5.18__py3-none-any.whl → 0.5.22__py3-none-any.whl
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.
Potentially problematic release.
This version of port-ocean might be problematic. Click here for more details.
- port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -1
- port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +1 -1
- port_ocean/clients/port/mixins/integrations.py +17 -0
- port_ocean/config/base.py +4 -6
- port_ocean/config/settings.py +20 -6
- port_ocean/core/event_listener/base.py +0 -7
- port_ocean/core/event_listener/polling.py +2 -1
- port_ocean/core/handlers/entity_processor/base.py +6 -1
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py +51 -11
- port_ocean/core/handlers/port_app_config/base.py +53 -15
- port_ocean/core/integrations/mixins/sync_raw.py +31 -5
- port_ocean/core/utils.py +0 -8
- port_ocean/log/sensetive.py +24 -9
- port_ocean/ocean.py +10 -6
- port_ocean/utils/misc.py +15 -3
- port_ocean/utils/queue_utils.py +1 -0
- port_ocean/utils/signal.py +1 -4
- {port_ocean-0.5.18.dist-info → port_ocean-0.5.22.dist-info}/METADATA +1 -1
- {port_ocean-0.5.18.dist-info → port_ocean-0.5.22.dist-info}/RECORD +22 -23
- port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/config.yaml +0 -17
- {port_ocean-0.5.18.dist-info → port_ocean-0.5.22.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.5.18.dist-info → port_ocean-0.5.22.dist-info}/WHEEL +0 -0
- {port_ocean-0.5.18.dist-info → port_ocean-0.5.22.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Any, TYPE_CHECKING, Optional, TypedDict
|
|
2
|
+
from urllib.parse import quote_plus
|
|
2
3
|
|
|
3
4
|
import httpx
|
|
4
5
|
from loguru import logger
|
|
@@ -6,6 +7,7 @@ from starlette import status
|
|
|
6
7
|
|
|
7
8
|
from port_ocean.clients.port.authentication import PortAuthentication
|
|
8
9
|
from port_ocean.clients.port.utils import handle_status_code
|
|
10
|
+
from port_ocean.log.sensetive import sensitive_log_filter
|
|
9
11
|
|
|
10
12
|
if TYPE_CHECKING:
|
|
11
13
|
from port_ocean.core.handlers.port_app_config.models import PortAppConfig
|
|
@@ -137,3 +139,18 @@ class IntegrationClientMixin:
|
|
|
137
139
|
)
|
|
138
140
|
handle_status_code(response)
|
|
139
141
|
logger.debug("Logs successfully ingested")
|
|
142
|
+
|
|
143
|
+
async def ingest_integration_kind_examples(
|
|
144
|
+
self, kind: str, data: list[dict[str, Any]], should_log: bool = True
|
|
145
|
+
):
|
|
146
|
+
logger.debug(f"Ingesting examples for kind: {kind}")
|
|
147
|
+
headers = await self.auth.headers()
|
|
148
|
+
response = await self.client.post(
|
|
149
|
+
f"{self.auth.api_url}/integration/{quote_plus(self.integration_identifier)}/kinds/{quote_plus(kind)}/examples",
|
|
150
|
+
headers=headers,
|
|
151
|
+
json={
|
|
152
|
+
"examples": sensitive_log_filter.mask_object(data, full_hide=True),
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
handle_status_code(response, should_log=should_log)
|
|
156
|
+
logger.debug(f"Examples for kind {kind} successfully ingested")
|
port_ocean/config/base.py
CHANGED
|
@@ -16,16 +16,15 @@ PROVIDER_CONFIG_PATTERN = r"^[a-zA-Z0-9]+ .*$"
|
|
|
16
16
|
|
|
17
17
|
def read_yaml_config_settings_source(
|
|
18
18
|
settings: "BaseOceanSettings", base_path: str
|
|
19
|
-
) -> str:
|
|
19
|
+
) -> dict[str, Any]:
|
|
20
20
|
yaml_file = getattr(settings.__config__, "yaml_file", "")
|
|
21
21
|
|
|
22
22
|
assert yaml_file, "Settings.yaml_file not properly configured"
|
|
23
23
|
path = Path(base_path, yaml_file)
|
|
24
24
|
|
|
25
25
|
if not path.exists():
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return path.read_text("utf-8")
|
|
26
|
+
return {}
|
|
27
|
+
return yaml.safe_load(path.read_text("utf-8"))
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
def parse_config_provider(value: str) -> tuple[str, str]:
|
|
@@ -122,8 +121,7 @@ def decamelize_config(
|
|
|
122
121
|
def load_providers(
|
|
123
122
|
settings: "BaseOceanSettings", existing_values: dict[str, Any], base_path: str
|
|
124
123
|
) -> dict[str, Any]:
|
|
125
|
-
|
|
126
|
-
data = yaml.safe_load(yaml_content)
|
|
124
|
+
data = read_yaml_config_settings_source(settings, base_path)
|
|
127
125
|
snake_case_config = decamelize_config(settings, data)
|
|
128
126
|
return parse_providers(settings, snake_case_config, existing_values)
|
|
129
127
|
|
port_ocean/config/settings.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
from typing import Any, Literal
|
|
2
2
|
|
|
3
|
-
from pydantic import Extra, AnyHttpUrl, parse_obj_as
|
|
3
|
+
from pydantic import Extra, AnyHttpUrl, parse_obj_as
|
|
4
|
+
from pydantic.class_validators import root_validator
|
|
4
5
|
from pydantic.env_settings import InitSettingsSource, EnvSettingsSource, BaseSettings
|
|
5
6
|
from pydantic.fields import Field
|
|
6
7
|
from pydantic.main import BaseModel
|
|
7
8
|
|
|
8
9
|
from port_ocean.config.base import BaseOceanSettings, BaseOceanModel
|
|
9
10
|
from port_ocean.core.event_listener import EventListenerSettingsType
|
|
11
|
+
from port_ocean.utils.misc import get_integration_name
|
|
10
12
|
|
|
11
13
|
LogLevelType = Literal["ERROR", "WARNING", "INFO", "DEBUG", "CRITICAL"]
|
|
12
14
|
|
|
@@ -36,22 +38,34 @@ class PortSettings(BaseOceanModel, extra=Extra.allow):
|
|
|
36
38
|
client_id: str = Field(..., sensitive=True)
|
|
37
39
|
client_secret: str = Field(..., sensitive=True)
|
|
38
40
|
base_url: AnyHttpUrl = parse_obj_as(AnyHttpUrl, "https://api.getport.io")
|
|
41
|
+
port_app_config_cache_ttl: int = 60
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
class IntegrationSettings(BaseOceanModel, extra=Extra.allow):
|
|
42
|
-
identifier: str
|
|
43
|
-
type: str
|
|
45
|
+
identifier: str = Field(..., min_length=1)
|
|
46
|
+
type: str = Field(..., min_length=1)
|
|
44
47
|
config: dict[str, Any] | BaseModel
|
|
45
48
|
|
|
46
|
-
@
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
+
@root_validator(pre=True)
|
|
50
|
+
def a(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
51
|
+
integ_type = values.get("type")
|
|
52
|
+
|
|
53
|
+
if not integ_type:
|
|
54
|
+
integ_type = get_integration_name()
|
|
55
|
+
|
|
56
|
+
values["type"] = integ_type.lower() if integ_type else None
|
|
57
|
+
values["identifier"] = values.get(
|
|
58
|
+
"identifier", f"my-{integ_type}-integration".lower()
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return values
|
|
49
62
|
|
|
50
63
|
|
|
51
64
|
class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
52
65
|
initialize_port_resources: bool = True
|
|
53
66
|
scheduled_resync_interval: int | None = None
|
|
54
67
|
client_timeout: int = 30
|
|
68
|
+
send_raw_data_examples: bool = True
|
|
55
69
|
port: PortSettings
|
|
56
70
|
event_listener: EventListenerSettingsType
|
|
57
71
|
integration: IntegrationSettings
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from asyncio import Task
|
|
3
2
|
from typing import TypedDict, Callable, Any, Awaitable
|
|
4
3
|
|
|
5
4
|
from pydantic import Extra
|
|
@@ -22,7 +21,6 @@ class BaseEventListener:
|
|
|
22
21
|
events: EventListenerEvents,
|
|
23
22
|
):
|
|
24
23
|
self.events = events
|
|
25
|
-
self._tasks_to_close: list[Task[Any]] = []
|
|
26
24
|
|
|
27
25
|
async def start(self) -> None:
|
|
28
26
|
signal_handler.register(self._stop)
|
|
@@ -32,11 +30,6 @@ class BaseEventListener:
|
|
|
32
30
|
async def _start(self) -> None:
|
|
33
31
|
pass
|
|
34
32
|
|
|
35
|
-
def stop(self) -> None:
|
|
36
|
-
self._stop()
|
|
37
|
-
for task in self._tasks_to_close:
|
|
38
|
-
task.cancel()
|
|
39
|
-
|
|
40
33
|
def _stop(self) -> None:
|
|
41
34
|
"""
|
|
42
35
|
Can be used for event listeners that need cleanup before exiting.
|
|
@@ -10,6 +10,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
10
10
|
EventListenerSettings,
|
|
11
11
|
)
|
|
12
12
|
from port_ocean.utils.repeat import repeat_every
|
|
13
|
+
from port_ocean.utils.signal import signal_handler
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class PollingEventListenerSettings(EventListenerSettings):
|
|
@@ -79,7 +80,7 @@ class PollingEventListener(BaseEventListener):
|
|
|
79
80
|
running_task: Task[Any] = get_event_loop().create_task(
|
|
80
81
|
self.events["on_resync"]({}) # type: ignore
|
|
81
82
|
)
|
|
82
|
-
|
|
83
|
+
signal_handler.register(running_task.cancel)
|
|
83
84
|
|
|
84
85
|
await running_task
|
|
85
86
|
|
|
@@ -40,6 +40,7 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
40
40
|
mapping: ResourceConfig,
|
|
41
41
|
raw_data: list[RAW_ITEM],
|
|
42
42
|
parse_all: bool = False,
|
|
43
|
+
send_raw_data_examples_amount: int = 0,
|
|
43
44
|
) -> EntitySelectorDiff:
|
|
44
45
|
pass
|
|
45
46
|
|
|
@@ -48,6 +49,7 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
48
49
|
mapping: ResourceConfig,
|
|
49
50
|
raw_data: list[RAW_ITEM],
|
|
50
51
|
parse_all: bool = False,
|
|
52
|
+
send_raw_data_examples_amount: int = 0,
|
|
51
53
|
) -> EntitySelectorDiff:
|
|
52
54
|
"""Public method to parse raw entity data and map it to an EntityDiff.
|
|
53
55
|
|
|
@@ -55,9 +57,12 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
55
57
|
mapping (ResourceConfig): The configuration for entity mapping.
|
|
56
58
|
raw_data (list[RawEntity]): The raw data to be parsed.
|
|
57
59
|
parse_all (bool): Whether to parse all data or just data that passed the selector.
|
|
60
|
+
send_raw_data_examples_amount (bool): Whether to send example data to the integration service.
|
|
58
61
|
|
|
59
62
|
Returns:
|
|
60
63
|
EntityDiff: The parsed entity differences.
|
|
61
64
|
"""
|
|
62
65
|
with logger.contextualize(kind=mapping.kind):
|
|
63
|
-
return await self._parse_items(
|
|
66
|
+
return await self._parse_items(
|
|
67
|
+
mapping, raw_data, parse_all, send_raw_data_examples_amount
|
|
68
|
+
)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import functools
|
|
3
|
+
from dataclasses import dataclass, field
|
|
3
4
|
from functools import lru_cache
|
|
4
|
-
from typing import Any
|
|
5
|
-
from loguru import logger
|
|
5
|
+
from typing import Any, Optional
|
|
6
6
|
|
|
7
7
|
import pyjq as jq # type: ignore
|
|
8
|
+
from loguru import logger
|
|
8
9
|
|
|
10
|
+
from port_ocean.context.ocean import ocean
|
|
9
11
|
from port_ocean.core.handlers.entity_processor.base import BaseEntityProcessor
|
|
10
12
|
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
11
13
|
from port_ocean.core.models import Entity
|
|
@@ -17,6 +19,18 @@ from port_ocean.exceptions.core import EntityProcessorException
|
|
|
17
19
|
from port_ocean.utils.queue_utils import process_in_queue
|
|
18
20
|
|
|
19
21
|
|
|
22
|
+
@dataclass
|
|
23
|
+
class MappedEntity:
|
|
24
|
+
"""Represents the entity after applying the mapping
|
|
25
|
+
|
|
26
|
+
This class holds the mapping entity along with the selector boolean value and optionally the raw data.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
entity: dict[str, Any] = field(default_factory=dict)
|
|
30
|
+
did_entity_pass_selector: bool = False
|
|
31
|
+
raw_data: Optional[dict[str, Any]] = None
|
|
32
|
+
|
|
33
|
+
|
|
20
34
|
class JQEntityProcessor(BaseEntityProcessor):
|
|
21
35
|
"""Processes and parses entities using JQ expressions.
|
|
22
36
|
|
|
@@ -78,13 +92,17 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
78
92
|
raw_entity_mappings: dict[str, Any],
|
|
79
93
|
selector_query: str,
|
|
80
94
|
parse_all: bool = False,
|
|
81
|
-
) ->
|
|
95
|
+
) -> MappedEntity:
|
|
82
96
|
should_run = await self._search_as_bool(data, selector_query)
|
|
83
97
|
if parse_all or should_run:
|
|
84
98
|
mapped_entity = await self._search_as_object(data, raw_entity_mappings)
|
|
85
|
-
return
|
|
99
|
+
return MappedEntity(
|
|
100
|
+
mapped_entity,
|
|
101
|
+
did_entity_pass_selector=should_run,
|
|
102
|
+
raw_data=data if should_run else None,
|
|
103
|
+
)
|
|
86
104
|
|
|
87
|
-
return
|
|
105
|
+
return MappedEntity()
|
|
88
106
|
|
|
89
107
|
async def _calculate_entity(
|
|
90
108
|
self,
|
|
@@ -93,7 +111,7 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
93
111
|
items_to_parse: str | None,
|
|
94
112
|
selector_query: str,
|
|
95
113
|
parse_all: bool = False,
|
|
96
|
-
) -> list[
|
|
114
|
+
) -> list[MappedEntity]:
|
|
97
115
|
if items_to_parse:
|
|
98
116
|
items = await self._search(data, items_to_parse)
|
|
99
117
|
if isinstance(items, list):
|
|
@@ -118,13 +136,27 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
118
136
|
data, raw_entity_mappings, selector_query, parse_all
|
|
119
137
|
)
|
|
120
138
|
]
|
|
121
|
-
return [(
|
|
139
|
+
return [MappedEntity()]
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
async def _send_examples(data: list[dict[str, Any]], kind: str) -> None:
|
|
143
|
+
try:
|
|
144
|
+
if data:
|
|
145
|
+
await ocean.port_client.ingest_integration_kind_examples(
|
|
146
|
+
kind, data, should_log=False
|
|
147
|
+
)
|
|
148
|
+
except Exception as ex:
|
|
149
|
+
logger.warning(
|
|
150
|
+
f"Failed to send raw data example {ex}",
|
|
151
|
+
exc_info=True,
|
|
152
|
+
)
|
|
122
153
|
|
|
123
154
|
async def _parse_items(
|
|
124
155
|
self,
|
|
125
156
|
mapping: ResourceConfig,
|
|
126
157
|
raw_results: list[RAW_ITEM],
|
|
127
158
|
parse_all: bool = False,
|
|
159
|
+
send_raw_data_examples_amount: int = 0,
|
|
128
160
|
) -> EntitySelectorDiff:
|
|
129
161
|
raw_entity_mappings: dict[str, Any] = mapping.port.entity.mappings.dict(
|
|
130
162
|
exclude_unset=True
|
|
@@ -141,13 +173,21 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
141
173
|
|
|
142
174
|
passed_entities = []
|
|
143
175
|
failed_entities = []
|
|
176
|
+
examples_to_send: list[dict[str, Any]] = []
|
|
144
177
|
for entities_results in calculated_entities_results:
|
|
145
|
-
for
|
|
146
|
-
if entity.get("identifier") and entity.get("blueprint"):
|
|
147
|
-
parsed_entity = Entity.parse_obj(entity)
|
|
148
|
-
if did_entity_pass_selector:
|
|
178
|
+
for result in entities_results:
|
|
179
|
+
if result.entity.get("identifier") and result.entity.get("blueprint"):
|
|
180
|
+
parsed_entity = Entity.parse_obj(result.entity)
|
|
181
|
+
if result.did_entity_pass_selector:
|
|
149
182
|
passed_entities.append(parsed_entity)
|
|
183
|
+
if (
|
|
184
|
+
len(examples_to_send) < send_raw_data_examples_amount
|
|
185
|
+
and result.raw_data is not None
|
|
186
|
+
):
|
|
187
|
+
examples_to_send.append(result.raw_data)
|
|
150
188
|
else:
|
|
151
189
|
failed_entities.append(parsed_entity)
|
|
152
190
|
|
|
191
|
+
await self._send_examples(examples_to_send, mapping.kind)
|
|
192
|
+
|
|
153
193
|
return EntitySelectorDiff(passed=passed_entities, failed=failed_entities)
|
|
@@ -5,8 +5,36 @@ from loguru import logger
|
|
|
5
5
|
from pydantic import ValidationError
|
|
6
6
|
|
|
7
7
|
from port_ocean.context.event import event
|
|
8
|
+
from port_ocean.context.ocean import PortOceanContext
|
|
8
9
|
from port_ocean.core.handlers.base import BaseHandler
|
|
9
10
|
from port_ocean.core.handlers.port_app_config.models import PortAppConfig
|
|
11
|
+
from port_ocean.utils.misc import get_time
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PortAppConfigCache:
|
|
15
|
+
_port_app_config: PortAppConfig | None
|
|
16
|
+
_retrieval_time: float
|
|
17
|
+
|
|
18
|
+
def __init__(self, cache_ttl: int):
|
|
19
|
+
self._cache_ttl = cache_ttl
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def port_app_config(self) -> PortAppConfig:
|
|
23
|
+
if self._port_app_config is None:
|
|
24
|
+
raise ValueError("Port app config is not set")
|
|
25
|
+
return self._port_app_config
|
|
26
|
+
|
|
27
|
+
@port_app_config.setter
|
|
28
|
+
def port_app_config(self, value: PortAppConfig) -> None:
|
|
29
|
+
self._retrieval_time = get_time()
|
|
30
|
+
self._port_app_config = value
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def is_cache_invalid(self) -> bool:
|
|
34
|
+
return (
|
|
35
|
+
not self._port_app_config
|
|
36
|
+
or self._retrieval_time + self._cache_ttl < get_time()
|
|
37
|
+
)
|
|
10
38
|
|
|
11
39
|
|
|
12
40
|
class BasePortAppConfig(BaseHandler):
|
|
@@ -21,24 +49,34 @@ class BasePortAppConfig(BaseHandler):
|
|
|
21
49
|
|
|
22
50
|
CONFIG_CLASS: Type[PortAppConfig] = PortAppConfig
|
|
23
51
|
|
|
52
|
+
def __init__(self, context: PortOceanContext):
|
|
53
|
+
super().__init__(context)
|
|
54
|
+
self._app_config_cache = PortAppConfigCache(
|
|
55
|
+
self.context.config.port.port_app_config_cache_ttl
|
|
56
|
+
)
|
|
57
|
+
|
|
24
58
|
@abstractmethod
|
|
25
59
|
async def _get_port_app_config(self) -> dict[str, Any]:
|
|
26
60
|
pass
|
|
27
61
|
|
|
28
|
-
async def get_port_app_config(self) -> PortAppConfig:
|
|
29
|
-
"""
|
|
62
|
+
async def get_port_app_config(self, use_cache: bool = True) -> PortAppConfig:
|
|
63
|
+
"""
|
|
64
|
+
Retrieve and parse the port application configuration.
|
|
30
65
|
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
:param use_cache: Determines whether to use the cached port-app-config if it exists, or to fetch it regardless
|
|
67
|
+
:return: The parsed port application configuration.
|
|
33
68
|
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
if not use_cache or self._app_config_cache.is_cache_invalid:
|
|
70
|
+
raw_config = await self._get_port_app_config()
|
|
71
|
+
try:
|
|
72
|
+
self._app_config_cache.port_app_config = self.CONFIG_CLASS.parse_obj(
|
|
73
|
+
raw_config
|
|
74
|
+
)
|
|
75
|
+
except ValidationError:
|
|
76
|
+
logger.error(
|
|
77
|
+
"Invalid port app config found. Please check that the integration has been configured correctly."
|
|
78
|
+
)
|
|
79
|
+
raise
|
|
80
|
+
|
|
81
|
+
event.port_app_config = self._app_config_cache.port_app_config
|
|
82
|
+
return self._app_config_cache.port_app_config
|
|
@@ -30,6 +30,9 @@ from port_ocean.core.utils import zip_and_sum
|
|
|
30
30
|
from port_ocean.exceptions.core import OceanAbortException
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
SEND_RAW_DATA_EXAMPLES_AMOUNT = 5
|
|
34
|
+
|
|
35
|
+
|
|
33
36
|
class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
34
37
|
"""Mixin class for synchronization of raw constructed entities.
|
|
35
38
|
|
|
@@ -124,10 +127,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
124
127
|
self,
|
|
125
128
|
raw_diff: list[tuple[ResourceConfig, list[RAW_ITEM]]],
|
|
126
129
|
parse_all: bool = False,
|
|
130
|
+
send_raw_data_examples_amount: int = 0,
|
|
127
131
|
) -> list[EntitySelectorDiff]:
|
|
128
132
|
return await asyncio.gather(
|
|
129
133
|
*(
|
|
130
|
-
self.entity_processor.parse_items(
|
|
134
|
+
self.entity_processor.parse_items(
|
|
135
|
+
mapping, results, parse_all, send_raw_data_examples_amount
|
|
136
|
+
)
|
|
131
137
|
for mapping, results in raw_diff
|
|
132
138
|
)
|
|
133
139
|
)
|
|
@@ -138,8 +144,11 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
138
144
|
results: list[dict[Any, Any]],
|
|
139
145
|
user_agent_type: UserAgentType,
|
|
140
146
|
parse_all: bool = False,
|
|
147
|
+
send_raw_data_examples_amount: int = 0,
|
|
141
148
|
) -> EntitySelectorDiff:
|
|
142
|
-
objects_diff = await self._calculate_raw(
|
|
149
|
+
objects_diff = await self._calculate_raw(
|
|
150
|
+
[(resource, results)], parse_all, send_raw_data_examples_amount
|
|
151
|
+
)
|
|
143
152
|
await self.entities_state_applier.upsert(
|
|
144
153
|
objects_diff[0].passed, user_agent_type
|
|
145
154
|
)
|
|
@@ -171,19 +180,32 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
171
180
|
else:
|
|
172
181
|
async_generators.append(result)
|
|
173
182
|
|
|
183
|
+
send_raw_data_examples_amount = (
|
|
184
|
+
SEND_RAW_DATA_EXAMPLES_AMOUNT if ocean.config.send_raw_data_examples else 0
|
|
185
|
+
)
|
|
174
186
|
entities = (
|
|
175
187
|
await self._register_resource_raw(
|
|
176
|
-
resource_config,
|
|
188
|
+
resource_config,
|
|
189
|
+
raw_results,
|
|
190
|
+
user_agent_type,
|
|
191
|
+
send_raw_data_examples_amount=send_raw_data_examples_amount,
|
|
177
192
|
)
|
|
178
193
|
).passed
|
|
179
194
|
|
|
180
195
|
for generator in async_generators:
|
|
181
196
|
try:
|
|
182
197
|
async for items in generator:
|
|
198
|
+
if send_raw_data_examples_amount > 0:
|
|
199
|
+
send_raw_data_examples_amount = max(
|
|
200
|
+
0, send_raw_data_examples_amount - len(entities)
|
|
201
|
+
)
|
|
183
202
|
entities.extend(
|
|
184
203
|
(
|
|
185
204
|
await self._register_resource_raw(
|
|
186
|
-
resource_config,
|
|
205
|
+
resource_config,
|
|
206
|
+
items,
|
|
207
|
+
user_agent_type,
|
|
208
|
+
send_raw_data_examples_amount=send_raw_data_examples_amount,
|
|
187
209
|
)
|
|
188
210
|
).passed
|
|
189
211
|
)
|
|
@@ -362,7 +384,11 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
362
384
|
EventType.RESYNC,
|
|
363
385
|
trigger_type=trigger_type,
|
|
364
386
|
):
|
|
365
|
-
|
|
387
|
+
# If a resync is triggered due to a mappings change, we want to make sure that we have the updated version
|
|
388
|
+
# rather than the old cache
|
|
389
|
+
app_config = await self.port_app_config_handler.get_port_app_config(
|
|
390
|
+
use_cache=False
|
|
391
|
+
)
|
|
366
392
|
|
|
367
393
|
creation_results: list[tuple[list[Entity], list[Exception]]] = []
|
|
368
394
|
|
port_ocean/core/utils.py
CHANGED
|
@@ -28,14 +28,6 @@ def is_same_entity(first_entity: Entity, second_entity: Entity) -> bool:
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def get_unique(array: list[Entity]) -> list[Entity]:
|
|
32
|
-
result: list[Entity] = []
|
|
33
|
-
for item in array:
|
|
34
|
-
if all(not is_same_entity(item, seen_item) for seen_item in result):
|
|
35
|
-
result.append(item)
|
|
36
|
-
return result
|
|
37
|
-
|
|
38
|
-
|
|
39
31
|
def get_port_diff(
|
|
40
32
|
before: Iterable[Entity],
|
|
41
33
|
after: Iterable[Entity],
|
port_ocean/log/sensetive.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import Callable, TYPE_CHECKING
|
|
2
|
+
from typing import Any, Callable, TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
if TYPE_CHECKING:
|
|
5
5
|
from loguru import Record
|
|
@@ -35,16 +35,31 @@ class SensitiveLogFilter:
|
|
|
35
35
|
[re.compile(re.escape(token.strip())) for token in tokens if token.strip()]
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
+
def mask_string(self, string: str, full_hide: bool = False) -> str:
|
|
39
|
+
masked_string = string
|
|
40
|
+
for pattern in self.compiled_patterns:
|
|
41
|
+
replace: Callable[[re.Match[str]], str] | str = (
|
|
42
|
+
"[REDACTED]"
|
|
43
|
+
if full_hide
|
|
44
|
+
else lambda match: match.group()[:6] + "[REDACTED]"
|
|
45
|
+
)
|
|
46
|
+
masked_string = pattern.sub(replace, masked_string)
|
|
47
|
+
return masked_string
|
|
48
|
+
|
|
49
|
+
def mask_object(self, obj: Any, full_hide: bool = False) -> Any:
|
|
50
|
+
if isinstance(obj, str):
|
|
51
|
+
return self.mask_string(obj, full_hide)
|
|
52
|
+
if isinstance(obj, list):
|
|
53
|
+
return [self.mask_object(o, full_hide) for o in obj]
|
|
54
|
+
if isinstance(obj, dict):
|
|
55
|
+
for k, v in obj.items():
|
|
56
|
+
obj[k] = self.mask_object(v, full_hide)
|
|
57
|
+
|
|
58
|
+
return obj
|
|
59
|
+
|
|
38
60
|
def create_filter(self, full_hide: bool = False) -> Callable[["Record"], bool]:
|
|
39
61
|
def _filter(record: "Record") -> bool:
|
|
40
|
-
|
|
41
|
-
replace: Callable[[re.Match[str]], str] | str = (
|
|
42
|
-
"[REDACTED]"
|
|
43
|
-
if full_hide
|
|
44
|
-
else lambda match: match.group()[:6] + "[REDACTED]"
|
|
45
|
-
)
|
|
46
|
-
record["message"] = pattern.sub(replace, record["message"])
|
|
47
|
-
|
|
62
|
+
record["message"] = self.mask_string(record["message"], full_hide)
|
|
48
63
|
return True
|
|
49
64
|
|
|
50
65
|
return _filter
|
port_ocean/ocean.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import sys
|
|
3
3
|
import threading
|
|
4
|
-
from
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from typing import Callable, Any, Dict, AsyncIterator
|
|
5
6
|
|
|
6
7
|
from fastapi import FastAPI, APIRouter
|
|
7
8
|
from loguru import logger
|
|
@@ -21,7 +22,7 @@ from port_ocean.core.integrations.base import BaseIntegration
|
|
|
21
22
|
from port_ocean.log.sensetive import sensitive_log_filter
|
|
22
23
|
from port_ocean.middlewares import request_handler
|
|
23
24
|
from port_ocean.utils.repeat import repeat_every
|
|
24
|
-
from port_ocean.utils.signal import init_signal_handler
|
|
25
|
+
from port_ocean.utils.signal import signal_handler, init_signal_handler
|
|
25
26
|
from port_ocean.version import __integration_version__
|
|
26
27
|
|
|
27
28
|
|
|
@@ -93,14 +94,17 @@ class Ocean:
|
|
|
93
94
|
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
94
95
|
self.fast_api_app.include_router(self.integration_router, prefix="/integration")
|
|
95
96
|
|
|
96
|
-
@
|
|
97
|
-
async def
|
|
98
|
-
init_signal_handler()
|
|
97
|
+
@asynccontextmanager
|
|
98
|
+
async def lifecycle(_: FastAPI) -> AsyncIterator[None]:
|
|
99
99
|
try:
|
|
100
|
+
init_signal_handler()
|
|
100
101
|
await self.integration.start()
|
|
101
102
|
await self._setup_scheduled_resync()
|
|
103
|
+
yield None
|
|
104
|
+
signal_handler.exit()
|
|
102
105
|
except Exception:
|
|
103
|
-
logger.exception("
|
|
106
|
+
logger.exception("Integration had a fatal error. Shutting down.")
|
|
104
107
|
sys.exit("Server stopped")
|
|
105
108
|
|
|
109
|
+
self.fast_api_app.router.lifespan_context = lifecycle
|
|
106
110
|
await self.fast_api_app(scope, receive, send)
|
port_ocean/utils/misc.py
CHANGED
|
@@ -29,13 +29,25 @@ def get_function_location(func: Callable[..., Any]) -> str:
|
|
|
29
29
|
return f"{file_path}:{line_number}"
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def
|
|
32
|
+
def get_pyproject_data() -> dict[str, Any] | None:
|
|
33
33
|
try:
|
|
34
34
|
with open("./pyproject.toml", "rb") as toml_file:
|
|
35
35
|
pyproject_data = tomli.load(toml_file)
|
|
36
|
-
return pyproject_data["tool"]["poetry"]
|
|
36
|
+
return pyproject_data["tool"]["poetry"]
|
|
37
37
|
except (FileNotFoundError, KeyError):
|
|
38
|
-
return
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_integration_version() -> str:
|
|
42
|
+
if data := get_pyproject_data():
|
|
43
|
+
return data["version"]
|
|
44
|
+
return ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_integration_name() -> str:
|
|
48
|
+
if data := get_pyproject_data():
|
|
49
|
+
return data["name"]
|
|
50
|
+
return ""
|
|
39
51
|
|
|
40
52
|
|
|
41
53
|
def get_spec_file(path: Path = Path(".")) -> dict[str, Any] | None:
|
port_ocean/utils/queue_utils.py
CHANGED
|
@@ -33,6 +33,7 @@ async def process_in_queue(
|
|
|
33
33
|
This function executes multiple asynchronous tasks in a bounded way
|
|
34
34
|
(e.g. having 200 tasks to execute, while running only 20 concurrently),
|
|
35
35
|
to prevent overload and memory issues when dealing with large sets of data and tasks.
|
|
36
|
+
read more -> https://stackoverflow.com/questions/38831322/making-1-milion-requests-with-aiohttp-asyncio-literally
|
|
36
37
|
|
|
37
38
|
Usage:
|
|
38
39
|
```python
|
port_ocean/utils/signal.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import signal
|
|
2
1
|
from typing import Callable, Any
|
|
3
2
|
|
|
4
3
|
from werkzeug.local import LocalProxy, LocalStack
|
|
@@ -13,10 +12,8 @@ from port_ocean.utils.misc import generate_uuid
|
|
|
13
12
|
class SignalHandler:
|
|
14
13
|
def __init__(self) -> None:
|
|
15
14
|
self._handlers: dict[str, Callable[[], Any]] = {}
|
|
16
|
-
signal.signal(signal.SIGINT, lambda _, __: self._exit())
|
|
17
|
-
signal.signal(signal.SIGTERM, lambda _, __: self._exit())
|
|
18
15
|
|
|
19
|
-
def
|
|
16
|
+
def exit(self) -> None:
|
|
20
17
|
"""
|
|
21
18
|
Handles the exit signal.
|
|
22
19
|
"""
|
|
@@ -20,17 +20,16 @@ port_ocean/cli/cookiecutter/hooks/post_gen_project.py,sha256=4WpFM5O7FXuaqBMrhtn
|
|
|
20
20
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore,sha256=9Mz_WI7XBpKzlJ7ILb4vlcuzYkh98Ql3bP_5GHN1sRY,1034
|
|
21
21
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore,sha256=32p1lDW_g5hyBz486GWfDeR9m7ikFlASVri5a8vmNoo,2698
|
|
22
22
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore,sha256=kCpRPdl3S_jqYYZaOrc0-xa6-l3KqVjNRXc6jCkd_-Q,12
|
|
23
|
-
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml,sha256=
|
|
23
|
+
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml,sha256=jTYvu-Iayl-bxc917Y7ejcC9KyvH-LSq4-bdYZCYsuM,457
|
|
24
24
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md,sha256=nzAmB0Bjnd2eZo79OjrlyVOdpTBHTmTxvO7c2C8Q-VQ,292
|
|
25
25
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile,sha256=Hh1dBnL959V2n28pmqFpXSrNvSMQjX6fDCUos8ITiu0,326
|
|
26
26
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Makefile,sha256=kTa72qEp8pi-joOH6zl8oeIgjEHSCF628p2074yHHNA,1779
|
|
27
27
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md,sha256=5VZmgDRW9gO4d8UuzkujslOIDfIDBiAGL2Hd74HK770,468
|
|
28
28
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore,sha256=kCpRPdl3S_jqYYZaOrc0-xa6-l3KqVjNRXc6jCkd_-Q,12
|
|
29
|
-
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/config.yaml,sha256=zKaBQNCbWEE3MFxDHaMn9NIeBGVPiZUV6cBjH35f2kw,906
|
|
30
29
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py,sha256=_TRsA2s6GV2E3CTI8CHcsH-ZuH4_Eh5-juDXWaET0ho,65
|
|
31
30
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py,sha256=KxxtBrRygYOP0bulXySVYwX0fm_mm3QHqeEqwDwgXgY,1669
|
|
32
31
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml,sha256=kENq8nNmFPIzw9vySheyG4xHxAPuBSpZO1CYGD6G2NE,46
|
|
33
|
-
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml,sha256=
|
|
32
|
+
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml,sha256=zmkwpiBDiDhMKp1egULLnx6ftVBcVYh4ztGohs53rWA,1928
|
|
34
33
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties,sha256=timmRpoSd50BdxLf45OcFUk2qs855z610kzz3yLAqJw,124
|
|
35
34
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
35
|
port_ocean/cli/utils.py,sha256=IUK2UbWqjci-lrcDdynZXqVP5B5TcjF0w5CpEVUks-k,54
|
|
@@ -41,15 +40,15 @@ port_ocean/clients/port/client.py,sha256=3GYCM0ZkX3pB6sNoOb-7_6dm0Jr5_vqhflD9ilt
|
|
|
41
40
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
41
|
port_ocean/clients/port/mixins/blueprints.py,sha256=BiqkhvDFdkySWgL1NHI-LAQ9ieZWazZAojPo9E8d7U4,4575
|
|
43
42
|
port_ocean/clients/port/mixins/entities.py,sha256=Lg5Sa6jQuhDTQKLURVavqXlBQt4-XPUUigB9JqQ1X0k,7364
|
|
44
|
-
port_ocean/clients/port/mixins/integrations.py,sha256=
|
|
43
|
+
port_ocean/clients/port/mixins/integrations.py,sha256=ypRYi7_VlnP2YEPgE8zS-_6WgPzUmsRi95K_pwVIpKE,5943
|
|
45
44
|
port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
|
|
46
45
|
port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
|
|
47
46
|
port_ocean/clients/port/types.py,sha256=nvlgiAq4WH5_F7wQbz_GAWl-faob84LVgIjZ2Ww5mTk,451
|
|
48
47
|
port_ocean/clients/port/utils.py,sha256=O9mBu6zp4TfpS4SQ3qCPpn9ZVyYF8GKnji4UnYhM3yg,2373
|
|
49
48
|
port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
-
port_ocean/config/base.py,sha256=
|
|
49
|
+
port_ocean/config/base.py,sha256=J_FUvlo0D4FbEbr4mgJsbGB5JjW9I7zMwfekj6T30KY,6389
|
|
51
50
|
port_ocean/config/dynamic.py,sha256=qOFkRoJsn_BW7581omi_AoMxoHqasf_foxDQ_G11_SI,2030
|
|
52
|
-
port_ocean/config/settings.py,sha256=
|
|
51
|
+
port_ocean/config/settings.py,sha256=ILWgh9358QI9TSbUtZo_reEtF33687_kW0nTniXEFJQ,2308
|
|
53
52
|
port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
53
|
port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
|
|
55
54
|
port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -62,12 +61,12 @@ port_ocean/core/defaults/clean.py,sha256=S3UAfca-oU89WJKIB4OgGjGjPr0vxBQ2aRZsLTZ
|
|
|
62
61
|
port_ocean/core/defaults/common.py,sha256=QnLFTkT3yIWIRtLQb7fUvvfe5AfInYJy0q5LjlzHkOw,3553
|
|
63
62
|
port_ocean/core/defaults/initialize.py,sha256=z7IdE_WN1_gF5_60OwSlid9-di7pYOJhzX8VdFjkXdc,7702
|
|
64
63
|
port_ocean/core/event_listener/__init__.py,sha256=mzJ33wRq0kh60fpVdOHVmvMTUQIvz3vxmifyBgwDn0E,889
|
|
65
|
-
port_ocean/core/event_listener/base.py,sha256=
|
|
64
|
+
port_ocean/core/event_listener/base.py,sha256=4RujgPz4VfDFlviu4qLGJFnJougSCL-Ewf0rfTQfwgc,1133
|
|
66
65
|
port_ocean/core/event_listener/factory.py,sha256=AYYfSHPAF7P5H-uQECXT0JVJjKDHrYkWJJBSL4mGkg8,3697
|
|
67
66
|
port_ocean/core/event_listener/http.py,sha256=UjONC_OODHjpGjvsBPAvO6zGzosdmv5Hx96B4WFqpXs,2577
|
|
68
67
|
port_ocean/core/event_listener/kafka.py,sha256=0U_TwmlmtS8N2lprkCmnyOmdqOAvghWxHhSfyu46kEs,6875
|
|
69
68
|
port_ocean/core/event_listener/once.py,sha256=KdvUqjIcyp8XqhY1GfR_KYJfParFIRrjCtcMf3JMegk,2150
|
|
70
|
-
port_ocean/core/event_listener/polling.py,sha256=
|
|
69
|
+
port_ocean/core/event_listener/polling.py,sha256=3UxjgQJly5y-hA8R798oFWb7bFsYMxSc6GRozA3biiM,3539
|
|
71
70
|
port_ocean/core/handlers/__init__.py,sha256=R2HIRD8JTzupY4mXuXveMBQbuiPiiB3Oivmpc5C2jjI,610
|
|
72
71
|
port_ocean/core/handlers/base.py,sha256=cTarblazu8yh8xz2FpB-dzDKuXxtoi143XJgPbV_DcM,157
|
|
73
72
|
port_ocean/core/handlers/entities_state_applier/__init__.py,sha256=kgLZDCeCEzi4r-0nzW9k78haOZNf6PX7mJOUr34A4c8,173
|
|
@@ -77,11 +76,11 @@ port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=G6RyPgBAy
|
|
|
77
76
|
port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha256=1zncwCbE-Gej0xaWKlzZgoXxOBe9bgs_YxlZ8QW3NdI,1751
|
|
78
77
|
port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=82BvU8t5w9uhsxX8hbnwuRPuWhW3cMeuT_5sVIkip1I,1550
|
|
79
78
|
port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
|
|
80
|
-
port_ocean/core/handlers/entity_processor/base.py,sha256=
|
|
81
|
-
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=
|
|
79
|
+
port_ocean/core/handlers/entity_processor/base.py,sha256=3e5QHlSjE7CCAv0WFSyQ7AhFEOS6-9eHj-ZYXGuvqFQ,2216
|
|
80
|
+
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=mF3ZyO1aSVxfefoeXSlTaIAFdoqITdsEuxlo2d1dUxM,6913
|
|
82
81
|
port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
|
|
83
82
|
port_ocean/core/handlers/port_app_config/api.py,sha256=6VbKPwFzsWG0IYsVD81hxSmfqtHUFqrfUuj1DBX5g4w,853
|
|
84
|
-
port_ocean/core/handlers/port_app_config/base.py,sha256=
|
|
83
|
+
port_ocean/core/handlers/port_app_config/base.py,sha256=oufdNLzUmEgJY5PgIz75zDlowWrjA-y8WR4UnM58E58,2897
|
|
85
84
|
port_ocean/core/handlers/port_app_config/models.py,sha256=WtF2uW4KPUPfDpy6E2tl9qXh5GUcDucVvKt0DCTYv6w,1985
|
|
86
85
|
port_ocean/core/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
87
86
|
port_ocean/core/integrations/base.py,sha256=3jU0skK_gMLB8a_fN8whsRKva-Dz058TFoI0vXTbryU,3193
|
|
@@ -89,11 +88,11 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
|
89
88
|
port_ocean/core/integrations/mixins/events.py,sha256=Ddfx2L4FpghV38waF8OfVeOV0bHBxNIgjU-q5ffillI,2341
|
|
90
89
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
91
90
|
port_ocean/core/integrations/mixins/sync.py,sha256=TKqRytxXONVhuCo3CB3rDvWNbITnZz33TYTDs3SWWVk,3880
|
|
92
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
91
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=DQ5pNqUedYXXykuMqecMhqi0GwMAvMWUZsTcjmkycxY,16137
|
|
93
92
|
port_ocean/core/integrations/mixins/utils.py,sha256=7y1rGETZIjOQadyIjFJXIHKkQFKx_SwiP-TrAIsyyLY,2303
|
|
94
93
|
port_ocean/core/models.py,sha256=bDO_I4Yd33TEZIh2QSV8UwXQIuwE7IgrINkYDHI0dkc,714
|
|
95
94
|
port_ocean/core/ocean_types.py,sha256=ltnn22eRuDMFW02kIgmIAu6S06-i9jJV2NJ-MZcwwj0,879
|
|
96
|
-
port_ocean/core/utils.py,sha256=
|
|
95
|
+
port_ocean/core/utils.py,sha256=6rYtsb1bjW8owxWngGiV3awMLZkP3tXZdxXClmRD1SU,1824
|
|
97
96
|
port_ocean/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
97
|
port_ocean/exceptions/api.py,sha256=TLmTMqn4uHGaHgZK8PMIJ0TVJlPB4iP7xl9rx7GtCyY,426
|
|
99
98
|
port_ocean/exceptions/base.py,sha256=uY4DX7fIITDFfemCJDWpaZi3bD51lcANc5swpoNvMJA,46
|
|
@@ -108,9 +107,9 @@ port_ocean/helpers/retry.py,sha256=hcfVmt2Fp53EQViYiOWA-FTWD80jvS-mWKhwMHiBM7Q,1
|
|
|
108
107
|
port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
109
108
|
port_ocean/log/handlers.py,sha256=k9G_Mb4ga2-Jke9irpdlYqj6EYiwv0gEsh4TgyqqOmI,2853
|
|
110
109
|
port_ocean/log/logger_setup.py,sha256=NGU9EdRTPLloiHdgSw0JYaYGr9mx1hYJUEPFHP6BA14,2175
|
|
111
|
-
port_ocean/log/sensetive.py,sha256=
|
|
110
|
+
port_ocean/log/sensetive.py,sha256=wkyvkKMbyLTjZDSbvvLHL9bv4RvD0DPAyL3uWSttUOA,2916
|
|
112
111
|
port_ocean/middlewares.py,sha256=6GrhldYAazxSwK2TbS-J28XdZ-9wO3PgCcyIMhnnJvI,2480
|
|
113
|
-
port_ocean/ocean.py,sha256=
|
|
112
|
+
port_ocean/ocean.py,sha256=nRTTh6faPrt77ozvKBFk6eCNxwzKcTDzJ4KPgpO4VRk,4304
|
|
114
113
|
port_ocean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
114
|
port_ocean/run.py,sha256=vyShtqg_jEiE6M4SJpci6c4oRD9k2ztesAXEx_6Sc9M,1906
|
|
116
115
|
port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
|
|
@@ -118,13 +117,13 @@ port_ocean/utils/__init__.py,sha256=KMGnCPXZJbNwtgxtyMycapkDz8tpSyw23MSYT3iVeHs,
|
|
|
118
117
|
port_ocean/utils/async_http.py,sha256=arnH458TExn2Dju_Sy6pHas_vF5RMWnOp-jBz5WAAcE,1226
|
|
119
118
|
port_ocean/utils/async_iterators.py,sha256=buFBiPdsqkNMCk91h6ZG8hJa181j7RjgHajbfgeB8A8,1608
|
|
120
119
|
port_ocean/utils/cache.py,sha256=3KItZDE2yVrbVDr-hoM8lNna8s2dlpxhP4ICdLjH4LQ,2231
|
|
121
|
-
port_ocean/utils/misc.py,sha256=
|
|
122
|
-
port_ocean/utils/queue_utils.py,sha256=
|
|
120
|
+
port_ocean/utils/misc.py,sha256=WZjrEDRfyeqbesVt_Nkp2yjazbKF-BOnxJMFAI721yQ,1965
|
|
121
|
+
port_ocean/utils/queue_utils.py,sha256=Pzb6e8PcjylZpXcb9EEIK-QcTty_E2k1egMiJF5J_8Q,2500
|
|
123
122
|
port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,3231
|
|
124
|
-
port_ocean/utils/signal.py,sha256=
|
|
123
|
+
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
|
125
124
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
126
|
-
port_ocean-0.5.
|
|
127
|
-
port_ocean-0.5.
|
|
128
|
-
port_ocean-0.5.
|
|
129
|
-
port_ocean-0.5.
|
|
130
|
-
port_ocean-0.5.
|
|
125
|
+
port_ocean-0.5.22.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
126
|
+
port_ocean-0.5.22.dist-info/METADATA,sha256=zWwXPhSqwMtx0mAMEKtFtIXtOMW8612WjalUu0WeQHQ,6554
|
|
127
|
+
port_ocean-0.5.22.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
128
|
+
port_ocean-0.5.22.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
129
|
+
port_ocean-0.5.22.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{{ "# This is an example configuration file for the integration service." }}
|
|
2
|
-
{{ "# Please copy this file to config.yaml file in the integration folder and edit it to your needs." }}
|
|
3
|
-
|
|
4
|
-
port:
|
|
5
|
-
clientId: "{{ '{{' }} from env PORT_CLIENT_ID {{ '}}' }}" {{ "# Can be loaded via environment variable: PORT_CLIENT_ID" }}
|
|
6
|
-
clientSecret: "{{ '{{' }} from env PORT_CLIENT_SECRET {{ '}}' }}" {{ "# Can be loaded via environment variable: PORT_CLIENT_SECRET" }}
|
|
7
|
-
{{ "# The event listener to use for the integration service." }}
|
|
8
|
-
eventListener:
|
|
9
|
-
type: KAFKA
|
|
10
|
-
integration:
|
|
11
|
-
{{ "# The identifier of this integration instance." }}
|
|
12
|
-
identifier: "{{ '{{' }} from env INTEGRATION_IDENTIFIER {{ '}}' }}"
|
|
13
|
-
{{ "# The type of the integration." }}
|
|
14
|
-
type: "My Integration type (Gitlab, Jira, etc.)"
|
|
15
|
-
config:
|
|
16
|
-
myGitToken: "{{ '{{' }} from env MY_GIT_TOKEN {{ '}}' }}"
|
|
17
|
-
someApplicationUrl: "https://I-Am-Not-A-Real-Url.com"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|