port-ocean 0.10.11__tar.gz → 0.11.0__tar.gz
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-0.10.11 → port_ocean-0.11.0}/PKG-INFO +2 -2
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +14 -7
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/base.py +1 -1
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/sync_raw.py +3 -1
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/ocean.py +6 -1
- port_ocean-0.11.0/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +236 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/helpers/fixtures.py +22 -8
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/test_smoke.py +3 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/pyproject.toml +29 -28
- port_ocean-0.10.11/port_ocean/tests/test_sample.py +0 -2
- {port_ocean-0.10.11 → port_ocean-0.11.0}/LICENSE.md +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/README.md +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/new.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/client.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/mixins/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/mixins/entities.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/mixins/integrations.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/config/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/config/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/config/dynamic.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/config/settings.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/consumers/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/context/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/context/event.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/context/ocean.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/http.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/kafka.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/once.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/event_listener/polling.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/events.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/models.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/ocean_types.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/core.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/helpers/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/helpers/async_client.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/helpers/retry.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/log/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/log/sensetive.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/py.typed +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/run.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/conftest.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/helpers/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/helpers/ocean_app.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/async_iterators.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/misc.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/utils/time.py +0 -0
- {port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: port-ocean
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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,11 +28,11 @@ Requires-Dist: cookiecutter (>=2.1.1,<3.0.0) ; extra == "cli"
|
|
|
28
28
|
Requires-Dist: fastapi (>=0.100,<0.112)
|
|
29
29
|
Requires-Dist: httpx (>=0.24.1,<0.28.0)
|
|
30
30
|
Requires-Dist: jinja2-time (>=0.2.0,<0.3.0) ; extra == "cli"
|
|
31
|
+
Requires-Dist: jq (>=1.8.0,<2.0.0)
|
|
31
32
|
Requires-Dist: loguru (>=0.7.0,<0.8.0)
|
|
32
33
|
Requires-Dist: pydantic[dotenv] (>=1.10.8,<2.0.0)
|
|
33
34
|
Requires-Dist: pydispatcher (>=2.0.7,<3.0.0)
|
|
34
35
|
Requires-Dist: pyhumps (>=3.8.0,<4.0.0)
|
|
35
|
-
Requires-Dist: pyjq (>=2.6.0,<3.0.0)
|
|
36
36
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
37
37
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
38
38
|
Requires-Dist: rich (>=13.4.1,<14.0.0) ; extra == "cli"
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import functools
|
|
3
2
|
from asyncio import Task
|
|
4
3
|
from dataclasses import dataclass, field
|
|
5
4
|
from functools import lru_cache
|
|
6
5
|
from typing import Any, Optional
|
|
7
6
|
|
|
8
|
-
import
|
|
7
|
+
import jq # type: ignore
|
|
9
8
|
from loguru import logger
|
|
10
9
|
|
|
11
10
|
from port_ocean.context.ocean import ocean
|
|
@@ -52,8 +51,8 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
52
51
|
try:
|
|
53
52
|
loop = asyncio.get_event_loop()
|
|
54
53
|
compiled_pattern = self._compile(pattern)
|
|
55
|
-
|
|
56
|
-
return await loop.run_in_executor(None,
|
|
54
|
+
func = compiled_pattern.input_value(data)
|
|
55
|
+
return await loop.run_in_executor(None, func.first)
|
|
57
56
|
except Exception as exc:
|
|
58
57
|
logger.debug(
|
|
59
58
|
f"Failed to search for pattern {pattern} in data {data}, {exc}"
|
|
@@ -62,10 +61,18 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
62
61
|
|
|
63
62
|
async def _search_as_bool(self, data: dict[str, Any], pattern: str) -> bool:
|
|
64
63
|
loop = asyncio.get_event_loop()
|
|
64
|
+
start_time = loop.time()
|
|
65
65
|
compiled_pattern = self._compile(pattern)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
func = compiled_pattern.input_value(data)
|
|
67
|
+
compile_time = loop.time() - start_time
|
|
68
|
+
value = await loop.run_in_executor(None, func.first)
|
|
69
|
+
execute_time = loop.time() - start_time - compile_time
|
|
70
|
+
logger.debug(
|
|
71
|
+
f"Search for pattern {execute_time:.2f} seconds, compile time {compile_time:.2f} seconds",
|
|
72
|
+
pattern=pattern,
|
|
73
|
+
compile_time=compile_time,
|
|
74
|
+
execute_time=execute_time,
|
|
75
|
+
)
|
|
69
76
|
if isinstance(value, bool):
|
|
70
77
|
return value
|
|
71
78
|
|
|
@@ -54,7 +54,7 @@ class BaseIntegration(SyncRawMixin, SyncMixin):
|
|
|
54
54
|
"""
|
|
55
55
|
Initializes handlers, establishes integration at the specified port, and starts the event listener.
|
|
56
56
|
"""
|
|
57
|
-
logger.info("Starting integration")
|
|
57
|
+
logger.info("Starting integration", integration_type=self.context.config.integration.type)
|
|
58
58
|
if self.started:
|
|
59
59
|
raise IntegrationAlreadyStartedException("Integration already started")
|
|
60
60
|
|
|
@@ -456,7 +456,8 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
456
456
|
|
|
457
457
|
creation_results.append(await task)
|
|
458
458
|
except asyncio.CancelledError as e:
|
|
459
|
-
logger.warning("Resync aborted successfully")
|
|
459
|
+
logger.warning("Resync aborted successfully, skipping delete phase. This leads to an incomplete state")
|
|
460
|
+
raise
|
|
460
461
|
else:
|
|
461
462
|
if not did_fetched_current_state:
|
|
462
463
|
logger.warning(
|
|
@@ -489,3 +490,4 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
489
490
|
{"before": entities_at_port, "after": flat_created_entities},
|
|
490
491
|
user_agent_type,
|
|
491
492
|
)
|
|
493
|
+
logger.info("Resync finished successfully")
|
|
@@ -82,6 +82,10 @@ class Ocean:
|
|
|
82
82
|
try:
|
|
83
83
|
await self.integration.sync_raw_all()
|
|
84
84
|
await self.resync_state_updater.update_after_resync()
|
|
85
|
+
except asyncio.CancelledError:
|
|
86
|
+
logger.warning(
|
|
87
|
+
"resync was cancelled by the scheduled resync, skipping state update"
|
|
88
|
+
)
|
|
85
89
|
except Exception as e:
|
|
86
90
|
await self.resync_state_updater.update_after_resync(
|
|
87
91
|
IntegrationStateStatus.Failed
|
|
@@ -92,7 +96,8 @@ class Ocean:
|
|
|
92
96
|
loop = asyncio.get_event_loop()
|
|
93
97
|
if interval is not None:
|
|
94
98
|
logger.info(
|
|
95
|
-
f"Setting up scheduled resync, the integration will automatically perform a full resync every {interval} minutes)"
|
|
99
|
+
f"Setting up scheduled resync, the integration will automatically perform a full resync every {interval} minutes)",
|
|
100
|
+
scheduled_interval=interval,
|
|
96
101
|
)
|
|
97
102
|
repeated_function = repeat_every(
|
|
98
103
|
seconds=interval * 60,
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from unittest.mock import AsyncMock, Mock
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from port_ocean.context.ocean import PortOceanContext
|
|
6
|
+
from port_ocean.core.handlers.entity_processor.jq_entity_processor import (
|
|
7
|
+
JQEntityProcessor,
|
|
8
|
+
)
|
|
9
|
+
from port_ocean.core.ocean_types import CalculationResult
|
|
10
|
+
from port_ocean.exceptions.core import EntityProcessorException
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.asyncio
|
|
14
|
+
class TestJQEntityProcessor:
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def mocked_processor(self, monkeypatch: Any) -> JQEntityProcessor:
|
|
18
|
+
mock_context = AsyncMock()
|
|
19
|
+
monkeypatch.setattr(PortOceanContext, "app", mock_context)
|
|
20
|
+
return JQEntityProcessor(mock_context)
|
|
21
|
+
|
|
22
|
+
async def test_compile(self, mocked_processor: JQEntityProcessor) -> None:
|
|
23
|
+
pattern = ".foo"
|
|
24
|
+
compiled = mocked_processor._compile(pattern)
|
|
25
|
+
assert compiled is not None
|
|
26
|
+
|
|
27
|
+
async def test_search(self, mocked_processor: JQEntityProcessor) -> None:
|
|
28
|
+
data = {"foo": "bar"}
|
|
29
|
+
pattern = ".foo"
|
|
30
|
+
result = await mocked_processor._search(data, pattern)
|
|
31
|
+
assert result == "bar"
|
|
32
|
+
|
|
33
|
+
async def test_search_as_bool(self, mocked_processor: JQEntityProcessor) -> None:
|
|
34
|
+
data = {"foo": True}
|
|
35
|
+
pattern = ".foo"
|
|
36
|
+
result = await mocked_processor._search_as_bool(data, pattern)
|
|
37
|
+
assert result is True
|
|
38
|
+
|
|
39
|
+
async def test_search_as_object(self, mocked_processor: JQEntityProcessor) -> None:
|
|
40
|
+
data = {"foo": {"bar": "baz"}}
|
|
41
|
+
obj = {"foo": ".foo.bar"}
|
|
42
|
+
result = await mocked_processor._search_as_object(data, obj)
|
|
43
|
+
assert result == {"foo": "baz"}
|
|
44
|
+
|
|
45
|
+
async def test_get_mapped_entity(self, mocked_processor: JQEntityProcessor) -> None:
|
|
46
|
+
data = {"foo": "bar"}
|
|
47
|
+
raw_entity_mappings = {"foo": ".foo"}
|
|
48
|
+
selector_query = '.foo == "bar"'
|
|
49
|
+
result = await mocked_processor._get_mapped_entity(
|
|
50
|
+
data, raw_entity_mappings, selector_query
|
|
51
|
+
)
|
|
52
|
+
assert result.entity == {"foo": "bar"}
|
|
53
|
+
assert result.did_entity_pass_selector is True
|
|
54
|
+
|
|
55
|
+
async def test_calculate_entity(self, mocked_processor: JQEntityProcessor) -> None:
|
|
56
|
+
data = {"foo": "bar"}
|
|
57
|
+
raw_entity_mappings = {"foo": ".foo"}
|
|
58
|
+
selector_query = '.foo == "bar"'
|
|
59
|
+
result, errors = await mocked_processor._calculate_entity(
|
|
60
|
+
data, raw_entity_mappings, None, selector_query
|
|
61
|
+
)
|
|
62
|
+
assert len(result) == 1
|
|
63
|
+
assert result[0].entity == {"foo": "bar"}
|
|
64
|
+
assert result[0].did_entity_pass_selector is True
|
|
65
|
+
assert not errors
|
|
66
|
+
|
|
67
|
+
async def test_parse_items(self, mocked_processor: JQEntityProcessor) -> None:
|
|
68
|
+
mapping = Mock()
|
|
69
|
+
mapping.port.entity.mappings.dict.return_value = {
|
|
70
|
+
"identifier": ".foo",
|
|
71
|
+
"blueprint": ".foo",
|
|
72
|
+
"properties": {"foo": ".foo"},
|
|
73
|
+
}
|
|
74
|
+
mapping.port.items_to_parse = None
|
|
75
|
+
mapping.selector.query = '.foo == "bar"'
|
|
76
|
+
raw_results = [{"foo": "bar"}]
|
|
77
|
+
result = await mocked_processor._parse_items(mapping, raw_results)
|
|
78
|
+
assert isinstance(result, CalculationResult)
|
|
79
|
+
assert len(result.entity_selector_diff.passed) == 1
|
|
80
|
+
assert result.entity_selector_diff.passed[0].properties.get("foo") == "bar"
|
|
81
|
+
assert not result.errors
|
|
82
|
+
|
|
83
|
+
async def test_in_operator(self, mocked_processor: JQEntityProcessor) -> None:
|
|
84
|
+
data = {
|
|
85
|
+
"key": "GetPort_SelfService",
|
|
86
|
+
"name": "GetPort SelfService",
|
|
87
|
+
"desc": "Test",
|
|
88
|
+
"qualifier": "VW",
|
|
89
|
+
"visibility": "public",
|
|
90
|
+
"selectionMode": "NONE",
|
|
91
|
+
"subViews": [
|
|
92
|
+
{
|
|
93
|
+
"key": "GetPort_SelfService_Second",
|
|
94
|
+
"name": "GetPort SelfService Second",
|
|
95
|
+
"qualifier": "SVW",
|
|
96
|
+
"selectionMode": "NONE",
|
|
97
|
+
"subViews": [
|
|
98
|
+
{
|
|
99
|
+
"key": "GetPort_SelfService_Third",
|
|
100
|
+
"name": "GetPort SelfService Third",
|
|
101
|
+
"qualifier": "SVW",
|
|
102
|
+
"selectionMode": "NONE",
|
|
103
|
+
"subViews": [],
|
|
104
|
+
"referencedBy": [],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"key": "Port_Test",
|
|
108
|
+
"name": "Port Test",
|
|
109
|
+
"qualifier": "SVW",
|
|
110
|
+
"selectionMode": "NONE",
|
|
111
|
+
"subViews": [],
|
|
112
|
+
"referencedBy": [],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
"referencedBy": [],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"key": "Python",
|
|
119
|
+
"name": "Python",
|
|
120
|
+
"qualifier": "SVW",
|
|
121
|
+
"selectionMode": "NONE",
|
|
122
|
+
"subViews": [
|
|
123
|
+
{
|
|
124
|
+
"key": "Time",
|
|
125
|
+
"name": "Time",
|
|
126
|
+
"qualifier": "SVW",
|
|
127
|
+
"selectionMode": "NONE",
|
|
128
|
+
"subViews": [
|
|
129
|
+
{
|
|
130
|
+
"key": "port_*****",
|
|
131
|
+
"name": "port-*****",
|
|
132
|
+
"qualifier": "SVW",
|
|
133
|
+
"selectionMode": "NONE",
|
|
134
|
+
"subViews": [
|
|
135
|
+
{
|
|
136
|
+
"key": "port_*****:REferenced",
|
|
137
|
+
"name": "REferenced",
|
|
138
|
+
"qualifier": "VW",
|
|
139
|
+
"visibility": "public",
|
|
140
|
+
"originalKey": "REferenced",
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"referencedBy": [],
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"referencedBy": [],
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"referencedBy": [],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"key": "GetPort_SelfService:Authentication_Application",
|
|
153
|
+
"name": "Authentication Application",
|
|
154
|
+
"desc": "For auth services",
|
|
155
|
+
"qualifier": "APP",
|
|
156
|
+
"visibility": "private",
|
|
157
|
+
"selectedBranches": ["main"],
|
|
158
|
+
"originalKey": "Authentication_Application",
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
"referencedBy": [],
|
|
162
|
+
}
|
|
163
|
+
pattern = '.subViews | map(select((.qualifier | IN("VW", "SVW"))) | .key)'
|
|
164
|
+
result = await mocked_processor._search(data, pattern)
|
|
165
|
+
assert result == ["GetPort_SelfService_Second", "Python"]
|
|
166
|
+
|
|
167
|
+
async def test_failure_of_jq_expression(
|
|
168
|
+
self, mocked_processor: JQEntityProcessor
|
|
169
|
+
) -> None:
|
|
170
|
+
data = {"foo": "bar"}
|
|
171
|
+
pattern = ".foo."
|
|
172
|
+
result = await mocked_processor._search(data, pattern)
|
|
173
|
+
assert result is None
|
|
174
|
+
|
|
175
|
+
async def test_search_as_object_failure(
|
|
176
|
+
self, mocked_processor: JQEntityProcessor
|
|
177
|
+
) -> None:
|
|
178
|
+
data = {"foo": {"bar": "baz"}}
|
|
179
|
+
obj = {"foo": ".foo.bar."}
|
|
180
|
+
result = await mocked_processor._search_as_object(data, obj)
|
|
181
|
+
assert result == {"foo": None}
|
|
182
|
+
|
|
183
|
+
async def test_double_quotes_in_jq_expression(
|
|
184
|
+
self, mocked_processor: JQEntityProcessor
|
|
185
|
+
) -> None:
|
|
186
|
+
data = {"foo": "bar"}
|
|
187
|
+
pattern = '"shalom"'
|
|
188
|
+
result = await mocked_processor._search(data, pattern)
|
|
189
|
+
assert result == "shalom"
|
|
190
|
+
|
|
191
|
+
async def test_search_as_bool_failure(
|
|
192
|
+
self, mocked_processor: JQEntityProcessor
|
|
193
|
+
) -> None:
|
|
194
|
+
data = {"foo": "bar"}
|
|
195
|
+
pattern = ".foo"
|
|
196
|
+
with pytest.raises(
|
|
197
|
+
EntityProcessorException,
|
|
198
|
+
match="Expected boolean value, got <class 'str'> instead",
|
|
199
|
+
):
|
|
200
|
+
await mocked_processor._search_as_bool(data, pattern)
|
|
201
|
+
|
|
202
|
+
@pytest.mark.timeout(3)
|
|
203
|
+
async def test_search_performance_10000(
|
|
204
|
+
self, mocked_processor: JQEntityProcessor
|
|
205
|
+
) -> None:
|
|
206
|
+
"""
|
|
207
|
+
This test is to check the performance of the search method when called 10000 times.
|
|
208
|
+
"""
|
|
209
|
+
data = {"foo": "bar"}
|
|
210
|
+
pattern = ".foo"
|
|
211
|
+
for _ in range(10000):
|
|
212
|
+
result = await mocked_processor._search(data, pattern)
|
|
213
|
+
assert result == "bar"
|
|
214
|
+
|
|
215
|
+
@pytest.mark.timeout(15)
|
|
216
|
+
async def test_parse_items_performance_10000(
|
|
217
|
+
self, mocked_processor: JQEntityProcessor
|
|
218
|
+
) -> None:
|
|
219
|
+
"""
|
|
220
|
+
This test is to check the performance of the parse_items method when called 10000 times.
|
|
221
|
+
"""
|
|
222
|
+
mapping = Mock()
|
|
223
|
+
mapping.port.entity.mappings.dict.return_value = {
|
|
224
|
+
"identifier": ".foo",
|
|
225
|
+
"blueprint": ".foo",
|
|
226
|
+
"properties": {"foo": ".foo"},
|
|
227
|
+
}
|
|
228
|
+
mapping.port.items_to_parse = None
|
|
229
|
+
mapping.selector.query = '.foo == "bar"'
|
|
230
|
+
raw_results = [{"foo": "bar"}]
|
|
231
|
+
for _ in range(10000):
|
|
232
|
+
result = await mocked_processor._parse_items(mapping, raw_results)
|
|
233
|
+
assert isinstance(result, CalculationResult)
|
|
234
|
+
assert len(result.entity_selector_diff.passed) == 1
|
|
235
|
+
assert result.entity_selector_diff.passed[0].properties.get("foo") == "bar"
|
|
236
|
+
assert not result.errors
|
|
@@ -2,6 +2,7 @@ from os import environ, path
|
|
|
2
2
|
from typing import Any, AsyncGenerator, Callable, List, Tuple, Union
|
|
3
3
|
|
|
4
4
|
import pytest_asyncio
|
|
5
|
+
from loguru import logger
|
|
5
6
|
from pydantic import BaseModel
|
|
6
7
|
|
|
7
8
|
from port_ocean.clients.port.client import PortClient
|
|
@@ -33,15 +34,28 @@ def get_port_client_for_integration(
|
|
|
33
34
|
|
|
34
35
|
async def cleanup_integration(client: PortClient, blueprints: List[str]) -> None:
|
|
35
36
|
for blueprint in blueprints:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
try:
|
|
38
|
+
bp = await client.get_blueprint(blueprint)
|
|
39
|
+
if bp is not None:
|
|
40
|
+
migration_id = await client.delete_blueprint(
|
|
41
|
+
identifier=blueprint, delete_entities=True
|
|
42
|
+
)
|
|
43
|
+
if migration_id:
|
|
44
|
+
await client.wait_for_migration_to_complete(
|
|
45
|
+
migration_id=migration_id
|
|
46
|
+
)
|
|
47
|
+
except Exception as bp_e:
|
|
48
|
+
logger.info(f"Skipping missing blueprint ({blueprint}): {bp_e}")
|
|
43
49
|
headers = await client.auth.headers()
|
|
44
|
-
|
|
50
|
+
try:
|
|
51
|
+
await client.client.delete(
|
|
52
|
+
f"{client.auth.api_url}/integrations/{client.integration_identifier}",
|
|
53
|
+
headers=headers,
|
|
54
|
+
)
|
|
55
|
+
except Exception as int_e:
|
|
56
|
+
logger.info(
|
|
57
|
+
f"Failed to delete integration ({client.integration_identifier}): {int_e}"
|
|
58
|
+
)
|
|
45
59
|
|
|
46
60
|
|
|
47
61
|
class SmokeTestDetails(BaseModel):
|
|
@@ -7,6 +7,9 @@ from port_ocean.clients.port.types import UserAgentType
|
|
|
7
7
|
from port_ocean.tests.helpers.fixtures import SmokeTestDetails
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
pytestmark = pytest.mark.smoke
|
|
11
|
+
|
|
12
|
+
|
|
10
13
|
@pytest.mark.skipif(
|
|
11
14
|
environ.get("SMOKE_TEST_SUFFIX", None) is None,
|
|
12
15
|
reason="You need to run the fake integration once",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "port-ocean"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0"
|
|
4
4
|
description = "Port Ocean is a CLI tool for managing your Port projects."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
homepage = "https://app.getport.io"
|
|
@@ -36,7 +36,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
36
36
|
|
|
37
37
|
[tool.poetry.dependencies]
|
|
38
38
|
python = "^3.11"
|
|
39
|
-
pydantic = {version = "^1.10.8", extras = ["dotenv"]}
|
|
39
|
+
pydantic = { version = "^1.10.8", extras = ["dotenv"] }
|
|
40
40
|
loguru = "^0.7.0"
|
|
41
41
|
pyyaml = "^6.0"
|
|
42
42
|
werkzeug = ">=2.3.4,<4.0.0"
|
|
@@ -44,7 +44,7 @@ fastapi = ">=0.100,<0.112"
|
|
|
44
44
|
uvicorn = ">=0.22,<0.31"
|
|
45
45
|
confluent-kafka = "^2.1.1"
|
|
46
46
|
httpx = ">=0.24.1,<0.28.0"
|
|
47
|
-
|
|
47
|
+
jq = "^1.8.0"
|
|
48
48
|
urllib3 = ">=1.26.16,<3.0.0"
|
|
49
49
|
six = "^1.16.0"
|
|
50
50
|
pyhumps = "^3.8.0"
|
|
@@ -78,6 +78,7 @@ types-python-dateutil = "^2.9.0.20240316"
|
|
|
78
78
|
types-pyyaml = "^6.0.12.10"
|
|
79
79
|
types-toml = "^0.10.8.6"
|
|
80
80
|
yamllint = "^1.32.0"
|
|
81
|
+
pytest-timeout = "^2.3.1"
|
|
81
82
|
|
|
82
83
|
[tool.towncrier]
|
|
83
84
|
directory = "changelog"
|
|
@@ -87,35 +88,35 @@ package_dir = "."
|
|
|
87
88
|
package = "port_ocean"
|
|
88
89
|
underlines = [""]
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
[[tool.towncrier.type]]
|
|
92
|
+
directory = "breaking"
|
|
93
|
+
name = "Breaking Changes"
|
|
94
|
+
showcontent = true
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
[[tool.towncrier.type]]
|
|
97
|
+
directory = "deprecation"
|
|
98
|
+
name = "Deprecations"
|
|
99
|
+
showcontent = true
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
[[tool.towncrier.type]]
|
|
102
|
+
directory = "feature"
|
|
103
|
+
name = "Features"
|
|
104
|
+
showcontent = true
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
[[tool.towncrier.type]]
|
|
107
|
+
directory = "improvement"
|
|
108
|
+
name = "Improvements"
|
|
109
|
+
showcontent = true
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
[[tool.towncrier.type]]
|
|
112
|
+
directory = "bugfix"
|
|
113
|
+
name = "Bug Fixes"
|
|
114
|
+
showcontent = true
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
[[tool.towncrier.type]]
|
|
117
|
+
directory = "doc"
|
|
118
|
+
name = "Improved Documentation"
|
|
119
|
+
showcontent = true
|
|
119
120
|
|
|
120
121
|
[tool.mypy]
|
|
121
122
|
exclude = [
|
|
@@ -174,4 +175,4 @@ exclude = '''
|
|
|
174
175
|
[tool.pytest.ini_options]
|
|
175
176
|
asyncio_mode = "auto"
|
|
176
177
|
asyncio_default_fixture_loop_scope = "function"
|
|
177
|
-
addopts = "-vv -n auto --ignore-glob='./integrations/*' ./port_ocean/tests"
|
|
178
|
+
addopts = "-vv -n auto --durations=10 --color=yes --ignore-glob='./integrations/*' ./port_ocean/tests"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/__init__.py
RENAMED
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entities_state_applier/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/entity_processor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/port_app_config/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/resync_state_updater/__init__.py
RENAMED
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/core/handlers/resync_state_updater/updater.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.10.11 → port_ocean-0.11.0}/port_ocean/tests/clients/port/mixins/test_entities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|