port-ocean 0.9.14__tar.gz → 0.10.1__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.9.14 → port_ocean-0.10.1}/PKG-INFO +1 -1
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/new.py +38 -4
- port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.env.example +2 -0
- port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/blueprints.json +41 -0
- port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/port-app-config.yml +16 -0
- port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +15 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md +1 -1
- port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CONTRIBUTING.md +7 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Makefile +3 -3
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore +1 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/main.py +16 -1
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/pyproject.toml +7 -3
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/client.py +17 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/mixins/entities.py +1 -1
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/config/settings.py +9 -8
- port_ocean-0.10.1/port_ocean/core/event_listener/base.py +84 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/event_listener/http.py +1 -1
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/event_listener/kafka.py +8 -4
- port_ocean-0.10.1/port_ocean/core/event_listener/once.py +159 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/event_listener/polling.py +14 -10
- port_ocean-0.10.1/port_ocean/core/handlers/resync_state_updater/__init__.py +5 -0
- port_ocean-0.10.1/port_ocean/core/handlers/resync_state_updater/updater.py +84 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/models.py +5 -2
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/utils.py +3 -2
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/ocean.py +25 -7
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/misc.py +7 -1
- port_ocean-0.10.1/port_ocean/utils/time.py +54 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/pyproject.toml +4 -3
- port_ocean-0.9.14/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml +0 -15
- port_ocean-0.9.14/port_ocean/core/event_listener/base.py +0 -47
- port_ocean-0.9.14/port_ocean/core/event_listener/once.py +0 -64
- {port_ocean-0.9.14 → port_ocean-0.10.1}/LICENSE.md +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/README.md +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/bootstrap.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cli.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/defaults/__init___.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/defaults/clean.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/defaults/dock.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/defaults/group.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/list_integrations.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/main.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/pull.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/sail.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/commands/version.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/cookiecutter.json +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/extensions.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/hooks/post_gen_project.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.dockerignore +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.gitignore +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.gitignore +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/debug.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/poetry.toml +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/sonar-project.properties +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/tests/test_sample.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/cli/utils.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/authentication.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/mixins/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/mixins/blueprints.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/mixins/integrations.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/mixins/migrations.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/retry_transport.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/types.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/clients/port/utils.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/config/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/config/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/config/dynamic.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/consumers/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/consumers/kafka_consumer.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/context/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/context/event.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/context/ocean.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/context/resource.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/defaults/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/defaults/clean.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/defaults/common.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/defaults/initialize.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/event_listener/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/event_listener/factory.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/port/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/port/applier.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entity_processor/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entity_processor/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/entity_processor/jq_entity_processor.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/port_app_config/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/port_app_config/api.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/port_app_config/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/handlers/port_app_config/models.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/events.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/handler.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/sync.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/sync_raw.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/integrations/mixins/utils.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/core/ocean_types.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/api.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/base.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/clients.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/context.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/core.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/port_defaults.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/exceptions/utils.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/helpers/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/helpers/async_client.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/helpers/retry.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/log/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/log/handlers.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/log/logger_setup.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/log/sensetive.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/middlewares.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/py.typed +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/run.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/sonar-project.properties +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/tests/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/tests/test_sample.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/__init__.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/async_http.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/async_iterators.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/cache.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/queue_utils.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/repeat.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/utils/signal.py +0 -0
- {port_ocean-0.9.14 → port_ocean-0.10.1}/port_ocean/version.py +0 -0
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
|
+
import json
|
|
4
5
|
from cookiecutter.main import cookiecutter # type: ignore
|
|
6
|
+
import os
|
|
5
7
|
|
|
6
8
|
from port_ocean.cli.commands.main import cli_start, print_logo, console
|
|
7
9
|
from port_ocean.cli.utils import cli_root_path
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
def add_vscode_configuration(result: str, name: str) -> None:
|
|
13
|
+
vscode_entry_root_path = "${workspaceFolder}/integrations/" + name
|
|
14
|
+
new_vscode_entry = {
|
|
15
|
+
"console": "integratedTerminal",
|
|
16
|
+
"cwd": vscode_entry_root_path,
|
|
17
|
+
"envFile": f"{vscode_entry_root_path}/.env",
|
|
18
|
+
"justMyCode": True,
|
|
19
|
+
"name": f"Run {name} integration",
|
|
20
|
+
"program": f"{vscode_entry_root_path}/debug.py",
|
|
21
|
+
"python": f"{vscode_entry_root_path}/.venv/bin/python",
|
|
22
|
+
"request": "launch",
|
|
23
|
+
"type": "debugpy",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
vs_code_json_path = os.path.join(os.path.dirname(result), "../.vscode/launch.json")
|
|
27
|
+
if not os.path.exists(vs_code_json_path):
|
|
28
|
+
return
|
|
29
|
+
vs_code_json = json.load(open(vs_code_json_path, "r"))
|
|
30
|
+
vs_code_json["configurations"].append(new_vscode_entry)
|
|
31
|
+
|
|
32
|
+
with open(vs_code_json_path, "w") as vs_code_json_file:
|
|
33
|
+
json.dump(vs_code_json, vs_code_json_file, indent=2)
|
|
34
|
+
vs_code_json_file.write("\n")
|
|
35
|
+
|
|
36
|
+
|
|
10
37
|
@cli_start.command()
|
|
11
38
|
@click.argument("path", default=".", type=click.Path(exists=True))
|
|
12
39
|
@click.option(
|
|
@@ -37,6 +64,9 @@ def new(path: str, is_private_integration: bool) -> None:
|
|
|
37
64
|
)
|
|
38
65
|
name = result.split("/")[-1]
|
|
39
66
|
|
|
67
|
+
if not is_private_integration:
|
|
68
|
+
add_vscode_configuration(result, name)
|
|
69
|
+
|
|
40
70
|
console.print(
|
|
41
71
|
"\n🌊 Ahoy, Captain! Your project is ready to set sail into the vast ocean of possibilities!",
|
|
42
72
|
style="bold",
|
|
@@ -47,10 +77,14 @@ def new(path: str, is_private_integration: bool) -> None:
|
|
|
47
77
|
f"▶️ [bold][blue]cd {path}/{name} && make install && . .venv/bin/activate[/blue][/bold]\n"
|
|
48
78
|
)
|
|
49
79
|
console.print(
|
|
50
|
-
"⚓️
|
|
51
|
-
f"▶️ [bold][blue]ocean sail {path}/{name}[/blue][/bold] \n"
|
|
80
|
+
f"⚓️ Copy example env file: Run [bold][blue]cp {path}/{name}.env.example {path}/{name}/.env [/blue][/bold] and set your port credentials in the created file.\n"
|
|
52
81
|
)
|
|
53
82
|
console.print(
|
|
54
|
-
"⚓️
|
|
55
|
-
f"▶️ [bold][blue]
|
|
83
|
+
"⚓️ Set sail with [blue]Ocean[/blue]: Run [bold][blue]ocean sail[/blue] <path_to_integration>[/bold] to run the project using Ocean.\n"
|
|
84
|
+
f"▶️ [bold][blue]ocean sail {path}/{name}[/blue][/bold] \n"
|
|
56
85
|
)
|
|
86
|
+
if not is_private_integration:
|
|
87
|
+
console.print(
|
|
88
|
+
"⚓️ Smooth sailing with [blue]Make[/blue]: Alternatively, you can run [bold][blue]make run[/blue][/bold] to launch your project using Make. \n"
|
|
89
|
+
f"▶️ [bold][blue]make run {path}/{name}[/blue][/bold]"
|
|
90
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"identifier": "{{ cookiecutter.integration_slug }}ExampleBlueprint",
|
|
4
|
+
"title": "{{ cookiecutter.integration_name }} Example",
|
|
5
|
+
"icon": "Blueprint",
|
|
6
|
+
"schema": {
|
|
7
|
+
"properties": {
|
|
8
|
+
"status": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": [
|
|
11
|
+
"VALID",
|
|
12
|
+
"FAILED"
|
|
13
|
+
],
|
|
14
|
+
"enumColors": {
|
|
15
|
+
"VALID": "green",
|
|
16
|
+
"FAILED": "red"
|
|
17
|
+
},
|
|
18
|
+
"title": "Status"
|
|
19
|
+
},
|
|
20
|
+
"text": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"title": "Text"
|
|
23
|
+
},
|
|
24
|
+
"component": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"title": "Component"
|
|
27
|
+
},
|
|
28
|
+
"service": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"title": "Service"
|
|
31
|
+
},
|
|
32
|
+
"score": {
|
|
33
|
+
"type": "number",
|
|
34
|
+
"title": "Score"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"required": []
|
|
38
|
+
},
|
|
39
|
+
"relations": {}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
resources:
|
|
2
|
+
- kind: {{ cookiecutter.integration_slug}}-example-kind
|
|
3
|
+
selector:
|
|
4
|
+
query: 'true'
|
|
5
|
+
port:
|
|
6
|
+
entity:
|
|
7
|
+
mappings:
|
|
8
|
+
identifier: .my_custom_id
|
|
9
|
+
title: '(.my_component + " @ " + .my_service)'
|
|
10
|
+
blueprint: '"{{ cookiecutter.integration_slug }}ExampleBlueprint"'
|
|
11
|
+
properties:
|
|
12
|
+
status: .my_enum
|
|
13
|
+
text: .my_custom_text
|
|
14
|
+
component: .my_component
|
|
15
|
+
service: .my_service
|
|
16
|
+
score: .my_special_score
|
port_ocean-0.10.1/port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
description: {{cookiecutter.integration_name}} integration for Port Ocean
|
|
2
|
+
icon: Cookiecutter # Should be one of the available icons in Port
|
|
3
|
+
features:
|
|
4
|
+
- type: exporter
|
|
5
|
+
section: Under Development # Should be one of the available sections in Port
|
|
6
|
+
resources:
|
|
7
|
+
- kind: {{ cookiecutter.integration_slug }}-example-kind
|
|
8
|
+
# - kind: <ResourceName2>
|
|
9
|
+
configurations:
|
|
10
|
+
- name: my{{ cookiecutter.integration_slug}}Token
|
|
11
|
+
# required: true
|
|
12
|
+
type: string
|
|
13
|
+
sensitive: true
|
|
14
|
+
- name: someApplicationUrl
|
|
15
|
+
type: url
|
|
@@ -55,8 +55,8 @@ lint:
|
|
|
55
55
|
run:
|
|
56
56
|
$(ACTIVATE) && ocean sail
|
|
57
57
|
|
|
58
|
-
test:
|
|
59
|
-
$(ACTIVATE) && poetry run pytest
|
|
58
|
+
test:
|
|
59
|
+
$(ACTIVATE) && poetry run pytest -n auto
|
|
60
60
|
|
|
61
61
|
clean:
|
|
62
62
|
@find . -name '.venv' -type d -exec rm -rf {} \;
|
|
@@ -71,4 +71,4 @@ clean:
|
|
|
71
71
|
rm -rf htmlcov
|
|
72
72
|
rm -rf .tox/
|
|
73
73
|
rm -rf docs/_build
|
|
74
|
-
rm -rf dist/
|
|
74
|
+
rm -rf dist/
|
|
@@ -15,6 +15,21 @@ async def on_resync(kind: str) -> list[dict[Any, Any]]:
|
|
|
15
15
|
# return [{"some_project_key": "someProjectValue", ...}]
|
|
16
16
|
# if kind == "issues":
|
|
17
17
|
# return [{"some_issue_key": "someIssueValue", ...}]
|
|
18
|
+
|
|
19
|
+
# Initial stub to show complete flow, replace this with your own logic
|
|
20
|
+
if kind == "{{ cookiecutter.integration_slug }}-example-kind":
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
"my_custom_id": f"id_{x}",
|
|
24
|
+
"my_custom_text": f"very long text with {x} in it",
|
|
25
|
+
"my_special_score": x * 32 % 3,
|
|
26
|
+
"my_component": f"component-{x}",
|
|
27
|
+
"my_service": f"service-{x %2}",
|
|
28
|
+
"my_enum": "VALID" if x % 2 == 0 else "FAILED",
|
|
29
|
+
}
|
|
30
|
+
for x in range(25)
|
|
31
|
+
]
|
|
32
|
+
|
|
18
33
|
return []
|
|
19
34
|
|
|
20
35
|
|
|
@@ -38,4 +53,4 @@ async def on_resync(kind: str) -> list[dict[Any, Any]]:
|
|
|
38
53
|
async def on_start() -> None:
|
|
39
54
|
# Something to do when the integration starts
|
|
40
55
|
# For example create a client to query 3rd party services - GitHub, Jira, etc...
|
|
41
|
-
print("Starting integration")
|
|
56
|
+
print("Starting {{ cookiecutter.integration_slug }} integration")
|
|
@@ -9,13 +9,17 @@ python = "^3.11"
|
|
|
9
9
|
port_ocean = { version = "^{% version %}", extras = ["cli"] }
|
|
10
10
|
|
|
11
11
|
[tool.poetry.group.dev.dependencies]
|
|
12
|
+
# Uncomment this if you want to debug the ocean core together with your integration
|
|
13
|
+
# port_ocean = { path = '../../', develop = true, extras = ['all'] }
|
|
12
14
|
pytest = "^7.2"
|
|
15
|
+
pytest-xdist = "^3.6.1"
|
|
16
|
+
pre-commit = "^3.7.1"
|
|
17
|
+
requests = "^2.32.3"
|
|
13
18
|
black = "^23.3.0"
|
|
14
19
|
mypy = "^1.3.0"
|
|
15
20
|
ruff = "^0.0.278"
|
|
16
21
|
pylint = "^2.17.4"
|
|
17
22
|
towncrier = "^23.6.0"
|
|
18
|
-
pytest-xdist = "^3.6.1"
|
|
19
23
|
|
|
20
24
|
[tool.towncrier]
|
|
21
25
|
directory = "changelog"
|
|
@@ -54,8 +58,8 @@ underlines = [""]
|
|
|
54
58
|
showcontent = true
|
|
55
59
|
|
|
56
60
|
[build-system]
|
|
57
|
-
requires = ["poetry>=0.
|
|
58
|
-
build-backend = "poetry.masonry.api"
|
|
61
|
+
requires = ["poetry-core>=1.0.0"]
|
|
62
|
+
build-backend = "poetry.core.masonry.api"
|
|
59
63
|
|
|
60
64
|
[tool.mypy]
|
|
61
65
|
exclude = [
|
|
@@ -13,6 +13,7 @@ from port_ocean.clients.port.utils import (
|
|
|
13
13
|
get_internal_http_client,
|
|
14
14
|
)
|
|
15
15
|
from port_ocean.exceptions.clients import KafkaCredentialsNotFound
|
|
16
|
+
from typing import Any
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class PortClient(
|
|
@@ -75,3 +76,19 @@ class PortClient(
|
|
|
75
76
|
handle_status_code(response)
|
|
76
77
|
|
|
77
78
|
return response.json()["organization"]["id"]
|
|
79
|
+
|
|
80
|
+
async def update_integration_state(
|
|
81
|
+
self, state: dict[str, Any], should_raise: bool = True, should_log: bool = True
|
|
82
|
+
) -> dict[str, Any]:
|
|
83
|
+
if should_log:
|
|
84
|
+
logger.debug(f"Updating integration resync state with: {state}")
|
|
85
|
+
response = await self.client.patch(
|
|
86
|
+
f"{self.api_url}/integration/{self.integration_identifier}/resync-state",
|
|
87
|
+
headers=await self.auth.headers(),
|
|
88
|
+
json=state,
|
|
89
|
+
)
|
|
90
|
+
handle_status_code(response, should_raise, should_log)
|
|
91
|
+
if response.is_success and should_log:
|
|
92
|
+
logger.info("Integration resync state updated successfully")
|
|
93
|
+
|
|
94
|
+
return response.json().get("integration", {})
|
|
@@ -58,7 +58,7 @@ class EntityClientMixin:
|
|
|
58
58
|
)
|
|
59
59
|
handle_status_code(response, should_raise)
|
|
60
60
|
result = response.json()
|
|
61
|
-
result_entity = Entity.parse_obj(result)
|
|
61
|
+
result_entity = Entity.parse_obj(result["entity"])
|
|
62
62
|
# Set the results of the search relation and identifier to the entity
|
|
63
63
|
entity.identifier = result_entity.identifier or entity.identifier
|
|
64
64
|
entity.relations = result_entity.relations or entity.relations
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Literal, Type
|
|
1
|
+
from typing import Any, Literal, Type, cast
|
|
2
2
|
|
|
3
3
|
from pydantic import Extra, AnyHttpUrl, parse_obj_as, parse_raw_as
|
|
4
4
|
from pydantic.class_validators import root_validator, validator
|
|
@@ -55,9 +55,8 @@ class IntegrationSettings(BaseOceanModel, extra=Extra.allow):
|
|
|
55
55
|
integ_type = get_integration_name()
|
|
56
56
|
|
|
57
57
|
values["type"] = integ_type.lower() if integ_type else None
|
|
58
|
-
|
|
59
|
-
"identifier"
|
|
60
|
-
)
|
|
58
|
+
if not values.get("identifier"):
|
|
59
|
+
values["identifier"] = f"my-{integ_type}-integration".lower()
|
|
61
60
|
|
|
62
61
|
return values
|
|
63
62
|
|
|
@@ -71,12 +70,14 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
|
71
70
|
client_timeout: int = 30
|
|
72
71
|
send_raw_data_examples: bool = True
|
|
73
72
|
port: PortSettings
|
|
74
|
-
event_listener: EventListenerSettingsType
|
|
73
|
+
event_listener: EventListenerSettingsType = Field(
|
|
74
|
+
default=cast(EventListenerSettingsType, {"type": "POLLING"})
|
|
75
|
+
)
|
|
75
76
|
# If an identifier or type is not provided, it will be generated based on the integration name
|
|
76
77
|
integration: IntegrationSettings = Field(
|
|
77
78
|
default_factory=lambda: IntegrationSettings(type="", identifier="")
|
|
78
79
|
)
|
|
79
|
-
runtime: Runtime =
|
|
80
|
+
runtime: Runtime = Runtime.OnPrem
|
|
80
81
|
|
|
81
82
|
@root_validator()
|
|
82
83
|
def validate_integration_config(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -100,8 +101,8 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
|
|
|
100
101
|
return values
|
|
101
102
|
|
|
102
103
|
@validator("runtime")
|
|
103
|
-
def validate_runtime(cls, runtime:
|
|
104
|
-
if runtime ==
|
|
104
|
+
def validate_runtime(cls, runtime: Runtime) -> Runtime:
|
|
105
|
+
if runtime == Runtime.Saas:
|
|
105
106
|
spec = get_spec_file()
|
|
106
107
|
if spec is None:
|
|
107
108
|
raise ValueError(
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import TypedDict, Callable, Any, Awaitable
|
|
3
|
+
|
|
4
|
+
from pydantic import Extra
|
|
5
|
+
|
|
6
|
+
from port_ocean.config.base import BaseOceanModel
|
|
7
|
+
from port_ocean.utils.signal import signal_handler
|
|
8
|
+
from port_ocean.context.ocean import ocean
|
|
9
|
+
from port_ocean.utils.misc import IntegrationStateStatus
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EventListenerEvents(TypedDict):
|
|
13
|
+
"""
|
|
14
|
+
A dictionary containing event types and their corresponding event handlers.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
on_resync: Callable[[dict[Any, Any]], Awaitable[None]]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseEventListener:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
events: EventListenerEvents,
|
|
24
|
+
):
|
|
25
|
+
self.events = events
|
|
26
|
+
|
|
27
|
+
async def start(self) -> None:
|
|
28
|
+
signal_handler.register(self._stop)
|
|
29
|
+
await self._start()
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def _start(self) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def _stop(self) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Can be used for event listeners that need cleanup before exiting.
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
async def _before_resync(self) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Can be used for event listeners that need to perform some action before resync.
|
|
44
|
+
"""
|
|
45
|
+
await ocean.app.resync_state_updater.update_before_resync()
|
|
46
|
+
|
|
47
|
+
async def _after_resync(self) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Can be used for event listeners that need to perform some action after resync.
|
|
50
|
+
"""
|
|
51
|
+
await ocean.app.resync_state_updater.update_after_resync()
|
|
52
|
+
|
|
53
|
+
async def _on_resync_failure(self, e: Exception) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Can be used for event listeners that need to handle resync failures.
|
|
56
|
+
"""
|
|
57
|
+
await ocean.app.resync_state_updater.update_after_resync(
|
|
58
|
+
IntegrationStateStatus.Failed
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def _resync(
|
|
62
|
+
self,
|
|
63
|
+
resync_args: dict[Any, Any],
|
|
64
|
+
) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Triggers the "on_resync" event.
|
|
67
|
+
"""
|
|
68
|
+
await self._before_resync()
|
|
69
|
+
try:
|
|
70
|
+
await self.events["on_resync"](resync_args)
|
|
71
|
+
await self._after_resync()
|
|
72
|
+
except Exception as e:
|
|
73
|
+
await self._on_resync_failure(e)
|
|
74
|
+
raise e
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EventListenerSettings(BaseOceanModel, extra=Extra.allow):
|
|
78
|
+
type: str
|
|
79
|
+
|
|
80
|
+
def to_request(self) -> dict[str, Any]:
|
|
81
|
+
"""
|
|
82
|
+
Converts the Settings object to a dictionary representation (request format).
|
|
83
|
+
"""
|
|
84
|
+
return {"type": self.type}
|
|
@@ -99,9 +99,13 @@ class KafkaEventListener(BaseEventListener):
|
|
|
99
99
|
return False
|
|
100
100
|
|
|
101
101
|
integration_identifier = after.get("identifier")
|
|
102
|
-
if integration_identifier
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
if integration_identifier != self.integration_identifier:
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
if after.get("updatedAt") == after.get("resyncState", {}).get("updatedAt"):
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
if "change.log" in topic:
|
|
105
109
|
return msg_value.get("changelogDestination", {}).get("type", "") == "KAFKA"
|
|
106
110
|
|
|
107
111
|
return False
|
|
@@ -122,7 +126,7 @@ class KafkaEventListener(BaseEventListener):
|
|
|
122
126
|
|
|
123
127
|
if "change.log" in topic and message is not None:
|
|
124
128
|
try:
|
|
125
|
-
await self.
|
|
129
|
+
await self._resync(message)
|
|
126
130
|
except Exception as e:
|
|
127
131
|
_type, _, tb = sys.exc_info()
|
|
128
132
|
logger.opt(exception=(_type, None, tb)).error(
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import signal
|
|
3
|
+
from typing import Literal, Any
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
|
|
7
|
+
from port_ocean.core.event_listener.base import (
|
|
8
|
+
BaseEventListener,
|
|
9
|
+
EventListenerEvents,
|
|
10
|
+
EventListenerSettings,
|
|
11
|
+
)
|
|
12
|
+
from port_ocean.utils.repeat import repeat_every
|
|
13
|
+
from port_ocean.context.ocean import ocean
|
|
14
|
+
from port_ocean.utils.time import convert_str_to_utc_datetime, convert_to_minutes
|
|
15
|
+
from port_ocean.utils.misc import IntegrationStateStatus
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OnceEventListenerSettings(EventListenerSettings):
|
|
19
|
+
"""
|
|
20
|
+
Once event listener configuration settings.
|
|
21
|
+
This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
type: Literal["ONCE"]
|
|
25
|
+
|
|
26
|
+
def to_request(self) -> dict[str, Any]:
|
|
27
|
+
return {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OnceEventListener(BaseEventListener):
|
|
31
|
+
"""
|
|
32
|
+
Once event listener.
|
|
33
|
+
|
|
34
|
+
This event listener is used to trigger a resync immediately.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
events (EventListenerEvents): A dictionary containing event types and their corresponding event handlers.
|
|
38
|
+
event_listener_config (OnceEventListenerSettings): The event listener configuration settings.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
events: EventListenerEvents,
|
|
44
|
+
event_listener_config: OnceEventListenerSettings,
|
|
45
|
+
):
|
|
46
|
+
super().__init__(events)
|
|
47
|
+
self.event_listener_config = event_listener_config
|
|
48
|
+
self.cached_integration: dict[str, Any] | None = None
|
|
49
|
+
|
|
50
|
+
async def get_current_integration_cached(self) -> dict[str, Any]:
|
|
51
|
+
if self.cached_integration:
|
|
52
|
+
return self.cached_integration
|
|
53
|
+
|
|
54
|
+
self.cached_integration = await ocean.port_client.get_current_integration()
|
|
55
|
+
return self.cached_integration
|
|
56
|
+
|
|
57
|
+
async def get_saas_resync_initialization_and_interval(
|
|
58
|
+
self,
|
|
59
|
+
) -> tuple[int | None, datetime.datetime | None]:
|
|
60
|
+
"""
|
|
61
|
+
Get the scheduled resync interval and the last updated time of the integration config for the saas application.
|
|
62
|
+
interval is the saas configured resync interval time.
|
|
63
|
+
start_time is the last updated time of the integration config.
|
|
64
|
+
return: (interval, start_time)
|
|
65
|
+
"""
|
|
66
|
+
if not ocean.app.is_saas():
|
|
67
|
+
return (None, None)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
integration = await self.get_current_integration_cached()
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.exception(f"Error occurred while getting current integration {e}")
|
|
73
|
+
return (None, None)
|
|
74
|
+
|
|
75
|
+
interval_str = (
|
|
76
|
+
integration.get("spec", {})
|
|
77
|
+
.get("appSpec", {})
|
|
78
|
+
.get("scheduledResyncInterval")
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if not interval_str:
|
|
82
|
+
logger.error(
|
|
83
|
+
"Unexpected: scheduledResyncInterval not found for Saas integration, Cannot predict the next resync"
|
|
84
|
+
)
|
|
85
|
+
return (None, None)
|
|
86
|
+
|
|
87
|
+
last_updated_saas_integration_config_str = integration.get(
|
|
88
|
+
"statusInfo", {}
|
|
89
|
+
).get("updatedAt")
|
|
90
|
+
|
|
91
|
+
# we use the last updated time of the integration config as the start time since in saas application the interval is configured by the user from the portal
|
|
92
|
+
if not last_updated_saas_integration_config_str:
|
|
93
|
+
logger.error(
|
|
94
|
+
"Unexpected: updatedAt not found for Saas integration, Cannot predict the next resync"
|
|
95
|
+
)
|
|
96
|
+
return (None, None)
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
convert_to_minutes(interval_str),
|
|
100
|
+
convert_str_to_utc_datetime(last_updated_saas_integration_config_str),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def _before_resync(self) -> None:
|
|
104
|
+
if not ocean.app.is_saas():
|
|
105
|
+
# in case of non-saas, we still want to update the state before and after the resync
|
|
106
|
+
await super()._before_resync()
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
(interval, start_time) = (
|
|
110
|
+
await self.get_saas_resync_initialization_and_interval()
|
|
111
|
+
)
|
|
112
|
+
await ocean.app.resync_state_updater.update_before_resync(interval, start_time)
|
|
113
|
+
|
|
114
|
+
async def _after_resync(self) -> None:
|
|
115
|
+
if not ocean.app.is_saas():
|
|
116
|
+
# in case of non-saas, we still want to update the state before and after the resync
|
|
117
|
+
await super()._after_resync()
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
(interval, start_time) = (
|
|
121
|
+
await self.get_saas_resync_initialization_and_interval()
|
|
122
|
+
)
|
|
123
|
+
await ocean.app.resync_state_updater.update_after_resync(
|
|
124
|
+
IntegrationStateStatus.Completed, interval, start_time
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
async def _on_resync_failure(self, e: Exception) -> None:
|
|
128
|
+
if not ocean.app.is_saas():
|
|
129
|
+
# in case of non-saas, we still want to update the state before and after the resync
|
|
130
|
+
await super()._after_resync()
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
(interval, start_time) = (
|
|
134
|
+
await self.get_saas_resync_initialization_and_interval()
|
|
135
|
+
)
|
|
136
|
+
await ocean.app.resync_state_updater.update_after_resync(
|
|
137
|
+
IntegrationStateStatus.Failed, interval, start_time
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
async def _start(self) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Starts the resync process, and exits the application once finished.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
# we use the `repeat_every` decorator to make sure the resync will be triggered, but won't stuck the application
|
|
146
|
+
# from finishing the startup process which is required to close the application gracefully
|
|
147
|
+
@repeat_every(seconds=0, max_repetitions=1)
|
|
148
|
+
async def resync_and_exit() -> None:
|
|
149
|
+
logger.info("Once event listener started")
|
|
150
|
+
try:
|
|
151
|
+
await self._resync({})
|
|
152
|
+
except Exception:
|
|
153
|
+
# we catch all exceptions here to make sure the application will exit gracefully
|
|
154
|
+
logger.exception("Error occurred while resyncing")
|
|
155
|
+
logger.info("Once event listener finished")
|
|
156
|
+
logger.info("Exiting application")
|
|
157
|
+
signal.raise_signal(signal.SIGINT)
|
|
158
|
+
|
|
159
|
+
await resync_and_exit()
|
|
@@ -49,7 +49,16 @@ class PollingEventListener(BaseEventListener):
|
|
|
49
49
|
):
|
|
50
50
|
super().__init__(events)
|
|
51
51
|
self.event_listener_config = event_listener_config
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
def should_resync(self, last_updated_at: str) -> bool:
|
|
54
|
+
_last_updated_at = (
|
|
55
|
+
ocean.app.resync_state_updater.last_integration_state_updated_at
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if _last_updated_at is None:
|
|
59
|
+
return self.event_listener_config.resync_on_start
|
|
60
|
+
|
|
61
|
+
return _last_updated_at != last_updated_at
|
|
53
62
|
|
|
54
63
|
async def _start(self) -> None:
|
|
55
64
|
"""
|
|
@@ -69,17 +78,12 @@ class PollingEventListener(BaseEventListener):
|
|
|
69
78
|
integration = await ocean.app.port_client.get_current_integration()
|
|
70
79
|
last_updated_at = integration["updatedAt"]
|
|
71
80
|
|
|
72
|
-
should_resync
|
|
73
|
-
self._last_updated_at is not None
|
|
74
|
-
or self.event_listener_config.resync_on_start
|
|
75
|
-
) and self._last_updated_at != last_updated_at
|
|
76
|
-
|
|
77
|
-
if should_resync:
|
|
81
|
+
if self.should_resync(last_updated_at):
|
|
78
82
|
logger.info("Detected change in integration, resyncing")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self.events["on_resync"]({}) # type: ignore
|
|
83
|
+
ocean.app.resync_state_updater.last_integration_state_updated_at = (
|
|
84
|
+
last_updated_at
|
|
82
85
|
)
|
|
86
|
+
running_task: Task[Any] = get_event_loop().create_task(self._resync({}))
|
|
83
87
|
signal_handler.register(running_task.cancel)
|
|
84
88
|
|
|
85
89
|
await running_task
|