PAMU_CDSce 1.1.3

dotnet add package PAMU_CDSce --version 1.1.3                
NuGet\Install-Package PAMU_CDSce -Version 1.1.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PAMU_CDSce" Version="1.1.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add PAMU_CDSce --version 1.1.3                
#r "nuget: PAMU_CDSce, 1.1.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install PAMU_CDSce as a Cake Addin
#addin nuget:?package=PAMU_CDSce&version=1.1.3

// Install PAMU_CDSce as a Cake Tool
#tool nuget:?package=PAMU_CDSce&version=1.1.3                

<h1 align="center">Common Data Service (current environment) mock</h1> <h2 align="center"><a href="https://github.com/thygesteffensen/PowerAutomateMockUp">Power Automate Mock-Up</a></h2> <h3 align="center">Battery included mock for Power Automate CDS connector. Using <a href="https://github.com/thygesteffensen/PowerAutomateMockUp">Power Automate Mock-Up</a> as the flow engine and <a href="http://github.com/delegateas/XrmMockup">XrmMockup</a> as Dynamics engine.</h3> <p align="center"> <img alt="Build status" src="https://github.com/thygesteffensen/PAMU_CDS/actions/workflows/release.yml/badge.svg?branch=main"> <a href="https://www.nuget.org/packages/PAMU_CDSce/"> <img alt="Nuget downloads" src="https://img.shields.io/nuget/dt/PAMU_CDSce"> </a> <a href="https://www.nuget.org/packages/PAMU_CDSce/"> <img alt="Nuget version" src="https://img.shields.io/nuget/v/PAMU_CDSce"> </a> <a href="https://www.nuget.org/packages/PAMU_CDSce/"> <img alt="Nuget prerelease version" src="https://img.shields.io/nuget/vpre/PAMU_CDSce"> </a> </p>

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.

Getting Started

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.

Download flows

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>

Configuring

PAMU_CDS can be configured to behave in a certain way. PAMU can also be configured, see here

Do not execute flows

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"});

Actions

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.

Action
Create a new record
Delete a record
Execute a changeset request
Get a record
Get file or image content
List records
Perform a bound action
Perform a unbound action
Releate records
Unrelate records
Update a record
Upload file or image content

Unsupported actions

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.

General

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.

Symbol meaning:
  • ✔ 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

Create a new record ✔

Delete a record ✔

Executes a changeset request ❗

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.

Get a record ❗

This action supports 4 parameters

  1. Entity name - Table name
  2. Entity id - row id
  3. $select
  4. $expand
3. $select

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.

4. $expand

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>)                 

<value>         ::= <string> *(<parameters>)            

<parameters>    ::= '('<parameter> *(;<paramters>)')'   
<paramter>      ::= $<string>=(<properties>|<function>) 

<properties>    ::= <string> *(,<string>)               
<function>      ::= <string> '('+(<string>)')'           

Get file or image content ❌

Action is not implemented and will not be implemented in the near future.

List records ❗

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.

Filter Query

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

Order By is not working as in Power Automate, simply because the QueryExpression does not support ordering of linked entities.

Expand Query

Is not supported at the moment.

Skip token

Not supported.

Perform a bound action ❌

XrmMockup does not support actions. If you want support check support custom action plugins #65.

Perform an unbound action ❌

XrmMockup does not support actions. If you want support check support custom action plugins #65.

Predict ❌

Action is not implemented and will not be implemented in the near future.

Relate records ✔

Unrelate records ✔

Update record ✔

Upload file or image content ❌

Action is not implemented and will not be implemented in the near future.

Trigger

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.

Code style

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.

Installation

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

Tests are located in the Tests project and they are written using Nunit as test framework.

Contribute

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.

Credits

Delegate A/S and the team behind XrmMockup.

License

MIT

Product Compatible and additional computed target framework versions.
.NET Framework net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.3 181 5/18/2023
1.1.3-patch 90 5/17/2023
1.1.3-alpha.1 81 5/18/2023
1.1.2 104 5/17/2023
1.1.2-patch 81 5/17/2023
1.1.2-alpha.1 82 5/17/2023
1.1.1 154 5/15/2023
1.1.1-alpha.2 82 5/15/2023
1.1.1-alpha.1 81 5/15/2023
1.1.0 104 5/15/2023
1.1.0-alpha.4 83 5/15/2023
1.1.0-alpha.3 84 5/15/2023
1.1.0-alpha.2 96 3/7/2023
1.1.0-alpha.1 96 3/7/2023
1.0.1 394 1/31/2021
1.0.0 356 1/30/2021
1.0.0-alpha.37 172 1/31/2021
1.0.0-alpha.36 165 1/30/2021
1.0.0-alpha.35 164 1/29/2021
1.0.0-alpha.34 165 1/29/2021
1.0.0-alpha.33 172 1/28/2021
1.0.0-alpha.32 159 1/28/2021
1.0.0-alpha.31 168 1/28/2021
1.0.0-alpha.30 167 1/28/2021
1.0.0-alpha.29 175 1/28/2021
1.0.0-alpha.28 155 1/28/2021
1.0.0-alpha.27 179 1/23/2021
1.0.0-alpha.26 204 1/21/2021
1.0.0-alpha.25 214 1/21/2021
1.0.0-alpha.24 202 1/21/2021
1.0.0-alpha.23 173 1/19/2021
1.0.0-alpha.22 183 1/19/2021
1.0.0-alpha.21 174 1/19/2021
1.0.0-alpha.20 225 1/16/2021
1.0.0-alpha.19 161 1/16/2021
1.0.0-alpha.18 164 1/16/2021
1.0.0-alpha.17 170 1/16/2021
1.0.0-alpha.16 183 1/16/2021
1.0.0-alpha.15 187 1/16/2021
1.0.0-alpha.14 213 1/14/2021
1.0.0-alpha.13 221 1/14/2021
1.0.0-alpha.12 170 1/13/2021
1.0.0-alpha.11 175 1/13/2021
1.0.0-alpha.10 210 1/13/2021
1.0.0-alpha.9 163 1/12/2021
1.0.0-alpha.8 195 1/12/2021
1.0.0-alpha.7 226 12/21/2020
1.0.0-alpha.6 229 12/21/2020
1.0.0-alpha.5 234 12/21/2020
1.0.0-alpha.4 202 12/8/2020
1.0.0-alpha.3 255 12/6/2020