-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add feature: Signal child workflow (#311)
Implement features/child_workflow/signal for the 4 languages: - A child workflow is defined that waits for a signal and then returns the signal data - A parent workflow is defined that starts the child workflow, sends the signal, and returns the result of the child workflow. - We assert that the result of the parent workflow is equal to the signal data that it sent.
- Loading branch information
1 parent
a809899
commit 6385bb5
Showing
9 changed files
with
266 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package signal | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"go.temporal.io/features/harness/go/harness" | ||
"go.temporal.io/sdk/client" | ||
"go.temporal.io/sdk/workflow" | ||
) | ||
|
||
const ( | ||
UnblockSignal = "unblock-signal" | ||
UnblockMessage = "unblock" | ||
) | ||
|
||
// A workflow that starts a child workflow, unblocks it, and returns the result | ||
// of the child workflow. | ||
func Workflow(ctx workflow.Context) (string, error) { | ||
ctx = workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{ | ||
WorkflowExecutionTimeout: 10 * time.Minute, | ||
WorkflowTaskTimeout: time.Minute, | ||
}) | ||
childWorkflowFut := workflow.ExecuteChildWorkflow(ctx, ChildWorkflow) | ||
childWorkflowFut.SignalChildWorkflow(ctx, UnblockSignal, UnblockMessage) | ||
result := "" | ||
err := childWorkflowFut.Get(ctx, &result) | ||
if err != nil { | ||
return "", err | ||
} | ||
return result, nil | ||
} | ||
|
||
// A workflow that waits for a signal and returns the data received. | ||
func ChildWorkflow(ctx workflow.Context) (string, error) { | ||
unblockMessage := "" | ||
signalCh := workflow.GetSignalChannel(ctx, UnblockSignal) | ||
signalCh.Receive(ctx, &unblockMessage) | ||
return unblockMessage, nil | ||
} | ||
|
||
var Feature = harness.Feature{ | ||
Workflows: []interface{}{Workflow, ChildWorkflow}, | ||
ExpectRunResult: UnblockMessage, | ||
Execute: func(ctx context.Context, runner *harness.Runner) (client.WorkflowRun, error) { | ||
run, err := runner.ExecuteDefault(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return run, nil | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package child_workflow.signal; | ||
|
||
import io.temporal.client.WorkflowClient; | ||
import io.temporal.client.WorkflowOptions; | ||
import io.temporal.sdkfeatures.Assertions; | ||
import io.temporal.sdkfeatures.Feature; | ||
import io.temporal.sdkfeatures.Run; | ||
import io.temporal.sdkfeatures.Runner; | ||
import io.temporal.worker.Worker; | ||
import io.temporal.workflow.Async; | ||
import io.temporal.workflow.Promise; | ||
import io.temporal.workflow.SignalMethod; | ||
import io.temporal.workflow.Workflow; | ||
import io.temporal.workflow.WorkflowInterface; | ||
import io.temporal.workflow.WorkflowMethod; | ||
import java.time.Duration; | ||
|
||
@WorkflowInterface | ||
public interface feature extends Feature { | ||
|
||
@WorkflowInterface | ||
public interface ChildWorkflow { | ||
|
||
@WorkflowMethod | ||
String workflow(); | ||
|
||
@SignalMethod | ||
void unblock(String message); | ||
} | ||
|
||
class ChildWorkflowImpl implements ChildWorkflow { | ||
/* | ||
* A workflow that waits for a signal and returns the data received. | ||
*/ | ||
|
||
private String childWorkflowUnblockMessage; | ||
|
||
@Override | ||
public String workflow() { | ||
Workflow.await(() -> childWorkflowUnblockMessage != null); | ||
return childWorkflowUnblockMessage; | ||
} | ||
|
||
@Override | ||
public void unblock(String message) { | ||
childWorkflowUnblockMessage = message; | ||
} | ||
} | ||
|
||
@WorkflowMethod | ||
String workflow(); | ||
|
||
class Impl implements feature { | ||
|
||
@Override | ||
public void prepareWorker(Worker worker) { | ||
worker.registerWorkflowImplementationTypes(ChildWorkflowImpl.class); | ||
} | ||
|
||
private static final String UNBLOCK_MESSAGE = "unblock"; | ||
|
||
/* | ||
* Parent workflow | ||
* | ||
* A workflow that starts a child workflow, unblocks it, and returns the | ||
* result of the child workflow. | ||
*/ | ||
|
||
@Override | ||
public String workflow() { | ||
ChildWorkflow child = Workflow.newChildWorkflowStub(ChildWorkflow.class); | ||
Promise<String> childResult = Async.function(child::workflow); | ||
child.unblock(UNBLOCK_MESSAGE); | ||
return childResult.get(); | ||
} | ||
|
||
/* Test */ | ||
|
||
@Override | ||
public Run execute(Runner runner) throws Exception { | ||
var options = | ||
WorkflowOptions.newBuilder() | ||
.setTaskQueue(runner.config.taskQueue) | ||
.setWorkflowExecutionTimeout(Duration.ofMinutes(1)) | ||
.build(); | ||
var stub = runner.client.newWorkflowStub(feature.class, options); | ||
var execution = WorkflowClient.start(stub::workflow); | ||
var method = runner.featureInfo.metadata.getWorkflowMethods().get(0); | ||
return new Run(method, execution); | ||
} | ||
|
||
@Override | ||
public void checkResult(Runner runner, Run run) { | ||
var resultStr = runner.waitForRunResult(run, String.class); | ||
Assertions.assertEquals(UNBLOCK_MESSAGE, resultStr); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from datetime import timedelta | ||
from typing import Optional | ||
from uuid import uuid4 | ||
|
||
from temporalio import workflow | ||
from temporalio.client import WorkflowHandle | ||
|
||
from harness.python.feature import Runner, register_feature | ||
|
||
UNBLOCK_MESSAGE = "unblock" | ||
|
||
|
||
@workflow.defn | ||
class Workflow: | ||
""" | ||
A workflow that starts a child workflow, unblocks it, and returns the result of the child workflow. | ||
""" | ||
|
||
@workflow.run | ||
async def run(self) -> str: | ||
child_handle = await workflow.start_child_workflow(ChildWorkflow.run) | ||
await child_handle.signal(ChildWorkflow.unblock, UNBLOCK_MESSAGE) | ||
return await child_handle | ||
|
||
|
||
@workflow.defn | ||
class ChildWorkflow: | ||
""" | ||
A workflow that waits for a signal and returns the data received. | ||
""" | ||
|
||
def __init__(self) -> None: | ||
self._unblock_message: Optional[str] = None | ||
|
||
@workflow.run | ||
async def run(self) -> str: | ||
await workflow.wait_condition(lambda: self._unblock_message is not None) | ||
assert self._unblock_message is not None | ||
return self._unblock_message | ||
|
||
@workflow.signal | ||
def unblock(self, message: Optional[str]) -> None: | ||
self._unblock_message = message | ||
|
||
|
||
async def start(runner: Runner) -> WorkflowHandle: | ||
return await runner.client.start_workflow( | ||
Workflow, | ||
id=f"{runner.feature.rel_dir}-{uuid4()}", | ||
task_queue=runner.task_queue, | ||
execution_timeout=timedelta(minutes=1), | ||
) | ||
|
||
|
||
register_feature( | ||
workflows=[Workflow, ChildWorkflow], | ||
expect_run_result=UNBLOCK_MESSAGE, | ||
start=start, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { randomUUID } from 'crypto'; | ||
import * as assert from 'assert'; | ||
import { Feature } from '@temporalio/harness'; | ||
import * as wf from '@temporalio/workflow'; | ||
|
||
const unblockMessage = 'unblock'; | ||
|
||
// A workflow that starts a child workflow, unblocks it, and returns the result | ||
// of the child workflow. | ||
export async function workflow(): Promise<string> { | ||
const childHandle = await wf.startChild(childWorkflow); | ||
await childHandle.signal(unblock, unblockMessage); | ||
return await childHandle.result(); | ||
} | ||
|
||
const unblock = wf.defineSignal<[string]>('unblock'); | ||
|
||
// A workflow that waits for a signal and returns the data received. | ||
export async function childWorkflow(): Promise<string> { | ||
let unblockMessage = ''; | ||
wf.setHandler(unblock, (message: string) => { | ||
unblockMessage = message; | ||
}); | ||
await wf.condition(() => unblockMessage !== ''); | ||
return unblockMessage; | ||
} | ||
|
||
export const feature = new Feature({ | ||
workflow, | ||
async execute(runner) { | ||
return await runner.client.start(workflow, { | ||
taskQueue: runner.options.taskQueue, | ||
workflowId: `${runner.source.relDir}-${randomUUID()}`, | ||
workflowExecutionTimeout: 60000, | ||
...(runner.feature.options.workflowStartOptions ?? {}), | ||
}); | ||
}, | ||
async checkResult(runner, handle) { | ||
const result = await handle.result(); | ||
assert.equal(result, unblockMessage); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters