port-ocean 0.28.2__py3-none-any.whl → 0.29.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- integrations/_infra/Dockerfile.Deb +6 -1
- integrations/_infra/Dockerfile.local +1 -0
- port_ocean/clients/port/authentication.py +19 -0
- port_ocean/clients/port/client.py +3 -0
- port_ocean/clients/port/mixins/actions.py +93 -0
- port_ocean/clients/port/mixins/blueprints.py +0 -12
- port_ocean/clients/port/mixins/entities.py +79 -44
- port_ocean/clients/port/mixins/integrations.py +7 -2
- port_ocean/config/settings.py +35 -3
- port_ocean/context/ocean.py +7 -5
- port_ocean/core/defaults/initialize.py +12 -5
- port_ocean/core/event_listener/__init__.py +7 -0
- port_ocean/core/event_listener/actions_only.py +42 -0
- port_ocean/core/event_listener/base.py +4 -1
- port_ocean/core/event_listener/factory.py +18 -9
- port_ocean/core/event_listener/http.py +4 -3
- port_ocean/core/event_listener/kafka.py +3 -2
- port_ocean/core/event_listener/once.py +5 -2
- port_ocean/core/event_listener/polling.py +4 -3
- port_ocean/core/event_listener/webhooks_only.py +3 -2
- port_ocean/core/handlers/actions/__init__.py +7 -0
- port_ocean/core/handlers/actions/abstract_executor.py +150 -0
- port_ocean/core/handlers/actions/execution_manager.py +434 -0
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py +479 -17
- port_ocean/core/handlers/entity_processor/jq_input_evaluator.py +137 -0
- port_ocean/core/handlers/port_app_config/models.py +4 -2
- port_ocean/core/handlers/resync_state_updater/updater.py +4 -2
- port_ocean/core/handlers/webhook/abstract_webhook_processor.py +16 -0
- port_ocean/core/handlers/webhook/processor_manager.py +30 -12
- port_ocean/core/integrations/mixins/sync_raw.py +10 -5
- port_ocean/core/integrations/mixins/utils.py +250 -29
- port_ocean/core/models.py +35 -2
- port_ocean/core/utils/utils.py +16 -5
- port_ocean/exceptions/execution_manager.py +22 -0
- port_ocean/helpers/metric/metric.py +1 -1
- port_ocean/helpers/retry.py +4 -40
- port_ocean/log/logger_setup.py +2 -2
- port_ocean/ocean.py +31 -5
- port_ocean/tests/clients/port/mixins/test_entities.py +71 -5
- port_ocean/tests/core/event_listener/test_kafka.py +14 -7
- port_ocean/tests/core/handlers/actions/test_execution_manager.py +837 -0
- port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +932 -1
- port_ocean/tests/core/handlers/entity_processor/test_jq_input_evaluator.py +932 -0
- port_ocean/tests/core/handlers/webhook/test_processor_manager.py +3 -1
- port_ocean/tests/core/utils/test_get_port_diff.py +164 -0
- port_ocean/tests/helpers/test_retry.py +241 -1
- port_ocean/tests/utils/test_cache.py +240 -0
- port_ocean/utils/cache.py +45 -9
- {port_ocean-0.28.2.dist-info → port_ocean-0.29.0.dist-info}/METADATA +2 -1
- {port_ocean-0.28.2.dist-info → port_ocean-0.29.0.dist-info}/RECORD +53 -43
- {port_ocean-0.28.2.dist-info → port_ocean-0.29.0.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.28.2.dist-info → port_ocean-0.29.0.dist-info}/WHEEL +0 -0
- {port_ocean-0.28.2.dist-info → port_ocean-0.29.0.dist-info}/entry_points.txt +0 -0
|
@@ -4,6 +4,7 @@ from typing import TypedDict, Callable, Any, Awaitable
|
|
|
4
4
|
from pydantic import Extra
|
|
5
5
|
|
|
6
6
|
from port_ocean.config.base import BaseOceanModel
|
|
7
|
+
from port_ocean.core.models import EventListenerType
|
|
7
8
|
from port_ocean.utils.signal import signal_handler
|
|
8
9
|
from port_ocean.context.ocean import ocean
|
|
9
10
|
from port_ocean.utils.misc import IntegrationStateStatus
|
|
@@ -78,8 +79,10 @@ class BaseEventListener:
|
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
class EventListenerSettings(BaseOceanModel, extra=Extra.allow):
|
|
81
|
-
type:
|
|
82
|
+
type: EventListenerType
|
|
82
83
|
should_resync: bool = True
|
|
84
|
+
should_process_webhooks: bool = True
|
|
85
|
+
should_run_actions: bool = True
|
|
83
86
|
|
|
84
87
|
def get_changelog_destination_details(self) -> dict[str, Any]:
|
|
85
88
|
"""
|
|
@@ -13,6 +13,10 @@ from port_ocean.core.event_listener import (
|
|
|
13
13
|
KafkaEventListenerSettings,
|
|
14
14
|
PollingEventListenerSettings,
|
|
15
15
|
)
|
|
16
|
+
from port_ocean.core.event_listener.actions_only import (
|
|
17
|
+
ActionsOnlyEventListener,
|
|
18
|
+
ActionsOnlyEventListenerSettings,
|
|
19
|
+
)
|
|
16
20
|
from port_ocean.core.event_listener.base import (
|
|
17
21
|
BaseEventListener,
|
|
18
22
|
EventListenerEvents,
|
|
@@ -21,6 +25,7 @@ from port_ocean.core.event_listener.webhooks_only import (
|
|
|
21
25
|
WebhooksOnlyEventListener,
|
|
22
26
|
WebhooksOnlyEventListenerSettings,
|
|
23
27
|
)
|
|
28
|
+
from port_ocean.core.models import EventListenerType
|
|
24
29
|
from port_ocean.exceptions.core import UnsupportedEventListenerTypeException
|
|
25
30
|
|
|
26
31
|
|
|
@@ -57,12 +62,11 @@ class EventListenerFactory:
|
|
|
57
62
|
wrapped_events: EventListenerEvents = {"on_resync": self.events["on_resync"]}
|
|
58
63
|
event_listener: BaseEventListener
|
|
59
64
|
config = self.context.config.event_listener
|
|
60
|
-
_type = config.type.lower()
|
|
61
65
|
assert_message = "Invalid event listener config, expected KafkaEventListenerSettings and got {0}"
|
|
62
|
-
logger.info(f"Found event listener type: {
|
|
66
|
+
logger.info(f"Found event listener type: {config.type}")
|
|
63
67
|
|
|
64
|
-
match
|
|
65
|
-
case
|
|
68
|
+
match config.type:
|
|
69
|
+
case EventListenerType.KAFKA:
|
|
66
70
|
assert isinstance(
|
|
67
71
|
config, KafkaEventListenerSettings
|
|
68
72
|
), assert_message.format(type(config))
|
|
@@ -75,31 +79,36 @@ class EventListenerFactory:
|
|
|
75
79
|
self.context.config.integration.type,
|
|
76
80
|
)
|
|
77
81
|
|
|
78
|
-
case
|
|
82
|
+
case EventListenerType.WEBHOOK:
|
|
79
83
|
assert isinstance(
|
|
80
84
|
config, HttpEventListenerSettings
|
|
81
85
|
), assert_message.format(type(config))
|
|
82
86
|
event_listener = HttpEventListener(wrapped_events, config)
|
|
83
87
|
|
|
84
|
-
case
|
|
88
|
+
case EventListenerType.POLLING:
|
|
85
89
|
assert isinstance(
|
|
86
90
|
config, PollingEventListenerSettings
|
|
87
91
|
), assert_message.format(type(config))
|
|
88
92
|
event_listener = PollingEventListener(wrapped_events, config)
|
|
89
93
|
|
|
90
|
-
case
|
|
94
|
+
case EventListenerType.ONCE:
|
|
91
95
|
assert isinstance(
|
|
92
96
|
config, OnceEventListenerSettings
|
|
93
97
|
), assert_message.format(type(config))
|
|
94
98
|
event_listener = OnceEventListener(wrapped_events, config)
|
|
95
|
-
case
|
|
99
|
+
case EventListenerType.WEBHOOKS_ONLY:
|
|
96
100
|
assert isinstance(
|
|
97
101
|
config, WebhooksOnlyEventListenerSettings
|
|
98
102
|
), assert_message.format(type(config))
|
|
99
103
|
event_listener = WebhooksOnlyEventListener(wrapped_events, config)
|
|
104
|
+
case EventListenerType.ACTIONS_ONLY:
|
|
105
|
+
assert isinstance(
|
|
106
|
+
config, ActionsOnlyEventListenerSettings
|
|
107
|
+
), assert_message.format(type(config))
|
|
108
|
+
event_listener = ActionsOnlyEventListener(wrapped_events, config)
|
|
100
109
|
case _:
|
|
101
110
|
raise UnsupportedEventListenerTypeException(
|
|
102
|
-
f"Event listener {
|
|
111
|
+
f"Event listener {config.type} not supported"
|
|
103
112
|
)
|
|
104
113
|
|
|
105
114
|
return event_listener
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any, Literal
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter
|
|
4
4
|
from loguru import logger
|
|
@@ -11,6 +11,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
11
11
|
EventListenerEvents,
|
|
12
12
|
EventListenerSettings,
|
|
13
13
|
)
|
|
14
|
+
from port_ocean.core.models import EventListenerType
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class HttpEventListenerSettings(EventListenerSettings):
|
|
@@ -19,12 +20,12 @@ class HttpEventListenerSettings(EventListenerSettings):
|
|
|
19
20
|
The `HttpEventListenerSettings` specifically includes settings related to the HTTP event listener (Webhook).
|
|
20
21
|
|
|
21
22
|
Attributes:
|
|
22
|
-
type (
|
|
23
|
+
type (EventListenerType): A literal indicating the type of the event listener, which is set to "WEBHOOK" for this class.
|
|
23
24
|
app_host (AnyHttpUrl): The base URL of the application hosting the webhook.
|
|
24
25
|
The "AnyHttpUrl" type indicates that the value must be a valid HTTP/HTTPS URL.
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
|
-
type: Literal[
|
|
28
|
+
type: Literal[EventListenerType.WEBHOOK]
|
|
28
29
|
app_host: AnyHttpUrl = Field(..., sensitive=True)
|
|
29
30
|
|
|
30
31
|
def get_changelog_destination_details(self) -> dict[str, Any]:
|
|
@@ -17,6 +17,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
17
17
|
EventListenerSettings,
|
|
18
18
|
)
|
|
19
19
|
from pydantic import validator
|
|
20
|
+
from port_ocean.core.models import EventListenerType
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class KafkaEventListenerSettings(EventListenerSettings):
|
|
@@ -25,7 +26,7 @@ class KafkaEventListenerSettings(EventListenerSettings):
|
|
|
25
26
|
The `KafkaEventListenerSettings` specifically includes settings related to the Kafka event listener.
|
|
26
27
|
|
|
27
28
|
Attributes:
|
|
28
|
-
type (
|
|
29
|
+
type (EventListenerType): A literal indicating the type of the event listener, which is set to "KAFKA" for this class.
|
|
29
30
|
brokers (str): The comma-separated list of Kafka broker URLs to connect to.
|
|
30
31
|
security_protocol (str): The security protocol used for communication with Kafka brokers.
|
|
31
32
|
The default value is "SASL_SSL".
|
|
@@ -38,7 +39,7 @@ class KafkaEventListenerSettings(EventListenerSettings):
|
|
|
38
39
|
The default value is 1 second.
|
|
39
40
|
"""
|
|
40
41
|
|
|
41
|
-
type: Literal[
|
|
42
|
+
type: Literal[EventListenerType.KAFKA]
|
|
42
43
|
brokers: str = (
|
|
43
44
|
"b-1-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196,b-2-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196,b-3-public.publicclusterprod.t9rw6w.c1.kafka.eu-west-1.amazonaws.com:9196"
|
|
44
45
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import signal
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any, Literal
|
|
4
4
|
|
|
5
5
|
from loguru import logger
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
9
9
|
EventListenerEvents,
|
|
10
10
|
EventListenerSettings,
|
|
11
11
|
)
|
|
12
|
+
from port_ocean.core.models import EventListenerType
|
|
12
13
|
from port_ocean.utils.repeat import repeat_every
|
|
13
14
|
from port_ocean.context.ocean import ocean
|
|
14
15
|
from port_ocean.utils.time import convert_str_to_utc_datetime, convert_to_minutes
|
|
@@ -21,7 +22,9 @@ class OnceEventListenerSettings(EventListenerSettings):
|
|
|
21
22
|
This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
|
-
type: Literal[
|
|
25
|
+
type: Literal[EventListenerType.ONCE]
|
|
26
|
+
should_process_webhooks: bool = False
|
|
27
|
+
should_run_actions: bool = False
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class OnceEventListener(BaseEventListener):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from asyncio import Task, get_event_loop
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any, Literal
|
|
3
3
|
|
|
4
4
|
from loguru import logger
|
|
5
5
|
|
|
@@ -9,6 +9,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
9
9
|
EventListenerEvents,
|
|
10
10
|
EventListenerSettings,
|
|
11
11
|
)
|
|
12
|
+
from port_ocean.core.models import EventListenerType
|
|
12
13
|
from port_ocean.utils.repeat import repeat_every
|
|
13
14
|
from port_ocean.utils.signal import signal_handler
|
|
14
15
|
|
|
@@ -16,14 +17,14 @@ from port_ocean.utils.signal import signal_handler
|
|
|
16
17
|
class PollingEventListenerSettings(EventListenerSettings):
|
|
17
18
|
"""
|
|
18
19
|
Attributes:
|
|
19
|
-
type (
|
|
20
|
+
type (EventListenerType): A literal indicating the type of the event listener, which is set to "POLLING" for this class.
|
|
20
21
|
resync_on_start (bool): A flag indicating whether to trigger a resync event on the start of the polling event listener.
|
|
21
22
|
If True, the "on_resync" event will be triggered immediately when the polling listener starts.
|
|
22
23
|
interval (int): The interval in seconds at which the polling event listener checks for changes in the integration.
|
|
23
24
|
The default interval is set to 60 seconds.
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
|
-
type: Literal[
|
|
27
|
+
type: Literal[EventListenerType.POLLING]
|
|
27
28
|
resync_on_start: bool = True
|
|
28
29
|
interval: int = 60
|
|
29
30
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from typing import Literal
|
|
2
|
-
|
|
3
2
|
from loguru import logger
|
|
4
3
|
|
|
5
4
|
from port_ocean.core.event_listener.base import (
|
|
@@ -7,6 +6,7 @@ from port_ocean.core.event_listener.base import (
|
|
|
7
6
|
EventListenerEvents,
|
|
8
7
|
EventListenerSettings,
|
|
9
8
|
)
|
|
9
|
+
from port_ocean.core.models import EventListenerType
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class WebhooksOnlyEventListenerSettings(EventListenerSettings):
|
|
@@ -14,8 +14,9 @@ class WebhooksOnlyEventListenerSettings(EventListenerSettings):
|
|
|
14
14
|
This class inherits from `EventListenerSettings`, which provides a foundation for creating event listener settings.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
type: Literal[
|
|
17
|
+
type: Literal[EventListenerType.WEBHOOKS_ONLY]
|
|
18
18
|
should_resync: bool = False
|
|
19
|
+
should_run_actions: bool = False
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class WebhooksOnlyEventListener(BaseEventListener):
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Optional, Type
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from port_ocean.core.handlers.webhook.abstract_webhook_processor import (
|
|
6
|
+
AbstractWebhookProcessor,
|
|
7
|
+
)
|
|
8
|
+
from port_ocean.core.models import ActionRun
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AbstractExecutor(ABC):
|
|
12
|
+
"""
|
|
13
|
+
Abstract base class for action executors that handle integration-specific actions.
|
|
14
|
+
|
|
15
|
+
This class defines the core interface that all action executors must implement.
|
|
16
|
+
It provides a standardized way to handle action execution, rate limiting, and
|
|
17
|
+
webhook processing for asynchronous status updates.
|
|
18
|
+
|
|
19
|
+
Class Attributes:
|
|
20
|
+
ACTION_NAME (str): The unique identifier for this action, matching the action name
|
|
21
|
+
in the integration's `.port/spec.yaml` file.
|
|
22
|
+
PARTITION_KEY (str): The key used to partition action runs for concurrent execution.
|
|
23
|
+
If provided, runs with the same partition key value will be executed sequentially.
|
|
24
|
+
WEBHOOK_PROCESSOR_CLASS (Optional[Type[AbstractWebhookProcessor]]): The webhook processor
|
|
25
|
+
class used to handle asynchronous action status updates.
|
|
26
|
+
WEBHOOK_PATH (str): The URL path where webhook events for this action should be sent.
|
|
27
|
+
|
|
28
|
+
Implementation Requirements:
|
|
29
|
+
1. Set ACTION_NAME to match the action name in the integration's `.port/spec.yaml`.
|
|
30
|
+
2. Implement rate limit checking methods to prevent API quota exhaustion.
|
|
31
|
+
3. Implement the execute method to perform the actual action logic.
|
|
32
|
+
4. Optionally set PARTITION_KEY to control concurrent execution.
|
|
33
|
+
5. Optionally provide WEBHOOK_PROCESSOR_CLASS and WEBHOOK_PATH for async updates.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
```python
|
|
37
|
+
class MyActionExecutor(AbstractExecutor):
|
|
38
|
+
ACTION_NAME = "my_action"
|
|
39
|
+
PARTITION_KEY = "resource_id" # Optional
|
|
40
|
+
WEBHOOK_PROCESSOR_CLASS = MyWebhookProcessor # Optional
|
|
41
|
+
WEBHOOK_PATH = "/webhook/my_action" # Optional
|
|
42
|
+
|
|
43
|
+
async def is_close_to_rate_limit(self) -> bool:
|
|
44
|
+
return await self._check_rate_limit()
|
|
45
|
+
|
|
46
|
+
async def get_remaining_seconds_until_rate_limit(self) -> float:
|
|
47
|
+
return await self._get_rate_limit_wait_time()
|
|
48
|
+
|
|
49
|
+
async def execute(self, run: ActionRun) -> None:
|
|
50
|
+
# Implement action logic here
|
|
51
|
+
pass
|
|
52
|
+
```
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
ACTION_NAME: str
|
|
56
|
+
WEBHOOK_PROCESSOR_CLASS: Optional[Type[AbstractWebhookProcessor]]
|
|
57
|
+
WEBHOOK_PATH: str
|
|
58
|
+
|
|
59
|
+
async def _get_partition_key(self, run: ActionRun) -> str | None:
|
|
60
|
+
"""
|
|
61
|
+
This method should return a string used to identify runs that must be executed sequentially,
|
|
62
|
+
or return None to allow runs to execute in parallel.
|
|
63
|
+
|
|
64
|
+
For example, in order to execute runs of the same workflow in sequential order,
|
|
65
|
+
this method should return the workflow name.
|
|
66
|
+
"""
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
async def is_close_to_rate_limit(self) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Check if the action is approaching its rate limit threshold.
|
|
73
|
+
|
|
74
|
+
This method should implement integration-specific logic to determine if
|
|
75
|
+
the action is close to hitting API rate limits. If the rate limit threshold is reached,
|
|
76
|
+
the execution manager will wait for the rate limit to reset before acknowledging the run and executing it.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
bool: True if the action is close to its rate limit, False otherwise.
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
```python
|
|
83
|
+
async def is_close_to_rate_limit(self) -> bool:
|
|
84
|
+
rate_info = await self.client.get_rate_limit_info()
|
|
85
|
+
return rate_info.remaining / rate_info.limit < 0.1 # 10% threshold
|
|
86
|
+
```
|
|
87
|
+
"""
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
async def get_remaining_seconds_until_rate_limit(self) -> float:
|
|
92
|
+
"""
|
|
93
|
+
Calculate the number of seconds to wait before executing the next action.
|
|
94
|
+
|
|
95
|
+
This method should implement integration-specific logic to determine how long
|
|
96
|
+
to wait before the rate limit resets or quota becomes available. It's used
|
|
97
|
+
in conjunction with is_close_to_rate_limit() to implement backoff strategies.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
float: The number of seconds to wait before executing the next action.
|
|
101
|
+
Should return 0.0 if no wait is needed.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
```python
|
|
105
|
+
async def get_remaining_seconds_until_rate_limit(self) -> float:
|
|
106
|
+
rate_info = await self.client.get_rate_limit_info()
|
|
107
|
+
if rate_info.reset_time > datetime.now():
|
|
108
|
+
return (rate_info.reset_time - datetime.now()).total_seconds()
|
|
109
|
+
return 0.0
|
|
110
|
+
```
|
|
111
|
+
"""
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
async def execute(self, run: ActionRun) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Execute the integration action with the provided run configuration.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
run (ActionRun): The action run configuration
|
|
121
|
+
containing all necessary parameters and context for execution.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
Exception: Any error that occurs during execution. These will be caught by
|
|
125
|
+
the execution manager and reported as run failures.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
```python
|
|
129
|
+
async def execute(self, run: ActionRun) -> None:
|
|
130
|
+
try:
|
|
131
|
+
# Extract parameters
|
|
132
|
+
params = run.payload.integrationActionExecutionProperties
|
|
133
|
+
resource_id = params.get("resource_id")
|
|
134
|
+
if not resource_id:
|
|
135
|
+
raise ValueError("resource_id is required")
|
|
136
|
+
|
|
137
|
+
# Perform action
|
|
138
|
+
result = await self.client.update_resource(resource_id, params)
|
|
139
|
+
|
|
140
|
+
# Update run status
|
|
141
|
+
await ocean.port_client.patch_run(
|
|
142
|
+
run.id,
|
|
143
|
+
{"status": RunStatus.SUCCESS, "summary": "Resource updated"}
|
|
144
|
+
)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
# Error will be caught by execution manager
|
|
147
|
+
raise Exception(f"Failed to update resource: {str(e)}")
|
|
148
|
+
```
|
|
149
|
+
"""
|
|
150
|
+
pass
|