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.
@@ -220,26 +220,32 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
220
220
  )
221
221
  modified_objects = []
222
222
 
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
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
- 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
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.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
- ),
478
+ mock_sync_raw_mixin_with_jq_processor.entities_state_applier,
479
+ "upsert",
480
+ side_effect=upsert_side_effect,
467
481
  ):
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
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
- # 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"]
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
- # 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
- )
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
- # 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()
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
- # 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
- )
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
- # 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()
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
- # 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
- )
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
- # 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()
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.18.5
3
+ Version: 0.18.6
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
@@ -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=XLxH9_OOudLW4Q5lrUWWpcXIM3KdEDnYwEEP661Rfao,24465
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=SSf1ZRZVcIta9zfx2-SpYFc_-MoHPDJSa1MkbIx3icI,31172
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.5.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
166
- port_ocean-0.18.5.dist-info/METADATA,sha256=qIcD5yXlV1899BUdBrFNoPzqda6yrqyrQFG27dZoOoc,6669
167
- port_ocean-0.18.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
168
- port_ocean-0.18.5.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
169
- port_ocean-0.18.5.dist-info/RECORD,,
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,,