Skip to content

Commit a9dd8be

Browse files
feat: add IS resume triggers
1 parent 4b6fcbc commit a9dd8be

7 files changed

Lines changed: 416 additions & 0 deletions

File tree

packages/uipath-core/src/uipath/core/triggers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
"UiPathResumeTrigger",
55
"UiPathResumeTriggerType",
66
"UiPathApiTrigger",
7+
"UiPathIntegrationTrigger",
78
"UiPathResumeTriggerName",
89
]
910

1011
from uipath.core.triggers.trigger import (
1112
UiPathApiTrigger,
13+
UiPathIntegrationTrigger,
1214
UiPathResumeTrigger,
1315
UiPathResumeTriggerName,
1416
UiPathResumeTriggerType,

packages/uipath-core/src/uipath/core/triggers/trigger.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ class UiPathApiTrigger(BaseModel):
5353
model_config = ConfigDict(validate_by_name=True)
5454

5555

56+
class UiPathIntegrationTrigger(BaseModel):
57+
"""Integration Services (Inbox) resume trigger request.
58+
59+
Mirrors Orchestrator's `IntegrationResumeDto`: the configuration needed to
60+
register a remote event trigger through the Connections service and
61+
correlate the eventual payload back to the suspended job via `inbox_id`.
62+
"""
63+
64+
connector: str = Field(alias="connector")
65+
connection_id: str = Field(alias="connectionId")
66+
operation: str = Field(alias="operation")
67+
object_name: str = Field(alias="objectName")
68+
filter_expression: str | None = Field(default=None, alias="filterExpression")
69+
parameters: dict[str, str] | None = Field(default=None, alias="parameters")
70+
inbox_id: str = Field(alias="inboxId")
71+
72+
model_config = ConfigDict(validate_by_name=True)
73+
74+
5675
class UiPathResumeTrigger(BaseModel):
5776
"""Information needed to resume execution."""
5877

@@ -65,6 +84,9 @@ class UiPathResumeTrigger(BaseModel):
6584
)
6685
item_key: str | None = Field(default=None, alias="itemKey")
6786
api_resume: UiPathApiTrigger | None = Field(default=None, alias="apiResume")
87+
integration_resume: UiPathIntegrationTrigger | None = Field(
88+
default=None, alias="integrationResume"
89+
)
6890
folder_path: str | None = Field(default=None, alias="folderPath")
6991
folder_key: str | None = Field(default=None, alias="folderKey")
7092
payload: Any | None = Field(default=None, alias="interruptObject", exclude=True)

packages/uipath-platform/src/uipath/platform/common/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
WaitEphemeralIndex,
4949
WaitEphemeralIndexRaw,
5050
WaitEscalation,
51+
WaitIntegrationEvent,
5152
WaitJob,
5253
WaitJobRaw,
5354
WaitSystemAgent,
@@ -89,6 +90,7 @@
8990
"WaitEphemeralIndexRaw",
9091
"DocumentExtractionValidation",
9192
"WaitDocumentExtractionValidation",
93+
"WaitIntegrationEvent",
9294
"RequestSpec",
9395
"Endpoint",
9496
"UiPathUrl",

packages/uipath-platform/src/uipath/platform/common/interrupt_models.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,23 @@ class WaitDocumentExtractionValidation(BaseModel):
259259

260260
extraction_validation: StartExtractionValidationResponse
261261
task_url: str | None = None
262+
263+
264+
class WaitIntegrationEvent(BaseModel):
265+
"""Model representing a wait on an Integration Services event.
266+
267+
Used to suspend a job until a remote event (e.g. Slack message, Teams reply)
268+
is delivered by Integration Services. The SDK resolves `connection_name`
269+
(scoped to `connection_folder_path` when provided) to the underlying
270+
connection id and generates a fresh `inbox_id` when the trigger is created;
271+
the rest of the fields describe which remote event to subscribe to via
272+
the Connections service.
273+
"""
274+
275+
connector: str
276+
connection_name: str
277+
connection_folder_path: str | None = None
278+
operation: str
279+
object_name: str
280+
filter_expression: str | None = None
281+
parameters: dict[str, str] | None = None

packages/uipath-platform/src/uipath/platform/orchestrator/_jobs_service.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,52 @@ def _retrieve_api_payload_spec(
674674
},
675675
)
676676

677+
def retrieve_inbox_payload(self, inbox_id: str) -> Any:
678+
"""Fetch payload data for Integration Services (Inbox) triggers.
679+
680+
Unlike `retrieve_api_payload`, this returns the response body as-is.
681+
Orchestrator's `GET /JobTriggers/GetPayload/{inboxId}` returns the
682+
stored payload directly without an envelope.
683+
684+
Args:
685+
inbox_id: The Id of the inbox to fetch the payload for.
686+
687+
Returns:
688+
The stored payload.
689+
"""
690+
spec = self._retrieve_api_payload_spec(inbox_id=inbox_id)
691+
692+
response = self.request(
693+
spec.method,
694+
url=spec.endpoint,
695+
headers=spec.headers,
696+
)
697+
698+
return response.json()
699+
700+
async def retrieve_inbox_payload_async(self, inbox_id: str) -> Any:
701+
"""Asynchronously fetch payload data for Integration Services (Inbox) triggers.
702+
703+
Unlike `retrieve_api_payload_async`, this returns the response body
704+
as-is. Orchestrator's `GET /JobTriggers/GetPayload/{inboxId}` returns
705+
the stored payload directly without an envelope.
706+
707+
Args:
708+
inbox_id: The Id of the inbox to fetch the payload for.
709+
710+
Returns:
711+
The stored payload.
712+
"""
713+
spec = self._retrieve_api_payload_spec(inbox_id=inbox_id)
714+
715+
response = await self.request_async(
716+
spec.method,
717+
url=spec.endpoint,
718+
headers=spec.headers,
719+
)
720+
721+
return response.json()
722+
677723
def _extract_first_inbox_id(self, response: Any) -> str:
678724
if len(response["value"]) > 0:
679725
return response["value"][0]["ItemKey"]

packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from uipath.core.serialization import serialize_object
1414
from uipath.core.triggers import (
1515
UiPathApiTrigger,
16+
UiPathIntegrationTrigger,
1617
UiPathResumeTrigger,
1718
UiPathResumeTriggerName,
1819
UiPathResumeTriggerType,
@@ -43,6 +44,7 @@
4344
WaitEphemeralIndex,
4445
WaitEphemeralIndexRaw,
4546
WaitEscalation,
47+
WaitIntegrationEvent,
4648
WaitJob,
4749
WaitJobRaw,
4850
WaitSystemAgent,
@@ -401,6 +403,19 @@ async def read_trigger(self, trigger: UiPathResumeTrigger) -> Any | None:
401403
f"Error fetching API trigger payload for inbox {trigger.api_resume.inbox_id}: {str(e)}",
402404
) from e
403405

406+
case UiPathResumeTriggerType.INBOX:
407+
if trigger.integration_resume and trigger.integration_resume.inbox_id:
408+
try:
409+
return await uipath.jobs.retrieve_inbox_payload_async(
410+
trigger.integration_resume.inbox_id
411+
)
412+
except Exception as e:
413+
raise UiPathFaultedTriggerError(
414+
ErrorCategory.SYSTEM,
415+
f"Failed to get trigger payload"
416+
f"Error fetching Inbox trigger payload for inbox {trigger.integration_resume.inbox_id}: {str(e)}",
417+
) from e
418+
404419
case _:
405420
raise UiPathFaultedTriggerError(
406421
ErrorCategory.SYSTEM,
@@ -461,6 +476,9 @@ async def create_trigger(self, suspend_value: Any) -> UiPathResumeTrigger:
461476
case UiPathResumeTriggerType.API:
462477
self._handle_api_trigger(suspend_value, resume_trigger)
463478

479+
case UiPathResumeTriggerType.INBOX:
480+
await self._handle_inbox_trigger(suspend_value, resume_trigger)
481+
464482
case UiPathResumeTriggerType.DEEP_RAG:
465483
await self._handle_deep_rag_job_trigger(
466484
suspend_value, resume_trigger
@@ -545,6 +563,8 @@ def _determine_trigger_type(self, value: Any) -> UiPathResumeTriggerType:
545563
value, (DocumentExtractionValidation, WaitDocumentExtractionValidation)
546564
):
547565
return UiPathResumeTriggerType.IXP_VS_ESCALATION
566+
if isinstance(value, WaitIntegrationEvent):
567+
return UiPathResumeTriggerType.INBOX
548568
# default to API trigger
549569
return UiPathResumeTriggerType.API
550570

@@ -579,6 +599,8 @@ def _determine_trigger_name(self, value: Any) -> UiPathResumeTriggerName:
579599
return UiPathResumeTriggerName.BATCH_RAG
580600
if isinstance(value, (DocumentExtraction, WaitDocumentExtraction)):
581601
return UiPathResumeTriggerName.EXTRACTION
602+
if isinstance(value, WaitIntegrationEvent):
603+
return UiPathResumeTriggerName.INBOX
582604
# default to API trigger
583605
return UiPathResumeTriggerName.API
584606

@@ -901,6 +923,55 @@ def _handle_api_trigger(
901923
inbox_id=str(uuid.uuid4()), request=serialize_object(value)
902924
)
903925

926+
async def _handle_inbox_trigger(
927+
self, value: WaitIntegrationEvent, resume_trigger: UiPathResumeTrigger
928+
) -> None:
929+
"""Handle Inbox-type resume triggers.
930+
931+
Resolves `connection_name` (scoped to `connection_folder_path` when
932+
provided) to a connection id via the Connections service, populates
933+
`integration_resume` with the Integration Services configuration plus a
934+
freshly generated `inbox_id`. The Connections-service registration is
935+
performed server-side by Orchestrator's `CreateResumeTriggerTaskHandler`
936+
once the job suspends.
937+
938+
Args:
939+
value: The suspend value (WaitIntegrationEvent)
940+
resume_trigger: The resume trigger to populate
941+
942+
Raises:
943+
Exception: If no connection matches `connection_name`, or if more
944+
than one exact match is found.
945+
"""
946+
uipath = UiPath()
947+
connections = await uipath.connections.list_async(
948+
name=value.connection_name,
949+
folder_path=value.connection_folder_path,
950+
)
951+
connection = next(
952+
(c for c in connections if c.name == value.connection_name), None
953+
)
954+
if connection is None:
955+
raise Exception(
956+
f"No connection named '{value.connection_name}' found"
957+
+ (
958+
f" in folder '{value.connection_folder_path}'"
959+
if value.connection_folder_path
960+
else ""
961+
)
962+
)
963+
assert connection.id is not None
964+
965+
resume_trigger.integration_resume = UiPathIntegrationTrigger(
966+
connector=value.connector,
967+
connection_id=connection.id,
968+
operation=value.operation,
969+
object_name=value.object_name,
970+
filter_expression=value.filter_expression,
971+
parameters=value.parameters,
972+
inbox_id=str(uuid.uuid4()),
973+
)
974+
904975

905976
class UiPathResumeTriggerHandler:
906977
"""Combined handler for creating and reading resume triggers.

0 commit comments

Comments
 (0)