Skip to content

Commit 5282445

Browse files
Merge pull request #45 from thygesteffensen/feature/assertness
feat!: Added flow result
2 parents 736a160 + ed74956 commit 5282445

File tree

4 files changed

+217
-5
lines changed

4 files changed

+217
-5
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#nullable enable
2+
using System.Collections.Generic;
3+
using Parser.ExpressionParser;
4+
using Parser.FlowParser.ActionExecutors;
5+
6+
namespace Parser.FlowParser
7+
{
8+
public class FlowResult
9+
{
10+
public FlowResult()
11+
{
12+
ActionStates = new Dictionary<string, ActionState>();
13+
}
14+
15+
public Dictionary<string, ActionState> ActionStates { get; set; }
16+
public int NumberOfExecutedActions { get; set; }
17+
}
18+
19+
public class ActionState
20+
{
21+
private ValueContainer? _actionInput;
22+
#nullable enable
23+
public ValueContainer? ActionInput
24+
{
25+
get => _actionInput;
26+
set
27+
{
28+
_actionInput = value;
29+
if (_actionInput == null || _actionInput.Type() != ValueContainer.ValueType.Object) return;
30+
if (_actionInput.AsDict().ContainsKey("parameters"))
31+
{
32+
ActionParameters = _actionInput.AsDict()["parameters"];
33+
}
34+
}
35+
}
36+
37+
public ValueContainer? ActionParameters { set; get; }
38+
39+
public ActionResult? ActionOutput { get; set; }
40+
#nullable disable
41+
public string ActionName { get; set; }
42+
public string ActionType { get; set; }
43+
public int ActionOrder { get; set; }
44+
}
45+
}

PowerAutomateMockUp/FlowParser/FlowRunner.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Linq;
45
using System.Threading.Tasks;
@@ -7,16 +8,15 @@
78
using Newtonsoft.Json;
89
using Newtonsoft.Json.Linq;
910
using Parser.ExpressionParser;
10-
using Parser.ExpressionParser.Functions.Base;
1111
using Parser.FlowParser.ActionExecutors;
1212

1313
namespace Parser.FlowParser
1414
{
1515
public interface IFlowRunner
1616
{
1717
void InitializeFlowRunner(in string path);
18-
Task Trigger();
19-
Task Trigger(ValueContainer triggerOutput);
18+
Task<FlowResult> Trigger();
19+
Task<FlowResult> Trigger(ValueContainer triggerOutput);
2020
}
2121

2222
public class FlowRunner : IFlowRunner
@@ -26,6 +26,8 @@ public class FlowRunner : IFlowRunner
2626
private readonly IScopeDepthManager _scopeManager;
2727
private readonly IActionExecutorFactory _actionExecutorFactory;
2828
private readonly ILogger<FlowRunner> _logger;
29+
private readonly Dictionary<string, ActionState> _actionSates;
30+
private int _actionsExecuted;
2931
private JProperty _trigger;
3032

3133
public FlowRunner(
@@ -41,6 +43,8 @@ public FlowRunner(
4143
_actionExecutorFactory =
4244
actionExecutorFactory ?? throw new ArgumentNullException(nameof(actionExecutorFactory));
4345
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
46+
_actionSates = new Dictionary<string, ActionState>();
47+
_actionsExecuted = 0;
4448
}
4549

4650
public void InitializeFlowRunner(in string path)
@@ -54,7 +58,7 @@ public void InitializeFlowRunner(in string path)
5458
_scopeManager.Push("root", flowDefinition.SelectToken("$.actions").OfType<JProperty>(), null);
5559
}
5660

57-
public async Task Trigger()
61+
public async Task<FlowResult> Trigger()
5862
{
5963
var trigger = GetActionExecutor(_trigger);
6064

@@ -67,13 +71,25 @@ public async Task Trigger()
6771
}
6872

6973
await RunFlow();
74+
75+
return new FlowResult
76+
{
77+
ActionStates = _actionSates,
78+
NumberOfExecutedActions = _actionsExecuted
79+
};
7080
}
7181

72-
public async Task Trigger(ValueContainer triggerOutput)
82+
public async Task<FlowResult> Trigger(ValueContainer triggerOutput)
7383
{
7484
_state.AddTriggerOutputs(triggerOutput);
7585

7686
await RunFlow();
87+
88+
return new FlowResult
89+
{
90+
ActionStates = _actionSates,
91+
NumberOfExecutedActions = _actionsExecuted
92+
};
7793
}
7894

7995
private async Task RunFlow()
@@ -95,6 +111,18 @@ private async Task RunFlow()
95111
var actionExecutor = GetActionExecutor(currentAd);
96112

97113
var actionResult = await ExecuteAction(actionExecutor, currentAd);
114+
115+
if (_flowRunnerSettings.LogActionsStates)
116+
{
117+
_actionSates[currentAd.Name] = new ActionState
118+
{
119+
ActionInput = actionExecutor?.Inputs,
120+
ActionOutput = actionResult,
121+
ActionOrder = _actionsExecuted++,
122+
ActionName = actionExecutor?.ActionName
123+
};
124+
}
125+
98126
if (!(actionResult?.ContinueExecution ?? true))
99127
{
100128
break;

PowerAutomateMockUp/FlowParser/FlowSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ public class FlowSettings
77
public bool FailOnUnknownAction { get; set; } = true;
88

99
public List<string> IgnoreActions { get; set; } = new List<string>();
10+
11+
public bool LogActionsStates { get; set; } = true;
1012
}
1113
}

Test/FullFlowTestV2.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using NUnit.Framework;
6+
using Parser;
7+
using Parser.ExpressionParser;
8+
using Parser.FlowParser;
9+
using Parser.FlowParser.ActionExecutors;
10+
11+
namespace Test
12+
{
13+
[TestFixture]
14+
public class FullFlowTestV2
15+
{
16+
private static readonly string TestFlowPath = System.IO.Path.GetFullPath(@"FlowSamples");
17+
18+
[Test]
19+
public async Task TestFlowFalse()
20+
{
21+
var path = @$"{TestFlowPath}\PowerAutomateMockUpSampleFlow.json";
22+
23+
var services = new ServiceCollection();
24+
services.AddFlowRunner();
25+
26+
services.AddFlowActionByName<UpdateAccountInvalidId>(UpdateAccountInvalidId.FlowActionName);
27+
services.AddFlowActionByApiIdAndOperationsName<SendEmailNotification>(SendEmailNotification.ApiId,
28+
SendEmailNotification.SupportedOperations);
29+
services.AddFlowActionByName<GetRecordValidId>(GetRecordValidId.FlowActionName);
30+
services.AddFlowActionByName<UpdateAccountValidId>(UpdateAccountValidId.FlowActionName);
31+
services.AddFlowActionByName<SendOutWarning>(SendOutWarning.FlowActionName);
32+
33+
34+
var sp = services.BuildServiceProvider();
35+
var flowRunner = sp.GetRequiredService<IFlowRunner>();
36+
37+
flowRunner.InitializeFlowRunner(path);
38+
39+
var flowResult = await flowRunner.Trigger(new ValueContainer(
40+
new Dictionary<string, ValueContainer>
41+
{
42+
{"body/name", new ValueContainer("Alice Bob")},
43+
{"body/accountid", new ValueContainer(Guid.NewGuid().ToString())}
44+
}));
45+
46+
Assert.AreEqual(7, flowResult.NumberOfExecutedActions);
47+
48+
const string actionName = "Send_me_an_email_notification";
49+
Assert.IsTrue(flowResult.ActionStates.ContainsKey(actionName), "Action is expected to be triggered.");
50+
Assert.NotNull(flowResult.ActionStates[actionName].ActionParameters, "Action input is expected.");
51+
var actionInput = flowResult.ActionStates[actionName].ActionParameters.AsDict();
52+
Assert.IsTrue(actionInput.ContainsKey("NotificationEmailDefinition"), "Action input should contain this object.");
53+
var notification = actionInput["NotificationEmailDefinition"].AsDict();
54+
Assert.AreEqual("A new Account have been added", notification["notificationSubject"].GetValue<string>(), "Asserting the input");
55+
56+
Assert.IsFalse(flowResult.ActionStates.ContainsKey(SendOutWarning.FlowActionName), "Action is not expected to be triggered.");
57+
}
58+
59+
private class UpdateAccountInvalidId : OpenApiConnectionActionExecutorBase
60+
{
61+
public const string FlowActionName = "Update_Account_-_Invalid_Id";
62+
63+
public UpdateAccountInvalidId(IExpressionEngine expressionEngine) : base(expressionEngine)
64+
{
65+
}
66+
67+
public override Task<ActionResult> Execute()
68+
{
69+
return Task.FromResult(new ActionResult
70+
{
71+
ActionStatus = ActionStatus.Failed,
72+
ActionOutput = new ValueContainer(true)
73+
});
74+
}
75+
}
76+
77+
private class SendEmailNotification : OpenApiConnectionActionExecutorBase
78+
{
79+
public const string ApiId = "/providers/Microsoft.PowerApps/apis/shared_flowpush";
80+
public static readonly string[] SupportedOperations = {"SendEmailNotification"};
81+
82+
public SendEmailNotification(IExpressionEngine expressionEngine) : base(expressionEngine)
83+
{
84+
}
85+
86+
public override Task<ActionResult> Execute()
87+
{
88+
Console.WriteLine($"Email Title: {Parameters["NotificationEmailDefinition/notificationSubject"]}");
89+
Console.WriteLine($"Email Content: {Parameters["NotificationEmailDefinition/notificationBody"]}");
90+
91+
return Task.FromResult(new ActionResult {ActionOutput = new ValueContainer(true)});
92+
}
93+
}
94+
95+
private class GetRecordValidId : OpenApiConnectionActionExecutorBase
96+
{
97+
public const string FlowActionName = "Get_a_record_-_Valid_Id";
98+
99+
public GetRecordValidId(IExpressionEngine expressionEngine) : base(expressionEngine)
100+
{
101+
}
102+
103+
public override Task<ActionResult> Execute()
104+
{
105+
return Task.FromResult(new ActionResult {ActionOutput = new ValueContainer(true)});
106+
}
107+
}
108+
109+
private class UpdateAccountValidId : OpenApiConnectionActionExecutorBase
110+
{
111+
public const string FlowActionName = "Update_Account_-_Valid_Id";
112+
113+
public override Task<ActionResult> Execute()
114+
{
115+
return Task.FromResult(new ActionResult());
116+
}
117+
118+
public UpdateAccountValidId(IExpressionEngine expressionEngine) : base(expressionEngine)
119+
{
120+
}
121+
}
122+
123+
private class SendOutWarning : OpenApiConnectionActionExecutorBase
124+
{
125+
public const string FlowActionName = "Send_an_error_message_to_owner";
126+
127+
public SendOutWarning(IExpressionEngine expressionEngine) : base(expressionEngine)
128+
{
129+
}
130+
131+
public override Task<ActionResult> Execute()
132+
{
133+
return Task.FromResult(new ActionResult());
134+
}
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)