port-ocean 0.18.5__py3-none-any.whl → 0.18.6__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.
- port_ocean/core/integrations/mixins/sync_raw.py +25 -19
- port_ocean/tests/core/handlers/mixins/test_sync_raw.py +149 -124
- {port_ocean-0.18.5.dist-info → port_ocean-0.18.6.dist-info}/METADATA +1 -1
- {port_ocean-0.18.5.dist-info → port_ocean-0.18.6.dist-info}/RECORD +7 -7
- {port_ocean-0.18.5.dist-info → port_ocean-0.18.6.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.18.5.dist-info → port_ocean-0.18.6.dist-info}/WHEEL +0 -0
- {port_ocean-0.18.5.dist-info → port_ocean-0.18.6.dist-info}/entry_points.txt +0 -0
@@ -220,26 +220,32 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
220
220
|
)
|
221
221
|
modified_objects = []
|
222
222
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
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
|
234
|
-
)
|
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)}")
|
240
|
-
modified_objects = await self.entities_state_applier.upsert(
|
241
|
-
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
223
|
+
if event.event_type == EventType.RESYNC:
|
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
|
242
229
|
)
|
230
|
+
if changed_entities:
|
231
|
+
logger.info("Upserting changed entities", changed_entities=len(changed_entities),
|
232
|
+
total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
233
|
+
await self.entities_state_applier.upsert(
|
234
|
+
changed_entities, user_agent_type
|
235
|
+
)
|
236
|
+
else:
|
237
|
+
logger.info("Entities in batch didn't changed since last sync, skipping", total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
238
|
+
modified_objects = [ocean.port_client._reduce_entity(entity) for entity in objects_diff[0].entity_selector_diff.passed]
|
239
|
+
except Exception as e:
|
240
|
+
logger.warning(f"Failed to resolve batch entities with Port, falling back to upserting all entities: {str(e)}")
|
241
|
+
modified_objects = await self.entities_state_applier.upsert(
|
242
|
+
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
243
|
+
)
|
244
|
+
else:
|
245
|
+
modified_objects = await self.entities_state_applier.upsert(
|
246
|
+
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
247
|
+
)
|
248
|
+
|
243
249
|
|
244
250
|
return CalculationResult(
|
245
251
|
objects_diff[0].entity_selector_diff._replace(passed=modified_objects),
|
@@ -435,66 +435,63 @@ async def test_register_raw(
|
|
435
435
|
mock_sync_raw_mixin_with_jq_processor: SyncRawMixin,
|
436
436
|
mock_resource_config: ResourceConfig,
|
437
437
|
) -> None:
|
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
|
-
]
|
453
438
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
439
|
+
kind = "service"
|
440
|
+
user_agent_type = UserAgentType.exporter
|
441
|
+
raw_entity = [
|
442
|
+
{"id": "entity_1", "name": "entity_1", "web_url": "https://example.com"},
|
443
|
+
]
|
444
|
+
expected_result = [
|
445
|
+
{
|
446
|
+
"identifier": "entity_1",
|
447
|
+
"blueprint": "service",
|
448
|
+
"name": "entity_1",
|
449
|
+
"properties": {"url": "https://example.com"},
|
450
|
+
},
|
451
|
+
]
|
452
|
+
|
453
|
+
async with event_context(EventType.HTTP_REQUEST, trigger_type="machine") as event:
|
454
|
+
# Use patch to mock the method instead of direct assignment
|
455
|
+
with patch.object(
|
456
|
+
mock_sync_raw_mixin_with_jq_processor.port_app_config_handler,
|
457
|
+
"get_port_app_config",
|
458
|
+
return_value=PortAppConfig(
|
459
|
+
enable_merge_entity=True,
|
460
|
+
delete_dependent_entities=True,
|
461
|
+
create_missing_related_entities=False,
|
462
|
+
resources=[mock_resource_config],
|
463
|
+
),
|
464
|
+
):
|
465
|
+
# Ensure the event.port_app_config is set correctly
|
466
|
+
event.port_app_config = await mock_sync_raw_mixin_with_jq_processor.port_app_config_handler.get_port_app_config(
|
467
|
+
use_cache=False
|
468
|
+
)
|
469
|
+
|
470
|
+
def upsert_side_effect(
|
471
|
+
entities: list[Entity], user_agent_type: UserAgentType
|
472
|
+
) -> list[Entity]:
|
473
|
+
# Simulate returning the passed entities
|
474
|
+
return entities
|
475
|
+
|
476
|
+
# Patch the upsert method with the side effect
|
458
477
|
with patch.object(
|
459
|
-
mock_sync_raw_mixin_with_jq_processor.
|
460
|
-
"
|
461
|
-
|
462
|
-
enable_merge_entity=True,
|
463
|
-
delete_dependent_entities=True,
|
464
|
-
create_missing_related_entities=False,
|
465
|
-
resources=[mock_resource_config],
|
466
|
-
),
|
478
|
+
mock_sync_raw_mixin_with_jq_processor.entities_state_applier,
|
479
|
+
"upsert",
|
480
|
+
side_effect=upsert_side_effect,
|
467
481
|
):
|
468
|
-
#
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
-
)
|
482
|
+
# Call the register_raw method
|
483
|
+
registered_entities = (
|
484
|
+
await mock_sync_raw_mixin_with_jq_processor.register_raw(
|
485
|
+
kind, raw_entity, user_agent_type
|
490
486
|
)
|
487
|
+
)
|
491
488
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
489
|
+
# Assert that the registered entities match the expected results
|
490
|
+
assert len(registered_entities) == len(expected_result)
|
491
|
+
for entity, result in zip(registered_entities, expected_result):
|
492
|
+
assert entity.identifier == result["identifier"]
|
493
|
+
assert entity.blueprint == result["blueprint"]
|
494
|
+
assert entity.properties == result["properties"]
|
498
495
|
|
499
496
|
|
500
497
|
@pytest.mark.asyncio
|
@@ -689,30 +686,26 @@ async def test_register_resource_raw_no_changes_upsert_not_called_entitiy_is_ret
|
|
689
686
|
mock_sync_raw_mixin: SyncRawMixin,
|
690
687
|
mock_port_app_config: PortAppConfig,
|
691
688
|
) -> None:
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
#
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
], # Use the first resource from the config
|
707
|
-
[{"some": "data"}],
|
708
|
-
UserAgentType.exporter,
|
709
|
-
)
|
689
|
+
entity = Entity(identifier="1", blueprint="service")
|
690
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
691
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
|
692
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
|
693
|
+
|
694
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
695
|
+
event.port_app_config = mock_port_app_config
|
696
|
+
|
697
|
+
# Test execution
|
698
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
699
|
+
mock_port_app_config.resources[0], # Use the first resource from the config
|
700
|
+
[{"some": "data"}],
|
701
|
+
UserAgentType.exporter,
|
702
|
+
)
|
710
703
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
704
|
+
# Assertions
|
705
|
+
assert len(result.entity_selector_diff.passed) == 1
|
706
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
707
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
|
708
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
716
709
|
|
717
710
|
|
718
711
|
@pytest.mark.asyncio
|
@@ -720,57 +713,89 @@ async def test_register_resource_raw_with_changes_upsert_called_and_entities_are
|
|
720
713
|
mock_sync_raw_mixin: SyncRawMixin,
|
721
714
|
mock_port_app_config: PortAppConfig,
|
722
715
|
) -> None:
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
UserAgentType.exporter,
|
738
|
-
)
|
716
|
+
entity = Entity(identifier="1", blueprint="service")
|
717
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[CalculationResult(entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]), errors=[], misconfigurations=[], misonfigured_entity_keys=[])]) # type: ignore
|
718
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([entity])) # type: ignore
|
719
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock(return_value=[entity]) # type: ignore
|
720
|
+
|
721
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
722
|
+
event.port_app_config = mock_port_app_config
|
723
|
+
|
724
|
+
# Test execution
|
725
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
726
|
+
mock_port_app_config.resources[0],
|
727
|
+
[{"some": "data"}],
|
728
|
+
UserAgentType.exporter,
|
729
|
+
)
|
739
730
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
731
|
+
# Assertions
|
732
|
+
assert len(result.entity_selector_diff.passed) == 1
|
733
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
734
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
|
735
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
745
736
|
|
746
737
|
|
747
738
|
@pytest.mark.asyncio
|
748
739
|
async def test_register_resource_raw_with_errors(
|
749
740
|
mock_sync_raw_mixin: SyncRawMixin, mock_port_app_config: PortAppConfig
|
750
741
|
) -> None:
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
742
|
+
failed_entity = Entity(identifier="1", blueprint="service")
|
743
|
+
error = Exception("Test error")
|
744
|
+
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
|
745
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock(return_value=([])) # type: ignore
|
746
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock() # type: ignore
|
747
|
+
|
748
|
+
async with event_context(EventType.RESYNC, trigger_type="machine") as event:
|
749
|
+
event.port_app_config = mock_port_app_config
|
750
|
+
|
751
|
+
# Test execution
|
752
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
753
|
+
mock_port_app_config.resources[0],
|
754
|
+
[{"some": "data"}],
|
755
|
+
UserAgentType.exporter,
|
756
|
+
)
|
757
|
+
|
758
|
+
# Assertions
|
759
|
+
assert len(result.entity_selector_diff.passed) == 0
|
760
|
+
assert len(result.entity_selector_diff.failed) == 1
|
761
|
+
assert len(result.errors) == 1
|
762
|
+
assert result.errors[0] == error
|
763
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
764
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_called_once()
|
765
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_not_called()
|
766
|
+
|
767
|
+
|
768
|
+
@pytest.mark.asyncio
|
769
|
+
async def test_register_resource_raw_skip_event_type_http_request_upsert_called_and_no_entitites_diff_calculation(
|
770
|
+
mock_sync_raw_mixin: SyncRawMixin,
|
771
|
+
mock_port_app_config: PortAppConfig,
|
772
|
+
mock_context: PortOceanContext,
|
773
|
+
monkeypatch: pytest.MonkeyPatch,
|
774
|
+
) -> None:
|
775
|
+
# Mock dependencies
|
776
|
+
entity = Entity(identifier="1", blueprint="service")
|
777
|
+
calculation_result = CalculationResult(
|
778
|
+
entity_selector_diff=EntitySelectorDiff(passed=[entity], failed=[]),
|
779
|
+
errors=[],
|
780
|
+
misconfigurations=[],
|
781
|
+
misonfigured_entity_keys=[],
|
782
|
+
)
|
783
|
+
mock_sync_raw_mixin._calculate_raw = AsyncMock(return_value=[calculation_result]) # type: ignore
|
784
|
+
mock_sync_raw_mixin._map_entities_compared_with_port = AsyncMock() # type: ignore
|
785
|
+
mock_sync_raw_mixin.entities_state_applier.upsert = AsyncMock(return_value=[entity]) # type: ignore
|
786
|
+
|
787
|
+
async with event_context(EventType.HTTP_REQUEST, trigger_type="machine") as event:
|
788
|
+
event.port_app_config = mock_port_app_config
|
789
|
+
|
790
|
+
# Test execution
|
791
|
+
result = await mock_sync_raw_mixin._register_resource_raw(
|
792
|
+
mock_port_app_config.resources[0],
|
793
|
+
[{"some": "data"}],
|
794
|
+
UserAgentType.exporter,
|
795
|
+
)
|
768
796
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
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()
|
797
|
+
# Assertions
|
798
|
+
assert len(result.entity_selector_diff.passed) == 1
|
799
|
+
mock_sync_raw_mixin._calculate_raw.assert_called_once()
|
800
|
+
mock_sync_raw_mixin._map_entities_compared_with_port.assert_not_called()
|
801
|
+
mock_sync_raw_mixin.entities_state_applier.upsert.assert_called_once()
|
@@ -102,7 +102,7 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
102
102
|
port_ocean/core/integrations/mixins/events.py,sha256=0jKRsBw6lU8Mqs7MaQK4n-t_H6Z4NEkXZ5VWzqTrKEc,2396
|
103
103
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
104
104
|
port_ocean/core/integrations/mixins/sync.py,sha256=B9fEs8faaYLLikH9GBjE_E61vo0bQDjIGQsQ1SRXOlA,3931
|
105
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
105
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=xDGz4pOfpd6G_pMQveEHQCDdOTk-Uun_aP0G_0IxsDA,24784
|
106
106
|
port_ocean/core/integrations/mixins/utils.py,sha256=oN4Okz6xlaefpid1_Pud8HPSw9BwwjRohyNsknq-Myg,2309
|
107
107
|
port_ocean/core/models.py,sha256=FvTp-BlpbvLbMbngE0wsiimsCfmIhUR1PvsE__Z--1I,2206
|
108
108
|
port_ocean/core/ocean_types.py,sha256=j_-or1VxDy22whLLxwxgzIsE4wAhFLH19Xff9l4oJA8,1124
|
@@ -135,7 +135,7 @@ port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=-8iHM33Oe
|
|
135
135
|
port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
|
136
136
|
port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
|
137
137
|
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=FnEnaDjuoAbKvKyv6xJ46n3j0ZcaT70Sg2zc7oy7HAA,13596
|
138
|
-
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=
|
138
|
+
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=gxQ4e9hQuMS8-o5UbiUSt1I1uaK0DCO3yCFDVigpZvo,31740
|
139
139
|
port_ocean/tests/core/handlers/port_app_config/test_api.py,sha256=eJZ6SuFBLz71y4ca3DNqKag6d6HUjNJS0aqQPwiLMTI,1999
|
140
140
|
port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=s3D98JP3YV9V6T5PCDPE2852gkqGiDmo03UyexESX_I,6872
|
141
141
|
port_ocean/tests/core/test_utils.py,sha256=Z3kdhb5V7Svhcyy3EansdTpgHL36TL6erNtU-OPwAcI,2647
|
@@ -162,8 +162,8 @@ port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,32
|
|
162
162
|
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
163
163
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
164
164
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
165
|
-
port_ocean-0.18.
|
166
|
-
port_ocean-0.18.
|
167
|
-
port_ocean-0.18.
|
168
|
-
port_ocean-0.18.
|
169
|
-
port_ocean-0.18.
|
165
|
+
port_ocean-0.18.6.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
166
|
+
port_ocean-0.18.6.dist-info/METADATA,sha256=71enLFwwGK7wlTo-ETXIVzGAtGQ5VHEFfs0y75kAtP0,6669
|
167
|
+
port_ocean-0.18.6.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
168
|
+
port_ocean-0.18.6.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
169
|
+
port_ocean-0.18.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|