Skip to content

Commit

Permalink
Fix SetOutput handling
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Nov 17, 2023
1 parent c99ce46 commit bf205c7
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ namespace Elsa.Extensions;
/// </summary>
public static class MemoryBlockReferenceExtensions
{
/// <summary>
/// Gets the value of the memory reference referenced by the specified <see cref="MemoryBlockReference"/>.
/// </summary>
/// <param name="context">The <see cref="ExpressionExecutionContext"/> to get the value from.</param>
/// <param name="blockId">The ID of the memory block to get the value for.</param>
/// <returns>The value of the memory reference referenced by the specified <see cref="blockId"/>.</returns>
public static object? Get(this ExpressionExecutionContext context, string blockId)
{
var matchingContext = context.FindContextContainingBlock(blockId) ?? context;
return matchingContext.Memory.TryGetBlock(blockId, out var block) ? block.Value : default;
}

/// <summary>
/// Gets the value of the memory reference referenced by the specified <see cref="MemoryBlockReference"/>.
/// </summary>
/// <param name="context">The <see cref="ActivityExecutionContext"/> to get the value from.</param>
/// <param name="blockId">The ID of the memory block to get the value for.</param>
/// <returns>The value of the memory reference referenced by the specified <see cref="blockId"/>.</returns>
public static object? Get(this ActivityExecutionContext context, string blockId) => context.ExpressionExecutionContext.Get(blockId);

/// <summary>
/// Gets the value of the memory reference referenced by the specified <see cref="MemoryBlockReference"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected override void Execute(ActivityExecutionContext context)
{
var outputName = OutputName.Get(context);
var outputValue = OutputValue.GetOrDefault(context);
var ancestorContext = context.GetAncestors().FirstOrDefault(x => x.ActivityDescriptor.Outputs.Any(y => y.Name == outputName));
var ancestorContext = context.ParentActivityExecutionContext?.GetAncestors().FirstOrDefault(x => x.ActivityDescriptor.Outputs.Any(y => y.Name == outputName));

if (ancestorContext != null)
SetAncestorActivityOutput(context, ancestorContext, outputValue);
Expand Down Expand Up @@ -73,7 +73,7 @@ private void SetAncestorActivityOutput(ActivityExecutionContext context, Activit
context.Set(variable, outputValue);

// Also set the output on the composite activity's output property.
var compositeActivityContext = ancestorContext.ParentActivityExecutionContext;
var compositeActivityContext = ancestorContext;
var compositeActivity = compositeActivityContext.Activity;
var compositeActivityDescriptor = compositeActivityContext.ActivityDescriptor;
var compositeOutputDescriptor = compositeActivityDescriptor.Outputs.FirstOrDefault(x => x.Name == outputName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,22 @@ private async ValueTask OnChildCompletedAsync(ActivityCompletedContext context)
// Copy any collected outputs into the synthetic properties.
foreach (var outputDescriptor in activityExecutionContext.ActivityDescriptor.Outputs)
{
// Create a local scope variable for each output property.
var variable = new Variable
{
Id = outputDescriptor.Name,
Name = outputDescriptor.Name
};
var output = (Output?)outputDescriptor.ValueGetter(activityExecutionContext.Activity);
var value = activityExecutionContext.Get(output);

// Use the variable to read the value from the memory.
var value = variable.Get(activityExecutionContext);

// Assign the value to the output synthetic property.
// Make sure to select a parent scope to avoid naming collisions between outputs defined on the current scope and outputs defined on parent scopes.
var parentActivityExecutionContext = activityExecutionContext.ParentActivityExecutionContext ?? activityExecutionContext;
var output = SyntheticProperties.TryGetValue(outputDescriptor.Name, out var outputValue) ? (Output?)outputValue : default;
parentActivityExecutionContext.Set(output, value);
if (value == null)
{
// If direct output mapping is used, we can read the output value directly from the memory.
value = activityExecutionContext.Get(outputDescriptor.Name);

// Make sure to select a parent scope to avoid naming collisions between outputs defined on the current scope and outputs defined on parent scopes.
var parentActivityExecutionContext = activityExecutionContext.ParentActivityExecutionContext ?? activityExecutionContext;
parentActivityExecutionContext.Set(output, value, outputDescriptor.Name);
}
else
{
activityExecutionContext.Set(output, value, outputDescriptor.Name);
}
}

// Complete this activity with the signal value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@
<None Update="Scenarios\ParentChildOutputMapping\Workflows\d.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scenarios\ParentChildOutputMapping\Workflows\producer.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scenarios\ParentChildOutputMapping\Workflows\consumer.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,26 @@ public async Task Test2()
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "FooBar" }, lines);
}

[Fact(DisplayName = "Parent workflow receives output from child workflow when using SetOutput activity.")]
public async Task Test3()
{
// Populate registries.
await _services.PopulateRegistriesAsync();

// Import child workflow.
var producerFileName = "Scenarios/ParentChildOutputMapping/Workflows/producer.json";
await _services.ImportWorkflowDefinitionAsync(producerFileName);

// Import parent workflow.
var consumerFileName = "Scenarios/ParentChildOutputMapping/Workflows/consumer.json";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(consumerFileName);

// Execute.
await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);

// Assert expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Foo" }, lines);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"id": "cae99c483081a2c",
"definitionId": "a7e44620e65e7184",
"name": "Consumer",
"createdAt": "2023-11-17T19:47:21.937582+00:00",
"version": 3,
"toolVersion": "3.0.0.0",
"variables": [
{
"id": "137ac38a229059f0",
"name": "Variable1",
"typeName": "Object",
"isArray": false,
"storageDriverTypeName": "Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
}
],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": false,
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "cv7o3nnxp1dkxp1n",
"nodeId": "Workflow1:cv7o3nnxp1dkxp1n",
"metadata": {
"displayText": "Flowchart1"
},
"customProperties": {
"source": "FlowchartJsonConverter.cs:47",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"workflowDefinitionId": "69d6496437ff9d33",
"workflowDefinitionVersionId": "5c020d0052fb2860",
"latestAvailablePublishedVersion": 2,
"latestAvailablePublishedVersionId": "5c020d0052fb2860",
"id": "xcyl3fg7gg2uxcd4",
"nodeId": "Workflow1:cv7o3nnxp1dkxp1n:xcyl3fg7gg2uxcd4",
"name": "InternalPremiumCalculationGeneric1",
"type": "Producer",
"version": 2,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2280,
"y": 2420
},
"size": {
"width": 318.140625,
"height": 50
}
}
},
"output1": {
"typeName": "Object",
"memoryReference": {
"id": "137ac38a229059f0"
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "CSharp",
"value": "return Variable.Variable1.ToString();"
},
"memoryReference": {
"id": "b3b9071594676f52:input-0"
}
},
"id": "b3b9071594676f52",
"nodeId": "Workflow1:cv7o3nnxp1dkxp1n:b3b9071594676f52",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2695.5703125,
"y": 2420
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "xcyl3fg7gg2uxcd4",
"port": "Done"
},
"target": {
"activity": "b3b9071594676f52",
"port": "In"
}
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{
"id": "5c020d0052fb2860",
"definitionId": "69d6496437ff9d33",
"name": "Producer",
"createdAt": "2023-11-17T19:45:52.798011+00:00",
"version": 2,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [
{
"type": "Object",
"name": "Output1",
"displayName": "Output 1",
"category": "Primitives",
"isArray": false
}
],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "nn98bbdkmvkbkuda",
"nodeId": "Workflow1:nn98bbdkmvkbkuda",
"metadata": {
"displayText": "Flowchart1"
},
"customProperties": {
"source": "FlowchartJsonConverter.cs:47",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"outputName": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Output1"
},
"memoryReference": {
"id": "825749ed-b1ad-48f0-aa51-f739c71420a4"
}
},
"outputValue": {
"typeName": "Object",
"expression": {
"type": "Literal",
"value": "Foo"
},
"memoryReference": {
"id": "427d771a-a3e2-4a1c-bb40-c1f4ccf3d0c7"
}
},
"id": "4c9j38qta2bmc096",
"nodeId": "Workflow1:nn98bbdkmvkbkuda:4c9j38qta2bmc096",
"name": "SetOutput1",
"type": "Elsa.SetOutput",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 400,
"y": 40
},
"size": {
"width": 175.578125,
"height": 50
}
},
"displayText": ""
}
}
],
"connections": []
}
}

0 comments on commit bf205c7

Please sign in to comment.