port-ocean 0.22.1__py3-none-any.whl → 0.22.2__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.
- port_ocean/core/handlers/port_app_config/models.py +12 -3
- port_ocean/core/integrations/mixins/sync.py +1 -1
- port_ocean/core/integrations/mixins/sync_raw.py +1 -1
- port_ocean/tests/core/handlers/entities_state_applier/test_applier.py +24 -0
- port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +1 -1
- port_ocean/tests/core/handlers/port_app_config/test_base.py +82 -0
- port_ocean/tests/helpers/fixtures.py +2 -3
- {port_ocean-0.22.1.dist-info → port_ocean-0.22.2.dist-info}/METADATA +2 -1
- {port_ocean-0.22.1.dist-info → port_ocean-0.22.2.dist-info}/RECORD +12 -12
- {port_ocean-0.22.1.dist-info → port_ocean-0.22.2.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.22.1.dist-info → port_ocean-0.22.2.dist-info}/WHEEL +0 -0
- {port_ocean-0.22.1.dist-info → port_ocean-0.22.2.dist-info}/entry_points.txt +0 -0
|
@@ -51,6 +51,7 @@ class ResourceConfig(BaseModel):
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
class PortAppConfig(BaseModel):
|
|
54
|
+
_default_entity_deletion_threshold: float = 0.9
|
|
54
55
|
enable_merge_entity: bool = Field(alias="enableMergeEntity", default=True)
|
|
55
56
|
delete_dependent_entities: bool = Field(
|
|
56
57
|
alias="deleteDependentEntities", default=True
|
|
@@ -58,8 +59,8 @@ class PortAppConfig(BaseModel):
|
|
|
58
59
|
create_missing_related_entities: bool = Field(
|
|
59
60
|
alias="createMissingRelatedEntities", default=True
|
|
60
61
|
)
|
|
61
|
-
entity_deletion_threshold: float = Field(
|
|
62
|
-
alias="entityDeletionThreshold", default=
|
|
62
|
+
entity_deletion_threshold: float | None = Field(
|
|
63
|
+
alias="entityDeletionThreshold", default=None
|
|
63
64
|
)
|
|
64
65
|
resources: list[ResourceConfig] = Field(default_factory=list)
|
|
65
66
|
|
|
@@ -71,8 +72,13 @@ class PortAppConfig(BaseModel):
|
|
|
71
72
|
"validation_only": False,
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
def get_entity_deletion_threshold(self) -> float | None:
|
|
76
|
+
if self.entity_deletion_threshold is not None:
|
|
77
|
+
return self.entity_deletion_threshold
|
|
78
|
+
return self._default_entity_deletion_threshold
|
|
79
|
+
|
|
74
80
|
def to_request(self) -> dict[str, Any]:
|
|
75
|
-
|
|
81
|
+
mapping = {
|
|
76
82
|
"deleteDependentEntities": self.delete_dependent_entities,
|
|
77
83
|
"createMissingRelatedEntities": self.create_missing_related_entities,
|
|
78
84
|
"enableMergeEntity": self.enable_merge_entity,
|
|
@@ -81,6 +87,9 @@ class PortAppConfig(BaseModel):
|
|
|
81
87
|
for resource in self.resources
|
|
82
88
|
],
|
|
83
89
|
}
|
|
90
|
+
if self.entity_deletion_threshold is not None:
|
|
91
|
+
mapping["entityDeletionThreshold"] = self.entity_deletion_threshold
|
|
92
|
+
return mapping
|
|
84
93
|
|
|
85
94
|
class Config:
|
|
86
95
|
allow_population_by_field_name = True
|
|
@@ -102,7 +102,7 @@ class SyncMixin(HandlerMixin):
|
|
|
102
102
|
entities, user_agent_type
|
|
103
103
|
)
|
|
104
104
|
await self.entities_state_applier.delete_diff(
|
|
105
|
-
{"before": entities_at_port, "after": modified_entities}, user_agent_type, app_config.
|
|
105
|
+
{"before": entities_at_port, "after": modified_entities}, user_agent_type, app_config.get_entity_deletion_threshold()
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
logger.info("Finished syncing change")
|
|
@@ -671,7 +671,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
671
671
|
)
|
|
672
672
|
await self.entities_state_applier.delete_diff(
|
|
673
673
|
{"before": entities_at_port, "after": generated_entities},
|
|
674
|
-
user_agent_type, app_config.
|
|
674
|
+
user_agent_type, app_config.get_entity_deletion_threshold()
|
|
675
675
|
)
|
|
676
676
|
|
|
677
677
|
logger.info("Resync finished successfully")
|
|
@@ -95,6 +95,30 @@ async def test_delete_diff_custom_threshold_above_threshold_not_deleted(
|
|
|
95
95
|
mock_safe_delete.assert_not_called()
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
@pytest.mark.asyncio
|
|
99
|
+
async def test_delete_diff_custom_threshold_0_not_deleted(
|
|
100
|
+
mock_context: PortOceanContext,
|
|
101
|
+
) -> None:
|
|
102
|
+
applier = HttpEntitiesStateApplier(mock_context)
|
|
103
|
+
entities = EntityDiff(
|
|
104
|
+
before=[
|
|
105
|
+
Entity(identifier="1", blueprint="test"),
|
|
106
|
+
Entity(identifier="2", blueprint="test"),
|
|
107
|
+
],
|
|
108
|
+
after=[
|
|
109
|
+
Entity(identifier="2", blueprint="test"),
|
|
110
|
+
Entity(identifier="3", blueprint="test"),
|
|
111
|
+
],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
with patch.object(applier, "_safe_delete") as mock_safe_delete:
|
|
115
|
+
await applier.delete_diff(
|
|
116
|
+
entities, UserAgentType.exporter, entity_deletion_threshold=0
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
mock_safe_delete.assert_not_called()
|
|
120
|
+
|
|
121
|
+
|
|
98
122
|
@pytest.mark.asyncio
|
|
99
123
|
async def test_applier_with_mock_context(
|
|
100
124
|
mock_ocean: Ocean,
|
|
@@ -249,7 +249,7 @@ class TestJQEntityProcessor:
|
|
|
249
249
|
result = await mocked_processor._search(data, pattern)
|
|
250
250
|
assert result == "bar"
|
|
251
251
|
|
|
252
|
-
@pytest.mark.timeout(
|
|
252
|
+
@pytest.mark.timeout(30)
|
|
253
253
|
async def test_parse_items_performance_10000(
|
|
254
254
|
self, mocked_processor: JQEntityProcessor
|
|
255
255
|
) -> None:
|
|
@@ -78,6 +78,88 @@ async def test_get_port_app_config_success(
|
|
|
78
78
|
result.resources[0].port.entity.mappings.properties["defaultBranch"]
|
|
79
79
|
== ".default_branch"
|
|
80
80
|
)
|
|
81
|
+
assert result.entity_deletion_threshold is None
|
|
82
|
+
port_app_config_handler.mock_get_port_app_config.assert_called_once()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.mark.asyncio
|
|
86
|
+
async def test_get_port_app_config_get_entity_deletion_threshold_with_flag_defined(
|
|
87
|
+
port_app_config_handler: MockPortAppConfig,
|
|
88
|
+
) -> None:
|
|
89
|
+
# Arrange
|
|
90
|
+
valid_config = {
|
|
91
|
+
"entityDeletionThreshold": 0.1,
|
|
92
|
+
"resources": [
|
|
93
|
+
{
|
|
94
|
+
"kind": "repository",
|
|
95
|
+
"selector": {"query": "true"},
|
|
96
|
+
"port": {
|
|
97
|
+
"entity": {
|
|
98
|
+
"mappings": {
|
|
99
|
+
"identifier": ".name",
|
|
100
|
+
"title": ".name",
|
|
101
|
+
"blueprint": '"service"',
|
|
102
|
+
"properties": {
|
|
103
|
+
"description": ".description",
|
|
104
|
+
"url": ".html_url",
|
|
105
|
+
"defaultBranch": ".default_branch",
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
}
|
|
113
|
+
port_app_config_handler.mock_get_port_app_config.return_value = valid_config
|
|
114
|
+
|
|
115
|
+
# Act
|
|
116
|
+
async with event_context(EventType.RESYNC, trigger_type="machine"):
|
|
117
|
+
result = await port_app_config_handler.get_port_app_config()
|
|
118
|
+
deletion_threshold = result.get_entity_deletion_threshold()
|
|
119
|
+
|
|
120
|
+
# Assert
|
|
121
|
+
assert isinstance(result, PortAppConfig)
|
|
122
|
+
assert deletion_threshold == 0.1
|
|
123
|
+
port_app_config_handler.mock_get_port_app_config.assert_called_once()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@pytest.mark.asyncio
|
|
127
|
+
async def test_get_port_app_config_get_entity_deletion_threshold_with_flag_not_defined(
|
|
128
|
+
port_app_config_handler: MockPortAppConfig,
|
|
129
|
+
) -> None:
|
|
130
|
+
# Arrange
|
|
131
|
+
valid_config = {
|
|
132
|
+
"resources": [
|
|
133
|
+
{
|
|
134
|
+
"kind": "repository",
|
|
135
|
+
"selector": {"query": "true"},
|
|
136
|
+
"port": {
|
|
137
|
+
"entity": {
|
|
138
|
+
"mappings": {
|
|
139
|
+
"identifier": ".name",
|
|
140
|
+
"title": ".name",
|
|
141
|
+
"blueprint": '"service"',
|
|
142
|
+
"properties": {
|
|
143
|
+
"description": ".description",
|
|
144
|
+
"url": ".html_url",
|
|
145
|
+
"defaultBranch": ".default_branch",
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
port_app_config_handler.mock_get_port_app_config.return_value = valid_config
|
|
154
|
+
|
|
155
|
+
# Act
|
|
156
|
+
async with event_context(EventType.RESYNC, trigger_type="machine"):
|
|
157
|
+
result = await port_app_config_handler.get_port_app_config()
|
|
158
|
+
deletion_threshold = result.get_entity_deletion_threshold()
|
|
159
|
+
|
|
160
|
+
# Assert
|
|
161
|
+
assert isinstance(result, PortAppConfig)
|
|
162
|
+
assert deletion_threshold == result._default_entity_deletion_threshold
|
|
81
163
|
port_app_config_handler.mock_get_port_app_config.assert_called_once()
|
|
82
164
|
|
|
83
165
|
|
|
@@ -2,7 +2,6 @@ from os import path
|
|
|
2
2
|
from typing import Any, Callable, Dict, List, Tuple, Union
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
|
-
import pytest_asyncio
|
|
6
5
|
|
|
7
6
|
from port_ocean.clients.port.client import PortClient
|
|
8
7
|
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
@@ -26,7 +25,7 @@ def port_client_for_fake_integration() -> Tuple[SmokeTestDetails, PortClient]:
|
|
|
26
25
|
return smoke_test_details, port_client
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
@
|
|
28
|
+
@pytest.fixture
|
|
30
29
|
def get_mocked_ocean_app(request: Any) -> Callable[[], Ocean]:
|
|
31
30
|
test_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
32
31
|
|
|
@@ -36,7 +35,7 @@ def get_mocked_ocean_app(request: Any) -> Callable[[], Ocean]:
|
|
|
36
35
|
return get_ocean_app
|
|
37
36
|
|
|
38
37
|
|
|
39
|
-
@
|
|
38
|
+
@pytest.fixture
|
|
40
39
|
def get_mock_ocean_resource_configs(request: Any) -> Callable[[], List[ResourceConfig]]:
|
|
41
40
|
module_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
42
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: port-ocean
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.2
|
|
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
|
|
@@ -35,6 +35,7 @@ Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
|
|
|
35
35
|
Requires-Dist: pydantic[dotenv] (>=1.10.8,<2.0.0)
|
|
36
36
|
Requires-Dist: pydispatcher (>=2.0.7,<3.0.0)
|
|
37
37
|
Requires-Dist: pyhumps (>=3.8.0,<4.0.0)
|
|
38
|
+
Requires-Dist: pytest-cov (>=6.0.0,<7.0.0)
|
|
38
39
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
39
40
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
40
41
|
Requires-Dist: rich (>=13.4.1,<14.0.0) ; extra == "cli"
|
|
@@ -96,7 +96,7 @@ port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=C6zJbS3m
|
|
|
96
96
|
port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
|
|
97
97
|
port_ocean/core/handlers/port_app_config/api.py,sha256=r_Th66NEw38IpRdnXZcRvI8ACfvxW_A6V62WLwjWXlQ,1044
|
|
98
98
|
port_ocean/core/handlers/port_app_config/base.py,sha256=4Nxt2g8voEIHJ4Y1Km5NJcaG2iSbCklw5P8-Kus7Y9k,3007
|
|
99
|
-
port_ocean/core/handlers/port_app_config/models.py,sha256=
|
|
99
|
+
port_ocean/core/handlers/port_app_config/models.py,sha256=Rf01rQpQZRse2qfr5PwgOz4pKwb53y0LuW9yp7v_ZoI,2912
|
|
100
100
|
port_ocean/core/handlers/queue/__init__.py,sha256=1fICM0ZLATmmj6f7cdq_eV2kmw0_jy7y2INuLQIpzIE,121
|
|
101
101
|
port_ocean/core/handlers/queue/abstract_queue.py,sha256=q_gpaWFFZHxM3XovEbgsDn8jEOLM45iAZWVC81Paxto,620
|
|
102
102
|
port_ocean/core/handlers/queue/local_queue.py,sha256=EzqsGIX43xbVAcePwTcCg5QDrXATQpy-VzWxxN_OyAM,574
|
|
@@ -112,8 +112,8 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
|
|
|
112
112
|
port_ocean/core/integrations/mixins/events.py,sha256=2L7P3Jhp8XBqddh2_o9Cn4N261nN1SySfrEdJoqLrIw,2714
|
|
113
113
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
114
114
|
port_ocean/core/integrations/mixins/live_events.py,sha256=8HklZmlyffYY_LeDe8xbt3Tb08rlLkqVhFF-2NQeJP4,4126
|
|
115
|
-
port_ocean/core/integrations/mixins/sync.py,sha256=
|
|
116
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
115
|
+
port_ocean/core/integrations/mixins/sync.py,sha256=Vm_898pLKBwfVewtwouDWsXoxcOLicnAy6pzyqqk6U8,4053
|
|
116
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=uATzik6gSmkpKlIhbcRavlDo-xtkUvMwNp1wZltJj-I,27487
|
|
117
117
|
port_ocean/core/integrations/mixins/utils.py,sha256=oN4Okz6xlaefpid1_Pud8HPSw9BwwjRohyNsknq-Myg,2309
|
|
118
118
|
port_ocean/core/models.py,sha256=YpJ2XOB3Zt9_M-rcMrMjugFNzBDg2hCUKgqvEt7now0,2348
|
|
119
119
|
port_ocean/core/ocean_types.py,sha256=4VipWFOHEh_d9LmWewQccwx1p2dtrRYW0YURVgNsAjo,1398
|
|
@@ -152,12 +152,12 @@ port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=-8iHM33Oe
|
|
|
152
152
|
port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
|
|
153
153
|
port_ocean/tests/core/conftest.py,sha256=BHfi7egDVNRpg61lHZlWj81_ohUG7DEVMdFe9yX-vkc,5517
|
|
154
154
|
port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
|
|
155
|
-
port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=
|
|
156
|
-
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=
|
|
155
|
+
port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=WNg1fWZsXu0MDnz9-ahRiPb_OPofWx7E8wxBx0cyZKs,8946
|
|
156
|
+
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=8WpMn559Mf0TFWmloRpZrVgr6yWwyA0C4n2lVHCtyq4,13596
|
|
157
157
|
port_ocean/tests/core/handlers/mixins/test_live_events.py,sha256=iAwVpr3n3PIkXQLw7hxd-iB_SR_vyfletVXJLOmyz28,12480
|
|
158
158
|
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=RTkHU2QS9f-Lt3u0OCBPPEeiaM2_9h5vOQxqLnrbQro,33560
|
|
159
159
|
port_ocean/tests/core/handlers/port_app_config/test_api.py,sha256=eJZ6SuFBLz71y4ca3DNqKag6d6HUjNJS0aqQPwiLMTI,1999
|
|
160
|
-
port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=
|
|
160
|
+
port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=hSh556bJM9zuELwhwnyKSfd9z06WqWXIfe-6hCl5iKI,9799
|
|
161
161
|
port_ocean/tests/core/handlers/queue/test_local_queue.py,sha256=9Ly0HzZXbs6Rbl_bstsIdInC3h2bgABU3roP9S_PnJM,2582
|
|
162
162
|
port_ocean/tests/core/handlers/webhook/test_abstract_webhook_processor.py,sha256=zKwHhPAYEZoZ5Z2UETp1t--mbkS8uyvlXThB0obZTTc,3340
|
|
163
163
|
port_ocean/tests/core/handlers/webhook/test_processor_manager.py,sha256=6Zap3rKBd8Td7-a9af6_FWOGNDN4oZhNdWeeaICsLD0,49386
|
|
@@ -167,7 +167,7 @@ port_ocean/tests/core/utils/test_entity_topological_sorter.py,sha256=zuq5WSPy_88
|
|
|
167
167
|
port_ocean/tests/core/utils/test_resolve_entities_diff.py,sha256=nSB0H87Gk6iFw7RMq9YfiZtC_u6X20ao5vmvywP5HsE,17945
|
|
168
168
|
port_ocean/tests/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
169
169
|
port_ocean/tests/helpers/fake_port_api.py,sha256=9rtjC6iTQMfzWK6WipkDzzG0b1IIaRmvdJLOyV613vE,6479
|
|
170
|
-
port_ocean/tests/helpers/fixtures.py,sha256=
|
|
170
|
+
port_ocean/tests/helpers/fixtures.py,sha256=dinEucKDTGAD2tKwbOqqHZSHNPsDLN2HxnLqA-WXGeI,1443
|
|
171
171
|
port_ocean/tests/helpers/integration.py,sha256=_RxS-RHpu11lrbhUXYPZp862HLWx8AoD7iZM6iXN8rs,1104
|
|
172
172
|
port_ocean/tests/helpers/ocean_app.py,sha256=N06vcNI1klqdcNFq-PXL5vm77u-hODsOSXnj9p8d1AI,2249
|
|
173
173
|
port_ocean/tests/helpers/port_client.py,sha256=5d6GNr8vNNSOkrz1AdOhxBUKuusr_-UPDP7AVpHasQw,599
|
|
@@ -188,8 +188,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
|
|
|
188
188
|
port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
|
|
189
189
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
190
190
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
191
|
-
port_ocean-0.22.
|
|
192
|
-
port_ocean-0.22.
|
|
193
|
-
port_ocean-0.22.
|
|
194
|
-
port_ocean-0.22.
|
|
195
|
-
port_ocean-0.22.
|
|
191
|
+
port_ocean-0.22.2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
192
|
+
port_ocean-0.22.2.dist-info/METADATA,sha256=Gnljw5UH_wpBycjJttdo1rAf8C8s7EeOP_KS5Ur6jWI,6764
|
|
193
|
+
port_ocean-0.22.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
194
|
+
port_ocean-0.22.2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
195
|
+
port_ocean-0.22.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|