{"id":17822080,"url":"https://github.com/thygesteffensen/pamu_cds","last_synced_at":"2026-02-26T11:33:20.425Z","repository":{"id":54429604,"uuid":"313557203","full_name":"thygesteffensen/PAMU_CDS","owner":"thygesteffensen","description":"This is a full-featured mock for Power Automate Common Data Service (current environment) connector build using PAMU.","archived":false,"fork":false,"pushed_at":"2023-05-18T13:15:23.000Z","size":4735,"stargazers_count":7,"open_issues_count":4,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-11-06T21:16:06.336Z","etag":null,"topics":["common-data-service","mock","pamu","power-automate"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thygesteffensen.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-17T08:40:31.000Z","updated_at":"2023-08-10T21:15:22.000Z","dependencies_parsed_at":"2024-10-27T18:26:31.411Z","dependency_job_id":null,"html_url":"https://github.com/thygesteffensen/PAMU_CDS","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/thygesteffensen/PAMU_CDS","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPAMU_CDS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPAMU_CDS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPAMU_CDS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPAMU_CDS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thygesteffensen","download_url":"https://codeload.github.com/thygesteffensen/PAMU_CDS/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thygesteffensen%2FPAMU_CDS/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29857551,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["common-data-service","mock","pamu","power-automate"],"created_at":"2024-10-27T17:34:59.637Z","updated_at":"2026-02-26T11:33:20.406Z","avatar_url":"https://github.com/thygesteffensen.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eCommon Data Service (current environment) mock\u003c/h1\u003e\n\u003ch2 align=\"center\"\u003e\u003ca href=\"https://github.com/thygesteffensen/PowerAutomateMockUp\"\u003ePower Automate Mock-Up\u003c/a\u003e\u003c/h2\u003e\n\u003ch3 align=\"center\"\u003eBattery included mock for Power Automate CDS connector. Using \u003ca href=\"https://github.com/thygesteffensen/PowerAutomateMockUp\"\u003ePower Automate Mock-Up\u003c/a\u003e as the flow engine and \u003ca href=\"http://github.com/delegateas/XrmMockup\"\u003eXrmMockup\u003c/a\u003e as Dynamics engine.\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n        \u003cimg alt=\"Build status\" src=\"https://github.com/thygesteffensen/PAMU_CDS/actions/workflows/release.yml/badge.svg?branch=main\"\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PAMU_CDSce/\"\u003e\n        \u003cimg alt=\"Nuget downloads\" src=\"https://img.shields.io/nuget/dt/PAMU_CDSce\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PAMU_CDSce/\"\u003e\n        \u003cimg alt=\"Nuget version\" src=\"https://img.shields.io/nuget/v/PAMU_CDSce\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.nuget.org/packages/PAMU_CDSce/\"\u003e\n        \u003cimg alt=\"Nuget prerelease version\" src=\"https://img.shields.io/nuget/vpre/PAMU_CDSce\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\nThis is a full featured mock for the [Common Date Service (current environment)](https://docs.microsoft.com/en-us/connectors/commondataserviceforapps/) connector for Power Automate.\n\nThe mock is built using [Power Automate Mock-Up](https://github.com/thygesteffensen/PowerAutomateMockup) as the flow engine and [XrmMockup](https://github.com/delegateas/XrmMockup) to mock the underlying Dynamics 365 instance.\n\n## Getting Started\n\nFirst of all, upgrade your XrmMockup with version [1.7.1](https://www.nuget.org/packages/XrmMockup365/) or higher.\n\nWhen configuring XrmMockup, add the following to the `XrmMockupSettings` (Only supported in XrmMockup365):\n```c#\nMockUpExtensions = new List\u003cIMockUpExtension\u003e {_pamuCds}\n```\n\nSomewhere before the XrmMockup configure step, do the following to setup Power Automate Mock-Up and add PAMU_CDS to the service collection:\n```c#\nvar services = new ServiceCollection();\nservices.AddFlowRunner();\nservices.AddPamuCds();\n\nvar sp = services.BuildServiceProvider();\n\n_pamuCds = sp.GetRequiredService\u003cXrmMockupCdsTrigger\u003e();\n_pamuCds.AddFlows(new Uri(System.IO.Path.GetFullPath(@\"Workflows\")));\n```\n\nThat's all. The flows in the folder will be executed like they would on the server and the actions will be triggered from XrmMockup.\n\nNow you can run your unit tests and the action executed in Power Automate flow will also be executed now, against your mock instance.\n\n### Download flows\nOne way to get the flows, is to export the soltuion containing the flows, then unzip and extract the flows to the desired location.\n\nIf you are using [XrmFramework](https://github.com/delegateas/XrmFramework) or if you're using [Daxif](https://github.com/delegateas/Daxif), you can execute the `F#` script availible [here](https://github.com/thygesteffensen/FlowUnitTester/blob/main/DG/DG.FlowUnitTester/Tools/Daxif/DownloadWorkflows.fsx).\n\nDepending 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:\n\n```xml\n\u003cItemGroup\u003e\n    \u003cContent Include=\"flows\\**\\*.json\"\u003e\n        \u003cCopyToOutputDirectory\u003ePreserveNewest\u003c/CopyToOutputDirectory\u003e\n    \u003c/Content\u003e\n\u003c/ItemGroup\u003e\n```\n\n### Configuring\nPAMU_CDS can be configured to behave in a certain way. PAMU can also be configured, see [here](https://github.com/thygesteffensen/PowerAutomateMockUp#configuration)\n\n#### Do not execute flows\nAdd the name of the flow description file, to ignore the flow when triggering from flow from XrmMockup.\n\n```cs\nservices.Configure\u003cCdsFlowSettings\u003e(x =\u003e \n    x.DontExecuteFlows = new[] {\"\u003cflow description file name\u003e.json\"});\n```\n\n## Actions\n\nThe 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.\n\n|    | **Action**|\n|----|----------|\n| ✔ | [Create a new record](#create-a-new-record-)\n| ✔ | [Delete a record](#delete-a-record-)\n| ❗ | [Execute a changeset request](#executes-a-changeset-request-)\n| ❗ | [Get a record](#get-a-record-)\n| ❌ | [Get file or image content](#get-file-or-image-content-)\n| ❗ | [List records](#list-records-)\n| ❌ | [Perform a bound action](#perform-a-bound-action-)\n| ❌ | [Perform a unbound action](#perform-an-unbound-action-)\n| ✔ | [Releate records](#relate-records-)\n| ✔ | [Unrelate records](#unrelate-records-)\n| ✔ | [Update a record](#update-record-)\n| ❌ | [Upload file or image content](#upload-file-or-image-content-)\n\n### Unsupported actions\nAs with [PAMU](https://github.com/thygesteffensen/PowerAutomateMockup), you can add actions using one of the three extension methods\n```cs\nservices.AddFlowActionByName\u003cGetMsnWeather\u003e(\"Get_forecast_for_today_(Metric)\");\nservices.AddFlowActionByApiIdAndOperationsName\u003cNotification\u003e(\n    \"/providers/Microsoft.PowerApps/apis/shared_flowpush\", \n    new []{ \"SendEmailNotification\", \"SendNotification\" });\nservices.AddFlowActionByFlowType\u003cIfActionExecutor\u003e(\"If\");\n```\n\nA more detialed guide is availible at [PAMU#Actions](https://github.com/thygesteffensen/PowerAutomateMockUp/tree/dev#adding-actions).\n\n### General\nEvery 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.\n\nThe body will almost be as the real deal, but with minor deviations. They are described below.\n\n#### Symbol meaning:\n\n- ✔ Action is intended to work 100% as the real thing, bugs might appear\n- ❗ Limited functionality. Action will work, but some logic is not implemented, yet\n- ❌ Action is not implemented\n\n### Create a new record ✔\n\n### Delete a record ✔ \n\n### Executes a changeset request ❗\n\nThis action could be implemented using the [ExecuteTransactionRequest](https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.messages.executetransactionrequest?view=dynamics-general-ce-9). However, XrmMockup does support the request and no issue is currently actively tracking this.\n\nThis action is not implemented as it works in Power Automate.\n\nCDS actions inside the change set action will be executed.\nIf 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.\nChanges made in D365 will still exists, meaning it is not a transaction.\n\nThis is basically the saem as a scope. The `ScopeActionExecutor` is used, to mock the behaviour.\n\n### Get a record ❗\n\nThis action supports 4 parameters\n1. Entity name - Table name\n2. Entity id - row id\n3. $select \n4. $expand\n\n#### 3. $select\nThe 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.\n\n#### 4. $expand\nThe expand query is a bit more complex. It's documentation can be seen [here](https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/webapi/retrieve-entity-using-web-api#retrieve-related-entities-for-an-entity-by-expanding-navigation-properties). \n\nSince 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](https://github/com/thygesteffensen/PowerAutomateMockUp), but more simple.\nAt the moment, the Parser does not cover all cases, but it's sufficient enough to cover the base case in the MVP.\n\nThe grammer for the parser is:\n```\n$expand=something($select=prop1,prop2),something2($select=prop1,prop2;$orderby=prop func;$filter=endswith(subhect,'1'))\n```\n\n````xml\n\u003cvalues\u003e        ::= \u003cvalue\u003e *(,\u003cvalue\u003e)                 \u003c!-- something($select=prop1,prop2),something2($select=prop1,prop2;$filter=prop func) --\u003e\n\n\u003cvalue\u003e         ::= \u003cstring\u003e *(\u003cparameters\u003e)            \u003c!-- something2($select=prop1,prop2;orderby=prop func) --\u003e\n\n\u003cparameters\u003e    ::= '('\u003cparameter\u003e *(;\u003cparamters\u003e)')'   \u003c!-- ($select=prop1,prop2;orderby=prop func) --\u003e\n\u003cparamter\u003e      ::= $\u003cstring\u003e=(\u003cproperties\u003e|\u003cfunction\u003e) \u003c!-- $select=prop1,prop2 --\u003e\n\n\u003cproperties\u003e    ::= \u003cstring\u003e *(,\u003cstring\u003e)               \u003c!-- prop1,prop2 --\u003e\n\u003cfunction\u003e      ::= \u003cstring\u003e '('+(\u003cstring\u003e)')'          \u003c!-- endswith(subhect,'1') --\u003e \n````\n\n### Get file or image content ❌\nAction is not implemented and will not be implemented in the near future.\n\n### List records ❗\nCurrently 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.\n\n|   | **Name**        | **Key**    | **Required** | **Type** | **Description**                                                                                                |\n|---|-----------------|------------|--------------|----------|----------------------------------------------------------------------------------------------------------------|\n| ✔ | Entity name     | entityName | True         | string   | Choose an option or add your own                                                                               |\n| ✔ | Select Query    | $select    |              | string   | Limit the properties returned while retrieving data.                                                           |\n| ❗ | Filter Query    | $filter    |              | string   | An ODATA filter query to restrict the entries returned (e.g. stringColumn eq 'string' OR numberColumn lt 123). |\n| ❗ | Order By        | $orderby   |              | string   | An ODATA orderBy query for specifying the order of entries.                                                    |\n| ❌ | Expand Query    | $expand    |              | string   | Related entries to include with requested entries (default = none).                                            |\n| ✔ | Fetch Xml Query | fetchXml   |              | string   | Fetch Xml query                                                                                                |\n| ✔ | Top Count       | $top       |              | integer  | Total number of entries to retrieve (default = all).                                                           |\n| ❗ | Skip token      | $skiptoken |              | string   | The skip token.                                                                                                |\n\nSkip token, as well as Odata.nextLink on response will not be implemented.\n\nFetch Xml Expression will simple be a `FetchExpression` instead of a QueryExpression. The correctness of `FetchExpression` will depend on XrmMockup.\n\n\n#### Filter Query\nThe 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.\n\nEvery Condition Operator, i.e. eq, ne, lt, is supported for strings, integers, decimals, booleans and null.\nThe functions Startswith, Endswith and substringof is supported, the others are not supported, as they cannot be easily mapped to a FilterExpression. \n\nThe CFG in EBNF for the parser is:\n```xml\nor ::= and ('or' or)+\nand ::= stm ('and' and)+\nstm ::= func | '(' or ')' | attr op val\nval ::= func | const\nop ::= eq ne ...\nattr ::= string\nfunc ::= string '(' params ')'\n```\n\n#### Order By\nOrder By is not working as in Power Automate, simply because the QueryExpression does not support ordering of linked entities. \n\n#### Expand Query\nIs not supported at the moment.\n\n#### Skip token\nNot supported.\n\n### Perform a bound action ❌\nXrmMockup does not support [actions](https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/customize/actions). If you want support check [support custom action plugins #65](https://github.com/delegateas/XrmMockup/issues/65).\n\n### Perform an unbound action ❌\nXrmMockup does not support [actions](https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/customize/actions). If you want support check [support custom action plugins #65](https://github.com/delegateas/XrmMockup/issues/65).\n\n\n### Predict ❌\nAction is not implemented and will not be implemented in the near future.\n\n### Relate records ✔\n\n### Unrelate records ✔\n\n### Update record ✔\n\n### Upload file or image content ❌\nAction is not implemented and will not be implemented in the near future.\n\n## Trigger\n\nXrmMockupCdsTrigger 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.\n\nThe trigger will apply a filters from the trigger, but only with limited functionality for now.\n\n## Code style\nThe code is written using [Riders](https://www.jetbrains.com/help/rider/Settings_Code_Style_CSHARP.html) default C# code style.\n\nCommits are written in [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) 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](https://github.com/semantic-release/semantic-release) is used.\n\n## Installation\n\nCurrently the project is still in alpha. To find the packages at nuget.com, you have to check 'Prerelease', before the nuget appears.\n\nYou also need [XrmMockup](https://github.com/delegateas/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.\n\n## Tests\n\nTests are located in the **Tests** project and they are written using Nunit as test framework.\n\n## Contribute\n\nThis 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.\n\n## Credits\nDelegate A/S and the team behind XrmMockup.\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthygesteffensen%2Fpamu_cds","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthygesteffensen%2Fpamu_cds","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthygesteffensen%2Fpamu_cds/lists"}