port-ocean 0.11.0__tar.gz → 0.12.2__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.11.0 → port_ocean-0.12.2}/PKG-INFO +1 -1
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +24 -13
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/log/sensetive.py +1 -1
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +36 -1
- port_ocean-0.12.2/port_ocean/tests/helpers/fixtures.py +46 -0
- port_ocean-0.12.2/port_ocean/tests/helpers/integration.py +31 -0
- port_ocean-0.12.2/port_ocean/tests/helpers/port_client.py +21 -0
- port_ocean-0.12.2/port_ocean/tests/helpers/smoke_test.py +82 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/test_smoke.py +1 -1
- port_ocean-0.12.2/port_ocean/tests/utils/test_async_iterators.py +45 -0
- port_ocean-0.12.2/port_ocean/utils/async_iterators.py +109 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/pyproject.toml +4 -1
- port_ocean-0.11.0/port_ocean/tests/helpers/fixtures.py +0 -124
- port_ocean-0.11.0/port_ocean/utils/async_iterators.py +0 -49
- {port_ocean-0.11.0 → port_ocean-0.12.2}/LICENSE.md +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/README.md +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/new.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/client.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/mixins/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/mixins/entities.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/mixins/integrations.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/config/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/config/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/config/dynamic.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/config/settings.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/consumers/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/context/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/context/event.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/context/ocean.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/http.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/kafka.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/once.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/event_listener/polling.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/resync_state_updater/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/resync_state_updater/updater.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/events.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/sync_raw.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/models.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/ocean_types.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/core/utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/core.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/helpers/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/helpers/async_client.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/helpers/retry.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/log/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/ocean.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/py.typed +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/run.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/clients/port/mixins/test_entities.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/conftest.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/helpers/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/tests/helpers/ocean_app.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/misc.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/utils/time.py +0 -0
- {port_ocean-0.11.0 → port_ocean-0.12.2}/port_ocean/version.py +0 -0
|
@@ -47,37 +47,48 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
47
47
|
pattern = "def env: {}; {} as $ENV | " + pattern
|
|
48
48
|
return jq.compile(pattern)
|
|
49
49
|
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _stop_iterator_handler(func: Any) -> Any:
|
|
52
|
+
"""
|
|
53
|
+
Wrap the function to handle StopIteration exceptions.
|
|
54
|
+
Prevents StopIteration from stopping the thread and skipping further queue processing.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def inner() -> Any:
|
|
58
|
+
try:
|
|
59
|
+
return func()
|
|
60
|
+
except StopIteration:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
return inner
|
|
64
|
+
|
|
50
65
|
async def _search(self, data: dict[str, Any], pattern: str) -> Any:
|
|
51
66
|
try:
|
|
52
67
|
loop = asyncio.get_event_loop()
|
|
53
68
|
compiled_pattern = self._compile(pattern)
|
|
54
69
|
func = compiled_pattern.input_value(data)
|
|
55
|
-
return await loop.run_in_executor(
|
|
70
|
+
return await loop.run_in_executor(
|
|
71
|
+
None, self._stop_iterator_handler(func.first)
|
|
72
|
+
)
|
|
56
73
|
except Exception as exc:
|
|
57
74
|
logger.debug(
|
|
58
|
-
f"
|
|
75
|
+
f"Search failed for pattern '{pattern}' in data: {data}, Error: {exc}"
|
|
59
76
|
)
|
|
60
77
|
return None
|
|
61
78
|
|
|
62
79
|
async def _search_as_bool(self, data: dict[str, Any], pattern: str) -> bool:
|
|
63
80
|
loop = asyncio.get_event_loop()
|
|
64
|
-
|
|
81
|
+
|
|
65
82
|
compiled_pattern = self._compile(pattern)
|
|
66
83
|
func = compiled_pattern.input_value(data)
|
|
67
|
-
|
|
68
|
-
value = await loop.run_in_executor(
|
|
69
|
-
|
|
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,
|
|
84
|
+
|
|
85
|
+
value = await loop.run_in_executor(
|
|
86
|
+
None, self._stop_iterator_handler(func.first)
|
|
75
87
|
)
|
|
76
88
|
if isinstance(value, bool):
|
|
77
89
|
return value
|
|
78
|
-
|
|
79
90
|
raise EntityProcessorException(
|
|
80
|
-
f"Expected boolean value, got {type(value)} instead"
|
|
91
|
+
f"Expected boolean value, got value:{value} of type: {type(value)} instead"
|
|
81
92
|
)
|
|
82
93
|
|
|
83
94
|
async def _search_as_object(
|
|
@@ -21,7 +21,7 @@ secret_patterns = {
|
|
|
21
21
|
"GitHub": r"[g|G][i|I][t|T][h|H][u|U][b|B].*['|\"][0-9a-zA-Z]{35,40}['|\"]",
|
|
22
22
|
"Google Cloud Platform API Key": r"AIza[0-9A-Za-z\\-_]{35}",
|
|
23
23
|
"Google Cloud Platform OAuth": r"[0-9]+-[0-9A-Za-z_]{32}\\.apps\\.googleusercontent\\.com",
|
|
24
|
-
"Google (GCP) Service-account":
|
|
24
|
+
"Google (GCP) Service-account": f'"type":{" "}"service_account"',
|
|
25
25
|
"Google OAuth Access Token": r"ya29\\.[0-9A-Za-z\\-_]+",
|
|
26
26
|
"Connection String": r"[a-zA-Z]+:\/\/[^/\s]+:[^/\s]+@[^/\s]+\/[^/\s]+",
|
|
27
27
|
}
|
|
@@ -195,10 +195,45 @@ class TestJQEntityProcessor:
|
|
|
195
195
|
pattern = ".foo"
|
|
196
196
|
with pytest.raises(
|
|
197
197
|
EntityProcessorException,
|
|
198
|
-
match="Expected boolean value, got <class 'str'> instead",
|
|
198
|
+
match="Expected boolean value, got value:bar of type: <class 'str'> instead",
|
|
199
199
|
):
|
|
200
200
|
await mocked_processor._search_as_bool(data, pattern)
|
|
201
201
|
|
|
202
|
+
@pytest.mark.parametrize(
|
|
203
|
+
"pattern, expected",
|
|
204
|
+
[
|
|
205
|
+
('.parameters[] | select(.name == "not_exists") | .value', None),
|
|
206
|
+
(
|
|
207
|
+
'.parameters[] | select(.name == "parameter_name") | .value',
|
|
208
|
+
"parameter_value",
|
|
209
|
+
),
|
|
210
|
+
(
|
|
211
|
+
'.parameters[] | select(.name == "another_parameter") | .value',
|
|
212
|
+
"another_value",
|
|
213
|
+
),
|
|
214
|
+
],
|
|
215
|
+
)
|
|
216
|
+
async def test_search_fails_on_stop_iteration(
|
|
217
|
+
self, mocked_processor: JQEntityProcessor, pattern: str, expected: Any
|
|
218
|
+
) -> None:
|
|
219
|
+
data = {
|
|
220
|
+
"parameters": [
|
|
221
|
+
{"name": "parameter_name", "value": "parameter_value"},
|
|
222
|
+
{"name": "another_parameter", "value": "another_value"},
|
|
223
|
+
{"name": "another_parameter", "value": "another_value2"},
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
result = await mocked_processor._search(data, pattern)
|
|
227
|
+
assert result == expected
|
|
228
|
+
|
|
229
|
+
async def test_return_a_list_of_values(
|
|
230
|
+
self, mocked_processor: JQEntityProcessor
|
|
231
|
+
) -> None:
|
|
232
|
+
data = {"parameters": ["parameter_value", "another_value", "another_value2"]}
|
|
233
|
+
pattern = ".parameters"
|
|
234
|
+
result = await mocked_processor._search(data, pattern)
|
|
235
|
+
assert result == ["parameter_value", "another_value", "another_value2"]
|
|
236
|
+
|
|
202
237
|
@pytest.mark.timeout(3)
|
|
203
238
|
async def test_search_performance_10000(
|
|
204
239
|
self, mocked_processor: JQEntityProcessor
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from os import path
|
|
2
|
+
from typing import Any, Callable, List, Tuple
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import pytest_asyncio
|
|
6
|
+
|
|
7
|
+
from port_ocean.clients.port.client import PortClient
|
|
8
|
+
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
9
|
+
from port_ocean.ocean import Ocean
|
|
10
|
+
from port_ocean.tests.helpers.ocean_app import (
|
|
11
|
+
get_integation_resource_configs,
|
|
12
|
+
get_integration_ocean_app,
|
|
13
|
+
)
|
|
14
|
+
from port_ocean.tests.helpers.smoke_test import (
|
|
15
|
+
SmokeTestDetails,
|
|
16
|
+
get_port_client_for_fake_integration,
|
|
17
|
+
get_smoke_test_details,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def port_client_for_fake_integration() -> Tuple[SmokeTestDetails, PortClient]:
|
|
23
|
+
smoke_test_details = get_smoke_test_details()
|
|
24
|
+
port_client = get_port_client_for_fake_integration()
|
|
25
|
+
|
|
26
|
+
return smoke_test_details, port_client
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest_asyncio.fixture
|
|
30
|
+
def get_mocked_ocean_app(request: Any) -> Callable[[], Ocean]:
|
|
31
|
+
test_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
32
|
+
|
|
33
|
+
def get_ocean_app() -> Ocean:
|
|
34
|
+
return get_integration_ocean_app(test_dir)
|
|
35
|
+
|
|
36
|
+
return get_ocean_app
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest_asyncio.fixture
|
|
40
|
+
def get_mock_ocean_resource_configs(request: Any) -> Callable[[], List[ResourceConfig]]:
|
|
41
|
+
module_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
42
|
+
|
|
43
|
+
def get_ocean_resource_configs() -> List[ResourceConfig]:
|
|
44
|
+
return get_integation_resource_configs(module_dir)
|
|
45
|
+
|
|
46
|
+
return get_ocean_resource_configs
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from port_ocean.clients.port.client import PortClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def cleanup_integration(client: PortClient, blueprints: List[str]) -> None:
|
|
9
|
+
for blueprint in blueprints:
|
|
10
|
+
try:
|
|
11
|
+
bp = await client.get_blueprint(blueprint)
|
|
12
|
+
if bp is not None:
|
|
13
|
+
migration_id = await client.delete_blueprint(
|
|
14
|
+
identifier=blueprint, delete_entities=True
|
|
15
|
+
)
|
|
16
|
+
if migration_id:
|
|
17
|
+
await client.wait_for_migration_to_complete(
|
|
18
|
+
migration_id=migration_id
|
|
19
|
+
)
|
|
20
|
+
except Exception as bp_e:
|
|
21
|
+
logger.info(f"Skipping missing blueprint ({blueprint}): {bp_e}")
|
|
22
|
+
headers = await client.auth.headers()
|
|
23
|
+
try:
|
|
24
|
+
await client.client.delete(
|
|
25
|
+
f"{client.auth.api_url}/integrations/{client.integration_identifier}",
|
|
26
|
+
headers=headers,
|
|
27
|
+
)
|
|
28
|
+
except Exception as int_e:
|
|
29
|
+
logger.info(
|
|
30
|
+
f"Failed to delete integration ({client.integration_identifier}): {int_e}"
|
|
31
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from port_ocean.clients.port.client import PortClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_port_client_for_integration(
|
|
7
|
+
client_id: str,
|
|
8
|
+
client_secret: str,
|
|
9
|
+
integration_identifier: str,
|
|
10
|
+
integration_type: str,
|
|
11
|
+
integration_version: str,
|
|
12
|
+
base_url: Union[str, None],
|
|
13
|
+
) -> PortClient:
|
|
14
|
+
return PortClient(
|
|
15
|
+
base_url=base_url or "https://api.getport/io",
|
|
16
|
+
client_id=client_id,
|
|
17
|
+
client_secret=client_secret,
|
|
18
|
+
integration_identifier=integration_identifier,
|
|
19
|
+
integration_type=integration_type,
|
|
20
|
+
integration_version=integration_version,
|
|
21
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from os import environ
|
|
2
|
+
from port_ocean.clients.port.client import PortClient
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from port_ocean.tests.helpers.integration import cleanup_integration
|
|
8
|
+
from port_ocean.tests.helpers.port_client import get_port_client_for_integration
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SmokeTestDetails(BaseModel):
|
|
12
|
+
integration_identifier: str
|
|
13
|
+
blueprint_department: str
|
|
14
|
+
blueprint_person: str
|
|
15
|
+
integration_type: str
|
|
16
|
+
integration_version: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_smoke_test_details() -> SmokeTestDetails:
|
|
20
|
+
blueprint_department = "fake-department"
|
|
21
|
+
blueprint_person = "fake-person"
|
|
22
|
+
integration_identifier = "smoke-test-integration"
|
|
23
|
+
smoke_test_suffix = environ.get("SMOKE_TEST_SUFFIX")
|
|
24
|
+
if smoke_test_suffix is not None:
|
|
25
|
+
integration_identifier = f"{integration_identifier}-{smoke_test_suffix}"
|
|
26
|
+
blueprint_person = f"{blueprint_person}-{smoke_test_suffix}"
|
|
27
|
+
blueprint_department = f"{blueprint_department}-{smoke_test_suffix}"
|
|
28
|
+
|
|
29
|
+
return SmokeTestDetails(
|
|
30
|
+
integration_identifier=integration_identifier,
|
|
31
|
+
blueprint_person=blueprint_person,
|
|
32
|
+
blueprint_department=blueprint_department,
|
|
33
|
+
integration_version="0.1.4-dev",
|
|
34
|
+
integration_type="smoke-test",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def cleanup_smoke_test() -> None:
|
|
39
|
+
smoke_test_details = get_smoke_test_details()
|
|
40
|
+
client_id = environ.get("PORT_CLIENT_ID")
|
|
41
|
+
client_secret = environ.get("PORT_CLIENT_SECRET")
|
|
42
|
+
|
|
43
|
+
if not client_secret or not client_id:
|
|
44
|
+
assert False, "Missing port credentials"
|
|
45
|
+
|
|
46
|
+
base_url = environ.get("PORT_BASE_URL")
|
|
47
|
+
client = get_port_client_for_integration(
|
|
48
|
+
client_id,
|
|
49
|
+
client_secret,
|
|
50
|
+
smoke_test_details.integration_identifier,
|
|
51
|
+
smoke_test_details.integration_type,
|
|
52
|
+
smoke_test_details.integration_version,
|
|
53
|
+
base_url,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
logger.info("Cleaning up fake integration")
|
|
57
|
+
await cleanup_integration(
|
|
58
|
+
client,
|
|
59
|
+
[smoke_test_details.blueprint_department, smoke_test_details.blueprint_person],
|
|
60
|
+
)
|
|
61
|
+
logger.info("Cleaning up fake integration complete")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_port_client_for_fake_integration() -> PortClient:
|
|
65
|
+
smoke_test_details = get_smoke_test_details()
|
|
66
|
+
client_id = environ.get("PORT_CLIENT_ID")
|
|
67
|
+
client_secret = environ.get("PORT_CLIENT_SECRET")
|
|
68
|
+
|
|
69
|
+
if not client_secret or not client_id:
|
|
70
|
+
assert False, "Missing port credentials"
|
|
71
|
+
|
|
72
|
+
base_url = environ.get("PORT_BASE_URL")
|
|
73
|
+
client = get_port_client_for_integration(
|
|
74
|
+
client_id,
|
|
75
|
+
client_secret,
|
|
76
|
+
smoke_test_details.integration_identifier,
|
|
77
|
+
smoke_test_details.integration_type,
|
|
78
|
+
smoke_test_details.integration_version,
|
|
79
|
+
base_url,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return client
|
|
@@ -4,7 +4,7 @@ import pytest
|
|
|
4
4
|
|
|
5
5
|
from port_ocean.clients.port.client import PortClient
|
|
6
6
|
from port_ocean.clients.port.types import UserAgentType
|
|
7
|
-
from port_ocean.tests.helpers.
|
|
7
|
+
from port_ocean.tests.helpers.smoke_test import SmokeTestDetails
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
pytestmark = pytest.mark.smoke
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Any, AsyncGenerator
|
|
2
|
+
import asyncio
|
|
3
|
+
from port_ocean.utils.async_iterators import semaphore_async_iterator
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.asyncio
|
|
8
|
+
async def test_semaphore_async_iterator() -> None:
|
|
9
|
+
max_concurrency = 5
|
|
10
|
+
semaphore = asyncio.BoundedSemaphore(max_concurrency)
|
|
11
|
+
|
|
12
|
+
concurrent_tasks = 0
|
|
13
|
+
max_concurrent_tasks = 0
|
|
14
|
+
lock = asyncio.Lock() # Protect shared variables
|
|
15
|
+
|
|
16
|
+
num_tasks = 20
|
|
17
|
+
|
|
18
|
+
async def mock_function() -> AsyncGenerator[str, None]:
|
|
19
|
+
nonlocal concurrent_tasks, max_concurrent_tasks
|
|
20
|
+
|
|
21
|
+
async with lock:
|
|
22
|
+
concurrent_tasks += 1
|
|
23
|
+
if concurrent_tasks > max_concurrent_tasks:
|
|
24
|
+
max_concurrent_tasks = concurrent_tasks
|
|
25
|
+
|
|
26
|
+
await asyncio.sleep(0.1)
|
|
27
|
+
yield "result"
|
|
28
|
+
|
|
29
|
+
async with lock:
|
|
30
|
+
concurrent_tasks -= 1
|
|
31
|
+
|
|
32
|
+
async def consume_iterator(async_iterator: Any) -> None:
|
|
33
|
+
async for _ in async_iterator:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
tasks = [
|
|
37
|
+
consume_iterator(semaphore_async_iterator(semaphore, mock_function))
|
|
38
|
+
for _ in range(num_tasks)
|
|
39
|
+
]
|
|
40
|
+
await asyncio.gather(*tasks)
|
|
41
|
+
|
|
42
|
+
assert (
|
|
43
|
+
max_concurrent_tasks <= max_concurrency
|
|
44
|
+
), f"Max concurrent tasks {max_concurrent_tasks} exceeded semaphore limit {max_concurrency}"
|
|
45
|
+
assert concurrent_tasks == 0, "Not all tasks have completed"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import aiostream
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from asyncio import Semaphore
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def stream_async_iterators_tasks(
|
|
10
|
+
*tasks: typing.AsyncIterable[typing.Any],
|
|
11
|
+
) -> typing.AsyncIterable[typing.Any]:
|
|
12
|
+
"""
|
|
13
|
+
This function takes a list of async iterators and streams the results of each iterator as they are available.
|
|
14
|
+
By using this function you can combine multiple async iterators into a single stream of results, instead of waiting
|
|
15
|
+
for each iterator to finish before starting the next one.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
```python
|
|
19
|
+
async def async_iterator1():
|
|
20
|
+
for i in range(10):
|
|
21
|
+
yield i
|
|
22
|
+
await asyncio.sleep(1)
|
|
23
|
+
|
|
24
|
+
async def async_iterator2():
|
|
25
|
+
for i in range(10, 20):
|
|
26
|
+
yield i
|
|
27
|
+
await asyncio.sleep(1)
|
|
28
|
+
|
|
29
|
+
async def main():
|
|
30
|
+
async for result in stream_async_iterators_tasks([async_iterator1(), async_iterator2()]):
|
|
31
|
+
print(result)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Caution - Before using this function, make sure that the third-party API you are calling allows the number of
|
|
35
|
+
concurrent requests you are making. If the API has a rate limit, you may need to adjust the number of concurrent
|
|
36
|
+
requests to avoid hitting the rate limit.
|
|
37
|
+
|
|
38
|
+
:param tasks: A list of async iterators
|
|
39
|
+
:return: A stream of results
|
|
40
|
+
"""
|
|
41
|
+
if not tasks:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
if len(tasks) == 1:
|
|
45
|
+
async for batch_items in tasks[0]:
|
|
46
|
+
yield batch_items
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
combine = aiostream.stream.merge(tasks[0], *tasks[1:])
|
|
50
|
+
async with combine.stream() as streamer:
|
|
51
|
+
async for batch_items in streamer:
|
|
52
|
+
yield batch_items
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def semaphore_async_iterator(
|
|
56
|
+
semaphore: "Semaphore",
|
|
57
|
+
function: typing.Callable[[], typing.AsyncIterator[typing.Any]],
|
|
58
|
+
) -> typing.AsyncIterator[typing.Any]:
|
|
59
|
+
"""
|
|
60
|
+
Executes an asynchronous iterator function under a semaphore to limit concurrency.
|
|
61
|
+
|
|
62
|
+
This function ensures that the provided asynchronous iterator function is executed
|
|
63
|
+
while respecting the concurrency limit imposed by the semaphore. It acquires the
|
|
64
|
+
semaphore before executing the function and releases it after the function completes,
|
|
65
|
+
thus controlling the number of concurrent executions.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
semaphore (asyncio.Semaphore | asyncio.BoundedSemaphore): The semaphore used to limit concurrency.
|
|
69
|
+
function (Callable[[], AsyncIterator[Any]]): A nullary asynchronous function, - apply arguments with `functools.partial` or an anonymous function (lambda)
|
|
70
|
+
that returns an asynchronous iterator. This function is executed under the semaphore.
|
|
71
|
+
|
|
72
|
+
Yields:
|
|
73
|
+
Any: The items yielded by the asynchronous iterator function.
|
|
74
|
+
|
|
75
|
+
Usage:
|
|
76
|
+
```python
|
|
77
|
+
import asyncio
|
|
78
|
+
|
|
79
|
+
async def async_iterator_function(param1, param2):
|
|
80
|
+
# Your async code here
|
|
81
|
+
yield ...
|
|
82
|
+
|
|
83
|
+
async def async_generator_function():
|
|
84
|
+
# Your async code to retrieve items
|
|
85
|
+
param1 = "your_param1"
|
|
86
|
+
yield param1
|
|
87
|
+
|
|
88
|
+
async def main():
|
|
89
|
+
semaphore = asyncio.BoundedSemaphore(50)
|
|
90
|
+
param2 = "your_param2"
|
|
91
|
+
|
|
92
|
+
tasks = [
|
|
93
|
+
semaphore_async_iterator(
|
|
94
|
+
semaphore,
|
|
95
|
+
lambda: async_iterator_function(param1, param2) # functools.partial(async_iterator_function, param1, param2)
|
|
96
|
+
)
|
|
97
|
+
async for param1 in async_generator_function()
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
async for batch in stream_async_iterators_tasks(*tasks):
|
|
101
|
+
# Process each batch
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
"""
|
|
107
|
+
async with semaphore:
|
|
108
|
+
async for result in function():
|
|
109
|
+
yield result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "port-ocean"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.12.2"
|
|
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"
|
|
@@ -176,3 +176,6 @@ exclude = '''
|
|
|
176
176
|
asyncio_mode = "auto"
|
|
177
177
|
asyncio_default_fixture_loop_scope = "function"
|
|
178
178
|
addopts = "-vv -n auto --durations=10 --color=yes --ignore-glob='./integrations/*' ./port_ocean/tests"
|
|
179
|
+
markers = [
|
|
180
|
+
"smoke: Smoke tests (deselect with '-m \"not smoke\"')"
|
|
181
|
+
]
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from os import environ, path
|
|
2
|
-
from typing import Any, AsyncGenerator, Callable, List, Tuple, Union
|
|
3
|
-
|
|
4
|
-
import pytest_asyncio
|
|
5
|
-
from loguru import logger
|
|
6
|
-
from pydantic import BaseModel
|
|
7
|
-
|
|
8
|
-
from port_ocean.clients.port.client import PortClient
|
|
9
|
-
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
10
|
-
from port_ocean.ocean import Ocean
|
|
11
|
-
from port_ocean.tests.helpers.ocean_app import (
|
|
12
|
-
get_integation_resource_configs,
|
|
13
|
-
get_integration_ocean_app,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_port_client_for_integration(
|
|
18
|
-
client_id: str,
|
|
19
|
-
client_secret: str,
|
|
20
|
-
integration_identifier: str,
|
|
21
|
-
integration_type: str,
|
|
22
|
-
integration_version: str,
|
|
23
|
-
base_url: Union[str, None],
|
|
24
|
-
) -> PortClient:
|
|
25
|
-
return PortClient(
|
|
26
|
-
base_url=base_url or "https://api.getport/io",
|
|
27
|
-
client_id=client_id,
|
|
28
|
-
client_secret=client_secret,
|
|
29
|
-
integration_identifier=integration_identifier,
|
|
30
|
-
integration_type=integration_type,
|
|
31
|
-
integration_version=integration_version,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
async def cleanup_integration(client: PortClient, blueprints: List[str]) -> None:
|
|
36
|
-
for blueprint in blueprints:
|
|
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}")
|
|
49
|
-
headers = await client.auth.headers()
|
|
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
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class SmokeTestDetails(BaseModel):
|
|
62
|
-
integration_identifier: str
|
|
63
|
-
blueprint_department: str
|
|
64
|
-
blueprint_person: str
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@pytest_asyncio.fixture()
|
|
68
|
-
async def port_client_for_fake_integration() -> (
|
|
69
|
-
AsyncGenerator[Tuple[SmokeTestDetails, PortClient], None]
|
|
70
|
-
):
|
|
71
|
-
blueprint_department = "fake-department"
|
|
72
|
-
blueprint_person = "fake-person"
|
|
73
|
-
integration_identifier = "smoke-test-integration"
|
|
74
|
-
smoke_test_suffix = environ.get("SMOKE_TEST_SUFFIX")
|
|
75
|
-
client_id = environ.get("PORT_CLIENT_ID")
|
|
76
|
-
client_secret = environ.get("PORT_CLIENT_SECRET")
|
|
77
|
-
|
|
78
|
-
if not client_secret or not client_id:
|
|
79
|
-
assert False, "Missing port credentials"
|
|
80
|
-
|
|
81
|
-
base_url = environ.get("PORT_BASE_URL")
|
|
82
|
-
integration_version = "0.1.1-dev"
|
|
83
|
-
integration_type = "smoke-test"
|
|
84
|
-
if smoke_test_suffix is not None:
|
|
85
|
-
integration_identifier = f"{integration_identifier}-{smoke_test_suffix}"
|
|
86
|
-
blueprint_person = f"{blueprint_person}-{smoke_test_suffix}"
|
|
87
|
-
blueprint_department = f"{blueprint_department}-{smoke_test_suffix}"
|
|
88
|
-
|
|
89
|
-
client = get_port_client_for_integration(
|
|
90
|
-
client_id,
|
|
91
|
-
client_secret,
|
|
92
|
-
integration_identifier,
|
|
93
|
-
integration_type,
|
|
94
|
-
integration_version,
|
|
95
|
-
base_url,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
smoke_test_details = SmokeTestDetails(
|
|
99
|
-
integration_identifier=integration_identifier,
|
|
100
|
-
blueprint_person=blueprint_person,
|
|
101
|
-
blueprint_department=blueprint_department,
|
|
102
|
-
)
|
|
103
|
-
yield smoke_test_details, client
|
|
104
|
-
await cleanup_integration(client, [blueprint_department, blueprint_person])
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@pytest_asyncio.fixture
|
|
108
|
-
def get_mocked_ocean_app(request: Any) -> Callable[[], Ocean]:
|
|
109
|
-
test_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
110
|
-
|
|
111
|
-
def get_ocean_app() -> Ocean:
|
|
112
|
-
return get_integration_ocean_app(test_dir)
|
|
113
|
-
|
|
114
|
-
return get_ocean_app
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@pytest_asyncio.fixture
|
|
118
|
-
def get_mock_ocean_resource_configs(request: Any) -> Callable[[], List[ResourceConfig]]:
|
|
119
|
-
module_dir = path.join(path.dirname(request.module.__file__), "..")
|
|
120
|
-
|
|
121
|
-
def get_ocean_resource_configs() -> List[ResourceConfig]:
|
|
122
|
-
return get_integation_resource_configs(module_dir)
|
|
123
|
-
|
|
124
|
-
return get_ocean_resource_configs
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
import aiostream
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async def stream_async_iterators_tasks(
|
|
7
|
-
*tasks: typing.AsyncIterable[typing.Any],
|
|
8
|
-
) -> typing.AsyncIterable[typing.Any]:
|
|
9
|
-
"""
|
|
10
|
-
This function takes a list of async iterators and streams the results of each iterator as they are available.
|
|
11
|
-
By using this function you can combine multiple async iterators into a single stream of results, instead of waiting
|
|
12
|
-
for each iterator to finish before starting the next one.
|
|
13
|
-
|
|
14
|
-
Usage:
|
|
15
|
-
```python
|
|
16
|
-
async def async_iterator1():
|
|
17
|
-
for i in range(10):
|
|
18
|
-
yield i
|
|
19
|
-
await asyncio.sleep(1)
|
|
20
|
-
|
|
21
|
-
async def async_iterator2():
|
|
22
|
-
for i in range(10, 20):
|
|
23
|
-
yield i
|
|
24
|
-
await asyncio.sleep(1)
|
|
25
|
-
|
|
26
|
-
async def main():
|
|
27
|
-
async for result in stream_async_iterators_tasks([async_iterator1(), async_iterator2()]):
|
|
28
|
-
print(result)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Caution - Before using this function, make sure that the third-party API you are calling allows the number of
|
|
32
|
-
concurrent requests you are making. If the API has a rate limit, you may need to adjust the number of concurrent
|
|
33
|
-
requests to avoid hitting the rate limit.
|
|
34
|
-
|
|
35
|
-
:param tasks: A list of async iterators
|
|
36
|
-
:return: A stream of results
|
|
37
|
-
"""
|
|
38
|
-
if not tasks:
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
if len(tasks) == 1:
|
|
42
|
-
async for batch_items in tasks[0]:
|
|
43
|
-
yield batch_items
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
combine = aiostream.stream.merge(tasks[0], *tasks[1:])
|
|
47
|
-
async with combine.stream() as streamer:
|
|
48
|
-
async for batch_items in streamer:
|
|
49
|
-
yield batch_items
|
|
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.11.0 → port_ocean-0.12.2}/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.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entities_state_applier/__init__.py
RENAMED
|
File without changes
|
{port_ocean-0.11.0 → port_ocean-0.12.2}/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.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/entity_processor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{port_ocean-0.11.0 → port_ocean-0.12.2}/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.11.0 → port_ocean-0.12.2}/port_ocean/core/handlers/resync_state_updater/__init__.py
RENAMED
|
File without changes
|
{port_ocean-0.11.0 → port_ocean-0.12.2}/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
|
|
File without changes
|
|
File without changes
|
{port_ocean-0.11.0 → port_ocean-0.12.2}/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
|