Battery included mock for Power Automate CDS connector. Using Power Automate Mock-Up as the flow engine and XrmMockup as Dynamics engine.
This is a full featured mock for the Common Date Service (current environment) connector for Power Automate.
The mock is built using Power Automate Mock-Up as the flow engine and XrmMockup to mock the underlying Dynamics 365 instance.
First of all, upgrade your XrmMockup with version 1.7.1 or higher.
When configuring XrmMockup, add the following to the XrmMockupSettings
(Only supported in XrmMockup365):
MockUpExtensions = new List<IMockUpExtension> {_pamuCds}
Somewhere before the XrmMockup configure step, do the following to setup Power Automate Mock-Up and add PAMU_CDS to the service collection:
var services = new ServiceCollection();
services.AddFlowRunner();
services.AddPamuCds();
var sp = services.BuildServiceProvider();
_pamuCds = sp.GetRequiredService<XrmMockupCdsTrigger>();
_pamuCds.AddFlows(new Uri(System.IO.Path.GetFullPath(@"Workflows")));
That's all. The flows in the folder will be executed like they would on the server and the actions will be triggered from XrmMockup.
Now you can run your unit tests and the action executed in Power Automate flow will also be executed now, against your mock instance.
One way to get the flows, is to export the soltuion containing the flows, then unzip and extract the flows to the desired location.
If you are using XrmFramework or if you're using Daxif, you can execute the F#
script availible here.
Depending on the location of the flows, they might have to be included in the .csproj
. If the flows are placed in a directory inside the test project, in a folder named flows
, simply add the following ItemGroup
, to copy the flows when building:
<ItemGroup>
<Content Include="flows\**\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
PAMU_CDS can be configured to behave in a certain way. PAMU can also be configured, see here
Add the name of the flow description file, to ignore the flow when triggering from flow from XrmMockup.
services.Configure<CdsFlowSettings>(x =>
x.DontExecuteFlows = new[] {"<flow description file name>.json"});
The focus right now is to create an MVP to use in my bachelor project, thus not all functions will be implemented for the moment. I will later create a description of how to contribute to this project, but not before the assignment have been handed in.
As with PAMU, you can add actions using one of the three extension methods
services.AddFlowActionByName<GetMsnWeather>("Get_forecast_for_today_(Metric)");
services.AddFlowActionByApiIdAndOperationsName<Notification>(
"/providers/Microsoft.PowerApps/apis/shared_flowpush",
new []{ "SendEmailNotification", "SendNotification" });
services.AddFlowActionByFlowType<IfActionExecutor>("If");
A more detialed guide is availible at PAMU#Actions.
Every call against CDS returns a JSON object with headers and body. Headers will not be generated, as the MVP does not support the use case.
The body will almost be as the real deal, but with minor deviations. They are described below.
- ✔ Action is intended to work 100% as the real thing, bugs might appear
- ❗ Limited functionality. Action will work, but some logic is not implemented, yet
- ❌ Action is not implemented
This action could be implemented using the ExecuteTransactionRequest. However, XrmMockup does support the request and no issue is currently actively tracking this.
This action is not implemented as it works in Power Automate.
CDS actions inside the change set action will be executed. If one of the CDS actions fails, the change set action will fail as well, piggy backing the error from the failing CDS action. Actions following the failing action inside the change set action will not be executed. Changes made in D365 will still exists, meaning it is not a transaction.
This is basically the saem as a scope. The ScopeActionExecutor
is used, to mock the behaviour.
This action supports 4 parameters
- Entity name - Table name
- Entity id - row id
- $select
- $expand
The select query is rather easy, since it is just are list of strings delimited by a comma, which can easily be converted to a ColumnSet.
The expand query is a bit more complex. It's documentation can be seen here.
Since the input of expand can have different forms, interpreting in can be a bit difficult. You cannot create a simple split string by comma, since this will not divide the odata filters corrext. Instead I have created a parser, similar to ExpressionParser in PAMU, but more simple. At the moment, the Parser does not cover all cases, but it's sufficient enough to cover the base case in the MVP.
The grammer for the parser is:
$expand=something($select=prop1,prop2),something2($select=prop1,prop2;$orderby=prop func;$filter=endswith(subhect,'1'))
<values> ::= <value> *(,<value>) <!-- something($select=prop1,prop2),something2($select=prop1,prop2;$filter=prop func) -->
<value> ::= <string> *(<parameters>) <!-- something2($select=prop1,prop2;orderby=prop func) -->
<parameters> ::= '('<parameter> *(;<paramters>)')' <!-- ($select=prop1,prop2;orderby=prop func) -->
<paramter> ::= $<string>=(<properties>|<function>) <!-- $select=prop1,prop2 -->
<properties> ::= <string> *(,<string>) <!-- prop1,prop2 -->
<function> ::= <string> '('+(<string>)')' <!-- endswith(subhect,'1') -->
Action is not implemented and will not be implemented in the near future.
Currently the list records in converted to a QueryExpression, but it should be converted to a FetchXML instead, since FetchXML cover more of the features of Odata than QueryExpression.
Name | Key | Required | Type | Description | |
---|---|---|---|---|---|
✔ | Entity name | entityName | True | string | Choose an option or add your own |
✔ | Select Query | $select | string | Limit the properties returned while retrieving data. | |
❗ | Filter Query | $filter | string | An ODATA filter query to restrict the entries returned (e.g. stringColumn eq 'string' OR numberColumn lt 123). | |
❗ | Order By | $orderby | string | An ODATA orderBy query for specifying the order of entries. | |
❌ | Expand Query | $expand | string | Related entries to include with requested entries (default = none). | |
✔ | Fetch Xml Query | fetchXml | string | Fetch Xml query | |
✔ | Top Count | $top | integer | Total number of entries to retrieve (default = all). | |
❗ | Skip token | $skiptoken | string | The skip token. |
Skip token, as well as Odata.nextLink on response will not be implemented.
Fetch Xml Expression will simple be a FetchExpression
instead of a QueryExpression. The correctness of FetchExpression
will depend on XrmMockup.
The filter query is made in OData in Power Automate. I have written a small Odata parser, which parses the Odata query to a FilterExpression, but not to a full extend.
Every Condition Operator, i.e. eq, ne, lt, is supported for strings, integers, decimals, booleans and null. The functions Startswith, Endswith and substringof is supported, the others are not supported, as they cannot be easily mapped to a FilterExpression.
The CFG in EBNF for the parser is:
or ::= and ('or' or)+
and ::= stm ('and' and)+
stm ::= func | '(' or ')' | attr op val
val ::= func | const
op ::= eq ne ...
attr ::= string
func ::= string '(' params ')'
Order By is not working as in Power Automate, simply because the QueryExpression does not support ordering of linked entities.
Is not supported at the moment.
Not supported.
XrmMockup does not support actions. If you want support check support custom action plugins #65.
XrmMockup does not support actions. If you want support check support custom action plugins #65.
Action is not implemented and will not be implemented in the near future.
Action is not implemented and will not be implemented in the near future.
XrmMockupCdsTrigger is a class to trigger a set of flows, based on the Request made to Dynamics. XrmMockupCdsTrigger is built to be used togehter with XrmMockup.
The trigger will apply a filters from the trigger, but only with limited functionality for now.
The code is written using Riders default C# code style.
Commits are written in conventional commit style, the commit messages are used to determine the version and when to release a new version. The pipeline is hosted on Github and Semantic Release is used.
Currently the project is still in alpha. To find the packages at nuget.com, you have to check 'Prerelease', before the nuget appears.
You also need XrmMockup to get the full functionality togehter with Dynamics 365 customizations. If you don't use XrmMockup or don't want to, you can provide a mock of IOrganizationService and add it to the service collection.
Tests are located in the Tests project and they are written using Nunit as test framework.
This is my bachelor project and I'm currently not accepting contributions until it have been handed in. Anyway, fell free to drop an issue with a suggestion or improvement.
Delegate A/S and the team behind XrmMockup.
MIT