port-ocean 0.5.21__py3-none-any.whl → 0.5.24__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}}/Dockerfile +1 -1
- port_ocean/clients/port/mixins/entities.py +1 -1
- port_ocean/clients/port/mixins/integrations.py +2 -1
- port_ocean/core/defaults/initialize.py +24 -40
- port_ocean/core/handlers/__init__.py +1 -2
- port_ocean/core/handlers/entity_processor/base.py +6 -17
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py +55 -44
- port_ocean/core/integrations/mixins/sync_raw.py +99 -58
- port_ocean/core/models.py +14 -0
- port_ocean/core/ocean_types.py +5 -1
- port_ocean/core/utils.py +28 -2
- port_ocean/utils/queue_utils.py +10 -3
- {port_ocean-0.5.21.dist-info → port_ocean-0.5.24.dist-info}/METADATA +1 -1
- {port_ocean-0.5.21.dist-info → port_ocean-0.5.24.dist-info}/RECORD +17 -17
- {port_ocean-0.5.21.dist-info → port_ocean-0.5.24.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.5.21.dist-info → port_ocean-0.5.24.dist-info}/WHEEL +0 -0
- {port_ocean-0.5.21.dist-info → port_ocean-0.5.24.dist-info}/entry_points.txt +0 -0
|
@@ -32,7 +32,7 @@ class EntityClientMixin:
|
|
|
32
32
|
) -> None:
|
|
33
33
|
validation_only = request_options["validation_only"]
|
|
34
34
|
async with self.semaphore:
|
|
35
|
-
logger.
|
|
35
|
+
logger.debug(
|
|
36
36
|
f"{'Validating' if validation_only else 'Upserting'} entity: {entity.identifier} of blueprint: {entity.blueprint}"
|
|
37
37
|
)
|
|
38
38
|
headers = await self.auth.headers(user_agent_type)
|
|
@@ -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
|
|
@@ -145,7 +146,7 @@ class IntegrationClientMixin:
|
|
|
145
146
|
logger.debug(f"Ingesting examples for kind: {kind}")
|
|
146
147
|
headers = await self.auth.headers()
|
|
147
148
|
response = await self.client.post(
|
|
148
|
-
f"{self.auth.api_url}/integration/{self.integration_identifier}/kinds/{kind}/examples",
|
|
149
|
+
f"{self.auth.api_url}/integration/{quote_plus(self.integration_identifier)}/kinds/{quote_plus(kind)}/examples",
|
|
149
150
|
headers=headers,
|
|
150
151
|
json={
|
|
151
152
|
"examples": sensitive_log_filter.mask_object(data, full_hide=True),
|
|
@@ -2,8 +2,8 @@ import asyncio
|
|
|
2
2
|
from typing import Type, Any
|
|
3
3
|
|
|
4
4
|
import httpx
|
|
5
|
-
from starlette import status
|
|
6
5
|
from loguru import logger
|
|
6
|
+
from starlette import status
|
|
7
7
|
|
|
8
8
|
from port_ocean.clients.port.client import PortClient
|
|
9
9
|
from port_ocean.clients.port.types import UserAgentType
|
|
@@ -12,6 +12,7 @@ from port_ocean.context.ocean import ocean
|
|
|
12
12
|
from port_ocean.core.defaults.common import Defaults, get_port_integration_defaults
|
|
13
13
|
from port_ocean.core.handlers.port_app_config.models import PortAppConfig
|
|
14
14
|
from port_ocean.core.models import Blueprint
|
|
15
|
+
from port_ocean.core.utils import gather_and_split_errors_from_results
|
|
15
16
|
from port_ocean.exceptions.port_defaults import (
|
|
16
17
|
AbortDefaultCreationError,
|
|
17
18
|
)
|
|
@@ -65,43 +66,30 @@ async def _create_resources(
|
|
|
65
66
|
defaults.blueprints
|
|
66
67
|
)
|
|
67
68
|
|
|
68
|
-
blueprints_results = await
|
|
69
|
-
|
|
69
|
+
blueprints_results, _ = await gather_and_split_errors_from_results(
|
|
70
|
+
[
|
|
70
71
|
port_client.get_blueprint(blueprint["identifier"], should_log=False)
|
|
71
72
|
for blueprint in creation_stage
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
],
|
|
74
|
+
lambda item: isinstance(item, Blueprint),
|
|
74
75
|
)
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
result.identifier
|
|
78
|
-
for result in blueprints_results
|
|
79
|
-
if not isinstance(result, httpx.HTTPStatusError)
|
|
80
|
-
and isinstance(result, Blueprint)
|
|
81
|
-
]
|
|
82
|
-
|
|
83
|
-
if existing_blueprints:
|
|
77
|
+
if blueprints_results:
|
|
84
78
|
logger.info(
|
|
85
|
-
f"Blueprints already exist: {
|
|
79
|
+
f"Blueprints already exist: {[result.identifier for result in blueprints_results]}. Skipping integration default creation..."
|
|
86
80
|
)
|
|
87
81
|
return
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
created_blueprints, errors = await gather_and_split_errors_from_results(
|
|
84
|
+
(
|
|
91
85
|
port_client.create_blueprint(
|
|
92
86
|
blueprint, user_agent_type=UserAgentType.exporter
|
|
93
87
|
)
|
|
94
88
|
for blueprint in creation_stage
|
|
95
|
-
)
|
|
96
|
-
return_exceptions=True,
|
|
89
|
+
)
|
|
97
90
|
)
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
created_blueprints = [
|
|
101
|
-
result["identifier"]
|
|
102
|
-
for result in create_results
|
|
103
|
-
if not isinstance(result, BaseException)
|
|
104
|
-
]
|
|
92
|
+
created_blueprints_identifiers = [bp["identifier"] for bp in created_blueprints]
|
|
105
93
|
|
|
106
94
|
if errors:
|
|
107
95
|
for error in errors:
|
|
@@ -110,8 +98,8 @@ async def _create_resources(
|
|
|
110
98
|
f"Failed to create resources: {error.response.text}. Rolling back changes..."
|
|
111
99
|
)
|
|
112
100
|
|
|
113
|
-
raise AbortDefaultCreationError(
|
|
114
|
-
|
|
101
|
+
raise AbortDefaultCreationError(created_blueprints_identifiers, errors)
|
|
102
|
+
created_pages_identifiers = []
|
|
115
103
|
try:
|
|
116
104
|
for patch_stage in blueprint_patches:
|
|
117
105
|
await asyncio.gather(
|
|
@@ -141,19 +129,11 @@ async def _create_resources(
|
|
|
141
129
|
)
|
|
142
130
|
)
|
|
143
131
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return_exceptions=True,
|
|
132
|
+
created_pages, pages_errors = await gather_and_split_errors_from_results(
|
|
133
|
+
(port_client.create_page(page) for page in defaults.pages)
|
|
147
134
|
)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
result.get("identifier", "")
|
|
151
|
-
for result in create_pages_result
|
|
152
|
-
if not isinstance(result, BaseException)
|
|
153
|
-
]
|
|
154
|
-
|
|
155
|
-
pages_errors = [
|
|
156
|
-
result for result in create_pages_result if isinstance(result, Exception)
|
|
135
|
+
created_pages_identifiers = [
|
|
136
|
+
page.get("identifier", "") for page in created_pages
|
|
157
137
|
]
|
|
158
138
|
|
|
159
139
|
if pages_errors:
|
|
@@ -164,7 +144,9 @@ async def _create_resources(
|
|
|
164
144
|
)
|
|
165
145
|
|
|
166
146
|
raise AbortDefaultCreationError(
|
|
167
|
-
|
|
147
|
+
created_blueprints_identifiers,
|
|
148
|
+
pages_errors,
|
|
149
|
+
created_pages_identifiers,
|
|
168
150
|
)
|
|
169
151
|
|
|
170
152
|
await port_client.create_integration(
|
|
@@ -176,7 +158,9 @@ async def _create_resources(
|
|
|
176
158
|
logger.error(
|
|
177
159
|
f"Failed to create resources: {err.response.text}. Rolling back changes..."
|
|
178
160
|
)
|
|
179
|
-
raise AbortDefaultCreationError(
|
|
161
|
+
raise AbortDefaultCreationError(
|
|
162
|
+
created_blueprints_identifiers, [err], created_pages_identifiers
|
|
163
|
+
)
|
|
180
164
|
|
|
181
165
|
|
|
182
166
|
async def _initialize_defaults(
|
|
@@ -4,7 +4,7 @@ from .entities_state_applier.base import (
|
|
|
4
4
|
from .entities_state_applier.port.applier import (
|
|
5
5
|
HttpEntitiesStateApplier,
|
|
6
6
|
)
|
|
7
|
-
from .entity_processor.base import BaseEntityProcessor
|
|
7
|
+
from .entity_processor.base import BaseEntityProcessor
|
|
8
8
|
from .entity_processor.jq_entity_processor import (
|
|
9
9
|
JQEntityProcessor,
|
|
10
10
|
)
|
|
@@ -12,7 +12,6 @@ from .port_app_config.api import APIPortAppConfig
|
|
|
12
12
|
from .port_app_config.base import BasePortAppConfig
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
|
-
"EntityPortDiff",
|
|
16
15
|
"BaseEntityProcessor",
|
|
17
16
|
"JQEntityProcessor",
|
|
18
17
|
"BasePortAppConfig",
|
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
2
|
|
|
4
3
|
from loguru import logger
|
|
5
4
|
|
|
6
5
|
from port_ocean.core.handlers.base import BaseHandler
|
|
7
6
|
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
8
|
-
from port_ocean.core.models import Entity
|
|
9
7
|
from port_ocean.core.ocean_types import (
|
|
10
8
|
RAW_ITEM,
|
|
9
|
+
CalculationResult,
|
|
11
10
|
EntitySelectorDiff,
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
@dataclass
|
|
16
|
-
class EntityPortDiff:
|
|
17
|
-
"""Represents the differences between entities for porting.
|
|
18
|
-
|
|
19
|
-
This class holds the lists of deleted, modified, and created entities as part
|
|
20
|
-
of the porting process.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
deleted: list[Entity] = field(default_factory=list)
|
|
24
|
-
modified: list[Entity] = field(default_factory=list)
|
|
25
|
-
created: list[Entity] = field(default_factory=list)
|
|
26
|
-
|
|
27
|
-
|
|
28
14
|
class BaseEntityProcessor(BaseHandler):
|
|
29
15
|
"""Abstract base class for processing and parsing entities.
|
|
30
16
|
|
|
@@ -41,7 +27,7 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
41
27
|
raw_data: list[RAW_ITEM],
|
|
42
28
|
parse_all: bool = False,
|
|
43
29
|
send_raw_data_examples_amount: int = 0,
|
|
44
|
-
) ->
|
|
30
|
+
) -> CalculationResult:
|
|
45
31
|
pass
|
|
46
32
|
|
|
47
33
|
async def parse_items(
|
|
@@ -50,7 +36,7 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
50
36
|
raw_data: list[RAW_ITEM],
|
|
51
37
|
parse_all: bool = False,
|
|
52
38
|
send_raw_data_examples_amount: int = 0,
|
|
53
|
-
) ->
|
|
39
|
+
) -> CalculationResult:
|
|
54
40
|
"""Public method to parse raw entity data and map it to an EntityDiff.
|
|
55
41
|
|
|
56
42
|
Args:
|
|
@@ -63,6 +49,9 @@ class BaseEntityProcessor(BaseHandler):
|
|
|
63
49
|
EntityDiff: The parsed entity differences.
|
|
64
50
|
"""
|
|
65
51
|
with logger.contextualize(kind=mapping.kind):
|
|
52
|
+
if not raw_data:
|
|
53
|
+
return CalculationResult(EntitySelectorDiff([], []), [])
|
|
54
|
+
|
|
66
55
|
return await self._parse_items(
|
|
67
56
|
mapping, raw_data, parse_all, send_raw_data_examples_amount
|
|
68
57
|
)
|
|
@@ -14,7 +14,9 @@ from port_ocean.core.models import Entity
|
|
|
14
14
|
from port_ocean.core.ocean_types import (
|
|
15
15
|
RAW_ITEM,
|
|
16
16
|
EntitySelectorDiff,
|
|
17
|
+
CalculationResult,
|
|
17
18
|
)
|
|
19
|
+
from port_ocean.core.utils import gather_and_split_errors_from_results, zip_and_sum
|
|
18
20
|
from port_ocean.exceptions.core import EntityProcessorException
|
|
19
21
|
from port_ocean.utils.queue_utils import process_in_queue
|
|
20
22
|
|
|
@@ -111,32 +113,34 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
111
113
|
items_to_parse: str | None,
|
|
112
114
|
selector_query: str,
|
|
113
115
|
parse_all: bool = False,
|
|
114
|
-
) -> list[MappedEntity]:
|
|
116
|
+
) -> tuple[list[MappedEntity], list[Exception]]:
|
|
117
|
+
raw_data = [data.copy()]
|
|
115
118
|
if items_to_parse:
|
|
116
119
|
items = await self._search(data, items_to_parse)
|
|
117
|
-
if isinstance(items, list):
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
{"item": item, **data},
|
|
122
|
-
raw_entity_mappings,
|
|
123
|
-
selector_query,
|
|
124
|
-
parse_all,
|
|
125
|
-
)
|
|
126
|
-
for item in items
|
|
127
|
-
]
|
|
120
|
+
if not isinstance(items, list):
|
|
121
|
+
logger.warning(
|
|
122
|
+
f"Failed to parse items for JQ expression {items_to_parse}, Expected list but got {type(items)}."
|
|
123
|
+
f" Skipping..."
|
|
128
124
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
125
|
+
return [], []
|
|
126
|
+
raw_data = [{"item": item, **data} for item in items]
|
|
127
|
+
|
|
128
|
+
entities, errors = await gather_and_split_errors_from_results(
|
|
129
|
+
[
|
|
130
|
+
self._get_mapped_entity(
|
|
131
|
+
raw,
|
|
132
|
+
raw_entity_mappings,
|
|
133
|
+
selector_query,
|
|
134
|
+
parse_all,
|
|
137
135
|
)
|
|
136
|
+
for raw in raw_data
|
|
138
137
|
]
|
|
139
|
-
|
|
138
|
+
)
|
|
139
|
+
if errors:
|
|
140
|
+
logger.error(
|
|
141
|
+
f"Failed to calculate entities with {len(errors)} errors. errors: {errors}"
|
|
142
|
+
)
|
|
143
|
+
return entities, errors
|
|
140
144
|
|
|
141
145
|
@staticmethod
|
|
142
146
|
async def _send_examples(data: list[dict[str, Any]], kind: str) -> None:
|
|
@@ -157,37 +161,44 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
157
161
|
raw_results: list[RAW_ITEM],
|
|
158
162
|
parse_all: bool = False,
|
|
159
163
|
send_raw_data_examples_amount: int = 0,
|
|
160
|
-
) ->
|
|
164
|
+
) -> CalculationResult:
|
|
161
165
|
raw_entity_mappings: dict[str, Any] = mapping.port.entity.mappings.dict(
|
|
162
166
|
exclude_unset=True
|
|
163
167
|
)
|
|
164
|
-
|
|
165
|
-
calculated_entities_results =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
logger.info(f"Parsing {len(raw_results)} raw results into entities")
|
|
169
|
+
calculated_entities_results, errors = zip_and_sum(
|
|
170
|
+
await process_in_queue(
|
|
171
|
+
raw_results,
|
|
172
|
+
self._calculate_entity,
|
|
173
|
+
raw_entity_mappings,
|
|
174
|
+
mapping.port.items_to_parse,
|
|
175
|
+
mapping.selector.query,
|
|
176
|
+
parse_all,
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
logger.debug(
|
|
180
|
+
f"Finished parsing raw results into entities with {len(errors)} errors. errors: {errors}"
|
|
172
181
|
)
|
|
173
182
|
|
|
174
183
|
passed_entities = []
|
|
175
184
|
failed_entities = []
|
|
176
185
|
examples_to_send: list[dict[str, Any]] = []
|
|
177
|
-
for
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
failed_entities.append(parsed_entity)
|
|
186
|
+
for result in calculated_entities_results:
|
|
187
|
+
if result.entity.get("identifier") and result.entity.get("blueprint"):
|
|
188
|
+
parsed_entity = Entity.parse_obj(result.entity)
|
|
189
|
+
if result.did_entity_pass_selector:
|
|
190
|
+
passed_entities.append(parsed_entity)
|
|
191
|
+
if (
|
|
192
|
+
len(examples_to_send) < send_raw_data_examples_amount
|
|
193
|
+
and result.raw_data is not None
|
|
194
|
+
):
|
|
195
|
+
examples_to_send.append(result.raw_data)
|
|
196
|
+
else:
|
|
197
|
+
failed_entities.append(parsed_entity)
|
|
190
198
|
|
|
191
199
|
await self._send_examples(examples_to_send, mapping.kind)
|
|
192
200
|
|
|
193
|
-
return
|
|
201
|
+
return CalculationResult(
|
|
202
|
+
EntitySelectorDiff(passed=passed_entities, failed=failed_entities),
|
|
203
|
+
errors,
|
|
204
|
+
)
|
|
@@ -3,6 +3,7 @@ import inspect
|
|
|
3
3
|
import typing
|
|
4
4
|
from typing import Callable, Awaitable, Any
|
|
5
5
|
|
|
6
|
+
import httpx
|
|
6
7
|
from loguru import logger
|
|
7
8
|
|
|
8
9
|
from port_ocean.clients.port.types import UserAgentType
|
|
@@ -24,12 +25,11 @@ from port_ocean.core.ocean_types import (
|
|
|
24
25
|
RawEntityDiff,
|
|
25
26
|
ASYNC_GENERATOR_RESYNC_TYPE,
|
|
26
27
|
RAW_ITEM,
|
|
27
|
-
|
|
28
|
+
CalculationResult,
|
|
28
29
|
)
|
|
29
|
-
from port_ocean.core.utils import zip_and_sum
|
|
30
|
+
from port_ocean.core.utils import zip_and_sum, gather_and_split_errors_from_results
|
|
30
31
|
from port_ocean.exceptions.core import OceanAbortException
|
|
31
32
|
|
|
32
|
-
|
|
33
33
|
SEND_RAW_DATA_EXAMPLES_AMOUNT = 5
|
|
34
34
|
|
|
35
35
|
|
|
@@ -101,21 +101,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
101
101
|
logger.info(
|
|
102
102
|
f"Found {len(tasks) + len(results)} resync tasks for {resource_config.kind}"
|
|
103
103
|
)
|
|
104
|
-
|
|
105
|
-
results_with_error = await asyncio.gather(*tasks, return_exceptions=True)
|
|
104
|
+
successful_results, errors = await gather_and_split_errors_from_results(tasks)
|
|
106
105
|
results.extend(
|
|
107
106
|
sum(
|
|
108
|
-
|
|
109
|
-
result
|
|
110
|
-
for result in results_with_error
|
|
111
|
-
if not isinstance(result, Exception)
|
|
112
|
-
],
|
|
107
|
+
successful_results,
|
|
113
108
|
[],
|
|
114
109
|
)
|
|
115
110
|
)
|
|
116
|
-
errors = [
|
|
117
|
-
result for result in results_with_error if isinstance(result, Exception)
|
|
118
|
-
]
|
|
119
111
|
|
|
120
112
|
logger.info(
|
|
121
113
|
f"Triggered {len(tasks)} tasks for {resource_config.kind}, failed: {len(errors)}"
|
|
@@ -128,7 +120,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
128
120
|
raw_diff: list[tuple[ResourceConfig, list[RAW_ITEM]]],
|
|
129
121
|
parse_all: bool = False,
|
|
130
122
|
send_raw_data_examples_amount: int = 0,
|
|
131
|
-
) -> list[
|
|
123
|
+
) -> list[CalculationResult]:
|
|
132
124
|
return await asyncio.gather(
|
|
133
125
|
*(
|
|
134
126
|
self.entity_processor.parse_items(
|
|
@@ -145,12 +137,12 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
145
137
|
user_agent_type: UserAgentType,
|
|
146
138
|
parse_all: bool = False,
|
|
147
139
|
send_raw_data_examples_amount: int = 0,
|
|
148
|
-
) ->
|
|
140
|
+
) -> CalculationResult:
|
|
149
141
|
objects_diff = await self._calculate_raw(
|
|
150
142
|
[(resource, results)], parse_all, send_raw_data_examples_amount
|
|
151
143
|
)
|
|
152
144
|
await self.entities_state_applier.upsert(
|
|
153
|
-
objects_diff[0].passed, user_agent_type
|
|
145
|
+
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
154
146
|
)
|
|
155
147
|
|
|
156
148
|
return objects_diff[0]
|
|
@@ -160,13 +152,15 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
160
152
|
resource: ResourceConfig,
|
|
161
153
|
results: list[RAW_ITEM],
|
|
162
154
|
user_agent_type: UserAgentType,
|
|
163
|
-
) -> list[Entity]:
|
|
155
|
+
) -> tuple[list[Entity], list[Exception]]:
|
|
164
156
|
objects_diff = await self._calculate_raw([(resource, results)])
|
|
157
|
+
entities_selector_diff, errors = objects_diff[0]
|
|
165
158
|
|
|
166
|
-
|
|
167
|
-
|
|
159
|
+
await self.entities_state_applier.delete(
|
|
160
|
+
entities_selector_diff.passed, user_agent_type
|
|
161
|
+
)
|
|
168
162
|
logger.info("Finished unregistering change")
|
|
169
|
-
return
|
|
163
|
+
return entities_selector_diff.passed, errors
|
|
170
164
|
|
|
171
165
|
async def _register_in_batches(
|
|
172
166
|
self, resource_config: ResourceConfig, user_agent_type: UserAgentType
|
|
@@ -183,39 +177,38 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
183
177
|
send_raw_data_examples_amount = (
|
|
184
178
|
SEND_RAW_DATA_EXAMPLES_AMOUNT if ocean.config.send_raw_data_examples else 0
|
|
185
179
|
)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
180
|
+
all_entities, register_errors = await self._register_resource_raw(
|
|
181
|
+
resource_config,
|
|
182
|
+
raw_results,
|
|
183
|
+
user_agent_type,
|
|
184
|
+
send_raw_data_examples_amount=send_raw_data_examples_amount,
|
|
185
|
+
)
|
|
186
|
+
errors.extend(register_errors)
|
|
187
|
+
passed_entities = list(all_entities.passed)
|
|
194
188
|
|
|
195
189
|
for generator in async_generators:
|
|
196
190
|
try:
|
|
197
191
|
async for items in generator:
|
|
198
192
|
if send_raw_data_examples_amount > 0:
|
|
199
193
|
send_raw_data_examples_amount = max(
|
|
200
|
-
0, send_raw_data_examples_amount - len(
|
|
194
|
+
0, send_raw_data_examples_amount - len(passed_entities)
|
|
201
195
|
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
send_raw_data_examples_amount=send_raw_data_examples_amount,
|
|
209
|
-
)
|
|
210
|
-
).passed
|
|
196
|
+
|
|
197
|
+
entities, register_errors = await self._register_resource_raw(
|
|
198
|
+
resource_config,
|
|
199
|
+
items,
|
|
200
|
+
user_agent_type,
|
|
201
|
+
send_raw_data_examples_amount=send_raw_data_examples_amount,
|
|
211
202
|
)
|
|
203
|
+
errors.extend(register_errors)
|
|
204
|
+
passed_entities.extend(entities.passed)
|
|
212
205
|
except* OceanAbortException as error:
|
|
213
206
|
errors.append(error)
|
|
214
207
|
|
|
215
208
|
logger.info(
|
|
216
|
-
f"Finished registering change for {len(results)} raw results for kind: {resource_config.kind}. {len(
|
|
209
|
+
f"Finished registering change for {len(results)} raw results for kind: {resource_config.kind}. {len(passed_entities)} entities were affected"
|
|
217
210
|
)
|
|
218
|
-
return
|
|
211
|
+
return passed_entities, errors
|
|
219
212
|
|
|
220
213
|
async def register_raw(
|
|
221
214
|
self,
|
|
@@ -244,16 +237,26 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
244
237
|
if not resource_mappings:
|
|
245
238
|
return []
|
|
246
239
|
|
|
247
|
-
diffs
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
240
|
+
diffs, errors = zip(
|
|
241
|
+
await asyncio.gather(
|
|
242
|
+
*(
|
|
243
|
+
self._register_resource_raw(
|
|
244
|
+
resource, results, user_agent_type, True
|
|
245
|
+
)
|
|
246
|
+
for resource in resource_mappings
|
|
247
|
+
)
|
|
251
248
|
)
|
|
252
249
|
)
|
|
253
250
|
|
|
254
|
-
|
|
255
|
-
(
|
|
256
|
-
|
|
251
|
+
if errors:
|
|
252
|
+
message = f"Failed to register {len(errors)} entities. Skipping delete phase due to incomplete state"
|
|
253
|
+
logger.error(message, exc_info=errors)
|
|
254
|
+
raise ExceptionGroup(
|
|
255
|
+
message,
|
|
256
|
+
errors,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
registered_entities, entities_to_delete = zip_and_sum(diffs)
|
|
257
260
|
|
|
258
261
|
registered_entities_attributes = {
|
|
259
262
|
(entity.identifier, entity.blueprint) for entity in registered_entities
|
|
@@ -306,13 +309,23 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
306
309
|
resource for resource in config.resources if resource.kind == kind
|
|
307
310
|
]
|
|
308
311
|
|
|
309
|
-
|
|
312
|
+
entities, errors = await asyncio.gather(
|
|
310
313
|
*(
|
|
311
314
|
self._unregister_resource_raw(resource, results, user_agent_type)
|
|
312
315
|
for resource in resource_mappings
|
|
313
316
|
)
|
|
314
317
|
)
|
|
315
318
|
|
|
319
|
+
if errors:
|
|
320
|
+
message = f"Failed to unregister all entities with {len(errors)} errors"
|
|
321
|
+
logger.error(message, exc_info=errors)
|
|
322
|
+
raise ExceptionGroup(
|
|
323
|
+
message,
|
|
324
|
+
errors,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
return entities
|
|
328
|
+
|
|
316
329
|
async def update_raw_diff(
|
|
317
330
|
self,
|
|
318
331
|
kind: str,
|
|
@@ -337,14 +350,16 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
337
350
|
with logger.contextualize(kind=kind):
|
|
338
351
|
logger.info(f"Found {len(resource_mappings)} resources for {kind}")
|
|
339
352
|
|
|
340
|
-
entities_before =
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
353
|
+
entities_before, _ = zip(
|
|
354
|
+
await self._calculate_raw(
|
|
355
|
+
[
|
|
356
|
+
(mapping, raw_desired_state["before"])
|
|
357
|
+
for mapping in resource_mappings
|
|
358
|
+
]
|
|
359
|
+
)
|
|
345
360
|
)
|
|
346
361
|
|
|
347
|
-
entities_after = await self._calculate_raw(
|
|
362
|
+
entities_after, after_errors = await self._calculate_raw(
|
|
348
363
|
[(mapping, raw_desired_state["after"]) for mapping in resource_mappings]
|
|
349
364
|
)
|
|
350
365
|
|
|
@@ -356,6 +371,14 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
356
371
|
(entities_diff.passed for entities_diff in entities_after), []
|
|
357
372
|
)
|
|
358
373
|
|
|
374
|
+
if after_errors:
|
|
375
|
+
message = (
|
|
376
|
+
f"Failed to calculate diff for entities with {len(after_errors)} errors. "
|
|
377
|
+
f"Skipping delete phase due to incomplete state"
|
|
378
|
+
)
|
|
379
|
+
logger.error(message, exc_info=after_errors)
|
|
380
|
+
entities_before_flatten = []
|
|
381
|
+
|
|
359
382
|
await self.entities_state_applier.apply_diff(
|
|
360
383
|
{"before": entities_before_flatten, "after": entities_after_flatten},
|
|
361
384
|
user_agent_type,
|
|
@@ -390,6 +413,20 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
390
413
|
use_cache=False
|
|
391
414
|
)
|
|
392
415
|
|
|
416
|
+
try:
|
|
417
|
+
entities_at_port = await ocean.port_client.search_entities(
|
|
418
|
+
user_agent_type
|
|
419
|
+
)
|
|
420
|
+
except httpx.HTTPError as e:
|
|
421
|
+
logger.warning(
|
|
422
|
+
"Failed to fetch the current state of entities at Port. "
|
|
423
|
+
"Skipping delete phase due to unknown initial state. "
|
|
424
|
+
f"Error: {e}\n"
|
|
425
|
+
f"Response status code: {e.response.status_code if isinstance(e, httpx.HTTPStatusError) else None}\n"
|
|
426
|
+
f"Response content: {e.response.text if isinstance(e, httpx.HTTPStatusError) else None}\n"
|
|
427
|
+
)
|
|
428
|
+
entities_at_port = []
|
|
429
|
+
|
|
393
430
|
creation_results: list[tuple[list[Entity], list[Exception]]] = []
|
|
394
431
|
|
|
395
432
|
try:
|
|
@@ -407,6 +444,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
407
444
|
except asyncio.CancelledError as e:
|
|
408
445
|
logger.warning("Resync aborted successfully")
|
|
409
446
|
else:
|
|
447
|
+
if not entities_at_port:
|
|
448
|
+
logger.warning(
|
|
449
|
+
"Due to an error before the resync, the previous state of entities at Port is unknown."
|
|
450
|
+
" Skipping delete phase due to unknown initial state."
|
|
451
|
+
)
|
|
452
|
+
return
|
|
453
|
+
|
|
410
454
|
logger.info("Starting resync diff calculation")
|
|
411
455
|
flat_created_entities, errors = zip_and_sum(creation_results) or [
|
|
412
456
|
[],
|
|
@@ -424,11 +468,8 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
424
468
|
|
|
425
469
|
logger.error(message, exc_info=error_group)
|
|
426
470
|
else:
|
|
427
|
-
entities_at_port = await ocean.port_client.search_entities(
|
|
428
|
-
user_agent_type
|
|
429
|
-
)
|
|
430
471
|
logger.info(
|
|
431
|
-
f"Running resync diff calculation, number of entities
|
|
472
|
+
f"Running resync diff calculation, number of entities at Port before resync: {len(entities_at_port)}, number of entities created during sync: {len(flat_created_entities)}"
|
|
432
473
|
)
|
|
433
474
|
await self.entities_state_applier.delete_diff(
|
|
434
475
|
{"before": entities_at_port, "after": flat_created_entities},
|
port_ocean/core/models.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
1
2
|
from typing import Any
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel
|
|
@@ -34,3 +35,16 @@ class Migration(BaseModel):
|
|
|
34
35
|
sourceBlueprint: str
|
|
35
36
|
mapping: dict[str, Any]
|
|
36
37
|
status: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class EntityPortDiff:
|
|
42
|
+
"""Represents the differences between entities for porting.
|
|
43
|
+
|
|
44
|
+
This class holds the lists of deleted, modified, and created entities as part
|
|
45
|
+
of the porting process.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
deleted: list[Entity] = field(default_factory=list)
|
|
49
|
+
modified: list[Entity] = field(default_factory=list)
|
|
50
|
+
created: list[Entity] = field(default_factory=list)
|
port_ocean/core/ocean_types.py
CHANGED
|
@@ -2,7 +2,6 @@ from typing import TypedDict, Any, AsyncIterator, Callable, Awaitable, NamedTupl
|
|
|
2
2
|
|
|
3
3
|
from port_ocean.core.models import Entity
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
RAW_ITEM = dict[Any, Any]
|
|
7
6
|
RAW_RESULT = list[RAW_ITEM]
|
|
8
7
|
ASYNC_GENERATOR_RESYNC_TYPE = AsyncIterator[RAW_RESULT]
|
|
@@ -28,6 +27,11 @@ class EntitySelectorDiff(NamedTuple):
|
|
|
28
27
|
failed: list[Entity]
|
|
29
28
|
|
|
30
29
|
|
|
30
|
+
class CalculationResult(NamedTuple):
|
|
31
|
+
entity_selector_diff: EntitySelectorDiff
|
|
32
|
+
errors: list[Exception]
|
|
33
|
+
|
|
34
|
+
|
|
31
35
|
class IntegrationEventsCallbacks(TypedDict):
|
|
32
36
|
start: list[START_EVENT_LISTENER]
|
|
33
37
|
resync: dict[str | None, list[RESYNC_EVENT_LISTENER]]
|
port_ocean/core/utils.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Iterable, Any, TypeVar, Callable, Awaitable
|
|
2
3
|
|
|
3
4
|
from pydantic import parse_obj_as, ValidationError
|
|
4
5
|
|
|
5
|
-
from port_ocean.core.handlers.entity_processor.base import EntityPortDiff
|
|
6
6
|
from port_ocean.core.models import Entity
|
|
7
|
+
from port_ocean.core.models import EntityPortDiff
|
|
7
8
|
from port_ocean.core.ocean_types import RAW_RESULT
|
|
8
9
|
from port_ocean.exceptions.core import RawObjectValidationException
|
|
9
10
|
|
|
@@ -28,6 +29,31 @@ def is_same_entity(first_entity: Entity, second_entity: Entity) -> bool:
|
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
Q = TypeVar("Q")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def gather_and_split_errors_from_results(
|
|
36
|
+
task: Iterable[Awaitable[Q]],
|
|
37
|
+
result_threshold_validation: Callable[[Q | Exception], bool] | None = None,
|
|
38
|
+
) -> tuple[list[Q], list[Exception]]:
|
|
39
|
+
valid_items: list[Q] = []
|
|
40
|
+
errors: list[Exception] = []
|
|
41
|
+
results = await asyncio.gather(*task, return_exceptions=True)
|
|
42
|
+
for item in results:
|
|
43
|
+
# return_exceptions will also catch Python BaseException which also includes KeyboardInterrupt, SystemExit, GeneratorExit
|
|
44
|
+
# https://docs.python.org/3/library/asyncio-task.html#asyncio.gather
|
|
45
|
+
# These exceptions should be raised and not caught for the application to exit properly.
|
|
46
|
+
# https://stackoverflow.com/a/17802352
|
|
47
|
+
if isinstance(item, BaseException):
|
|
48
|
+
raise item
|
|
49
|
+
elif isinstance(item, Exception):
|
|
50
|
+
errors.append(item)
|
|
51
|
+
elif not result_threshold_validation or result_threshold_validation(item):
|
|
52
|
+
valid_items.append(item)
|
|
53
|
+
|
|
54
|
+
return valid_items, errors
|
|
55
|
+
|
|
56
|
+
|
|
31
57
|
def get_port_diff(
|
|
32
58
|
before: Iterable[Entity],
|
|
33
59
|
after: Iterable[Entity],
|
port_ocean/utils/queue_utils.py
CHANGED
|
@@ -11,14 +11,18 @@ async def _start_processor_worker(
|
|
|
11
11
|
queue: Queue[Any | None],
|
|
12
12
|
func: Callable[..., Coroutine[Any, Any, T]],
|
|
13
13
|
results: list[T],
|
|
14
|
+
errors: list[Exception],
|
|
14
15
|
) -> None:
|
|
15
16
|
while True:
|
|
16
|
-
raw_params = await queue.get()
|
|
17
17
|
try:
|
|
18
|
+
raw_params = await queue.get()
|
|
18
19
|
if raw_params is None:
|
|
19
20
|
return
|
|
20
|
-
logger.debug(
|
|
21
|
+
logger.debug("Processing async task")
|
|
21
22
|
results.append(await func(*raw_params))
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.error(f"Error processing task: {e}")
|
|
25
|
+
errors.append(e)
|
|
22
26
|
finally:
|
|
23
27
|
queue.task_done()
|
|
24
28
|
|
|
@@ -59,11 +63,12 @@ async def process_in_queue(
|
|
|
59
63
|
queue: Queue[Any | None] = Queue(maxsize=concurrency * 2)
|
|
60
64
|
tasks: list[Task[Any]] = []
|
|
61
65
|
processing_results: list[T] = []
|
|
66
|
+
errors: list[Exception] = []
|
|
62
67
|
|
|
63
68
|
for i in range(concurrency):
|
|
64
69
|
tasks.append(
|
|
65
70
|
asyncio.create_task(
|
|
66
|
-
_start_processor_worker(queue, func, processing_results)
|
|
71
|
+
_start_processor_worker(queue, func, processing_results, errors)
|
|
67
72
|
)
|
|
68
73
|
)
|
|
69
74
|
|
|
@@ -77,5 +82,7 @@ async def process_in_queue(
|
|
|
77
82
|
|
|
78
83
|
await queue.join()
|
|
79
84
|
await asyncio.gather(*tasks)
|
|
85
|
+
if errors:
|
|
86
|
+
raise ExceptionGroup("Error processing tasks", errors)
|
|
80
87
|
|
|
81
88
|
return processing_results
|
|
@@ -22,7 +22,7 @@ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore,sha256=
|
|
|
22
22
|
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore,sha256=kCpRPdl3S_jqYYZaOrc0-xa6-l3KqVjNRXc6jCkd_-Q,12
|
|
23
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
|
-
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile,sha256=
|
|
25
|
+
port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile,sha256=LsH3vZqqEJkzeQG44cE7JkvPAuh_WPSqYam4YoMvG3M,328
|
|
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
|
|
@@ -39,8 +39,8 @@ port_ocean/clients/port/authentication.py,sha256=t3z6h4vld-Tzkpth15sstaMJg0rccX-
|
|
|
39
39
|
port_ocean/clients/port/client.py,sha256=3GYCM0ZkX3pB6sNoOb-7_6dm0Jr5_vqhflD9iltf_As,2640
|
|
40
40
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
port_ocean/clients/port/mixins/blueprints.py,sha256=BiqkhvDFdkySWgL1NHI-LAQ9ieZWazZAojPo9E8d7U4,4575
|
|
42
|
-
port_ocean/clients/port/mixins/entities.py,sha256=
|
|
43
|
-
port_ocean/clients/port/mixins/integrations.py,sha256=
|
|
42
|
+
port_ocean/clients/port/mixins/entities.py,sha256=pMERHqy1keb5w2k2xQsNuyZaKSAZ5ijVA4pzxEbAatY,7365
|
|
43
|
+
port_ocean/clients/port/mixins/integrations.py,sha256=ypRYi7_VlnP2YEPgE8zS-_6WgPzUmsRi95K_pwVIpKE,5943
|
|
44
44
|
port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
|
|
45
45
|
port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
|
|
46
46
|
port_ocean/clients/port/types.py,sha256=nvlgiAq4WH5_F7wQbz_GAWl-faob84LVgIjZ2Ww5mTk,451
|
|
@@ -59,7 +59,7 @@ port_ocean/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
59
59
|
port_ocean/core/defaults/__init__.py,sha256=8qCZg8n06WAdMu9s_FiRtDYLGPGHbOuS60vapeUoAks,142
|
|
60
60
|
port_ocean/core/defaults/clean.py,sha256=S3UAfca-oU89WJKIB4OgGjGjPr0vxBQ2aRZsLTZhQ04,2185
|
|
61
61
|
port_ocean/core/defaults/common.py,sha256=QnLFTkT3yIWIRtLQb7fUvvfe5AfInYJy0q5LjlzHkOw,3553
|
|
62
|
-
port_ocean/core/defaults/initialize.py,sha256=
|
|
62
|
+
port_ocean/core/defaults/initialize.py,sha256=Cm1QFO7CLoPDdbJVYycbkWlfJfq7hlGbdRj49clBNUM,7432
|
|
63
63
|
port_ocean/core/event_listener/__init__.py,sha256=mzJ33wRq0kh60fpVdOHVmvMTUQIvz3vxmifyBgwDn0E,889
|
|
64
64
|
port_ocean/core/event_listener/base.py,sha256=4RujgPz4VfDFlviu4qLGJFnJougSCL-Ewf0rfTQfwgc,1133
|
|
65
65
|
port_ocean/core/event_listener/factory.py,sha256=AYYfSHPAF7P5H-uQECXT0JVJjKDHrYkWJJBSL4mGkg8,3697
|
|
@@ -67,7 +67,7 @@ port_ocean/core/event_listener/http.py,sha256=UjONC_OODHjpGjvsBPAvO6zGzosdmv5Hx9
|
|
|
67
67
|
port_ocean/core/event_listener/kafka.py,sha256=0U_TwmlmtS8N2lprkCmnyOmdqOAvghWxHhSfyu46kEs,6875
|
|
68
68
|
port_ocean/core/event_listener/once.py,sha256=KdvUqjIcyp8XqhY1GfR_KYJfParFIRrjCtcMf3JMegk,2150
|
|
69
69
|
port_ocean/core/event_listener/polling.py,sha256=3UxjgQJly5y-hA8R798oFWb7bFsYMxSc6GRozA3biiM,3539
|
|
70
|
-
port_ocean/core/handlers/__init__.py,sha256=
|
|
70
|
+
port_ocean/core/handlers/__init__.py,sha256=d7ShmS90gLRzGKJA6oNy2Zs_dF2yjkmYZInRhBnO9Rw,572
|
|
71
71
|
port_ocean/core/handlers/base.py,sha256=cTarblazu8yh8xz2FpB-dzDKuXxtoi143XJgPbV_DcM,157
|
|
72
72
|
port_ocean/core/handlers/entities_state_applier/__init__.py,sha256=kgLZDCeCEzi4r-0nzW9k78haOZNf6PX7mJOUr34A4c8,173
|
|
73
73
|
port_ocean/core/handlers/entities_state_applier/base.py,sha256=FMsrBOVgaO4o7B1klLDY8fobTUDvyrerCKCICyYtkXs,2193
|
|
@@ -76,8 +76,8 @@ port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=G6RyPgBAy
|
|
|
76
76
|
port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha256=1zncwCbE-Gej0xaWKlzZgoXxOBe9bgs_YxlZ8QW3NdI,1751
|
|
77
77
|
port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=82BvU8t5w9uhsxX8hbnwuRPuWhW3cMeuT_5sVIkip1I,1550
|
|
78
78
|
port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
|
|
79
|
-
port_ocean/core/handlers/entity_processor/base.py,sha256=
|
|
80
|
-
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=
|
|
79
|
+
port_ocean/core/handlers/entity_processor/base.py,sha256=udR0w5TstTOS5xOfTjAZIEdldn4xr6Oyb3DylatYX3Q,1869
|
|
80
|
+
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=CLg3FxHDs-GIXmRFC0r_BlJd9VGIi9ySjbWkYfB73i8,7330
|
|
81
81
|
port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
|
|
82
82
|
port_ocean/core/handlers/port_app_config/api.py,sha256=6VbKPwFzsWG0IYsVD81hxSmfqtHUFqrfUuj1DBX5g4w,853
|
|
83
83
|
port_ocean/core/handlers/port_app_config/base.py,sha256=oufdNLzUmEgJY5PgIz75zDlowWrjA-y8WR4UnM58E58,2897
|
|
@@ -88,11 +88,11 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
|
88
88
|
port_ocean/core/integrations/mixins/events.py,sha256=Ddfx2L4FpghV38waF8OfVeOV0bHBxNIgjU-q5ffillI,2341
|
|
89
89
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
90
90
|
port_ocean/core/integrations/mixins/sync.py,sha256=TKqRytxXONVhuCo3CB3rDvWNbITnZz33TYTDs3SWWVk,3880
|
|
91
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
91
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=GmH5YPVnYO6mKEhoRer9F-vYy8mxPspaSWw2y9JJhgc,17954
|
|
92
92
|
port_ocean/core/integrations/mixins/utils.py,sha256=7y1rGETZIjOQadyIjFJXIHKkQFKx_SwiP-TrAIsyyLY,2303
|
|
93
|
-
port_ocean/core/models.py,sha256=
|
|
94
|
-
port_ocean/core/ocean_types.py,sha256=
|
|
95
|
-
port_ocean/core/utils.py,sha256=
|
|
93
|
+
port_ocean/core/models.py,sha256=YPIU7V3GKeDXIVkNzZn0w17YN2Akl-D8nFs-l4bEKcU,1143
|
|
94
|
+
port_ocean/core/ocean_types.py,sha256=3_d8-n626f1kWLQ_Jxw194LEyrOVupz05qs_Y1pvB-A,990
|
|
95
|
+
port_ocean/core/utils.py,sha256=rP8Fo2TSdSye4w0S8W0Vi4lpMJUOIbZ9kmtCkbGKxPY,2855
|
|
96
96
|
port_ocean/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
97
|
port_ocean/exceptions/api.py,sha256=TLmTMqn4uHGaHgZK8PMIJ0TVJlPB4iP7xl9rx7GtCyY,426
|
|
98
98
|
port_ocean/exceptions/base.py,sha256=uY4DX7fIITDFfemCJDWpaZi3bD51lcANc5swpoNvMJA,46
|
|
@@ -118,12 +118,12 @@ port_ocean/utils/async_http.py,sha256=arnH458TExn2Dju_Sy6pHas_vF5RMWnOp-jBz5WAAc
|
|
|
118
118
|
port_ocean/utils/async_iterators.py,sha256=buFBiPdsqkNMCk91h6ZG8hJa181j7RjgHajbfgeB8A8,1608
|
|
119
119
|
port_ocean/utils/cache.py,sha256=3KItZDE2yVrbVDr-hoM8lNna8s2dlpxhP4ICdLjH4LQ,2231
|
|
120
120
|
port_ocean/utils/misc.py,sha256=WZjrEDRfyeqbesVt_Nkp2yjazbKF-BOnxJMFAI721yQ,1965
|
|
121
|
-
port_ocean/utils/queue_utils.py,sha256=
|
|
121
|
+
port_ocean/utils/queue_utils.py,sha256=KWWl8YVnG-glcfIHhM6nefY-2sou_C6DVP1VynQwzB4,2762
|
|
122
122
|
port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,3231
|
|
123
123
|
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
|
124
124
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
125
|
-
port_ocean-0.5.
|
|
126
|
-
port_ocean-0.5.
|
|
127
|
-
port_ocean-0.5.
|
|
128
|
-
port_ocean-0.5.
|
|
129
|
-
port_ocean-0.5.
|
|
125
|
+
port_ocean-0.5.24.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
126
|
+
port_ocean-0.5.24.dist-info/METADATA,sha256=5EZHZqQ-zCeTf4LVLveW1-L-35tCSQ745mYQJ_FmTCM,6554
|
|
127
|
+
port_ocean-0.5.24.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
128
|
+
port_ocean-0.5.24.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
129
|
+
port_ocean-0.5.24.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|