Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue/4/associate #7

Merged
merged 14 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Build

on:
pull_request:
types: [assigned, opened, synchronize, reopened]

jobs:
test:
runs-on: windows-latest
name: Testing
steps:
- name: Checkout code base
uses: actions/checkout@v2

- name: Add nuget Source
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: nuget sources Add -Name Github -Source https://nuget.pkg.github.com/thygesteffensen/index.json -UserName thygesteffensen -Password $env:GH_TOKEN

- name: Set Github nuget API
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: nuget setapikey $env:GH_TOKEN -Source https://nuget.pkg.github.com/thygesteffensen/index.json

- name: Run tests
run: dotnet test --verbosity normal

- name: Run tests
run: dotnet test --verbosity normal

build:
runs-on: windows-latest
name: Building
steps:
- name: Checkout code base
uses: actions/checkout@v2

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1

- name: Add nuget Source
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: nuget sources Add -Name Github -Source https://nuget.pkg.github.com/thygesteffensen/index.json -UserName thygesteffensen -Password $env:GH_TOKEN

- name: Set Github nuget API
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: nuget setapikey $env:GH_TOKEN -Source https://nuget.pkg.github.com/thygesteffensen/index.json

- name: Run tests
run: dotnet test --verbosity normal

- name: Restore NuGet packages
run: nuget restore PAMU_CDS.sln

- name: Build solution
run: msbuild /p:OutputPath=../build /p:Configuration=Release /p:RestorePackages=false

- name: Archive build to artifacts
uses: actions/upload-artifact@v2
with:
name: build
path: |
build/PAMU_CDS.dll
retention-days: 5
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,8 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

# End of https://www.toptal.com/developers/gitignore/api/rider,csharp
# End of https://www.toptal.com/developers/gitignore/api/rider,csharp
PAMU_CDS.sln.DotSettings
PAMU_CDS/Properties/
build/*.dll
build/*.config
2 changes: 2 additions & 0 deletions PAMU_CDS/Actions/CreateRecordAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace PAMU_CDS.Actions
{
public class CreateRecordAction : OpenApiConnectionActionExecutorBase
{
public const string OperationId = "CreateRecord";

private readonly IOrganizationService _organizationService;
private readonly IState _state;

Expand Down
2 changes: 2 additions & 0 deletions PAMU_CDS/Actions/DeleteRecordAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace PAMU_CDS.Actions
{
public class DeleteRecordAction : OpenApiConnectionActionExecutorBase
{
public const string OperationId = "CreateRecord";

private readonly IOrganizationService _organizationService;

public DeleteRecordAction(IExpressionEngine expressionEngine, IOrganizationService organizationService) : base(
Expand Down
91 changes: 91 additions & 0 deletions PAMU_CDS/Actions/DisAndAssociateEntitiesAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using PAMU_CDS.Auxiliary;
using Parser.ExpressionParser;
using Parser.FlowParser.ActionExecutors;

namespace PAMU_CDS.Actions
{
public class DisAndAssociateEntitiesAction : OpenApiConnectionActionExecutorBase
{
private const string AssociateId = "AssociateEntities";
private const string DisassociateId = "DisassociateEntities";
public static readonly string[] OperationId = {AssociateId, DisassociateId};

private readonly IOrganizationService _organizationService;

public DisAndAssociateEntitiesAction(
IExpressionEngine expressionEngine,
IOrganizationService organizationService) : base(expressionEngine)
{
_organizationService = organizationService ?? throw new ArgumentNullException(nameof(organizationService));
}


public override Task<ActionResult> Execute()
{
var entity = new Entity();
entity = entity.CreateEntityFromParameters(Parameters);

OrganizationRequest associateRequest;

switch (Host.OperationId)
{
case AssociateId:
{
var relatedEntity = ExtractEntityReferenceFromOdataId("item/@odata.id");
associateRequest = new AssociateRequest
{
Target = entity.ToEntityReference(),
Relationship = new Relationship(Parameters["associationEntityRelationship"].GetValue<string>()),
RelatedEntities = new EntityReferenceCollection {relatedEntity}
};
break;
}
case DisassociateId:
{
var relatedEntity = ExtractEntityReferenceFromOdataId("$id");

associateRequest = new DisassociateRequest
{
Target = entity.ToEntityReference(),
Relationship = new Relationship(Parameters["associationEntityRelationship"].GetValue<string>()),
RelatedEntities = new EntityReferenceCollection {relatedEntity}
};
break;
}
default:
throw new PowerAutomateException(
$"Action {nameof(DisAndAssociateEntitiesAction)} can only handle {AssociateId} and {DisassociateId} operations, not {Host.OperationId}.");
}

try
{
// TODO: Figure out how this handle bad associations and error handling.
// assignees: thygesteffensen
_organizationService.Execute(associateRequest);
}
catch (InvalidPluginExecutionException)
{
// We need to do some experiments on how the error handling works. Take a look at one of your customers.
return Task.FromResult(new ActionResult {ActionStatus = ActionStatus.Failed});
}

return Task.FromResult(new ActionResult {ActionStatus = ActionStatus.Succeeded});
}

private EntityReference ExtractEntityReferenceFromOdataId(string itemKey)
{
// https://dglab6.crm4.dynamics.com/api/data/v9.1/contacts(8c711383-b933-eb11-a813-000d3ab11761)

var oDataId = Parameters[itemKey].GetValue<string>();
var entityName =
oDataId.Substring(oDataId.LastIndexOf('/') + 1, oDataId.IndexOf('(') - oDataId.LastIndexOf('/') - 2);
var entityId = oDataId.Substring(oDataId.IndexOf('(') + 1, oDataId.IndexOf(')') - oDataId.IndexOf('(') - 1);

return new EntityReference(entityName, new Guid(entityId));
}
}
}
2 changes: 2 additions & 0 deletions PAMU_CDS/Actions/GetItemAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace PAMU_CDS.Actions
{
public class GetItemAction : OpenApiConnectionActionExecutorBase
{
public const string OperationId = "GetItem";

private readonly IOrganizationService _organizationService;
private readonly IState _state;
private readonly ILogger<GetItemAction> _logger;
Expand Down
2 changes: 2 additions & 0 deletions PAMU_CDS/Actions/UpdateRecordAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace PAMU_CDS.Actions
{
public class UpdateRecordAction : OpenApiConnectionActionExecutorBase
{
public const string OperationId = "UpdateRecord";

private readonly IOrganizationService _organizationService;
private readonly IState _state;

Expand Down
20 changes: 15 additions & 5 deletions PAMU_CDS/CommonDataServiceCurrentEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public CommonDataServiceCurrentEnvironment(Uri flowFolderPath)
using var scope = sp.CreateScope();
var isp = scope.ServiceProvider;
var state = sp.GetRequiredService<IState>();

state.AddTriggerOutputs(currentEntity.ToValueContainer());

var flowRunner = sp.GetRequiredService<FlowRunner>();
Expand Down Expand Up @@ -119,10 +119,20 @@ private static ServiceCollection BuildServiceCollection(IOrganizationService org
services.AddFlowActionByApiIdAndOperationsName<CdsTrigger>(apiId,
new[] {"SubscribeWebhookTrigger"});

services.AddFlowActionByApiIdAndOperationsName<CreateRecordAction>(apiId, new[] {"CreateRecord"});
services.AddFlowActionByApiIdAndOperationsName<UpdateRecordAction>(apiId, new[] {"UpdateRecord"});
services.AddFlowActionByApiIdAndOperationsName<DeleteRecordAction>(apiId, new[] {"DeleteRecord"});
services.AddFlowActionByApiIdAndOperationsName<GetItemAction>(apiId, new[] {"GetItem"});
services.AddFlowActionByApiIdAndOperationsName<CreateRecordAction>(apiId,
new[] {CreateRecordAction.OperationId});

services.AddFlowActionByApiIdAndOperationsName<UpdateRecordAction>(apiId,
new[] {UpdateRecordAction.OperationId});

services.AddFlowActionByApiIdAndOperationsName<DeleteRecordAction>(apiId,
new[] {DeleteRecordAction.OperationId});

services.AddFlowActionByApiIdAndOperationsName<GetItemAction>(apiId,
new[] {GetItemAction.OperationId});

services.AddFlowActionByApiIdAndOperationsName<DisAndAssociateEntitiesAction>(apiId,
DisAndAssociateEntitiesAction.OperationId);
// services.AddFlowActionByFlowType<CreateRecordAction>("ExecuteChangeset");
// services.AddFlowActionByFlowType<CreateRecordAction>("ListRecords");
// // services.AddFlowActionByFlowType<>("PerformBoundAction");
Expand Down
32 changes: 16 additions & 16 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,14 @@ This is both a full featured mock and an example of how to use [Power Automate M

This mock i build using [Power Automate Mockup](https://github.com/thygesteffensen/PowerAutomateMockup) as the flow engine and [XrmMockup](https://github.com/delegateas/XrmMockup) to mock the underlying Dynamics 365.

## Code style
The code is written using [Riders](https://www.jetbrains.com/help/rider/Settings_Code_Style_CSHARP.html) default C# code style.

Commits 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.

## 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 a modified version of [XrmMockup](https://github.com/delegateas/XrmMockup), you can find that [here]()

## Tests

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

## How to use

### Introduction
This is a fully featured mock for the CDS ce connector and it works OOB if you're already using XrmMockup to test your Dynamics 365 plugins. If not, you can still use this, but you will also need to set up XrmMockup.

### Getting Started

First of all, replace your XrmMockup dependency with the development version developed to this project. The development version is build on the latest version of XrmMockup
First of all, replace your XrmMockup dependency with the development version developed to this project. The development version is build on the latest version of XrmMockup.

When configuring XrmMockup, add the following:
```c#
Expand All @@ -74,6 +59,21 @@ Coming soon
### Asserting Actions
Coming soon

## Code style
The code is written using [Riders](https://www.jetbrains.com/help/rider/Settings_Code_Style_CSHARP.html) default C# code style.

Commits 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.

## 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 a modified version of [XrmMockup](https://github.com/delegateas/XrmMockup), you can find that [here]()

## 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.
Expand Down
10 changes: 6 additions & 4 deletions Test/Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<TargetFrameworks>net48;net462</TargetFrameworks>

<IsPackable>false</IsPackable>

<ResolveAssemblyReferenceIgnoreTargetFrameworkAttributeVersionMismatch>true</ResolveAssemblyReferenceIgnoreTargetFrameworkAttributeVersionMismatch>
</PropertyGroup>

<ItemGroup>
Expand All @@ -17,13 +19,13 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PAMU_CDS\PAMU_CDS.csproj" />
<ProjectReference Include="..\PAMU_CDS\PAMU_CDS.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="TestFlows\Pure_CDS_ce.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestFlows\Pure_CDS_ce.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading