port-ocean 0.18.2__py3-none-any.whl → 0.18.4__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.
- integrations/_infra/Makefile +9 -1
- port_ocean/clients/port/client.py +3 -0
- port_ocean/clients/port/mixins/entities.py +3 -1
- port_ocean/clients/port/mixins/integrations.py +56 -4
- port_ocean/clients/port/mixins/organization.py +31 -0
- port_ocean/config/settings.py +17 -1
- port_ocean/core/defaults/initialize.py +56 -8
- port_ocean/core/integrations/mixins/sync_raw.py +18 -24
- port_ocean/core/models.py +6 -1
- port_ocean/exceptions/port_defaults.py +10 -0
- port_ocean/tests/clients/port/mixins/test_organization_mixin.py +27 -0
- port_ocean/tests/core/handlers/mixins/test_sync_raw.py +131 -214
- {port_ocean-0.18.2.dist-info → port_ocean-0.18.4.dist-info}/METADATA +1 -1
- {port_ocean-0.18.2.dist-info → port_ocean-0.18.4.dist-info}/RECORD +17 -15
- {port_ocean-0.18.2.dist-info → port_ocean-0.18.4.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.18.2.dist-info → port_ocean-0.18.4.dist-info}/WHEEL +0 -0
- {port_ocean-0.18.2.dist-info → port_ocean-0.18.4.dist-info}/entry_points.txt +0 -0
integrations/_infra/Makefile
CHANGED
|
@@ -41,7 +41,7 @@ define deactivate_virtualenv
|
|
|
41
41
|
fi
|
|
42
42
|
endef
|
|
43
43
|
|
|
44
|
-
.SILENT: install install/prod install/local-core lint lint/fix run test clean
|
|
44
|
+
.SILENT: install install/prod install/local-core lint lint/fix run test clean seed
|
|
45
45
|
|
|
46
46
|
install:
|
|
47
47
|
$(call deactivate_virtualenv) && \
|
|
@@ -85,3 +85,11 @@ clean:
|
|
|
85
85
|
rm -rf .tox/
|
|
86
86
|
rm -rf docs/_build
|
|
87
87
|
rm -rf dist/
|
|
88
|
+
|
|
89
|
+
seed:
|
|
90
|
+
@if [ -f "tests/seed_data.py" ]; then \
|
|
91
|
+
$(ACTIVATE) && python tests/seed_data.py; \
|
|
92
|
+
else \
|
|
93
|
+
echo "No seeding script found. Create tests/seed_data.py for this integration if needed."; \
|
|
94
|
+
exit 0; \
|
|
95
|
+
fi
|
|
@@ -5,6 +5,7 @@ from port_ocean.clients.port.mixins.blueprints import BlueprintClientMixin
|
|
|
5
5
|
from port_ocean.clients.port.mixins.entities import EntityClientMixin
|
|
6
6
|
from port_ocean.clients.port.mixins.integrations import IntegrationClientMixin
|
|
7
7
|
from port_ocean.clients.port.mixins.migrations import MigrationClientMixin
|
|
8
|
+
from port_ocean.clients.port.mixins.organization import OrganizationClientMixin
|
|
8
9
|
from port_ocean.clients.port.types import (
|
|
9
10
|
KafkaCreds,
|
|
10
11
|
)
|
|
@@ -21,6 +22,7 @@ class PortClient(
|
|
|
21
22
|
IntegrationClientMixin,
|
|
22
23
|
BlueprintClientMixin,
|
|
23
24
|
MigrationClientMixin,
|
|
25
|
+
OrganizationClientMixin,
|
|
24
26
|
):
|
|
25
27
|
def __init__(
|
|
26
28
|
self,
|
|
@@ -48,6 +50,7 @@ class PortClient(
|
|
|
48
50
|
)
|
|
49
51
|
BlueprintClientMixin.__init__(self, self.auth, self.client)
|
|
50
52
|
MigrationClientMixin.__init__(self, self.auth, self.client)
|
|
53
|
+
OrganizationClientMixin.__init__(self, self.auth, self.client)
|
|
51
54
|
|
|
52
55
|
async def get_kafka_creds(self) -> KafkaCreds:
|
|
53
56
|
logger.info("Fetching organization kafka credentials")
|
|
@@ -22,7 +22,9 @@ class EntityClientMixin:
|
|
|
22
22
|
# Semaphore is used to limit the number of concurrent requests to port, to avoid overloading it.
|
|
23
23
|
# The number of concurrent requests is set to 90% of the max connections limit, to leave some room for other
|
|
24
24
|
# requests that are not related to entities.
|
|
25
|
-
self.semaphore = asyncio.Semaphore(
|
|
25
|
+
self.semaphore = asyncio.Semaphore(
|
|
26
|
+
round(0.5 * PORT_HTTP_MAX_CONNECTIONS_LIMIT)
|
|
27
|
+
) # 50% of the max connections limit in order to avoid overloading port
|
|
26
28
|
|
|
27
29
|
async def upsert_entity(
|
|
28
30
|
self,
|
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Dict, TYPE_CHECKING, Optional, TypedDict
|
|
2
3
|
from urllib.parse import quote_plus
|
|
3
4
|
|
|
4
5
|
import httpx
|
|
5
6
|
from loguru import logger
|
|
6
7
|
from port_ocean.clients.port.authentication import PortAuthentication
|
|
7
8
|
from port_ocean.clients.port.utils import handle_status_code
|
|
9
|
+
from port_ocean.exceptions.port_defaults import DefaultsProvisionFailed
|
|
8
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
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
INTEGRATION_POLLING_INTERVAL_INITIAL_SECONDS = 3
|
|
17
|
+
INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR = 1.15
|
|
18
|
+
INTEGRATION_POLLING_RETRY_LIMIT = 30
|
|
19
|
+
CREATE_RESOURCES_PARAM_NAME = "integration_modes"
|
|
20
|
+
CREATE_RESOURCES_PARAM_VALUE = ["create_resources"]
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
class LogAttributes(TypedDict):
|
|
15
24
|
ingestUrl: str
|
|
16
25
|
|
|
@@ -50,12 +59,41 @@ class IntegrationClientMixin:
|
|
|
50
59
|
self._log_attributes = response["logAttributes"]
|
|
51
60
|
return self._log_attributes
|
|
52
61
|
|
|
62
|
+
async def _poll_integration_until_default_provisioning_is_complete(
|
|
63
|
+
self,
|
|
64
|
+
) -> Dict[str, Any]:
|
|
65
|
+
attempts = 0
|
|
66
|
+
current_interval_seconds = INTEGRATION_POLLING_INTERVAL_INITIAL_SECONDS
|
|
67
|
+
|
|
68
|
+
while attempts < INTEGRATION_POLLING_RETRY_LIMIT:
|
|
69
|
+
logger.info(
|
|
70
|
+
f"Fetching created integration and validating config, attempt {attempts+1}/{INTEGRATION_POLLING_RETRY_LIMIT}",
|
|
71
|
+
attempt=attempts,
|
|
72
|
+
)
|
|
73
|
+
response = await self._get_current_integration()
|
|
74
|
+
integration_json = response.json()
|
|
75
|
+
if integration_json.get("integration", {}).get("config", {}):
|
|
76
|
+
return integration_json
|
|
77
|
+
|
|
78
|
+
logger.info(
|
|
79
|
+
f"Integration config is still being provisioned, retrying in {current_interval_seconds} seconds"
|
|
80
|
+
)
|
|
81
|
+
await asyncio.sleep(current_interval_seconds)
|
|
82
|
+
|
|
83
|
+
attempts += 1
|
|
84
|
+
current_interval_seconds = int(
|
|
85
|
+
current_interval_seconds * INTEGRATION_POLLING_INTERVAL_BACKOFF_FACTOR
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
raise DefaultsProvisionFailed(INTEGRATION_POLLING_RETRY_LIMIT)
|
|
89
|
+
|
|
53
90
|
async def create_integration(
|
|
54
91
|
self,
|
|
55
92
|
_type: str,
|
|
56
93
|
changelog_destination: dict[str, Any],
|
|
57
94
|
port_app_config: Optional["PortAppConfig"] = None,
|
|
58
|
-
|
|
95
|
+
create_port_resources_origin_in_port: Optional[bool] = False,
|
|
96
|
+
) -> Dict[str, Any]:
|
|
59
97
|
logger.info(f"Creating integration with id: {self.integration_identifier}")
|
|
60
98
|
headers = await self.auth.headers()
|
|
61
99
|
json = {
|
|
@@ -65,12 +103,26 @@ class IntegrationClientMixin:
|
|
|
65
103
|
"changelogDestination": changelog_destination,
|
|
66
104
|
"config": {},
|
|
67
105
|
}
|
|
68
|
-
|
|
106
|
+
|
|
107
|
+
query_params = {}
|
|
108
|
+
|
|
109
|
+
if create_port_resources_origin_in_port:
|
|
110
|
+
query_params[CREATE_RESOURCES_PARAM_NAME] = CREATE_RESOURCES_PARAM_VALUE
|
|
111
|
+
|
|
112
|
+
if port_app_config and not create_port_resources_origin_in_port:
|
|
69
113
|
json["config"] = port_app_config.to_request()
|
|
70
114
|
response = await self.client.post(
|
|
71
|
-
f"{self.auth.api_url}/integration",
|
|
115
|
+
f"{self.auth.api_url}/integration",
|
|
116
|
+
headers=headers,
|
|
117
|
+
json=json,
|
|
118
|
+
params=query_params,
|
|
72
119
|
)
|
|
73
120
|
handle_status_code(response)
|
|
121
|
+
if create_port_resources_origin_in_port:
|
|
122
|
+
result = (
|
|
123
|
+
await self._poll_integration_until_default_provisioning_is_complete()
|
|
124
|
+
)
|
|
125
|
+
return result["integration"]
|
|
74
126
|
return response.json()["integration"]
|
|
75
127
|
|
|
76
128
|
async def patch_integration(
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import httpx
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from port_ocean.clients.port.authentication import PortAuthentication
|
|
5
|
+
from port_ocean.clients.port.utils import handle_status_code
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OrganizationClientMixin:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
auth: PortAuthentication,
|
|
12
|
+
client: httpx.AsyncClient,
|
|
13
|
+
):
|
|
14
|
+
self.auth = auth
|
|
15
|
+
self.client = client
|
|
16
|
+
|
|
17
|
+
async def _get_organization_feature_flags(self) -> httpx.Response:
|
|
18
|
+
logger.info("Fetching organization feature flags")
|
|
19
|
+
|
|
20
|
+
response = await self.client.get(
|
|
21
|
+
f"{self.auth.api_url}/organization",
|
|
22
|
+
headers=await self.auth.headers(),
|
|
23
|
+
)
|
|
24
|
+
return response
|
|
25
|
+
|
|
26
|
+
async def get_organization_feature_flags(
|
|
27
|
+
self, should_raise: bool = True, should_log: bool = True
|
|
28
|
+
) -> List[str]:
|
|
29
|
+
response = await self._get_organization_feature_flags()
|
|
30
|
+
handle_status_code(response, should_raise, should_log)
|
|
31
|
+
return response.json().get("organization", {}).get("featureFlags", [])
|
port_ocean/config/settings.py
CHANGED
|
@@ -8,7 +8,7 @@ from pydantic.main import BaseModel
|
|
|
8
8
|
|
|
9
9
|
from port_ocean.config.base import BaseOceanSettings, BaseOceanModel
|
|
10
10
|
from port_ocean.core.event_listener import EventListenerSettingsType
|
|
11
|
-
from port_ocean.core.models import Runtime
|
|
11
|
+
from port_ocean.core.models import CreatePortResourcesOrigin, Runtime
|
|
12
12
|
from port_ocean.utils.misc import get_integration_name, get_spec_file
|
|
13
13
|
|
|
14
14
|
LogLevelType = Literal["ERROR", "WARNING", "INFO", "DEBUG", "CRITICAL"]
|
|
@@ -68,6 +68,8 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
|
68
68
|
initialize_port_resources: bool = True
|
|
69
69
|
scheduled_resync_interval: int | None = None
|
|
70
70
|
client_timeout: int = 60
|
|
71
|
+
# Determines if Port should generate resources such as blueprints and pages instead of ocean
|
|
72
|
+
create_port_resources_origin: CreatePortResourcesOrigin | None = None
|
|
71
73
|
send_raw_data_examples: bool = True
|
|
72
74
|
port: PortSettings
|
|
73
75
|
event_listener: EventListenerSettingsType = Field(
|
|
@@ -101,6 +103,20 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
|
101
103
|
|
|
102
104
|
return values
|
|
103
105
|
|
|
106
|
+
@validator("create_port_resources_origin")
|
|
107
|
+
def validate_create_port_resources_origin(
|
|
108
|
+
cls, create_port_resources_origin: CreatePortResourcesOrigin | None
|
|
109
|
+
) -> CreatePortResourcesOrigin | None:
|
|
110
|
+
spec = get_spec_file()
|
|
111
|
+
if spec and spec.get("create_port_resources_origin", None):
|
|
112
|
+
spec_create_port_resources_origin = spec.get("create_port_resources_origin")
|
|
113
|
+
if spec_create_port_resources_origin in [
|
|
114
|
+
CreatePortResourcesOrigin.Port,
|
|
115
|
+
CreatePortResourcesOrigin.Ocean,
|
|
116
|
+
]:
|
|
117
|
+
return CreatePortResourcesOrigin(spec_create_port_resources_origin)
|
|
118
|
+
return create_port_resources_origin
|
|
119
|
+
|
|
104
120
|
@validator("runtime")
|
|
105
121
|
def validate_runtime(cls, runtime: Runtime) -> Runtime:
|
|
106
122
|
if runtime.is_saas_runtime:
|
|
@@ -13,12 +13,14 @@ from port_ocean.core.defaults.common import (
|
|
|
13
13
|
get_port_integration_defaults,
|
|
14
14
|
)
|
|
15
15
|
from port_ocean.core.handlers.port_app_config.models import PortAppConfig
|
|
16
|
-
from port_ocean.core.models import Blueprint
|
|
16
|
+
from port_ocean.core.models import Blueprint, CreatePortResourcesOrigin
|
|
17
17
|
from port_ocean.core.utils.utils import gather_and_split_errors_from_results
|
|
18
18
|
from port_ocean.exceptions.port_defaults import (
|
|
19
19
|
AbortDefaultCreationError,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG = "USE_PROVISIONED_DEFAULTS"
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
def deconstruct_blueprints_to_creation_steps(
|
|
24
26
|
raw_blueprints: list[dict[str, Any]],
|
|
@@ -54,8 +56,8 @@ def deconstruct_blueprints_to_creation_steps(
|
|
|
54
56
|
|
|
55
57
|
async def _initialize_required_integration_settings(
|
|
56
58
|
port_client: PortClient,
|
|
57
|
-
default_mapping: PortAppConfig,
|
|
58
59
|
integration_config: IntegrationConfiguration,
|
|
60
|
+
default_mapping: PortAppConfig | None = None,
|
|
59
61
|
) -> None:
|
|
60
62
|
try:
|
|
61
63
|
logger.info("Initializing integration at port")
|
|
@@ -70,6 +72,8 @@ async def _initialize_required_integration_settings(
|
|
|
70
72
|
integration_config.integration.type,
|
|
71
73
|
integration_config.event_listener.get_changelog_destination_details(),
|
|
72
74
|
port_app_config=default_mapping,
|
|
75
|
+
create_port_resources_origin_in_port=integration_config.create_port_resources_origin
|
|
76
|
+
== CreatePortResourcesOrigin.Port,
|
|
73
77
|
)
|
|
74
78
|
elif not integration.get("config"):
|
|
75
79
|
logger.info(
|
|
@@ -102,8 +106,10 @@ async def _initialize_required_integration_settings(
|
|
|
102
106
|
|
|
103
107
|
async def _create_resources(
|
|
104
108
|
port_client: PortClient,
|
|
105
|
-
defaults: Defaults,
|
|
109
|
+
defaults: Defaults | None = None,
|
|
106
110
|
) -> None:
|
|
111
|
+
if not defaults:
|
|
112
|
+
return
|
|
107
113
|
creation_stage, *blueprint_patches = deconstruct_blueprints_to_creation_steps(
|
|
108
114
|
defaults.blueprints
|
|
109
115
|
)
|
|
@@ -199,22 +205,64 @@ async def _create_resources(
|
|
|
199
205
|
async def _initialize_defaults(
|
|
200
206
|
config_class: Type[PortAppConfig], integration_config: IntegrationConfiguration
|
|
201
207
|
) -> None:
|
|
208
|
+
if not integration_config.initialize_port_resources:
|
|
209
|
+
return
|
|
210
|
+
|
|
202
211
|
port_client = ocean.port_client
|
|
203
212
|
defaults = get_port_integration_defaults(
|
|
204
213
|
config_class, integration_config.resources_path
|
|
205
214
|
)
|
|
206
|
-
|
|
215
|
+
|
|
216
|
+
if (
|
|
217
|
+
not integration_config.create_port_resources_origin
|
|
218
|
+
and integration_config.runtime.is_saas_runtime
|
|
219
|
+
):
|
|
220
|
+
logger.info("Setting resources origin to be Port")
|
|
221
|
+
integration_config.create_port_resources_origin = CreatePortResourcesOrigin.Port
|
|
222
|
+
|
|
223
|
+
if (
|
|
224
|
+
integration_config.create_port_resources_origin
|
|
225
|
+
== CreatePortResourcesOrigin.Port
|
|
226
|
+
):
|
|
227
|
+
logger.info(
|
|
228
|
+
"Resources origin is set to be Port, verifying integration is supported"
|
|
229
|
+
)
|
|
230
|
+
org_feature_flags = await port_client.get_organization_feature_flags()
|
|
231
|
+
if ORG_USE_PROVISIONED_DEFAULTS_FEATURE_FLAG not in org_feature_flags:
|
|
232
|
+
logger.info(
|
|
233
|
+
"Port origin for Integration is not supported, changing resources origin to use Ocean"
|
|
234
|
+
)
|
|
235
|
+
integration_config.create_port_resources_origin = (
|
|
236
|
+
CreatePortResourcesOrigin.Ocean
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if (
|
|
240
|
+
integration_config.create_port_resources_origin
|
|
241
|
+
!= CreatePortResourcesOrigin.Port
|
|
242
|
+
and not defaults
|
|
243
|
+
):
|
|
207
244
|
logger.warning("No defaults found. Skipping initialization...")
|
|
208
245
|
return None
|
|
209
246
|
|
|
210
|
-
if
|
|
247
|
+
if (
|
|
248
|
+
(defaults and defaults.port_app_config)
|
|
249
|
+
or integration_config.create_port_resources_origin
|
|
250
|
+
== CreatePortResourcesOrigin.Port
|
|
251
|
+
):
|
|
211
252
|
await _initialize_required_integration_settings(
|
|
212
|
-
port_client,
|
|
253
|
+
port_client,
|
|
254
|
+
integration_config,
|
|
255
|
+
defaults.port_app_config if defaults else None,
|
|
213
256
|
)
|
|
214
257
|
|
|
215
|
-
if
|
|
258
|
+
if (
|
|
259
|
+
integration_config.create_port_resources_origin
|
|
260
|
+
== CreatePortResourcesOrigin.Port
|
|
261
|
+
):
|
|
262
|
+
logger.info(
|
|
263
|
+
"Skipping creating defaults resources due to `create_port_resources_origin` being `Port`"
|
|
264
|
+
)
|
|
216
265
|
return
|
|
217
|
-
|
|
218
266
|
try:
|
|
219
267
|
logger.info("Found default resources, starting creation process")
|
|
220
268
|
await _create_resources(port_client, defaults)
|
|
@@ -220,33 +220,27 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
220
220
|
)
|
|
221
221
|
modified_objects = []
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
try:
|
|
224
|
+
changed_entities = await self._map_entities_compared_with_port(
|
|
225
|
+
objects_diff[0].entity_selector_diff.passed,
|
|
226
|
+
resource,
|
|
227
|
+
user_agent_type
|
|
228
|
+
)
|
|
229
|
+
if changed_entities:
|
|
230
|
+
logger.info("Upserting changed entities", changed_entities=len(changed_entities),
|
|
231
|
+
total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
|
232
|
+
await self.entities_state_applier.upsert(
|
|
233
|
+
changed_entities, user_agent_type
|
|
229
234
|
)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
changed_entities, user_agent_type
|
|
236
|
-
)
|
|
237
|
-
else:
|
|
238
|
-
logger.info("Entities in batch didn't changed since last sync, skipping", total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
|
239
|
-
|
|
240
|
-
modified_objects = [ocean.port_client._reduce_entity(entity) for entity in objects_diff[0].entity_selector_diff.passed]
|
|
241
|
-
except Exception as e:
|
|
242
|
-
logger.warning(f"Failed to resolve batch entities with Port, falling back to upserting all entities: {str(e)}")
|
|
243
|
-
modified_objects = await self.entities_state_applier.upsert(
|
|
244
|
-
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
245
|
-
)
|
|
246
|
-
else:
|
|
235
|
+
else:
|
|
236
|
+
logger.info("Entities in batch didn't changed since last sync, skipping", total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
|
237
|
+
modified_objects = [ocean.port_client._reduce_entity(entity) for entity in objects_diff[0].entity_selector_diff.passed]
|
|
238
|
+
except Exception as e:
|
|
239
|
+
logger.warning(f"Failed to resolve batch entities with Port, falling back to upserting all entities: {str(e)}")
|
|
247
240
|
modified_objects = await self.entities_state_applier.upsert(
|
|
248
241
|
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
249
|
-
|
|
242
|
+
)
|
|
243
|
+
|
|
250
244
|
return CalculationResult(
|
|
251
245
|
objects_diff[0].entity_selector_diff._replace(passed=modified_objects),
|
|
252
246
|
errors=objects_diff[0].errors,
|
port_ocean/core/models.py
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from enum import Enum
|
|
2
|
+
from enum import Enum, StrEnum
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
from pydantic.fields import Field
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
class CreatePortResourcesOrigin(StrEnum):
|
|
10
|
+
Ocean = "Ocean"
|
|
11
|
+
Port = "Port"
|
|
12
|
+
|
|
13
|
+
|
|
9
14
|
class Runtime(Enum):
|
|
10
15
|
Saas = "Saas"
|
|
11
16
|
OnPrem = "OnPrem"
|
|
@@ -14,3 +14,13 @@ class AbortDefaultCreationError(BaseOceanException):
|
|
|
14
14
|
|
|
15
15
|
class UnsupportedDefaultFileType(BaseOceanException):
|
|
16
16
|
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DefaultsProvisionFailed(BaseOceanException):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
retries: int,
|
|
23
|
+
):
|
|
24
|
+
super().__init__(
|
|
25
|
+
f"Failed to retrieve integration config after {retries} attempts"
|
|
26
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import MagicMock, AsyncMock
|
|
3
|
+
|
|
4
|
+
from port_ocean.clients.port.mixins.organization import OrganizationClientMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
async def mocked_org_mixin() -> OrganizationClientMixin:
|
|
9
|
+
auth = MagicMock()
|
|
10
|
+
auth.headers = AsyncMock()
|
|
11
|
+
auth.headers.return_value = {"auth": "enticated"}
|
|
12
|
+
client = MagicMock()
|
|
13
|
+
client.get = AsyncMock()
|
|
14
|
+
client.get.return_value = MagicMock()
|
|
15
|
+
client.get.return_value.json = MagicMock()
|
|
16
|
+
client.get.return_value.json.return_value = {
|
|
17
|
+
"organization": {"featureFlags": ["aa", "bb"]}
|
|
18
|
+
}
|
|
19
|
+
return OrganizationClientMixin(auth=auth, client=client)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def test_org_feature_flags_should_fetch_proper_json_path(
|
|
23
|
+
mocked_org_mixin: OrganizationClientMixin,
|
|
24
|
+
) -> None:
|
|
25
|
+
result = await mocked_org_mixin.get_organization_feature_flags()
|
|
26
|
+
|
|
27
|
+
assert result == ["aa", "bb"]
|
|
@@ -433,93 +433,76 @@ async def test_sync_raw_mixin_dependency(
|
|
|
433
433
|
@pytest.mark.asyncio
|
|
434
434
|
async def test_register_raw(
|
|
435
435
|
mock_sync_raw_mixin_with_jq_processor: SyncRawMixin,
|
|
436
|
-
|
|
437
|
-
mock_context: PortOceanContext,
|
|
438
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
436
|
+
mock_resource_config: ResourceConfig,
|
|
439
437
|
) -> None:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
monkeypatch.setattr(mock_context.app, "is_saas", lambda: False)
|
|
456
|
-
|
|
457
|
-
async with event_context(EventType.HTTP_REQUEST, trigger_type="machine") as event:
|
|
458
|
-
# Use patch to mock the method instead of direct assignment
|
|
459
|
-
with patch.object(
|
|
460
|
-
mock_sync_raw_mixin_with_jq_processor.port_app_config_handler,
|
|
461
|
-
"get_port_app_config",
|
|
462
|
-
return_value=PortAppConfig(
|
|
463
|
-
enable_merge_entity=True,
|
|
464
|
-
delete_dependent_entities=True,
|
|
465
|
-
create_missing_related_entities=False,
|
|
466
|
-
resources=[
|
|
467
|
-
ResourceConfig(
|
|
468
|
-
kind=kind,
|
|
469
|
-
selector=Selector(query="true"),
|
|
470
|
-
port=PortResourceConfig(
|
|
471
|
-
entity=MappingsConfig(
|
|
472
|
-
mappings=EntityMapping(
|
|
473
|
-
identifier=".id | tostring",
|
|
474
|
-
title=".name",
|
|
475
|
-
blueprint='"service"',
|
|
476
|
-
properties={"url": ".web_url"},
|
|
477
|
-
relations={},
|
|
478
|
-
)
|
|
479
|
-
)
|
|
480
|
-
),
|
|
481
|
-
)
|
|
482
|
-
],
|
|
483
|
-
),
|
|
484
|
-
):
|
|
485
|
-
# Ensure the event.port_app_config is set correctly
|
|
486
|
-
event.port_app_config = await mock_sync_raw_mixin_with_jq_processor.port_app_config_handler.get_port_app_config(
|
|
487
|
-
use_cache=False
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
def upsert_side_effect(
|
|
491
|
-
entities: list[Entity], user_agent_type: UserAgentType
|
|
492
|
-
) -> list[Entity]:
|
|
493
|
-
# Simulate returning the passed entities
|
|
494
|
-
return entities
|
|
438
|
+
# Mock the integration settings with skip_check_diff
|
|
439
|
+
with patch.object(ocean.config.integration, "skip_check_diff", False):
|
|
440
|
+
kind = "service"
|
|
441
|
+
user_agent_type = UserAgentType.exporter
|
|
442
|
+
raw_entity = [
|
|
443
|
+
{"id": "entity_1", "name": "entity_1", "web_url": "https://example.com"},
|
|
444
|
+
]
|
|
445
|
+
expected_result = [
|
|
446
|
+
{
|
|
447
|
+
"identifier": "entity_1",
|
|
448
|
+
"blueprint": "service",
|
|
449
|
+
"name": "entity_1",
|
|
450
|
+
"properties": {},
|
|
451
|
+
},
|
|
452
|
+
]
|
|
495
453
|
|
|
496
|
-
|
|
454
|
+
async with event_context(
|
|
455
|
+
EventType.HTTP_REQUEST, trigger_type="machine"
|
|
456
|
+
) as event:
|
|
457
|
+
# Use patch to mock the method instead of direct assignment
|
|
497
458
|
with patch.object(
|
|
498
|
-
mock_sync_raw_mixin_with_jq_processor.
|
|
499
|
-
"
|
|
500
|
-
|
|
459
|
+
mock_sync_raw_mixin_with_jq_processor.port_app_config_handler,
|
|
460
|
+
"get_port_app_config",
|
|
461
|
+
return_value=PortAppConfig(
|
|
462
|
+
enable_merge_entity=True,
|
|
463
|
+
delete_dependent_entities=True,
|
|
464
|
+
create_missing_related_entities=False,
|
|
465
|
+
resources=[mock_resource_config],
|
|
466
|
+
),
|
|
501
467
|
):
|
|
502
|
-
#
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
kind, raw_entity, user_agent_type
|
|
506
|
-
)
|
|
468
|
+
# Ensure the event.port_app_config is set correctly
|
|
469
|
+
event.port_app_config = await mock_sync_raw_mixin_with_jq_processor.port_app_config_handler.get_port_app_config(
|
|
470
|
+
use_cache=False
|
|
507
471
|
)
|
|
508
472
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
473
|
+
def upsert_side_effect(
|
|
474
|
+
entities: list[Entity], user_agent_type: UserAgentType
|
|
475
|
+
) -> list[Entity]:
|
|
476
|
+
# Simulate returning the passed entities
|
|
477
|
+
return entities
|
|
478
|
+
|
|
479
|
+
# Patch the upsert method with the side effect
|
|
480
|
+
with patch.object(
|
|
481
|
+
mock_sync_raw_mixin_with_jq_processor.entities_state_applier,
|
|
482
|
+
"upsert",
|
|
483
|
+
side_effect=upsert_side_effect,
|
|
484
|
+
):
|
|
485
|
+
# Call the register_raw method
|
|
486
|
+
registered_entities = (
|
|
487
|
+
await mock_sync_raw_mixin_with_jq_processor.register_raw(
|
|
488
|
+
kind, raw_entity, user_agent_type
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# Assert that the registered entities match the expected results
|
|
493
|
+
assert len(registered_entities) == len(expected_result)
|
|
494
|
+
for entity, result in zip(registered_entities, expected_result):
|
|
495
|
+
assert entity.identifier == result["identifier"]
|
|
496
|
+
assert entity.blueprint == result["blueprint"]
|
|
497
|
+
assert entity.properties == result["properties"]
|
|
515
498
|
|
|
516
499
|
|
|
517
500
|
@pytest.mark.asyncio
|
|
518
501
|
async def test_unregister_raw(
|
|
519
502
|
mock_sync_raw_mixin_with_jq_processor: SyncRawMixin,
|
|
520
|
-
mock_ocean: Ocean,
|
|
521
503
|
mock_context: PortOceanContext,
|
|
522
504
|
monkeypatch: pytest.MonkeyPatch,
|
|
505
|
+
mock_resource_config: ResourceConfig,
|
|
523
506
|
) -> None:
|
|
524
507
|
kind = "service"
|
|
525
508
|
user_agent_type = UserAgentType.exporter
|
|
@@ -547,23 +530,7 @@ async def test_unregister_raw(
|
|
|
547
530
|
enable_merge_entity=True,
|
|
548
531
|
delete_dependent_entities=True,
|
|
549
532
|
create_missing_related_entities=False,
|
|
550
|
-
resources=[
|
|
551
|
-
ResourceConfig(
|
|
552
|
-
kind=kind,
|
|
553
|
-
selector=Selector(query="true"),
|
|
554
|
-
port=PortResourceConfig(
|
|
555
|
-
entity=MappingsConfig(
|
|
556
|
-
mappings=EntityMapping(
|
|
557
|
-
identifier=".id | tostring",
|
|
558
|
-
title=".name",
|
|
559
|
-
blueprint='"service"',
|
|
560
|
-
properties={"url": ".web_url"},
|
|
561
|
-
relations={},
|
|
562
|
-
)
|
|
563
|
-
)
|
|
564
|
-
),
|
|
565
|
-
)
|
|
566
|
-
],
|
|
533
|
+
resources=[mock_resource_config],
|
|
567
534
|
),
|
|
568
535
|
):
|
|
569
536
|
# Ensure the event.port_app_config is set correctly
|
|
@@ -718,142 +685,92 @@ class CalculationResult:
|
|
|
718
685
|
|
|
719
686
|
|
|
720
687
|
@pytest.mark.asyncio
|
|
721
|
-
async def
|
|
722
|
-
mock_sync_raw_mixin: SyncRawMixin,
|
|
723
|
-
mock_port_app_config: PortAppConfig,
|
|
724
|
-
mock_context: PortOceanContext,
|
|
725
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
726
|
-
) -> None:
|
|
727
|
-
# Mock ocean.app.is_saas()
|
|
728
|
-
monkeypatch.setattr(mock_context.app, "is_saas", lambda: True)
|
|
729
|
-
|
|
730
|
-
# Mock dependencies
|
|
731
|
-
entity = Entity(identifier="1", blueprint="service")
|
|
732
|
-
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
|
733
|
-
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
|
|
734
|
-
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
|
|
735
|
-
|
|
736
|
-
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
737
|
-
event.port_app_config = mock_port_app_config
|
|
738
|
-
|
|
739
|
-
# Test execution
|
|
740
|
-
result = await mock_sync_raw_mixin._register_resource_raw(
|
|
741
|
-
mock_port_app_config.resources[0], # Use the first resource from the config
|
|
742
|
-
[{"some": "data"}],
|
|
743
|
-
UserAgentType.exporter,
|
|
744
|
-
)
|
|
745
|
-
|
|
746
|
-
# Assertions
|
|
747
|
-
assert len(result.entity_selector_diff.passed) == 1
|
|
748
|
-
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
|
749
|
-
mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
|
|
750
|
-
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
@pytest.mark.asyncio
|
|
754
|
-
async def test_register_resource_raw_saas_with_changes_upsert_called_and_entities_are_mapped(
|
|
688
|
+
async def test_register_resource_raw_no_changes_upsert_not_called_entitiy_is_returned(
|
|
755
689
|
mock_sync_raw_mixin: SyncRawMixin,
|
|
756
690
|
mock_port_app_config: PortAppConfig,
|
|
757
|
-
mock_context: PortOceanContext,
|
|
758
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
759
691
|
) -> None:
|
|
760
|
-
# Mock
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
692
|
+
# Mock the integration settings with skip_check_diff
|
|
693
|
+
with patch.object(ocean.config.integration, "skip_check_diff", False):
|
|
694
|
+
entity = Entity(identifier="1", blueprint="service")
|
|
695
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
|
696
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
|
|
697
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
|
|
698
|
+
|
|
699
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
700
|
+
event.port_app_config = mock_port_app_config
|
|
701
|
+
|
|
702
|
+
# Test execution
|
|
703
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
|
704
|
+
mock_port_app_config.resources[
|
|
705
|
+
0
|
|
706
|
+
], # Use the first resource from the config
|
|
707
|
+
[{"some": "data"}],
|
|
708
|
+
UserAgentType.exporter,
|
|
709
|
+
)
|
|
778
710
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
711
|
+
# Assertions
|
|
712
|
+
assert len(result.entity_selector_diff.passed) == 1
|
|
713
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
|
714
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
|
|
715
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
|
784
716
|
|
|
785
717
|
|
|
786
718
|
@pytest.mark.asyncio
|
|
787
|
-
async def
|
|
719
|
+
async def test_register_resource_raw_with_changes_upsert_called_and_entities_are_mapped(
|
|
788
720
|
mock_sync_raw_mixin: SyncRawMixin,
|
|
789
721
|
mock_port_app_config: PortAppConfig,
|
|
790
|
-
mock_context: PortOceanContext,
|
|
791
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
792
722
|
) -> None:
|
|
793
|
-
# Mock
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
event.port_app_config = mock_port_app_config
|
|
810
|
-
|
|
811
|
-
# Test execution
|
|
812
|
-
result = await mock_sync_raw_mixin._register_resource_raw(
|
|
813
|
-
mock_port_app_config.resources[0],
|
|
814
|
-
[{"some": "data"}],
|
|
815
|
-
UserAgentType.exporter,
|
|
816
|
-
)
|
|
723
|
+
# Mock the integration settings with skip_check_diff
|
|
724
|
+
with patch.object(ocean.config.integration, "skip_check_diff", False):
|
|
725
|
+
entity = Entity(identifier="1", blueprint="service")
|
|
726
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
|
727
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([entity])) # type: ignore
|
|
728
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock(return_value=[entity]) # type: ignore
|
|
729
|
+
|
|
730
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
731
|
+
event.port_app_config = mock_port_app_config
|
|
732
|
+
|
|
733
|
+
# Test execution
|
|
734
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
|
735
|
+
mock_port_app_config.resources[0],
|
|
736
|
+
[{"some": "data"}],
|
|
737
|
+
UserAgentType.exporter,
|
|
738
|
+
)
|
|
817
739
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
740
|
+
# Assertions
|
|
741
|
+
assert len(result.entity_selector_diff.passed) == 1
|
|
742
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
|
743
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
|
|
744
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
|
823
745
|
|
|
824
746
|
|
|
825
747
|
@pytest.mark.asyncio
|
|
826
|
-
async def
|
|
827
|
-
mock_sync_raw_mixin: SyncRawMixin,
|
|
828
|
-
mock_port_app_config: PortAppConfig,
|
|
829
|
-
mock_context: PortOceanContext,
|
|
830
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
748
|
+
async def test_register_resource_raw_with_errors(
|
|
749
|
+
mock_sync_raw_mixin: SyncRawMixin, mock_port_app_config: PortAppConfig
|
|
831
750
|
) -> None:
|
|
832
|
-
# Mock
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
UserAgentType.exporter,
|
|
850
|
-
)
|
|
751
|
+
# Mock the integration settings with skip_check_diff
|
|
752
|
+
with patch.object(ocean.config.integration, "skip_check_diff", False):
|
|
753
|
+
failed_entity = Entity(identifier="1", blueprint="service")
|
|
754
|
+
error = Exception("Test error")
|
|
755
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[], failed=[failed_entity]), errors=[error], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
|
756
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
|
|
757
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
|
|
758
|
+
|
|
759
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
|
760
|
+
event.port_app_config = mock_port_app_config
|
|
761
|
+
|
|
762
|
+
# Test execution
|
|
763
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
|
764
|
+
mock_port_app_config.resources[0],
|
|
765
|
+
[{"some": "data"}],
|
|
766
|
+
UserAgentType.exporter,
|
|
767
|
+
)
|
|
851
768
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
769
|
+
# Assertions
|
|
770
|
+
assert len(result.entity_selector_diff.passed) == 0
|
|
771
|
+
assert len(result.entity_selector_diff.failed) == 1
|
|
772
|
+
assert len(result.errors) == 1
|
|
773
|
+
assert result.errors[0] == error
|
|
774
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
|
775
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
|
776
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
|
|
@@ -3,7 +3,7 @@ integrations/_infra/Dockerfile.alpine,sha256=iauglyEzz5uEPBxsN-9SLFr6qca3Tf4b0DP
|
|
|
3
3
|
integrations/_infra/Dockerfile.base.builder,sha256=LwKLfJvQfKksMqacAT_aDQxFMC2Ty5fFKIa0Eu4QcCc,619
|
|
4
4
|
integrations/_infra/Dockerfile.base.runner,sha256=dsjTWgLQFm4x5gcm-IPhwkDv-M6VRKwdf-qct457h2c,357
|
|
5
5
|
integrations/_infra/Dockerfile.dockerignore,sha256=CM1Fxt3I2AvSvObuUZRmy5BNLSGC7ylnbpWzFgD4cso,1163
|
|
6
|
-
integrations/_infra/Makefile,sha256=
|
|
6
|
+
integrations/_infra/Makefile,sha256=NWX1QLd429KyCzvwNwpkWtSm5QTVarnh5SYO822-ea4,2360
|
|
7
7
|
integrations/_infra/grpcio.sh,sha256=m924poYznoRZ6Tt7Ct8Cs5AV_cmmOx598yIZ3z4DvZE,616
|
|
8
8
|
integrations/_infra/init.sh,sha256=nN8lTrOhB286UfFvD6sJ9YJ-9asT9zVSddQB-RAb7Z4,99
|
|
9
9
|
port_ocean/__init__.py,sha256=J3Mqp7d-CkEe9eMigGG8gSEiVKICY2bf7csNEwVOXk0,294
|
|
@@ -46,19 +46,20 @@ port_ocean/cli/utils.py,sha256=IUK2UbWqjci-lrcDdynZXqVP5B5TcjF0w5CpEVUks-k,54
|
|
|
46
46
|
port_ocean/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
47
|
port_ocean/clients/port/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
48
|
port_ocean/clients/port/authentication.py,sha256=6-uDMWsJ0xLe1-9IoYXHWmwtufj8rJR4BCRXJlSkCSQ,3447
|
|
49
|
-
port_ocean/clients/port/client.py,sha256=
|
|
49
|
+
port_ocean/clients/port/client.py,sha256=OaNeN3U7Hw0tK4jYE6ESJEPKbTf9nGp2jcJVq00gnf8,3546
|
|
50
50
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
port_ocean/clients/port/mixins/blueprints.py,sha256=POBl4uDocrgJBw4rvCAzwRcD4jk-uBL6pDAuKMTajdg,4633
|
|
52
|
-
port_ocean/clients/port/mixins/entities.py,sha256=
|
|
53
|
-
port_ocean/clients/port/mixins/integrations.py,sha256=
|
|
52
|
+
port_ocean/clients/port/mixins/entities.py,sha256=PJzVZTBW_OheFRGPRCZ6yPbVGEAKsMO9CNDNJUI1l48,10770
|
|
53
|
+
port_ocean/clients/port/mixins/integrations.py,sha256=cOmHdR0NdU8dXMD-5w3OpSEPPbaND0jSjJ4BGcHDsJQ,6900
|
|
54
54
|
port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
|
|
55
|
+
port_ocean/clients/port/mixins/organization.py,sha256=fCo_ZS8UlQXsyIx-odTuWkbnfcYmVnQfIsSyJuPOPjM,1031
|
|
55
56
|
port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
|
|
56
57
|
port_ocean/clients/port/types.py,sha256=nvlgiAq4WH5_F7wQbz_GAWl-faob84LVgIjZ2Ww5mTk,451
|
|
57
58
|
port_ocean/clients/port/utils.py,sha256=SjhgmJXAqH2JqXfGy8GoGwzUYiJvUhWDrJyxQcenxZc,2512
|
|
58
59
|
port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
60
|
port_ocean/config/base.py,sha256=x1gFbzujrxn7EJudRT81C6eN9WsYAb3vOHwcpcpX8Tc,6370
|
|
60
61
|
port_ocean/config/dynamic.py,sha256=qOFkRoJsn_BW7581omi_AoMxoHqasf_foxDQ_G11_SI,2030
|
|
61
|
-
port_ocean/config/settings.py,sha256=
|
|
62
|
+
port_ocean/config/settings.py,sha256=q5KgDIr8snIxejoKyWSyf21R_AYEEXiEd3-ry9qf3ss,5227
|
|
62
63
|
port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
64
|
port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
|
|
64
65
|
port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -69,7 +70,7 @@ port_ocean/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
69
70
|
port_ocean/core/defaults/__init__.py,sha256=8qCZg8n06WAdMu9s_FiRtDYLGPGHbOuS60vapeUoAks,142
|
|
70
71
|
port_ocean/core/defaults/clean.py,sha256=TOVe5b5FAjFspAkQuKA70k2BClCEFbrQ3xgiAoKXKYE,2427
|
|
71
72
|
port_ocean/core/defaults/common.py,sha256=zJsj7jvlqIMLGXhdASUlbKS8GIAf-FDKKB0O7jB6nx0,4166
|
|
72
|
-
port_ocean/core/defaults/initialize.py,sha256=
|
|
73
|
+
port_ocean/core/defaults/initialize.py,sha256=IYgc6XS5ARdcPM4IQrCSi4cdAomHxJVnP_OWOU9f05U,10382
|
|
73
74
|
port_ocean/core/event_listener/__init__.py,sha256=T3E52MKs79fNEW381p7zU9F2vOMvIiiTYWlqRUqnsg0,1135
|
|
74
75
|
port_ocean/core/event_listener/base.py,sha256=VdIdp7RLOSxH3ICyV-wCD3NiJoUzsh2KkJ0a9B29GeI,2847
|
|
75
76
|
port_ocean/core/event_listener/factory.py,sha256=M4Qi05pI840sjDIbdjUEgYe9Gp5ckoCkX-KgLBxUpZg,4096
|
|
@@ -101,9 +102,9 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
|
101
102
|
port_ocean/core/integrations/mixins/events.py,sha256=0jKRsBw6lU8Mqs7MaQK4n-t_H6Z4NEkXZ5VWzqTrKEc,2396
|
|
102
103
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
103
104
|
port_ocean/core/integrations/mixins/sync.py,sha256=B9fEs8faaYLLikH9GBjE_E61vo0bQDjIGQsQ1SRXOlA,3931
|
|
104
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
105
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=XLxH9_OOudLW4Q5lrUWWpcXIM3KdEDnYwEEP661Rfao,24465
|
|
105
106
|
port_ocean/core/integrations/mixins/utils.py,sha256=oN4Okz6xlaefpid1_Pud8HPSw9BwwjRohyNsknq-Myg,2309
|
|
106
|
-
port_ocean/core/models.py,sha256=
|
|
107
|
+
port_ocean/core/models.py,sha256=FvTp-BlpbvLbMbngE0wsiimsCfmIhUR1PvsE__Z--1I,2206
|
|
107
108
|
port_ocean/core/ocean_types.py,sha256=j_-or1VxDy22whLLxwxgzIsE4wAhFLH19Xff9l4oJA8,1124
|
|
108
109
|
port_ocean/core/utils/entity_topological_sorter.py,sha256=MDUjM6OuDy4Xj68o-7InNN0w1jqjxeDfeY8U02vySNI,3081
|
|
109
110
|
port_ocean/core/utils/utils.py,sha256=HmumOeH27N0NX1_OP3t4oGKt074ht9XyXhvfZ5I05s4,6474
|
|
@@ -114,7 +115,7 @@ port_ocean/exceptions/base.py,sha256=uY4DX7fIITDFfemCJDWpaZi3bD51lcANc5swpoNvMJA
|
|
|
114
115
|
port_ocean/exceptions/clients.py,sha256=LKLLs-Zy3caNG85rwxfOw2rMr8qqVV6SHUq4fRCZ99U,180
|
|
115
116
|
port_ocean/exceptions/context.py,sha256=mA8HII6Rl4QxKUz98ppy1zX3kaziaen21h1ZWuU3ADc,372
|
|
116
117
|
port_ocean/exceptions/core.py,sha256=Zmb1m6NnkSPWpAiQA5tgejm3zpDMt1WQEN47OJNo54A,856
|
|
117
|
-
port_ocean/exceptions/port_defaults.py,sha256=
|
|
118
|
+
port_ocean/exceptions/port_defaults.py,sha256=2a7Koy541KxMan33mU-gbauUxsumG3NT4itVxSpQqfw,666
|
|
118
119
|
port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnqI,197
|
|
119
120
|
port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
121
|
port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
|
|
@@ -130,10 +131,11 @@ port_ocean/run.py,sha256=COoRSmLG4hbsjIW5DzhV0NYVegI9xHd1POv6sg4U1No,2217
|
|
|
130
131
|
port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
|
|
131
132
|
port_ocean/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
133
|
port_ocean/tests/clients/port/mixins/test_entities.py,sha256=A9myrnkLhKSQrnOLv1Zz2wiOVSxW65Q9RIUIRbn_V7w,1586
|
|
134
|
+
port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=-8iHM33Oe8PuyEfj3O_6Yob8POp4fSmB0hnIT0Gv-8Y,868
|
|
133
135
|
port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
|
|
134
136
|
port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
|
|
135
137
|
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=FnEnaDjuoAbKvKyv6xJ46n3j0ZcaT70Sg2zc7oy7HAA,13596
|
|
136
|
-
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256
|
|
138
|
+
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=SSf1ZRZVcIta9zfx2-SpYFc_-MoHPDJSa1MkbIx3icI,31172
|
|
137
139
|
port_ocean/tests/core/test_utils.py,sha256=Z3kdhb5V7Svhcyy3EansdTpgHL36TL6erNtU-OPwAcI,2647
|
|
138
140
|
port_ocean/tests/core/utils/test_entity_topological_sorter.py,sha256=zuq5WSPy_88PemG3mOUIHTxWMR_js1R7tOzUYlgBd68,3447
|
|
139
141
|
port_ocean/tests/core/utils/test_resolve_entities_diff.py,sha256=4kTey1c0dWKbLXjJ-9m2GXrHyAWZeLQ2asdtYRRUdRs,16573
|
|
@@ -158,8 +160,8 @@ port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,32
|
|
|
158
160
|
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
|
159
161
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
160
162
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
161
|
-
port_ocean-0.18.
|
|
162
|
-
port_ocean-0.18.
|
|
163
|
-
port_ocean-0.18.
|
|
164
|
-
port_ocean-0.18.
|
|
165
|
-
port_ocean-0.18.
|
|
163
|
+
port_ocean-0.18.4.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
164
|
+
port_ocean-0.18.4.dist-info/METADATA,sha256=qe_3HL2F4NgoCId8n4gmLPqVUGKaOTU11XC05vMPO8I,6669
|
|
165
|
+
port_ocean-0.18.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
166
|
+
port_ocean-0.18.4.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
167
|
+
port_ocean-0.18.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|