port-ocean 0.18.5__py3-none-any.whl → 0.18.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|