Skip to content

Commit 0a2f1b1

Browse files
committed
Merge remote-tracking branch 'upstream/main' into signal_decorator
2 parents a21c194 + ea98ef7 commit 0a2f1b1

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

cadence/client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from grpc.aio import Channel, ClientInterceptor, secure_channel, insecure_channel
1616
from cadence.api.v1.service_workflow_pb2_grpc import WorkflowAPIStub
1717
from cadence.api.v1.service_workflow_pb2 import (
18+
SignalWorkflowExecutionRequest,
1819
StartWorkflowExecutionRequest,
1920
StartWorkflowExecutionResponse,
2021
SignalWithStartWorkflowExecutionRequest,
@@ -232,6 +233,51 @@ async def start_workflow(
232233
except Exception:
233234
raise
234235

236+
async def signal_workflow(
237+
self,
238+
workflow_id: str,
239+
run_id: str,
240+
signal_name: str,
241+
*signal_args: Any,
242+
) -> None:
243+
"""
244+
Send a signal to a running workflow execution.
245+
246+
Args:
247+
workflow_id: The workflow ID
248+
run_id: The run ID (can be empty string to signal current run)
249+
signal_name: Name of the signal
250+
*signal_args: Arguments to pass to the signal handler
251+
252+
Raises:
253+
ValueError: If signal encoding fails
254+
Exception: If the gRPC call fails
255+
"""
256+
signal_payload = None
257+
if signal_args:
258+
try:
259+
signal_payload = self.data_converter.to_data(list[Any](signal_args))
260+
except Exception as e:
261+
raise ValueError(f"Failed to encode signal input: {e}")
262+
263+
workflow_execution = WorkflowExecution()
264+
workflow_execution.workflow_id = workflow_id
265+
if run_id:
266+
workflow_execution.run_id = run_id
267+
268+
signal_request = SignalWorkflowExecutionRequest(
269+
domain=self.domain,
270+
workflow_execution=workflow_execution,
271+
identity=self.identity,
272+
request_id=str(uuid.uuid4()),
273+
signal_name=signal_name,
274+
)
275+
276+
if signal_payload:
277+
signal_request.signal_input.CopyFrom(signal_payload)
278+
279+
await self.workflow_stub.SignalWorkflowExecution(signal_request)
280+
235281
async def signal_with_start_workflow(
236282
self,
237283
workflow: Union[str, WorkflowDefinition],

tests/integration_tests/test_client.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,61 @@ async def test_workflow_stub_start_and_describe(helper: CadenceHelper):
140140
)
141141

142142

143+
@pytest.mark.usefixtures("helper")
144+
async def test_signal_workflow(helper: CadenceHelper):
145+
"""Test signal_workflow method.
146+
147+
This integration test verifies:
148+
1. Starting a workflow execution
149+
2. Sending a signal to the running workflow
150+
3. Signal appears in the workflow's history
151+
"""
152+
async with helper.client() as client:
153+
workflow_type = "test-workflow-signal"
154+
task_list_name = "test-task-list-signal"
155+
workflow_id = "test-workflow-signal-789"
156+
execution_timeout = timedelta(minutes=5)
157+
signal_name = "test-signal"
158+
signal_arg = {"action": "update", "value": 42}
159+
160+
execution = await client.start_workflow(
161+
workflow_type,
162+
task_list=task_list_name,
163+
execution_start_to_close_timeout=execution_timeout,
164+
workflow_id=workflow_id,
165+
)
166+
167+
await client.signal_workflow(
168+
execution.workflow_id,
169+
execution.run_id,
170+
signal_name,
171+
signal_arg,
172+
)
173+
174+
# Fetch workflow history to verify signal was recorded
175+
history_response = await client.workflow_stub.GetWorkflowExecutionHistory(
176+
GetWorkflowExecutionHistoryRequest(
177+
domain=DOMAIN_NAME,
178+
workflow_execution=execution,
179+
skip_archival=True,
180+
)
181+
)
182+
183+
# Verify signal event appears in history
184+
signal_events = [
185+
event
186+
for event in history_response.history.events
187+
if event.HasField("workflow_execution_signaled_event_attributes")
188+
]
189+
190+
assert len(signal_events) == 1, "Expected exactly one signal event in history"
191+
signal_event = signal_events[0]
192+
assert (
193+
signal_event.workflow_execution_signaled_event_attributes.signal_name
194+
== signal_name
195+
), f"Expected signal name '{signal_name}'"
196+
197+
143198
@pytest.mark.usefixtures("helper")
144199
async def test_signal_with_start_workflow(helper: CadenceHelper):
145200
"""Test signal_with_start_workflow method.

0 commit comments

Comments
 (0)