port-ocean 0.25.5__py3-none-any.whl → 0.26.1__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.
- integrations/_infra/Dockerfile.Deb +6 -0
- integrations/_infra/init.sh +1 -1
- port_ocean/clients/port/mixins/entities.py +47 -29
- port_ocean/tests/clients/port/mixins/test_entities.py +44 -3
- {port_ocean-0.25.5.dist-info → port_ocean-0.26.1.dist-info}/METADATA +1 -1
- {port_ocean-0.25.5.dist-info → port_ocean-0.26.1.dist-info}/RECORD +9 -9
- {port_ocean-0.25.5.dist-info → port_ocean-0.26.1.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.25.5.dist-info → port_ocean-0.26.1.dist-info}/WHEEL +0 -0
- {port_ocean-0.25.5.dist-info → port_ocean-0.26.1.dist-info}/entry_points.txt +0 -0
@@ -40,6 +40,7 @@ RUN apt-get update \
|
|
40
40
|
openssl \
|
41
41
|
curl \
|
42
42
|
acl \
|
43
|
+
sudo \
|
43
44
|
&& apt-get clean
|
44
45
|
|
45
46
|
LABEL INTEGRATION_VERSION=${INTEGRATION_VERSION}
|
@@ -68,6 +69,11 @@ RUN chmod a+x /app/init.sh
|
|
68
69
|
RUN ln -s /app/.venv/bin/ocean /usr/bin/ocean
|
69
70
|
# Add ocean user to ssl certs group
|
70
71
|
RUN setfacl -m u:ocean:rwX /etc/ssl/certs
|
72
|
+
|
73
|
+
# Allow ocean user to run update-ca-certificates without password (secure, limited sudo)
|
74
|
+
RUN echo "ocean ALL=(root) NOPASSWD: /usr/sbin/update-ca-certificates" >> /etc/sudoers.d/ocean-certs \
|
75
|
+
&& chmod 440 /etc/sudoers.d/ocean-certs
|
76
|
+
|
71
77
|
USER ocean
|
72
78
|
# Run the application
|
73
79
|
CMD ["bash", "/app/init.sh"]
|
integrations/_infra/init.sh
CHANGED
@@ -483,38 +483,56 @@ class EntityClientMixin:
|
|
483
483
|
query: dict[Any, Any] | None = None,
|
484
484
|
parameters_to_include: list[str] | None = None,
|
485
485
|
) -> list[Entity]:
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
{
|
490
|
-
|
491
|
-
|
492
|
-
|
486
|
+
if query is None:
|
487
|
+
datasource_prefix = f"port-ocean/{self.auth.integration_type}/"
|
488
|
+
datasource_suffix = (
|
489
|
+
f"/{self.auth.integration_identifier}/{user_agent_type.value}"
|
490
|
+
)
|
491
|
+
logger.info(
|
492
|
+
f"Searching entities with datasource prefix: {datasource_prefix} and suffix: {datasource_suffix}"
|
493
|
+
)
|
494
|
+
|
495
|
+
response = await self.client.post(
|
496
|
+
f"{self.auth.api_url}/blueprints/entities/datasource-entities",
|
497
|
+
json={
|
498
|
+
"datasource_prefix": datasource_prefix,
|
499
|
+
"datasource_suffix": datasource_suffix,
|
493
500
|
},
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
501
|
+
headers=await self.auth.headers(user_agent_type),
|
502
|
+
extensions={"retryable": True},
|
503
|
+
)
|
504
|
+
else:
|
505
|
+
default_query = {
|
506
|
+
"combinator": "and",
|
507
|
+
"rules": [
|
508
|
+
{
|
509
|
+
"property": "$datasource",
|
510
|
+
"operator": "contains",
|
511
|
+
"value": f"port-ocean/{self.auth.integration_type}/",
|
512
|
+
},
|
513
|
+
{
|
514
|
+
"property": "$datasource",
|
515
|
+
"operator": "contains",
|
516
|
+
"value": f"/{self.auth.integration_identifier}/{user_agent_type.value}",
|
517
|
+
},
|
518
|
+
],
|
519
|
+
}
|
520
|
+
|
521
|
+
if query.get("rules"):
|
522
|
+
query["rules"].extend(default_query["rules"])
|
523
|
+
|
524
|
+
logger.info(f"Searching entities with custom query: {query}")
|
525
|
+
response = await self.client.post(
|
526
|
+
f"{self.auth.api_url}/entities/search",
|
527
|
+
json=query,
|
528
|
+
headers=await self.auth.headers(user_agent_type),
|
529
|
+
params={
|
530
|
+
"exclude_calculated_properties": "true",
|
531
|
+
"include": parameters_to_include or ["blueprint", "identifier"],
|
498
532
|
},
|
499
|
-
|
500
|
-
|
533
|
+
extensions={"retryable": True},
|
534
|
+
)
|
501
535
|
|
502
|
-
if query is None:
|
503
|
-
query = default_query
|
504
|
-
elif query.get("rules"):
|
505
|
-
query["rules"].append(default_query)
|
506
|
-
|
507
|
-
logger.info(f"Searching entities with query {query}")
|
508
|
-
response = await self.client.post(
|
509
|
-
f"{self.auth.api_url}/entities/search",
|
510
|
-
json=query,
|
511
|
-
headers=await self.auth.headers(user_agent_type),
|
512
|
-
params={
|
513
|
-
"exclude_calculated_properties": "true",
|
514
|
-
"include": parameters_to_include or ["blueprint", "identifier"],
|
515
|
-
},
|
516
|
-
extensions={"retryable": True},
|
517
|
-
)
|
518
536
|
handle_port_status_code(response)
|
519
537
|
return [Entity.parse_obj(result) for result in response.json()["entities"]]
|
520
538
|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
from typing import Any,
|
2
|
-
from unittest.mock import MagicMock, patch
|
1
|
+
from typing import Any, Generator, List
|
2
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
3
3
|
|
4
4
|
import pytest
|
5
|
+
from httpx import ReadTimeout
|
5
6
|
|
6
7
|
from port_ocean.clients.port.mixins.entities import EntityClientMixin
|
7
8
|
from port_ocean.core.models import Entity
|
8
|
-
from httpx import ReadTimeout
|
9
9
|
|
10
10
|
# Mock the ocean context at module level
|
11
11
|
pytestmark = pytest.mark.usefixtures("mock_ocean")
|
@@ -172,3 +172,44 @@ async def test_batch_upsert_entities_read_timeout_should_raise_true(
|
|
172
172
|
await entity_client.upsert_entities_in_batches(
|
173
173
|
entities=all_entities, request_options=MagicMock(), should_raise=True
|
174
174
|
)
|
175
|
+
|
176
|
+
|
177
|
+
async def test_search_entities_uses_datasource_route_when_query_is_none(
|
178
|
+
entity_client: EntityClientMixin,
|
179
|
+
) -> None:
|
180
|
+
"""Test that search_entities uses datasource route when query is None"""
|
181
|
+
mock_response = MagicMock()
|
182
|
+
mock_response.json.return_value = {"entities": []}
|
183
|
+
mock_response.is_error = False
|
184
|
+
mock_response.status_code = 200
|
185
|
+
mock_response.headers = {}
|
186
|
+
entity_client.client.post = AsyncMock(return_value=mock_response) # type: ignore
|
187
|
+
entity_client.auth.headers = AsyncMock( # type: ignore
|
188
|
+
return_value={"Authorization": "Bearer test"}
|
189
|
+
)
|
190
|
+
|
191
|
+
entity_client.auth.integration_type = "test-integration"
|
192
|
+
entity_client.auth.integration_identifier = "test-identifier"
|
193
|
+
entity_client.auth.api_url = "https://api.getport.io/v1"
|
194
|
+
|
195
|
+
mock_user_agent_type = MagicMock()
|
196
|
+
mock_user_agent_type.value = "sync"
|
197
|
+
|
198
|
+
await entity_client.search_entities(
|
199
|
+
user_agent_type=mock_user_agent_type,
|
200
|
+
query=None,
|
201
|
+
)
|
202
|
+
|
203
|
+
entity_client.client.post.assert_called_once()
|
204
|
+
call_args = entity_client.client.post.call_args
|
205
|
+
|
206
|
+
assert (
|
207
|
+
call_args[0][0]
|
208
|
+
== "https://api.getport.io/v1/blueprints/entities/datasource-entities"
|
209
|
+
)
|
210
|
+
|
211
|
+
expected_json = {
|
212
|
+
"datasource_prefix": "port-ocean/test-integration/",
|
213
|
+
"datasource_suffix": "/test-identifier/sync",
|
214
|
+
}
|
215
|
+
assert call_args[1]["json"] == expected_json
|
@@ -1,4 +1,4 @@
|
|
1
|
-
integrations/_infra/Dockerfile.Deb,sha256=
|
1
|
+
integrations/_infra/Dockerfile.Deb,sha256=RW-BC8NaVmw3ASF7a3XNsjFN3ZN5gTVxbFrOmhBXq30,2251
|
2
2
|
integrations/_infra/Dockerfile.alpine,sha256=7E4Sb-8supsCcseerHwTkuzjHZoYcaHIyxiBZ-wewo0,3482
|
3
3
|
integrations/_infra/Dockerfile.base.builder,sha256=ESe1PKC6itp_AuXawbLI75k1Kruny6NTANaTinxOgVs,743
|
4
4
|
integrations/_infra/Dockerfile.base.runner,sha256=uAcs2IsxrAAUHGXt_qULA5INr-HFguf5a5fCKiqEzbY,384
|
@@ -8,7 +8,7 @@ integrations/_infra/Makefile,sha256=YgLKvuF_Dw4IA7X98Nus6zIW_3cJ60M1QFGs3imj5c4,
|
|
8
8
|
integrations/_infra/README.md,sha256=ZtJFSMCTU5zTeM8ddRuW1ZL1ga8z7Ic2F3mxmgOSjgo,1195
|
9
9
|
integrations/_infra/entry_local.sh,sha256=Sn2TexTEpruH2ixIAGsk-fZV6Y7pT3jd2Pi9TxBeFuw,633
|
10
10
|
integrations/_infra/grpcio.sh,sha256=m924poYznoRZ6Tt7Ct8Cs5AV_cmmOx598yIZ3z4DvZE,616
|
11
|
-
integrations/_infra/init.sh,sha256=
|
11
|
+
integrations/_infra/init.sh,sha256=yyAall_O2PHRA1qS5_Pm-kGm7HKlASLFfzmyWGic6vg,104
|
12
12
|
port_ocean/__init__.py,sha256=uMpjg5d_cXgnyCxA_LmICR8zqBmC6Fe9Ivu9hcvJ7EY,313
|
13
13
|
port_ocean/bootstrap.py,sha256=CN1M5pVecZ7z_Vfu86Dk2HjFMiuiwt6E_SSOLFCYRMk,1321
|
14
14
|
port_ocean/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -60,7 +60,7 @@ port_ocean/clients/port/authentication.py,sha256=r7r8Ag9WuwXy-CmgeOoj-PHbmJAQxhb
|
|
60
60
|
port_ocean/clients/port/client.py,sha256=dv0mxIOde6J-wFi1FXXZkoNPVHrZzY7RSMhNkDD9xgA,3566
|
61
61
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
62
|
port_ocean/clients/port/mixins/blueprints.py,sha256=aMCG4zePsMSMjMLiGrU37h5z5_ElfMzTcTvqvOI5wXY,4683
|
63
|
-
port_ocean/clients/port/mixins/entities.py,sha256=
|
63
|
+
port_ocean/clients/port/mixins/entities.py,sha256=X2NqH00eK6TMJ3a3QEQRVQlKHzyj5l1FiPkIhonnbPg,24234
|
64
64
|
port_ocean/clients/port/mixins/integrations.py,sha256=DdMLxSHL2zmlLecnkJk1lWJ2fxkY89pSSL-m7H0zBuI,11256
|
65
65
|
port_ocean/clients/port/mixins/migrations.py,sha256=vdL_A_NNUogvzujyaRLIoZEu5vmKDY2BxTjoGP94YzI,1467
|
66
66
|
port_ocean/clients/port/mixins/organization.py,sha256=A2cP5V49KnjoAXxjmnm_XGth4ftPSU0qURNfnyUyS_Y,1041
|
@@ -159,7 +159,7 @@ port_ocean/tests/cache/test_memory_cache.py,sha256=xlwIOBU0RVLYYJU83l_aoZDzZ6QID
|
|
159
159
|
port_ocean/tests/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
160
160
|
port_ocean/tests/clients/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
161
161
|
port_ocean/tests/clients/oauth/test_oauth_client.py,sha256=2XVMQUalDpiD539Z7_dk5BK_ngXQzsTmb2lNBsfEm9c,3266
|
162
|
-
port_ocean/tests/clients/port/mixins/test_entities.py,sha256=
|
162
|
+
port_ocean/tests/clients/port/mixins/test_entities.py,sha256=_ZEQT7UMzg1gW8kH8oFjgRVcLwQb7dFac48Tw_vcCqk,8018
|
163
163
|
port_ocean/tests/clients/port/mixins/test_integrations.py,sha256=vRt_EMsLozQC1LJNXxlvnHj3-FlOBGgAYxg5T0IAqtA,7621
|
164
164
|
port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=zzKYz3h8dl4Z5A2QG_924m0y9U6XTth1XYOfwNrd_24,914
|
165
165
|
port_ocean/tests/config/test_config.py,sha256=Rk4N-ldVSOfn1p23NzdVdfqUpPrqG2cMut4Sv-sAOrw,1023
|
@@ -203,8 +203,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
|
|
203
203
|
port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
|
204
204
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
205
205
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
206
|
-
port_ocean-0.
|
207
|
-
port_ocean-0.
|
208
|
-
port_ocean-0.
|
209
|
-
port_ocean-0.
|
210
|
-
port_ocean-0.
|
206
|
+
port_ocean-0.26.1.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
207
|
+
port_ocean-0.26.1.dist-info/METADATA,sha256=8L6RDbRiJhD116l965EDgl2SJAJQCFCGQZmUn_VLs50,6887
|
208
|
+
port_ocean-0.26.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
209
|
+
port_ocean-0.26.1.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
210
|
+
port_ocean-0.26.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|