port-ocean 0.25.4__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.
@@ -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"]
@@ -1,5 +1,5 @@
1
1
  if test -e /usr/local/share/ca-certificates/cert.crt; then
2
- update-ca-certificates
2
+ sudo update-ca-certificates
3
3
  fi
4
4
 
5
5
  ocean sail
@@ -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
- default_query = {
487
- "combinator": "and",
488
- "rules": [
489
- {
490
- "property": "$datasource",
491
- "operator": "contains",
492
- "value": f"port-ocean/{self.auth.integration_type}/",
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
- "property": "$datasource",
496
- "operator": "contains",
497
- "value": f"/{self.auth.integration_identifier}/{user_agent_type.value}",
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, List, Generator
2
- from unittest.mock import MagicMock, patch, AsyncMock
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.25.4
3
+ Version: 0.26.1
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
@@ -28,6 +28,7 @@ Requires-Dist: confluent-kafka (>=2.10.1,<3.0.0)
28
28
  Requires-Dist: cookiecutter (>=2.1.1,<3.0.0) ; extra == "cli"
29
29
  Requires-Dist: fastapi (>=0.115.3,<0.116.0)
30
30
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
31
+ Requires-Dist: jinja2 (>=3.1.6)
31
32
  Requires-Dist: jinja2-time (>=0.2.0,<0.3.0) ; extra == "cli"
32
33
  Requires-Dist: jq (>=1.8.0,<2.0.0)
33
34
  Requires-Dist: loguru (>=0.7.0,<0.8.0)
@@ -1,4 +1,4 @@
1
- integrations/_infra/Dockerfile.Deb,sha256=mGbUtGmNbA2nUqDmhoy5ehU-UpBxc4hlB8yutXjOpgU,1998
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=nN8lTrOhB286UfFvD6sJ9YJ-9asT9zVSddQB-RAb7Z4,99
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=_FKoSEUxc_fGcdoWm4ZAeUKUnnzkGPMDQEsxmpbj8Vo,23352
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=DeWbAQcaxT3LQQf_j9HA5nG7YgsQDvXmgK2aghlG9ug,6619
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.25.4.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
207
- port_ocean-0.25.4.dist-info/METADATA,sha256=5acMsO-B6_OoY9H01SpdvNQuaLiplfZbpBOMVptXxfg,6855
208
- port_ocean-0.25.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
209
- port_ocean-0.25.4.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
210
- port_ocean-0.25.4.dist-info/RECORD,,
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,,