diff --git a/.github/workflows/liquid-ci-cd-adapter-dataverse.yml b/.github/workflows/liquid-ci-cd-dataverse.yml
similarity index 73%
rename from .github/workflows/liquid-ci-cd-adapter-dataverse.yml
rename to .github/workflows/liquid-ci-cd-dataverse.yml
index f4a234e7..11d65912 100644
--- a/.github/workflows/liquid-ci-cd-adapter-dataverse.yml
+++ b/.github/workflows/liquid-ci-cd-dataverse.yml
@@ -1,17 +1,17 @@
# CI & CD workflow
-name: CI/CD - Liquid.Adapter.Dataverse component for Liquid Application Framework
+name: CI/CD - Liquid.Dataverse component for Liquid Application Framework
on:
push:
branches: [ main, releases/v2.X.X, releases/v6.X.X ]
paths:
- - 'src/Liquid.Adapter.Dataverse/**'
+ - 'src/Liquid.Dataverse/**'
pull_request:
branches: [ main, releases/** ]
types: [opened, synchronize, reopened]
paths:
- - 'src/Liquid.Adapter.Dataverse/**'
+ - 'src/Liquid.Dataverse/**'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -20,7 +20,7 @@ jobs:
call-reusable-build-workflow:
uses: Avanade/Liquid-Application-Framework/.github/workflows/base-liquid-ci-and-cd.yml@main
with:
- component_name: Liquid.Adapter.Dataverse
+ component_name: Liquid.Dataverse
secrets:
sonar_token: ${{ secrets.SONAR_TOKEN_DATAVERSE }}
nuget_token: ${{ secrets.PUBLISH_TO_NUGET_ORG }}
diff --git a/Liquid.Application.Framework.sln b/Liquid.Application.Framework.sln
index 7ee44681..32d4701e 100644
--- a/Liquid.Application.Framework.sln
+++ b/Liquid.Application.Framework.sln
@@ -63,9 +63,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.WebApi.Http.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Storage.AzureStorage", "src\Liquid.Storage.AzureStorage\Liquid.Storage.AzureStorage.csproj", "{F599C512-7224-4A27-A474-3AA9510D2A14}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Adapter.Dataverse", "src\Liquid.Adapter.Dataverse\Liquid.Adapter.Dataverse.csproj", "{CD31C6F6-F8DD-4B56-B08C-D9E8D5BA3E73}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Dataverse", "src\Liquid.Dataverse\Liquid.Dataverse.csproj", "{CD31C6F6-F8DD-4B56-B08C-D9E8D5BA3E73}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Adapter.Dataverse.Tests", "test\Liquid.Adapter.Dataverse.Tests\Liquid.Adapter.Dataverse.Tests.csproj", "{5B0DC38B-5BC9-4DAC-8527-8D1C33E97247}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Dataverse.Tests", "test\Liquid.Dataverse.Tests\Liquid.Dataverse.Tests.csproj", "{5B0DC38B-5BC9-4DAC-8527-8D1C33E97247}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Liquid.Storage.AzureStorage.Tests", "test\Liquid.Storage.AzureStorage.Tests\Liquid.Storage.AzureStorage.Tests.csproj", "{53341B04-6D30-4137-943B-20D8706351E8}"
EndProject
diff --git a/src/Liquid.Adapter.Dataverse/DataMappingException.cs b/src/Liquid.Adapter.Dataverse/DataMappingException.cs
deleted file mode 100644
index dd735104..00000000
--- a/src/Liquid.Adapter.Dataverse/DataMappingException.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.Serialization;
-
-namespace Liquid.Adapter.Dataverse
-{
- [Serializable]
- [ExcludeFromCodeCoverage]
- internal class DataMappingException : Exception
- {
- public DataMappingException()
- {
- }
-
- public DataMappingException(string? message) : base(message)
- {
- }
-
- public DataMappingException(string? message, Exception? innerException) : base(message, innerException)
- {
- }
-
- protected DataMappingException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/Liquid.Adapter.Dataverse/DataverseAdapter.cs b/src/Liquid.Adapter.Dataverse/DataverseAdapter.cs
deleted file mode 100644
index bf7137a1..00000000
--- a/src/Liquid.Adapter.Dataverse/DataverseAdapter.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using Microsoft.Crm.Sdk.Messages;
-using Microsoft.PowerPlatform.Dataverse.Client;
-using Microsoft.PowerPlatform.Dataverse.Client.Extensions;
-using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Messages;
-using Microsoft.Xrm.Sdk.Metadata;
-using Microsoft.Xrm.Sdk.Query;
-
-namespace Liquid.Adapter.Dataverse
-{
- ///
- public class DataverseAdapter : ILiquidDataverseAdapter
- {
- private readonly IDataverseClientFactory _serviceFactory;
- private readonly IOrganizationServiceAsync _client;
-
- ///
- /// Initialize a new instance of
- ///
- ///
- ///
- public DataverseAdapter(IDataverseClientFactory serviceFactory)
- {
- _serviceFactory = serviceFactory ?? throw new ArgumentNullException(nameof(serviceFactory));
- _client = _serviceFactory.GetClient();
- }
-
- public async Task GetById(Guid id, string entityName, ColumnSet? columns = null)
- {
- if (columns == null)
- columns = new ColumnSet(true);
-
- var result = await _client.RetrieveAsync(entityName, id, columns);
-
- return result;
- }
-
- ///
- public async Task> ListByFilter(string entityName, FilterExpression? filter = null, ColumnSet? columns = null)
- {
- List results = new List();
-
- QueryExpression queryData = new QueryExpression(entityName);
-
- if (filter != null)
- queryData.Criteria = filter;
-
- if (columns != null)
- queryData.ColumnSet = columns;
-
- var result = await _client.RetrieveMultipleAsync(queryData);
- if (result?.Entities != null)
- {
- foreach (var item in result.Entities)
- {
- results.Add(item);
- }
- }
-
- return results;
- }
-
- ///
- public async Task> ListByFilter(string entityName, QueryExpression query)
- {
- if (query is null)
- {
- throw new ArgumentNullException(nameof(query));
- }
-
- List results = new List();
-
- var result = await _client.RetrieveMultipleAsync(query);
-
- if (result?.Entities != null)
- {
- foreach (var item in result.Entities)
- {
- results.Add(item);
- }
- }
-
- return results;
- }
-
- ///
- public async Task GetMetadata(string entityName)
- {
- var retrieveEntityRequest = new RetrieveEntityRequest
- {
- EntityFilters = EntityFilters.All,
- LogicalName = entityName
- };
- var response = await _client.ExecuteAsync(retrieveEntityRequest);
- var metadata = (RetrieveEntityResponse)response;
- return metadata.EntityMetadata;
- }
-
- ///
- public async Task SetState(EntityReference entity, string state, string status)
- {
- var setStateRequest = new SetStateRequest()
- {
- EntityMoniker = new EntityReference
- {
- Id = entity.Id,
- LogicalName = entity.LogicalName,
- },
- State = new OptionSetValue(int.Parse(state)),
- Status = new OptionSetValue(int.Parse(status))
- };
-
- await _client.ExecuteAsync(setStateRequest);
- }
-
- ///
- public async Task Upsert(Entity entity)
- {
- var request = new UpsertRequest()
- {
- Target = entity
- };
-
- await _client.ExecuteAsync(request);
- }
-
- ///
- public Task Create(Entity targetEntity, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true)
- {
- var createRequest = new CreateRequest
- {
- Target = targetEntity
- };
-
- createRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousCustomLogic);
- createRequest.Parameters.Add("SuppressCallbackRegistrationExpanderJob", suppressPowerAutomateTrigger);
- createRequest.Parameters.Add("SuppressDuplicateDetection", suppressDuplicateDetectionRules);
-
- var resultCreate = (CreateResponse)_client.Execute(createRequest);
-
- return Task.FromResult(resultCreate.id);
- }
-
- ///
- public async Task Update(Entity entity, bool useOptimisticConcurrency = false, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true)
- {
- var updateRequest = new UpdateRequest
- {
- Target = entity
- };
-
- if (useOptimisticConcurrency)
- {
- updateRequest.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;
- }
- updateRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousCustomLogic);
- updateRequest.Parameters.Add("SuppressCallbackRegistrationExpanderJob", suppressPowerAutomateTrigger);
- updateRequest.Parameters.Add("SuppressDuplicateDetection", suppressDuplicateDetectionRules);
-
- await _client.ExecuteAsync(updateRequest);
- }
-
- ///
- public async Task Delete(Guid id, string entityName, bool bypassSynchronousLogic = false, bool useOptimisticConcurrency = false)
- {
- var deleteRequest = new DeleteRequest
- {
- Target = new EntityReference(entityName, id)
- };
-
- if (useOptimisticConcurrency)
- {
- deleteRequest.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;
- }
-
- deleteRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousLogic);
-
- await _client.ExecuteAsync(deleteRequest);
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Liquid.Adapter.Dataverse/ILiquidDataverseAdapter.cs b/src/Liquid.Adapter.Dataverse/ILiquidDataverseAdapter.cs
deleted file mode 100644
index 3df0b9fe..00000000
--- a/src/Liquid.Adapter.Dataverse/ILiquidDataverseAdapter.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Metadata;
-using Microsoft.Xrm.Sdk.Query;
-
-namespace Liquid.Adapter.Dataverse
-{
- ///
- /// Dataverse integration service definition.
- ///
- public interface ILiquidDataverseAdapter
- {
- ///
- /// Insert an using additional parameters to optionally prevent custom synchronous logic execution, suppress Power Automate trigger and enforce duplicate detection rules evaluation.
- ///
- /// entity definition.
- ///
- ///
- ///
- /// created entity Id.
- Task Create(Entity targetEntity, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true);
-
- ///
- /// Update an record using parameters to enforce optimistic concurrency, prevent custom synchronous logic execution, suppress Power Automate trigger, and enforce duplicate detection rules evaluation.
- ///
- /// entity definition.
- ///
- ///
- ///
- ///
- Task Update(Entity entity, bool useOptimisticConcurrency = false, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true);
-
- ///
- /// Read by .
- ///
- /// primarykey value.
- /// table name.
- /// column set should return.
- Task GetById(Guid id, string entityName, ColumnSet? columns = null);
-
- ///
- /// Read table according filter conditions.
- ///
- /// table name.
- /// query conditions.
- /// conlumn se should return.
- Task> ListByFilter(string entityName, FilterExpression filter, ColumnSet? columns = null);
-
- ///
- /// Exclude an item from table by primarykey optionally using parameters to prevent custom synchronous logic execution and enforce optimistic concurrency.
- ///
- /// primarykey value
- /// table name.
- ///
- ///
- Task Delete(Guid id, string entityName, bool bypassSynchronousLogic = false, bool useOptimisticConcurrency = false);
-
- ///
- /// Read table according query conditions.
- ///
- /// table name.
- /// query conditions
- Task> ListByFilter(string entityName, QueryExpression query);
-
- ///
- /// Read table properties.
- ///
- /// table name.
- /// set.
- Task GetMetadata(string entityName);
-
- ///
- /// Update state and status from an .
- ///
- /// entity reference.
- /// new state value.
- /// new status value.
- Task SetState(EntityReference entity, string state, string status);
-
- ///
- /// Insert or update an .
- ///
- /// entity definition.
- Task Upsert(Entity entity);
-
- }
-}
diff --git a/src/Liquid.Adapter.Dataverse/ILiquidMapper.cs b/src/Liquid.Adapter.Dataverse/ILiquidMapper.cs
deleted file mode 100644
index c9a7e866..00000000
--- a/src/Liquid.Adapter.Dataverse/ILiquidMapper.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Liquid.Adapter.Dataverse
-{
- ///
- /// Defines object that map data between two instance types.
- ///
- /// type of data source object.
- /// results object type.
- public interface ILiquidMapper
- {
- ///
- /// Create a new instance of
- /// with values obtained from .
- ///
- /// data source object instance.
- Task Map(TFrom dataObject, string? entityName = null);
- }
-}
diff --git a/src/Liquid.Adapter.Dataverse/LiquidMapper.cs b/src/Liquid.Adapter.Dataverse/LiquidMapper.cs
deleted file mode 100644
index 14fb5727..00000000
--- a/src/Liquid.Adapter.Dataverse/LiquidMapper.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-
-namespace Liquid.Adapter.Dataverse
-{
- ///
- [ExcludeFromCodeCoverage]
- public abstract class LiquidMapper : ILiquidMapper
- {
- private readonly string _mapperName;
-
- ///
- /// Create a new instance of
- ///
- /// Mapper implementation name.
- public LiquidMapper(string mapperName)
- {
- _mapperName = mapperName;
- }
- ///
- public async Task Map(TFrom dataObject, string? entityName = null)
- {
- if (dataObject is null)
- {
- throw new ArgumentNullException(nameof(dataObject));
- }
-
- try
- {
- return await MapImpl(dataObject, entityName);
- }
- catch (Exception e)
- {
- var msg = $"{_mapperName} throw data mapping error: '{e.Message}'";
-
- throw new DataMappingException(msg, e);
- }
- }
- ///
- ///
- ///
- ///
- ///
- ///
- protected abstract Task MapImpl(TFrom dataObject, string? entityName = null);
- }
-}
diff --git a/src/Liquid.Adapter.Dataverse/DataverseClientFactory.cs b/src/Liquid.Dataverse/DataverseClientFactory.cs
similarity index 85%
rename from src/Liquid.Adapter.Dataverse/DataverseClientFactory.cs
rename to src/Liquid.Dataverse/DataverseClientFactory.cs
index 7e97dac2..23aa552c 100644
--- a/src/Liquid.Adapter.Dataverse/DataverseClientFactory.cs
+++ b/src/Liquid.Dataverse/DataverseClientFactory.cs
@@ -2,7 +2,7 @@
using Microsoft.PowerPlatform.Dataverse.Client;
using System.Diagnostics.CodeAnalysis;
-namespace Liquid.Adapter.Dataverse
+namespace Liquid.Dataverse
{
///
public class DataverseClientFactory : IDataverseClientFactory
@@ -16,16 +16,13 @@ public class DataverseClientFactory : IDataverseClientFactory
///
public DataverseClientFactory(IOptions options)
{
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ ArgumentNullException.ThrowIfNull(options);
_options = options;
}
+ ///
[ExcludeFromCodeCoverage]
- ///
public IOrganizationServiceAsync GetClient()
{
var settings = _options.Value;
diff --git a/src/Liquid.Adapter.Dataverse/DataverseEntityMapper.cs b/src/Liquid.Dataverse/DataverseEntityMapper.cs
similarity index 84%
rename from src/Liquid.Adapter.Dataverse/DataverseEntityMapper.cs
rename to src/Liquid.Dataverse/DataverseEntityMapper.cs
index ee70548c..ba889a31 100644
--- a/src/Liquid.Adapter.Dataverse/DataverseEntityMapper.cs
+++ b/src/Liquid.Dataverse/DataverseEntityMapper.cs
@@ -1,10 +1,11 @@
-using Microsoft.Xrm.Sdk;
+using Liquid.Core.AbstractMappers;
+using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics.CodeAnalysis;
-namespace Liquid.Adapter.Dataverse
+namespace Liquid.Dataverse
{
///
/// Implementation of that
@@ -13,7 +14,7 @@ namespace Liquid.Adapter.Dataverse
[ExcludeFromCodeCoverage]
public class DataverseEntityMapper : LiquidMapper
{
- private readonly ILiquidDataverseAdapter _dataverseAdapter;
+ private readonly ILiquidDataverse _dataverseAdapter;
private Dictionary _entitiesMetadata = new Dictionary();
///
@@ -21,7 +22,7 @@ public class DataverseEntityMapper : LiquidMapper
///
///
///
- public DataverseEntityMapper(ILiquidDataverseAdapter adapter) : base(nameof(DataverseEntityMapper))
+ public DataverseEntityMapper(ILiquidDataverse adapter) : base(nameof(DataverseEntityMapper))
{
_dataverseAdapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
}
@@ -29,16 +30,19 @@ public DataverseEntityMapper(ILiquidDataverseAdapter adapter) : base(nameof(Data
///
protected override async Task MapImpl(string jsonString, string? entityName = null)
{
- if (entityName == null) { throw new ArgumentNullException(nameof(entityName)); }
+ ArgumentNullException.ThrowIfNull(entityName);
var entityAttributes = await GetEntityAttributes(entityName);
+ if (entityAttributes == null)
+ throw new ArgumentNullException(nameof(entityAttributes), $"Entity {entityName} not found.");
+
var entity = JsonToEntity(entityAttributes, jsonString);
return entity;
}
- private async Task> GetEntityAttributes(string entityName, List? noMappingFields = null)
+ private async Task?> GetEntityAttributes(string entityName, List? noMappingFields = null)
{
var entityMetadata = _entitiesMetadata.FirstOrDefault(x => x.Key == entityName).Value;
@@ -66,15 +70,21 @@ private async Task> GetEntityAttributes(string entityNam
return listAttributes;
}
- private Entity JsonToEntity(List attributes, string values)
+ private static Entity JsonToEntity(List attributes, string values)
{
var entidade = new Entity();
var valuesObject = JsonConvert.DeserializeObject(values);
+ if (valuesObject == null)
+ return entidade;
foreach (var atrribute in attributes)
{
var logicalName = atrribute.LogicalName.ToUpper();
- if (valuesObject[logicalName] != null && valuesObject[logicalName].ToString() != "")
+
+ if (valuesObject[logicalName] == null)
+ continue;
+
+ if (valuesObject[logicalName].ToString() != "")
{
switch (atrribute.AttributeType.ToString())
diff --git a/src/Liquid.Adapter.Dataverse/DataverseSettings.cs b/src/Liquid.Dataverse/DataverseSettings.cs
similarity index 94%
rename from src/Liquid.Adapter.Dataverse/DataverseSettings.cs
rename to src/Liquid.Dataverse/DataverseSettings.cs
index 04444d1a..518242ff 100644
--- a/src/Liquid.Adapter.Dataverse/DataverseSettings.cs
+++ b/src/Liquid.Dataverse/DataverseSettings.cs
@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
-namespace Liquid.Adapter.Dataverse
+namespace Liquid.Dataverse
{
///
/// Set of dataverse connection configs.
diff --git a/src/Liquid.Adapter.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs b/src/Liquid.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
similarity index 81%
rename from src/Liquid.Adapter.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
rename to src/Liquid.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
index 34730a67..9f90df54 100644
--- a/src/Liquid.Adapter.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
+++ b/src/Liquid.Dataverse/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
@@ -1,9 +1,10 @@
-using Microsoft.Extensions.Configuration;
+using Liquid.Core.Interfaces;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Xrm.Sdk;
using System.Diagnostics.CodeAnalysis;
-namespace Liquid.Adapter.Dataverse.Extensions.DependencyInjection
+namespace Liquid.Dataverse.Extensions.DependencyInjection
{
///
/// Extension methods of
@@ -12,7 +13,7 @@ namespace Liquid.Adapter.Dataverse.Extensions.DependencyInjection
public static class IServiceCollectionExtensions
{
///
- /// Registers service, it's dependency
+ /// Registers service, it's dependency
/// , and also set configuration
/// option .
/// Also register service.
@@ -29,7 +30,7 @@ public static IServiceCollection AddLiquidDataverseAdapter(this IServiceCollecti
services.AddTransient();
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton, DataverseEntityMapper>();
diff --git a/src/Liquid.Adapter.Dataverse/Extensions/EntityExtensions.cs b/src/Liquid.Dataverse/Extensions/EntityExtensions.cs
similarity index 94%
rename from src/Liquid.Adapter.Dataverse/Extensions/EntityExtensions.cs
rename to src/Liquid.Dataverse/Extensions/EntityExtensions.cs
index 7180c26f..dc24970c 100644
--- a/src/Liquid.Adapter.Dataverse/Extensions/EntityExtensions.cs
+++ b/src/Liquid.Dataverse/Extensions/EntityExtensions.cs
@@ -2,7 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
-namespace Liquid.Adapter.Dataverse.Extensions
+namespace Liquid.Dataverse.Extensions
{
///
/// Extension methods of .
diff --git a/src/Liquid.Adapter.Dataverse/IDataverseClientFactory.cs b/src/Liquid.Dataverse/IDataverseClientFactory.cs
similarity index 92%
rename from src/Liquid.Adapter.Dataverse/IDataverseClientFactory.cs
rename to src/Liquid.Dataverse/IDataverseClientFactory.cs
index fb054afb..96c2726e 100644
--- a/src/Liquid.Adapter.Dataverse/IDataverseClientFactory.cs
+++ b/src/Liquid.Dataverse/IDataverseClientFactory.cs
@@ -1,6 +1,6 @@
using Microsoft.PowerPlatform.Dataverse.Client;
-namespace Liquid.Adapter.Dataverse
+namespace Liquid.Dataverse
{
///
/// Defines Dataverse provider.
diff --git a/src/Liquid.Dataverse/ILiquidDataverse.cs b/src/Liquid.Dataverse/ILiquidDataverse.cs
new file mode 100644
index 00000000..7a3ddf6e
--- /dev/null
+++ b/src/Liquid.Dataverse/ILiquidDataverse.cs
@@ -0,0 +1,86 @@
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Metadata;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace Liquid.Dataverse
+{
+ ///
+ /// Dataverse integration service definition.
+ ///
+ public interface ILiquidDataverse
+ {
+ ///
+ /// Insert an using additional parameters to optionally prevent custom synchronous logic execution, suppress Power Automate trigger and enforce duplicate detection rules evaluation.
+ ///
+ /// entity definition.
+ ///
+ ///
+ ///
+ /// created entity Id.
+ Task Create(Entity targetEntity, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true);
+
+ ///
+ /// Update an record using parameters to enforce optimistic concurrency, prevent custom synchronous logic execution, suppress Power Automate trigger, and enforce duplicate detection rules evaluation.
+ ///
+ /// entity definition.
+ ///
+ ///
+ ///
+ ///
+ Task Update(Entity entity, bool useOptimisticConcurrency = false, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true);
+
+ ///
+ /// Read by .
+ ///
+ /// primarykey value.
+ /// table name.
+ /// column set should return.
+ Task GetById(Guid id, string entityName, ColumnSet? columns = null);
+
+ ///
+ /// Read table according filter conditions.
+ ///
+ /// table name.
+ /// query conditions.
+ /// conlumn se should return.
+ Task> ListByFilter(string entityName, FilterExpression filter, ColumnSet? columns = null);
+
+ ///
+ /// Exclude an item from table by primarykey optionally using parameters to prevent custom synchronous logic execution and enforce optimistic concurrency.
+ ///
+ /// primarykey value
+ /// table name.
+ ///
+ ///
+ Task Delete(Guid id, string entityName, bool bypassSynchronousLogic = false, bool useOptimisticConcurrency = false);
+
+ ///
+ /// Read table according query conditions.
+ ///
+ /// table name.
+ /// query conditions
+ Task> ListByFilter(string entityName, QueryExpression query);
+
+ ///
+ /// Read table properties.
+ ///
+ /// table name.
+ /// set.
+ Task GetMetadata(string entityName);
+
+ ///
+ /// Update state and status from an .
+ ///
+ /// entity reference.
+ /// new state value.
+ /// new status value.
+ Task SetState(EntityReference entity, string state, string status);
+
+ ///
+ /// Insert or update an .
+ ///
+ /// entity definition.
+ Task Upsert(Entity entity);
+
+ }
+}
diff --git a/src/Liquid.Adapter.Dataverse/Liquid.Adapter.Dataverse.csproj b/src/Liquid.Dataverse/Liquid.Dataverse.csproj
similarity index 84%
rename from src/Liquid.Adapter.Dataverse/Liquid.Adapter.Dataverse.csproj
rename to src/Liquid.Dataverse/Liquid.Dataverse.csproj
index 69e4e768..b975fda6 100644
--- a/src/Liquid.Adapter.Dataverse/Liquid.Adapter.Dataverse.csproj
+++ b/src/Liquid.Dataverse/Liquid.Dataverse.csproj
@@ -1,14 +1,14 @@
- net6.0
+ net8.0
enable
enable
Avanade Brazil
Avanade Inc.
Liquid - Modern Application Framework
Avanade 2019
- 6.0.0
+ 8.0.0-rc-01
true
true
Adapter for Microsoft Dataverse integrations.
@@ -18,9 +18,10 @@ This component is part of Liquid Application Framework.
-
-
-
+
+
+
+
diff --git a/src/Liquid.Dataverse/LiquidDataverse.cs b/src/Liquid.Dataverse/LiquidDataverse.cs
new file mode 100644
index 00000000..7458c2fa
--- /dev/null
+++ b/src/Liquid.Dataverse/LiquidDataverse.cs
@@ -0,0 +1,181 @@
+using Microsoft.Crm.Sdk.Messages;
+using Microsoft.PowerPlatform.Dataverse.Client;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Messages;
+using Microsoft.Xrm.Sdk.Metadata;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace Liquid.Dataverse
+{
+ ///
+ public class LiquidDataverse : ILiquidDataverse
+ {
+ private readonly IDataverseClientFactory _serviceFactory;
+ private readonly IOrganizationServiceAsync _client;
+
+ ///
+ /// Initialize a new instance of
+ ///
+ ///
+ ///
+ public LiquidDataverse(IDataverseClientFactory serviceFactory)
+ {
+ _serviceFactory = serviceFactory ?? throw new ArgumentNullException(nameof(serviceFactory));
+ _client = _serviceFactory.GetClient();
+ }
+
+ public async Task GetById(Guid id, string entityName, ColumnSet? columns = null)
+ {
+ if (columns == null)
+ columns = new ColumnSet(true);
+
+ var result = await _client.RetrieveAsync(entityName, id, columns);
+
+ return result;
+ }
+
+ ///
+ public async Task> ListByFilter(string entityName, FilterExpression? filter = null, ColumnSet? columns = null)
+ {
+ List results = new List();
+
+ QueryExpression queryData = new QueryExpression(entityName);
+
+ if (filter != null)
+ queryData.Criteria = filter;
+
+ if (columns != null)
+ queryData.ColumnSet = columns;
+
+ var result = await _client.RetrieveMultipleAsync(queryData);
+ if (result?.Entities != null)
+ {
+ foreach (var item in result.Entities)
+ {
+ results.Add(item);
+ }
+ }
+
+ return results;
+ }
+
+ ///
+ public async Task> ListByFilter(string entityName, QueryExpression query)
+ {
+ if (query is null)
+ {
+ throw new ArgumentNullException(nameof(query));
+ }
+
+ List results = new List();
+
+ var result = await _client.RetrieveMultipleAsync(query);
+
+ if (result?.Entities != null)
+ {
+ foreach (var item in result.Entities)
+ {
+ results.Add(item);
+ }
+ }
+
+ return results;
+ }
+
+ ///
+ public async Task GetMetadata(string entityName)
+ {
+ var retrieveEntityRequest = new RetrieveEntityRequest
+ {
+ EntityFilters = EntityFilters.All,
+ LogicalName = entityName
+ };
+ var response = await _client.ExecuteAsync(retrieveEntityRequest);
+ var metadata = (RetrieveEntityResponse)response;
+ return metadata.EntityMetadata;
+ }
+
+ ///
+ public async Task SetState(EntityReference entity, string state, string status)
+ {
+ var setStateRequest = new SetStateRequest()
+ {
+ EntityMoniker = new EntityReference
+ {
+ Id = entity.Id,
+ LogicalName = entity.LogicalName,
+ },
+ State = new OptionSetValue(int.Parse(state)),
+ Status = new OptionSetValue(int.Parse(status))
+ };
+
+ await _client.ExecuteAsync(setStateRequest);
+ }
+
+ ///
+ public async Task Upsert(Entity entity)
+ {
+ var request = new UpsertRequest()
+ {
+ Target = entity
+ };
+
+ await _client.ExecuteAsync(request);
+ }
+
+ ///
+ public Task Create(Entity targetEntity, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true)
+ {
+ var createRequest = new CreateRequest
+ {
+ Target = targetEntity
+ };
+
+ createRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousCustomLogic);
+ createRequest.Parameters.Add("SuppressCallbackRegistrationExpanderJob", suppressPowerAutomateTrigger);
+ createRequest.Parameters.Add("SuppressDuplicateDetection", suppressDuplicateDetectionRules);
+
+ var resultCreate = (CreateResponse)_client.Execute(createRequest);
+
+ return Task.FromResult(resultCreate.id);
+ }
+
+ ///
+ public async Task Update(Entity entity, bool useOptimisticConcurrency = false, bool bypassSynchronousCustomLogic = false, bool suppressPowerAutomateTrigger = false, bool suppressDuplicateDetectionRules = true)
+ {
+ var updateRequest = new UpdateRequest
+ {
+ Target = entity
+ };
+
+ if (useOptimisticConcurrency)
+ {
+ updateRequest.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;
+ }
+ updateRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousCustomLogic);
+ updateRequest.Parameters.Add("SuppressCallbackRegistrationExpanderJob", suppressPowerAutomateTrigger);
+ updateRequest.Parameters.Add("SuppressDuplicateDetection", suppressDuplicateDetectionRules);
+
+ await _client.ExecuteAsync(updateRequest);
+ }
+
+ ///
+ public async Task Delete(Guid id, string entityName, bool bypassSynchronousLogic = false, bool useOptimisticConcurrency = false)
+ {
+ var deleteRequest = new DeleteRequest
+ {
+ Target = new EntityReference(entityName, id)
+ };
+
+ if (useOptimisticConcurrency)
+ {
+ deleteRequest.ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches;
+ }
+
+ deleteRequest.Parameters.Add("BypassCustomPluginExecution", bypassSynchronousLogic);
+
+ await _client.ExecuteAsync(deleteRequest);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Liquid.Adapter.Dataverse/logo.png b/src/Liquid.Dataverse/logo.png
similarity index 100%
rename from src/Liquid.Adapter.Dataverse/logo.png
rename to src/Liquid.Dataverse/logo.png
diff --git a/test/Liquid.Adapter.Dataverse.Tests/DataverseClientFactoryTests.cs b/test/Liquid.Dataverse.Tests/DataverseClientFactoryTests.cs
similarity index 88%
rename from test/Liquid.Adapter.Dataverse.Tests/DataverseClientFactoryTests.cs
rename to test/Liquid.Dataverse.Tests/DataverseClientFactoryTests.cs
index 80cc6345..dd4131ae 100644
--- a/test/Liquid.Adapter.Dataverse.Tests/DataverseClientFactoryTests.cs
+++ b/test/Liquid.Dataverse.Tests/DataverseClientFactoryTests.cs
@@ -1,8 +1,7 @@
using Microsoft.Extensions.Options;
-using Microsoft.PowerPlatform.Dataverse.Client;
using NSubstitute;
-namespace Liquid.Adapter.Dataverse.Tests
+namespace Liquid.Dataverse.Tests
{
public class DataverseClientFactoryTests
{
@@ -12,10 +11,10 @@ public class DataverseClientFactoryTests
public DataverseClientFactoryTests()
{
_options = Substitute.For>();
- _options.Value.ReturnsForAnyArgs(new DataverseSettings() { ClientId = "4erewgewgh", ClientSecret = "greggrbnte", Url = "https://test"});
+ _options.Value.ReturnsForAnyArgs(new DataverseSettings() { ClientId = "4erewgewgh", ClientSecret = "greggrbnte", Url = "https://test" });
_sut = new DataverseClientFactory(_options);
- }
-
+ }
+
[Fact]
public void Ctor_WhenOptionsIsNull_ThenReturnArgumentNullException()
diff --git a/test/Liquid.Adapter.Dataverse.Tests/Liquid.Adapter.Dataverse.Tests.csproj b/test/Liquid.Dataverse.Tests/Liquid.Dataverse.Tests.csproj
similarity index 83%
rename from test/Liquid.Adapter.Dataverse.Tests/Liquid.Adapter.Dataverse.Tests.csproj
rename to test/Liquid.Dataverse.Tests/Liquid.Dataverse.Tests.csproj
index 5f91d323..50180b65 100644
--- a/test/Liquid.Adapter.Dataverse.Tests/Liquid.Adapter.Dataverse.Tests.csproj
+++ b/test/Liquid.Dataverse.Tests/Liquid.Dataverse.Tests.csproj
@@ -1,7 +1,7 @@
-
+
- net6.0
+ net8.0
enable
enable
@@ -23,7 +23,7 @@
-
+
diff --git a/test/Liquid.Adapter.Dataverse.Tests/DataverseAdapterTests.cs b/test/Liquid.Dataverse.Tests/LiquidDataverseTests.cs
similarity index 77%
rename from test/Liquid.Adapter.Dataverse.Tests/DataverseAdapterTests.cs
rename to test/Liquid.Dataverse.Tests/LiquidDataverseTests.cs
index 5d536b9a..13043af1 100644
--- a/test/Liquid.Adapter.Dataverse.Tests/DataverseAdapterTests.cs
+++ b/test/Liquid.Dataverse.Tests/LiquidDataverseTests.cs
@@ -6,38 +6,33 @@
using Microsoft.Xrm.Sdk.Query;
using NSubstitute;
using NSubstitute.ReceivedExtensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-namespace Liquid.Adapter.Dataverse.Tests
+namespace Liquid.Dataverse.Tests
{
- public class DataverseAdapterTests
+ public class LiquidDataverseTests
{
private readonly IOrganizationServiceAsync _client;
- private readonly ILiquidDataverseAdapter _sut;
- public DataverseAdapterTests()
+ private readonly ILiquidDataverse _sut;
+ public LiquidDataverseTests()
{
var clientFactory = Substitute.For();
_client = Substitute.For();
clientFactory.GetClient().Returns(_client);
- _sut = new DataverseAdapter(clientFactory);
+ _sut = new LiquidDataverse(clientFactory);
}
[Fact]
public void Ctor_WhenClientFactoryIsNull_ThrowArgumentNullException()
{
- Assert.Throws(() => new DataverseAdapter(null));
+ Assert.Throws(() => new LiquidDataverse(null));
}
[Fact]
public async Task GetById_WhenClientReturnResults_ReturnEntity()
{
- _client.RetrieveAsync(Arg.Any(), Arg.Any(),Arg.Any()).Returns(new Entity());
+ _client.RetrieveAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new Entity());
var guidId = Guid.NewGuid();
@@ -89,7 +84,7 @@ public async Task SetState_WhenCallResultSucessfully_ExecuteAsyncMethodCalled()
_client.ExecuteAsync(Arg.Any()).Returns(new OrganizationResponse());
var entity = new EntityReference();
-
+
await _sut.SetState(entity, "1234", "1212");
await _client.Received(1).ExecuteAsync(Arg.Any());
@@ -107,35 +102,35 @@ public async Task Upsert_WhenCallResultSucessfully_ExecuteAsyncMethodCalled()
await _client.Received(1).ExecuteAsync(Arg.Any());
}
- [Fact]
- public async Task Update_WithOptions_UseRightOrganizationRequestType_RequestParameters()
- {
- _client.UpdateAsync(Arg.Any()).Returns(Task.CompletedTask);
+ [Fact]
+ public async Task Update_WithOptions_UseRightOrganizationRequestType_RequestParameters()
+ {
+ _client.UpdateAsync(Arg.Any()).Returns(Task.CompletedTask);
- var updatedEntity = new Entity();
+ var updatedEntity = new Entity();
- await _sut.Update(updatedEntity, true, true, true);
+ await _sut.Update(updatedEntity, true, true, true);
- await _client.Received(1).ExecuteAsync(Arg.Is(ur =>
+ await _client.Received(1).ExecuteAsync(Arg.Is(ur =>
ur.Parameters.ContainsKey("BypassCustomPluginExecution")
&& ur.Parameters.ContainsKey("SuppressCallbackRegistrationExpanderJob")
- && ur.Parameters.ContainsKey("SuppressDuplicateDetection")
- ));
- }
+ && ur.Parameters.ContainsKey("SuppressDuplicateDetection")
+ ));
+ }
- [Fact]
- public async Task DeleteById_WithOptions_UseRightOrganizationRequestType_RequestParameters()
- {
+ [Fact]
+ public async Task DeleteById_WithOptions_UseRightOrganizationRequestType_RequestParameters()
+ {
_client.DeleteAsync(Arg.Any(), Arg.Any()).Returns(Task.CompletedTask);
- var guidId = Guid.NewGuid();
+ var guidId = Guid.NewGuid();
- await _sut.Delete(guidId, "entityname", true, true);
+ await _sut.Delete(guidId, "entityname", true, true);
- await _client.Received(1).ExecuteAsync(Arg.Is(dr =>
+ await _client.Received(1).ExecuteAsync(Arg.Is(dr =>
dr.Parameters.ContainsKey("BypassCustomPluginExecution")
));
- }
+ }
[Fact]
public async Task Create_WithOptions_UseRightOrganizationRequestType_RequestParameters()
@@ -144,8 +139,8 @@ public async Task Create_WithOptions_UseRightOrganizationRequestType_RequestPara
var result = await _sut.Create(new Entity(), false, false, true);
- _client.Received(1).Execute(Arg.Is(cr =>
- cr.Parameters.ContainsKey("BypassCustomPluginExecution")
+ _client.Received(1).Execute(Arg.Is(cr =>
+ cr.Parameters.ContainsKey("BypassCustomPluginExecution")
&& cr.Parameters.ContainsKey("SuppressCallbackRegistrationExpanderJob")
&& cr.Parameters.ContainsKey("SuppressDuplicateDetection")
));
diff --git a/test/Liquid.Adapter.Dataverse.Tests/Usings.cs b/test/Liquid.Dataverse.Tests/Usings.cs
similarity index 100%
rename from test/Liquid.Adapter.Dataverse.Tests/Usings.cs
rename to test/Liquid.Dataverse.Tests/Usings.cs