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.

@@ -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(round(0.9 * PORT_HTTP_MAX_CONNECTIONS_LIMIT))
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
- from typing import Any, TYPE_CHECKING, Optional, TypedDict
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
- ) -> dict:
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
- if port_app_config:
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", headers=headers, json=json
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", [])
@@ -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
- if not defaults:
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 defaults.port_app_config:
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, defaults.port_app_config, integration_config
253
+ port_client,
254
+ integration_config,
255
+ defaults.port_app_config if defaults else None,
213
256
  )
214
257
 
215
- if not integration_config.initialize_port_resources:
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
- if ocean.app.is_saas():
224
- try:
225
- changed_entities = await self._map_entities_compared_with_port(
226
- objects_diff[0].entity_selector_diff.passed,
227
- resource,
228
- user_agent_type
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
- if changed_entities:
232
- logger.info("Upserting changed entities", changed_entities=len(changed_entities),
233
- total_entities=len(objects_diff[0].entity_selector_diff.passed))
234
- await self.entities_state_applier.upsert(
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
- mock_ocean: Ocean,
437
- mock_context: PortOceanContext,
438
- monkeypatch: pytest.MonkeyPatch,
436
+ mock_resource_config: ResourceConfig,
439
437
  ) -> None:
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": {"url": "https://example.com"},
451
- },
452
- ]
453
-
454
- # Set is_saas to False
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
- # Patch the upsert method with the side effect
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.entities_state_applier,
499
- "upsert",
500
- side_effect=upsert_side_effect,
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
- # Call the register_raw method
503
- registered_entities = (
504
- await mock_sync_raw_mixin_with_jq_processor.register_raw(
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
- # Assert that the registered entities match the expected results
510
- assert len(registered_entities) == len(expected_result)
511
- for entity, result in zip(registered_entities, expected_result):
512
- assert entity.identifier == result["identifier"]
513
- assert entity.blueprint == result["blueprint"]
514
- assert entity.properties == result["properties"]
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 test_register_resource_raw_saas_no_changes_upsert_not_called_entitiy_is_returned(
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 ocean.app.is_saas()
761
- monkeypatch.setattr(mock_context.app, "is_saas", lambda: True)
762
-
763
- # Mock dependencies
764
- entity = Entity(identifier="1", blueprint="service")
765
- mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
766
- mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([entity])) # type: ignore
767
- mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock(return_value=[entity]) # type: ignore
768
-
769
- async with event_context(EventType.RESYNC, trigger_type="machine") as event:
770
- event.port_app_config = mock_port_app_config
771
-
772
- # Test execution
773
- result = await mock_sync_raw_mixin._register_resource_raw(
774
- mock_port_app_config.resources[0],
775
- [{"some": "data"}],
776
- UserAgentType.exporter,
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
- # Assertions
780
- assert len(result.entity_selector_diff.passed) == 1
781
- mock_sync_raw_mixin._calculate_raw.assert_called_once()
782
- mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
783
- mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
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 test_register_resource_raw_non_saas_upsert_called_and_no_entitites_diff_calculation(
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 ocean.app.is_saas()
794
- monkeypatch.setattr(mock_context.app, "is_saas", lambda: False)
795
-
796
- # Mock dependencies
797
- entity = Entity(identifier="1", blueprint="service")
798
- calculation_result = CalculationResult(
799
- entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]),
800
- errors=[],
801
- misconfigurations=[],
802
- misonfigured_entity_keys=[],
803
- )
804
- mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[calculation_result]) # type: ignore
805
- mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock() # type: ignore
806
- mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock(return_value=[entity]) # type: ignore
807
-
808
- async with event_context(EventType.RESYNC, trigger_type="machine") as event:
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
- # Assertions
819
- assert len(result.entity_selector_diff.passed) == 1
820
- mock_sync_raw_mixin._calculate_raw.assert_called_once()
821
- mock_sync_raw_mixin._map_entities_compared_with_port.assert_not_called()
822
- mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
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 test_register_resource_raw_saas_with_errors(
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 ocean.app.is_saas()
833
- monkeypatch.setattr(mock_context.app, "is_saas", lambda: True)
834
-
835
- # Mock dependencies
836
- failed_entity = Entity(identifier="1", blueprint="service")
837
- error = Exception("Test error")
838
- 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
839
- mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
840
- mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
841
-
842
- async with event_context(EventType.RESYNC, trigger_type="machine") as event:
843
- event.port_app_config = mock_port_app_config
844
-
845
- # Test execution
846
- result = await mock_sync_raw_mixin._register_resource_raw(
847
- mock_port_app_config.resources[0],
848
- [{"some": "data"}],
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
- # Assertions
853
- assert len(result.entity_selector_diff.passed) == 0
854
- assert len(result.entity_selector_diff.failed) == 1
855
- assert len(result.errors) == 1
856
- assert result.errors[0] == error
857
- mock_sync_raw_mixin._calculate_raw.assert_called_once()
858
- mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
859
- mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.18.2
3
+ Version: 0.18.4
4
4
  Summary: Port Ocean is a CLI tool for managing your Port projects.
5
5
  Home-page: https://app.getport.io
6
6
  Keywords: ocean,port-ocean,port
@@ -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=ofVvsebe7-inGBjZI976L6ghe1Z3RhKFvQhCf6clHn8,2142
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=Xd8Jk25Uh4WXY_WW-z1Qbv6F3ZTBFPoOolsxHMfozKw,3366
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=_aEGE_kGOucw6ZH9qleM5Tjt-YiPAbq9rjSOwcI-py0,10677
53
- port_ocean/clients/port/mixins/integrations.py,sha256=t8OSa7Iopnpp8IOEcp3a7WgwOcJEBdFow9UbGDKWxKI,4858
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=cxGnOTO9cEwwFcTSzOmwMw1yPkQHniVJFyCQCyEZ6yY,4333
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=PVR0awQc0eFKKD5wldMpqg5IKtfc8llreZ1FDN3cCIE,8570
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=Ke-wCBv2zVEgF-6QafXKRkmJ59qAsqSHr61XPfq19J8,24752
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=BDSylTaTMtisvFR2gM6gmRc-387tA0XZSFufKEYdk3A,2115
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=45Bno5JEB-GXztvKsy8mw7TrydQmw13-4JAo2oQmXkE,438
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=-qipUqVvXLSdcbDKHOivTMPPaKqC1euQmAhmK4FbFIY,33926
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.2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
162
- port_ocean-0.18.2.dist-info/METADATA,sha256=sq2TAoAyAoHZePy8yYbv9qbqH25qJnaNff16I-guId0,6669
163
- port_ocean-0.18.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
164
- port_ocean-0.18.2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
165
- port_ocean-0.18.2.dist-info/RECORD,,
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,,