This commit is contained in:
Release-Agent 2024-11-06 19:55:54 +00:00
Родитель be9ee13289
Коммит c16a939caa
21 изменённых файлов: 810 добавлений и 53 удалений

Просмотреть файл

@ -5,8 +5,9 @@
</PropertyGroup>
<PropertyGroup Condition="'$(ProjectSpecificFx)' == ''">
<TargetFrameworks>net462;net472;net48;netstandard2.0;net6.0</TargetFrameworks>
<TargetFrameworks>net462;net472;net48;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<NoWarn>NU5104</NoWarn>
</PropertyGroup>
<PropertyGroup>

Просмотреть файл

@ -9,10 +9,11 @@ using Microsoft.PowerPlatform.Dataverse.Client.Auth;
using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache;
using Microsoft.PowerPlatform.Dataverse.Client.Connector;
using Microsoft.PowerPlatform.Dataverse.Client.Connector.OnPremises;
using Microsoft.PowerPlatform.Dataverse.Client.Exceptions;
using Microsoft.PowerPlatform.Dataverse.Client.HttpUtils;
using Microsoft.PowerPlatform.Dataverse.Client.InternalExtensions;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
using Microsoft.Rest;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk.Messages;

Просмотреть файл

@ -1,6 +1,5 @@
using Microsoft.Extensions.Logging;
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
using Microsoft.Rest;
using Microsoft.Xrm.Sdk;
using Newtonsoft.Json.Linq;
using System;
@ -12,6 +11,7 @@ using System.Globalization;
using System.ServiceModel;
using System.Linq;
using System.Text;
using Microsoft.PowerPlatform.Dataverse.Client.Exceptions;
namespace Microsoft.PowerPlatform.Dataverse.Client
{
@ -21,9 +21,6 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
[LocalizableAttribute(false)]
internal sealed class DataverseTraceLogger : TraceLoggerBase
{
// Internal connection of exceptions since last clear.
private List<Exception> _ActiveExceptionsList;
internal ILogger _logger;
#region Properties
@ -79,8 +76,6 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
TraceSourceName = traceSourceName;
}
_ActiveExceptionsList = new List<Exception>();
base.Initialize();
}
@ -88,7 +83,6 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
{
_logger = logger;
TraceSourceName = DefaultTraceSourceName;
_ActiveExceptionsList = new List<Exception>();
base.Initialize();
}
@ -98,7 +92,6 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
if (base.LastError.Length > 0)
base.LastError = base.LastError.Remove(0, LastError.Length - 1);
LastException = null;
_ActiveExceptionsList.Clear();
}
/// <summary>
@ -151,39 +144,34 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
exception = new Exception(message);
}
StringBuilder detailedDump = new StringBuilder();
StringBuilder lastMessage = new StringBuilder();
StringBuilder detailedDump = new StringBuilder(4096);
StringBuilder lastMessage = new StringBuilder(2048);
lastMessage.AppendLine(message); // Added to fix missing last error line.
detailedDump.AppendLine(message); // Added to fix missing error line.
if (!(exception != null && _ActiveExceptionsList.Contains(exception))) // Skip this line if its already been done.
GetExceptionDetail(exception, detailedDump, 0, lastMessage);
GetExceptionDetail(exception, detailedDump, 0, lastMessage);
TraceEvent(eventType, (int)eventType, detailedDump.ToString(), exception);
if (eventType == TraceEventType.Error)
{
base.LastError += lastMessage.ToString();
if (!(exception != null && _ActiveExceptionsList.Contains(exception))) // Skip this line if its already been done.
// check and or alter the exception is its and HTTPOperationExecption.
if (exception is HttpOperationException httpOperationException)
{
// check and or alter the exception is its and HTTPOperationExecption.
if (exception is HttpOperationException httpOperationException)
string errorMessage = "Not Provided";
if (!string.IsNullOrWhiteSpace(httpOperationException.Response.Content))
{
string errorMessage = "Not Provided";
if (!string.IsNullOrWhiteSpace(httpOperationException.Response.Content))
{
JObject contentBody = JObject.Parse(httpOperationException.Response.Content);
errorMessage = string.IsNullOrEmpty(contentBody["error"]["message"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(contentBody["error"]["message"]?.ToString()).Trim();
}
Utils.DataverseOperationException webApiExcept = new Utils.DataverseOperationException(errorMessage, httpOperationException);
LastException = webApiExcept;
JObject contentBody = JObject.Parse(httpOperationException.Response.Content);
errorMessage = string.IsNullOrEmpty(contentBody["error"]["message"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(contentBody["error"]["message"]?.ToString()).Trim();
}
else
LastException = exception;
Utils.DataverseOperationException webApiExcept = new Utils.DataverseOperationException(errorMessage, httpOperationException);
LastException = webApiExcept;
}
else
LastException = exception;
}
_ActiveExceptionsList.Add(exception);
detailedDump.Clear();
lastMessage.Clear();
@ -196,18 +184,13 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
/// <param name="exception"></param>
public override void Log(Exception exception)
{
if (exception != null && _ActiveExceptionsList.Contains(exception))
return; // already logged this one .
StringBuilder detailedDump = new StringBuilder();
StringBuilder lastMessage = new StringBuilder();
StringBuilder detailedDump = new StringBuilder(4096);
StringBuilder lastMessage = new StringBuilder(2048);
GetExceptionDetail(exception, detailedDump, 0, lastMessage);
TraceEvent(TraceEventType.Error, (int)TraceEventType.Error, detailedDump.ToString(), exception);
base.LastError += lastMessage.ToString();
LastException = exception;
_ActiveExceptionsList.Add(exception);
detailedDump.Clear();
lastMessage.Clear();
}
@ -594,7 +577,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
{
if (errorDetails != null && errorDetails.Count > 0)
{
StringBuilder sw = new StringBuilder();
StringBuilder sw = new StringBuilder(2048);
sw.AppendLine("Error Details\t:");
foreach (var itm in errorDetails)
{

Просмотреть файл

@ -0,0 +1,99 @@
//using Microsoft.Rest;
using Microsoft.PowerPlatform.Dataverse.Client.Exceptions;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace Microsoft.PowerPlatform.Dataverse.Client.Utils
{
/// <summary>
/// Used to encompass a ServiceClient Connection Centric exceptions
/// </summary>
[Serializable]
public class DataverseConnectionException : Exception
{
/// <summary>
/// Creates a dataverse connection Exception
/// </summary>
/// <param name="message">Error Message</param>
public DataverseConnectionException(string message)
: base(message)
{
}
/// <summary>
/// Creates a dataverse connection Exception
/// </summary>
/// <param name="message">Error Message</param>
/// <param name="innerException">Supporting Exception</param>
public DataverseConnectionException(string message, Exception innerException)
: base(message, innerException)
{
this.HResult = innerException.HResult;
}
/// <summary>
/// Creates a dataverse connection Exception
/// </summary>
/// <param name="message">Error Message</param>
/// <param name="errorCode">Error code</param>
/// <param name="data">Data Properties</param>
/// <param name="helpLink">Help Link</param>
/// <param name="httpOperationException"></param>
public DataverseConnectionException(string message, int errorCode, string helpLink, IDictionary<string, string> data, HttpOperationException httpOperationException = null)
: base(message, httpOperationException)
{
HResult = errorCode;
HelpLink = helpLink;
Source = "Dataverse Server API";
foreach (var itm in data)
{
this.Data.Add(itm.Key, itm.Value);
}
}
/// <summary>
/// Creates a Dataverse Connection Exception from an httpOperationError
/// </summary>
/// <param name="httpOperationException"></param>
/// <returns></returns>
public static DataverseConnectionException GenerateClientConnectionException(HttpOperationException httpOperationException)
{
string errorDetailPrefixString = "@Microsoft.PowerApps.CDS.ErrorDetails.";
Dictionary<string, string> cdsErrorData = new Dictionary<string, string>();
JToken ErrorBlock = null;
try
{
if (!string.IsNullOrWhiteSpace(httpOperationException.Response.Content))
{
JObject contentBody = JObject.Parse(httpOperationException.Response.Content);
ErrorBlock = contentBody["error"];
}
}
catch { }
if (ErrorBlock != null)
{
string errorMessage = DataverseTraceLogger.GetFirstLineFromString(ErrorBlock["message"]?.ToString()).Trim();
var code = ErrorBlock["code"];
int HResult = code != null && !string.IsNullOrWhiteSpace(code.ToString()) ? Convert.ToInt32(code.ToString(), 16) : -1;
string HelpLink = ErrorBlock["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString();
foreach (var node in ErrorBlock.ToArray())
{
if (node.Path.Contains(errorDetailPrefixString))
{
cdsErrorData.Add(node.Value<JProperty>().Name.ToString().Replace(errorDetailPrefixString, string.Empty), node.HasValues ? node.Value<JProperty>().Value.ToString() : string.Empty);
}
}
return new DataverseConnectionException(errorMessage, HResult, HelpLink, cdsErrorData, httpOperationException);
}
else
return new DataverseConnectionException("Server Error, no error report generated from server", -1, string.Empty, cdsErrorData, httpOperationException);
}
}
}

Просмотреть файл

@ -0,0 +1,100 @@
//using Microsoft.Rest;
using Microsoft.PowerPlatform.Dataverse.Client.Exceptions;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerPlatform.Dataverse.Client.Utils
{
/// <summary>
/// Used to encompass a ServiceClient Operation Exception
/// </summary>
[Serializable]
public class DataverseOperationException : Exception
{
/// <summary>
/// Creates a CdsService Client Exception
/// </summary>
/// <param name="message">Error Message</param>
public DataverseOperationException(string message)
: base(message)
{
}
/// <summary>
/// Creates a CdsService Client Exception
/// </summary>
/// <param name="message">Error Message</param>
/// <param name="errorCode">Error code</param>
/// <param name="data">Data Properties</param>
/// <param name="helpLink">Help Link</param>
/// <param name="httpOperationException"></param>
public DataverseOperationException(string message, int errorCode, string helpLink, IDictionary<string, string> data, HttpOperationException httpOperationException = null)
: base(message, httpOperationException)
{
HResult = errorCode;
HelpLink = helpLink;
Source = "Dataverse Server API";
foreach (var itm in data)
{
this.Data.Add(itm.Key, itm.Value);
}
}
/// <summary>
/// Creates a CdsService Client Exception from a httpOperationResult.
/// </summary>
/// <param name="httpOperationException"></param>
public static DataverseOperationException GenerateClientOperationException(HttpOperationException httpOperationException)
{
string errorDetailPrefixString = "@Microsoft.PowerApps.CDS.ErrorDetails.";
Dictionary<string, string> cdsErrorData = new Dictionary<string, string>();
JToken ErrorBlock = null;
try
{
if (!string.IsNullOrWhiteSpace(httpOperationException.Response.Content))
{
JObject contentBody = JObject.Parse(httpOperationException.Response.Content);
ErrorBlock = contentBody["error"];
}
}
catch { }
if (ErrorBlock != null)
{
string errorMessage = DataverseTraceLogger.GetFirstLineFromString(ErrorBlock["message"]?.ToString()).Trim();
var code = ErrorBlock["code"];
int HResult = code != null && !string.IsNullOrWhiteSpace(code.ToString()) ? Convert.ToInt32(code.ToString(), 16) : -1;
string HelpLink = ErrorBlock["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString();
foreach (var node in ErrorBlock.ToArray())
{
if (node.Path.Contains(errorDetailPrefixString))
{
cdsErrorData.Add(node.Value<JProperty>().Name.ToString().Replace(errorDetailPrefixString, string.Empty), node.HasValues ? node.Value<JProperty>().Value.ToString() : string.Empty);
}
}
return new DataverseOperationException(errorMessage, HResult, HelpLink, cdsErrorData, httpOperationException);
}
else
return new DataverseOperationException("Server Error, no error report generated from server", -1, string.Empty, cdsErrorData, httpOperationException);
}
/// <summary>
/// Creates a CdsService Client Exception
/// </summary>
/// <param name="message">Error Message</param>
/// <param name="innerException">Supporting Exception</param>
public DataverseOperationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.PowerPlatform.Dataverse.Client.HttpUtils;
namespace Microsoft.PowerPlatform.Dataverse.Client.Exceptions
{
/// <summary>
/// Http Exception wrapper class
/// </summary>
public class HttpOperationException : Exception
{
/// <summary>
///
/// </summary>
public HttpOperationException()
{
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public HttpOperationException(string message) : this(message, null)
{
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public HttpOperationException(string message, Exception innerException) : base(message, innerException)
{
}
// Properties
/// <summary>
///
/// </summary>
public object Body { get; set; }
/// <summary>
///
/// </summary>
public HttpRequestMessageWrapper Request { get; set; }
/// <summary>
///
/// </summary>
public HttpResponseMessageWrapper Response { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
namespace Microsoft.PowerPlatform.Dataverse.Client.HttpUtils
{
/// <summary>
/// Base class used to wrap HTTP requests and responses to preserve data after disposal of
/// HttpClient.
/// </summary>
public abstract class HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the HttpMessageWrapper class.
/// </summary>
protected HttpMessageWrapper()
{
Headers = new Dictionary<string, IEnumerable<string>>();
}
/// <summary>
/// Exposes the HTTP message contents.
/// </summary>
public string Content { get; set; }
/// <summary>
/// Gets the collection of HTTP headers.
/// </summary>
public IDictionary<string, IEnumerable<string>> Headers { get; private set; }
/// <summary>
/// Copies HTTP message headers to the error object.
/// </summary>
/// <param name="headers">Collection of HTTP headers.</param>
protected void CopyHeaders(HttpHeaders headers)
{
if (headers != null)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
{
IEnumerable<string> values = null;
if (Headers.TryGetValue(header.Key, out values))
{
values = values.Concat(header.Value);
}
else
{
values = header.Value;
}
Headers[header.Key] = values;
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,61 @@
using Microsoft.PowerPlatform.Dataverse.Client.InternalExtensions;
using System;
using System.Collections.Generic;
using System.Net.Http;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Microsoft.PowerPlatform.Dataverse.Client.HttpUtils
{
/// <summary>
/// Wrapper around HttpRequestMessage type that copies properties of HttpRequestMessage so that
/// they are available after the HttpClient gets disposed.
/// </summary>
public class HttpRequestMessageWrapper : HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the HttpRequestMessageWrapper class from HttpRequestMessage
/// and content.
/// </summary>
public HttpRequestMessageWrapper(HttpRequestMessage httpRequest, string content)
{
if (httpRequest == null)
{
throw new ArgumentNullException("httpRequest");
}
CopyHeaders(httpRequest.Headers);
CopyHeaders(httpRequest.GetContentHeaders());
HttpRequestSanitizer.SanitizerHeaders(Headers);
Content = content;
Method = httpRequest.Method;
RequestUri = httpRequest.RequestUri;
#pragma warning disable CS0618 // Options are only supported in .net 6
if (httpRequest.Properties != null)
{
Properties = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> pair in httpRequest.Properties)
{
Properties[pair.Key] = pair.Value;
}
}
#pragma warning restore CS0618
}
/// <summary>
/// Gets or sets the HTTP method used by the HTTP request message.
/// </summary>
public HttpMethod Method { get; protected set; }
/// <summary>
/// Gets or sets the Uri used for the HTTP request.
/// </summary>
public Uri RequestUri { get; protected set; }
/// <summary>
/// Gets a set of properties for the HTTP request.
/// </summary>
public IDictionary<string, object> Properties { get; private set; }
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}

Просмотреть файл

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerPlatform.Dataverse.Client.HttpUtils
{
/// <summary>
/// Sanitizer used internal by <see cref="HttpRequestMessageWrapper"/>.
/// </summary>
internal class HttpRequestSanitizer
{
private readonly static string _redactedPlaceholder = "REDACTED";
private readonly static HashSet<string> _allowedHeaders = new HashSet<string>(new string[]
{
"x-ms-request-id",
"x-ms-client-request-id",
"x-ms-return-client-request-id",
"traceparent",
"MS-CV",
"Accept",
"Cache-Control",
"Connection",
"Content-Length",
"Content-Type",
"Date",
"ETag",
"Expires",
"If-Match",
"If-Modified-Since",
"If-None-Match",
"If-Unmodified-Since",
"Last-Modified",
"Pragma",
"Request-Id",
"Retry-After",
"Server",
"Transfer-Encoding",
"User-Agent",
"WWW-Authenticate" // OAuth Challenge header.
}, StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Sanitize value of sensitive headers in the given <paramref name="headers"/>.
/// </summary>
/// <param name="headers">A collection of headers to sanitize.</param>
public static void SanitizerHeaders(IDictionary<string, IEnumerable<string>> headers)
{
if (headers == null)
{
return;
}
var namesOfHeaderToSanitize = headers.Keys.Except(_allowedHeaders, StringComparer.OrdinalIgnoreCase).ToList();
foreach (string name in namesOfHeaderToSanitize)
{
headers[name] = new string[] { _redactedPlaceholder };
}
}
}
}

Просмотреть файл

@ -0,0 +1,45 @@
using System;
using System.Net.Http;
using System.Net;
using Microsoft.PowerPlatform.Dataverse.Client.InternalExtensions;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Microsoft.PowerPlatform.Dataverse.Client.HttpUtils
{
/// <summary>
/// Wrapper around HttpResponseMessage type that copies properties of HttpResponseMessage so that
/// they are available after the HttpClient gets disposed.
/// </summary>
public class HttpResponseMessageWrapper : HttpMessageWrapper
{
/// <summary>
/// Initializes a new instance of the HttpResponseMessageWrapper class from HttpResponseMessage
/// and content.
/// </summary>
public HttpResponseMessageWrapper(HttpResponseMessage httpResponse, string content)
{
if (httpResponse == null)
{
throw new ArgumentNullException("httpResponse");
}
CopyHeaders(httpResponse.Headers);
CopyHeaders(httpResponse.GetContentHeaders());
Content = content;
StatusCode = httpResponse.StatusCode;
ReasonPhrase = httpResponse.ReasonPhrase;
}
/// <summary>
/// Gets or sets the status code of the HTTP response.
/// </summary>
public HttpStatusCode StatusCode { get; protected set; }
/// <summary>
/// Exposes the reason phrase, typically sent along with the status code.
/// </summary>
public string ReasonPhrase { get; protected set; }
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}

Просмотреть файл

@ -0,0 +1,228 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Microsoft.PowerPlatform.Dataverse.Client.InternalExtensions
{
/// <summary>
/// Extensions for manipulating HTTP request and response objects.
/// </summary>
internal static class HttpExtensions
{
/// <summary>
/// Formats an HttpContent object as String.
/// </summary>
/// <param name="content">The HttpContent to format.</param>
/// <returns>The formatted string.</returns>
public static string AsString(this HttpContent content)
{
if (content != null)
{
// Await for the content.
return
content
.ReadAsStringAsync()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}
return null;
}
/// <summary>
/// Get the content headers of an HtttRequestMessage.
/// </summary>
/// <param name="request">The request message.</param>
/// <returns>The content headers.</returns>
public static HttpHeaders GetContentHeaders(this HttpRequestMessage request)
{
if (request != null && request.Content != null)
{
return request.Content.Headers;
}
return null;
}
/// <summary>
/// Get the content headers of an HttpResponseMessage.
/// </summary>
/// <param name="response">The response message.</param>
/// <returns>The content headers.</returns>
public static HttpHeaders GetContentHeaders(this HttpResponseMessage response)
{
if (response != null && response.Content != null)
{
return response.Content.Headers;
}
return null;
}
/// <summary>
/// Returns string representation of a HttpRequestMessage.
/// </summary>
/// <param name="httpRequest">Request object to format.</param>
/// <returns>The string, formatted into curly braces.</returns>
public static string AsFormattedString(this HttpRequestMessage httpRequest)
{
if (httpRequest == null)
{
throw new ArgumentNullException("httpRequest");
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(httpRequest.ToString());
if (httpRequest.Content != null)
{
stringBuilder.AppendLine();
stringBuilder.AppendLine("Body:");
stringBuilder.AppendLine("{");
stringBuilder.AppendLine(httpRequest.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult());
stringBuilder.AppendLine("}");
}
return stringBuilder.ToString();
}
/// <summary>
/// Returns string representation of a HttpResponseMessage.
/// </summary>
/// <param name="httpResponse">Response object to format.</param>
/// <returns>The string, formatted into curly braces.</returns>
public static string AsFormattedString(this HttpResponseMessage httpResponse)
{
if (httpResponse == null)
{
throw new ArgumentNullException("httpResponse");
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine(httpResponse.ToString());
if (httpResponse.Content != null)
{
stringBuilder.AppendLine();
stringBuilder.AppendLine("Body:");
stringBuilder.AppendLine("{");
stringBuilder.AppendLine(httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult());
stringBuilder.AppendLine("}");
}
return stringBuilder.ToString();
}
/// <summary>
/// Converts given dictionary into a log string.
/// </summary>
/// <typeparam name="TKey">The dictionary key type.</typeparam>
/// <typeparam name="TValue">The dictionary value type.</typeparam>
/// <param name="dictionary">The dictionary object.</param>
/// <returns>The string, formatted into curly braces.</returns>
public static string AsFormattedString<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
{
return "{}";
}
return "{" + string.Join(",",
dictionary.Select(kv => kv.Key.ToString() +
"=" +
(kv.Value == null ? string.Empty : kv.Value.ToString()))
.ToArray()) + "}";
}
/// <summary>
/// Serializes HttpHeaders as Json dictionary.
/// </summary>
/// <param name="headers">HttpHeaders</param>
/// <returns>Json string</returns>
public static JObject ToJson(this HttpHeaders headers)
{
if (headers == null || !headers.Any())
{
return new JObject();
}
else
{
return headers.ToDictionary(h => h.Key, h => h.Value).ToJson();
}
}
/// <summary>
/// Serializes header dictionary as Json dictionary.
/// </summary>
/// <param name="headers">Dictionary</param>
/// <returns>Json string</returns>
public static JObject ToJson(this IDictionary<string, IEnumerable<string>> headers)
{
if (headers == null || !headers.Any())
{
return new JObject();
}
else
{
var jObject = new JObject();
foreach (var httpResponseHeader in headers)
{
if (httpResponseHeader.Value.Count() > 1)
{
jObject[httpResponseHeader.Key] = new JArray(httpResponseHeader.Value);
}
else
{
jObject[httpResponseHeader.Key] = httpResponseHeader.Value.FirstOrDefault();
}
}
return jObject;
}
}
/// <summary>
/// Serializes HttpResponseHeaders and HttpContentHeaders as Json dictionary.
/// </summary>
/// <param name="message">HttpResponseMessage</param>
/// <returns>Json string</returns>
public static JObject GetHeadersAsJson(this HttpResponseMessage message)
{
if (message == null)
{
return new JObject();
}
var jObject = new JObject();
foreach (var httpResponseHeader in message.Headers)
{
if (httpResponseHeader.Value.Count() > 1)
{
jObject[httpResponseHeader.Key] = new JArray(httpResponseHeader.Value);
}
else
{
jObject[httpResponseHeader.Key] = httpResponseHeader.Value.FirstOrDefault();
}
}
if (message.Content != null)
{
foreach (var httpResponseHeader in message.Content.Headers)
{
if (httpResponseHeader.Value.Count() > 1)
{
jObject[httpResponseHeader.Key] = new JArray(httpResponseHeader.Value);
}
else
{
jObject[httpResponseHeader.Key] = httpResponseHeader.Value.FirstOrDefault();
}
}
}
return jObject;
}
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

Просмотреть файл

@ -32,10 +32,11 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="$(PackageVersion_Microsoft_Extensions)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(PackageVersion_Microsoft_Extensions)" />
<PackageReference Include="Microsoft.Identity.Client" version="$(PackageVersion_MSAL)" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="$(PackageVersion_RestClientRuntime)" />
<!--<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="$(PackageVersion_RestClientRuntime)" />-->
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="$(PackageVersion_Newtonsoft)" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="$(PackageVersion_SystemConfigurationConfigurationManager)" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.1" />
<PackageReference Include="System.Text.Json" Version="$(PackageVersion_SystemTextJson)" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="$(PackageVersion_MSAL)" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(PackageVersion_Microsoft_Extensions)" />
@ -44,7 +45,7 @@
<When Condition="'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net8.0'">
<ItemGroup>
<PackageReference Include="System.ServiceModel.Http" version="$(PackageVersion_System_ServiceModel_PostNet6)" />
<PackageReference Include="System.ServiceModel.Primitives" version="$(PackageVersion_System_ServiceModel_PostNet6)"/>
<PackageReference Include="System.ServiceModel.Primitives" version="$(PackageVersion_System_ServiceModel_PostNet6)" />
</ItemGroup>
</When>
<Otherwise>

Просмотреть файл

@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
using Microsoft.Rest;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk.Metadata;

Просмотреть файл

@ -15,6 +15,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Xrm.Sdk" Version="$(PackageVersion_CdsSdk)" />
<PackageReference Include="Microsoft.Crm.Sdk.Proxy" Version="$(PackageVersion_CrmProxy)" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.1" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -2,19 +2,19 @@
<PropertyGroup>
<RootNamespace>Microsoft.PowerPlatform.Dataverse.ServiceClientConverter</RootNamespace>
<ComponentAreaName>DataverseClient</ComponentAreaName>
<ComponentAreaName>DataverseClientConverter</ComponentAreaName>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<Import Project="..\..\..\..\Build.Common.core.props" />
<PropertyGroup>
<TargetFrameworks>net462;net472;net48</TargetFrameworks>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<DocumentationFile>$(OutDir)\Microsoft.PowerPlatform.Dataverse.ServiceClientConverter.xml</DocumentationFile>
<TargetFrameworks>net462;net472;net48</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.1.0.110" />
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.0.0" />
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.1.*" />
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.*" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerPlatform.Dataverse.ServiceClientConverter", "Microsoft.PowerPlatform.Dataverse.ServiceClientConverter.csproj", "{1222F666-B1B9-4219-9EA9-EDCCEFDB7687}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1222F666-B1B9-4219-9EA9-EDCCEFDB7687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1222F666-B1B9-4219-9EA9-EDCCEFDB7687}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1222F666-B1B9-4219-9EA9-EDCCEFDB7687}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1222F666-B1B9-4219-9EA9-EDCCEFDB7687}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6944BCB7-36A1-45BC-AA26-489DC42D4EDA}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -7,7 +7,9 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Auth;
using Microsoft.PowerPlatform.Dataverse.Client.Exceptions;
using Microsoft.PowerPlatform.Dataverse.Client.Extensions;
using Microsoft.PowerPlatform.Dataverse.Client.HttpUtils;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
using Microsoft.Xrm.Sdk;
@ -138,10 +140,10 @@ namespace Client_Core_Tests
// error throw.
Microsoft.Rest.HttpOperationException operationException = new Microsoft.Rest.HttpOperationException("HTTPOPEXC");
HttpOperationException operationException = new HttpOperationException("HTTPOPEXC");
HttpResponseMessage Resp500 = new HttpResponseMessage(System.Net.HttpStatusCode.ServiceUnavailable);
Resp500.Headers.Add("REQ_ID", "39393F77-8F8B-4416-846E-28B4D2AA5667");
operationException.Response = new Microsoft.Rest.HttpResponseMessageWrapper(Resp500, "{\"error\":{\"code\":\"0x80040203\",\"message\":\"Communication activity cannot have more than one Sender party\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionSourceKey\":\"Plugin/Microsoft.Crm.Common.ObjectModel.PhoneCallService\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepKey\":\"3ccabb1b-ea3e-db11-86a7-000a3a5473e8\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiDepthKey\":\"1\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiActivityIdKey\":\"1736f387-e025-4828-a2bb-74ea8ac768a2\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiPluginSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionCategory\":\"ClientError\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionMesageName\":\"InvalidArgument\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionHttpStatusCode\":\"400\",\"@Microsoft.PowerApps.CDS.HelpLink\":\"http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a80040203&client=platform\",\"@Microsoft.PowerApps.CDS.InnerError.Message\":\"Communication activity cannot have more than one Sender party\"}}");
operationException.Response = new HttpResponseMessageWrapper(Resp500, "{\"error\":{\"code\":\"0x80040203\",\"message\":\"Communication activity cannot have more than one Sender party\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionSourceKey\":\"Plugin/Microsoft.Crm.Common.ObjectModel.PhoneCallService\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepKey\":\"3ccabb1b-ea3e-db11-86a7-000a3a5473e8\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiDepthKey\":\"1\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiActivityIdKey\":\"1736f387-e025-4828-a2bb-74ea8ac768a2\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiPluginSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionCategory\":\"ClientError\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionMesageName\":\"InvalidArgument\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionHttpStatusCode\":\"400\",\"@Microsoft.PowerApps.CDS.HelpLink\":\"http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a80040203&client=platform\",\"@Microsoft.PowerApps.CDS.InnerError.Message\":\"Communication activity cannot have more than one Sender party\"}}");
logger.Log(operationException);
Assert.NotNull(logger.LastError);

Просмотреть файл

@ -7,14 +7,14 @@
<PackageVersion_CrmProxy>9.2.24073.11611-master</PackageVersion_CrmProxy>
<PackageVersion_Newtonsoft>13.0.1</PackageVersion_Newtonsoft>
<PackageVersion_RestClientRuntime>2.3.24</PackageVersion_RestClientRuntime>
<PackageVersion_XrmSdk>9.0.2.55</PackageVersion_XrmSdk>
<PackageVersion_XrmSdk>9.0.2.56</PackageVersion_XrmSdk>
<PackageVersion_Dep_OutlookXrmSdk>9.0.2.34</PackageVersion_Dep_OutlookXrmSdk>
<PackageVersion_BatchedTelemetry>3.0.8</PackageVersion_BatchedTelemetry>
<PackageVersion_DataverseClient>1.1.22</PackageVersion_DataverseClient>
<PackageVersion_CoverletCollector>3.1.0</PackageVersion_CoverletCollector>
<PackageVersion_Microsoft_Extensions>3.1.8</PackageVersion_Microsoft_Extensions>
<PackageVersion_SystemRuntime>6.0.0</PackageVersion_SystemRuntime>
<PackageVersion_SystemTextJson>7.0.3</PackageVersion_SystemTextJson>
<PackageVersion_SystemTextJson>8.0.4</PackageVersion_SystemTextJson>
<PackageVersion_SystemTextEncodingsWeb>7.0.0</PackageVersion_SystemTextEncodingsWeb>
<PackageVersion_SystemMemory>4.5.5</PackageVersion_SystemMemory>
<PackageVersion_SystemConfigurationConfigurationManager>6.0.0</PackageVersion_SystemConfigurationConfigurationManager>

Просмотреть файл

@ -1443,7 +1443,7 @@
</member>
<member name="F:Microsoft.PowerPlatform.Dataverse.Client.ImportSolutionProperties.CONVERTTOMANAGED">
<summary>
Direct the system to convert any matching unmanaged customizations into your managed solution</summary>
Obsolete. The system will convert unmanaged solution components to managed when you import a managed solution.</summary>
</member>
<member name="F:Microsoft.PowerPlatform.Dataverse.Client.ImportSolutionProperties.DESIREDLAYERORDERPARAM">
<summary>

Просмотреть файл

@ -7,6 +7,20 @@ Notice:
Note: Only AD on FullFramework, OAuth, Certificate, ClientSecret Authentication types are supported at this time.
++CURRENTRELEASEID++
***** POSSIBLE Breaking Changes *****
Minor Release Bump,
Added .net 8.0 Target.
.net 6.0 Target will be removed in a subsequent release.
Removed dependance on Microsoft.Rest.Client. this was primary used for exception handling, and the necessary components have been reworked in to DVSC Exception management classes.
Fix memory consumption when too many exception are throw by DV client. Git: https://github.com/microsoft/PowerPlatform-DataverseServiceClient/issues/474
Dependency Changes:
Modified:
System.Text.Json to 8.0.4
Removed:
Microsoft.Rest.Client - Necessary carried over in client.
1.1.32:
Fix for endless retry loop issue in WebAPI calls when specific error states are encountered.
Fix for Logging MSAL telemetry when using ILogger
Previously, Logs for MSAL were not written to the configured ILogger, they would only go to Trace Source and InMemory Logs.

Просмотреть файл

@ -21,7 +21,6 @@
<dependency id="Microsoft.Extensions.Caching.Memory" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client.Extensions.Msal" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Rest.ClientRuntime" version="[$PackageVersion_RestClientRuntime$,)" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="[$PackageVersion_Newtonsoft$,)" exclude="Build,Analyzers" />
<dependency id="System.Text.Json" version="[$PackageVersion_SystemTextJson$,)" exclude="Build,Analyzers" />
<dependency id="System.ServiceModel.Http" version="[$PackageVersion_System_ServiceModel_PreNet6$,)" />
@ -35,7 +34,6 @@
<dependency id="Microsoft.Extensions.Caching.Memory" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client.Extensions.Msal" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Rest.ClientRuntime" version="[$PackageVersion_RestClientRuntime$,)" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="[$PackageVersion_Newtonsoft$,)" exclude="Build,Analyzers" />
<dependency id="System.Text.Json" version="[$PackageVersion_SystemTextJson$,)" exclude="Build,Analyzers" />
<dependency id="System.ServiceModel.Http" version="[$PackageVersion_System_ServiceModel_PreNet6$,)" />
@ -48,7 +46,6 @@
<dependency id="Microsoft.Extensions.Caching.Memory" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client.Extensions.Msal" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Rest.ClientRuntime" version="[$PackageVersion_RestClientRuntime$,)" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="[$PackageVersion_Newtonsoft$,)" exclude="Build,Analyzers" />
<dependency id="System.Text.Json" version="[$PackageVersion_SystemTextJson$,)" exclude="Build,Analyzers" />
<dependency id="System.ServiceModel.Http" version="[$PackageVersion_System_ServiceModel_PreNet6$,)" />
@ -75,12 +72,37 @@
<dependency id="Microsoft.Extensions.Caching.Memory" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client.Extensions.Msal" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Rest.ClientRuntime" version="[$PackageVersion_RestClientRuntime$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.VisualBasic" version="10.3.0" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="[$PackageVersion_Newtonsoft$,)" exclude="Build,Analyzers" />
<dependency id="System.Configuration.ConfigurationManager" version="[$PackageVersion_SystemConfigurationConfigurationManager$,)" exclude="Build,Analyzers" />
<dependency id="System.Runtime.Caching" version="4.7.0" exclude="Build,Analyzers" />
</group>
<group targetFramework="NET8.0">
<dependency id="System.Collections" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Globalization" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Linq" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Linq.Expressions" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Private.DataContractSerialization" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Reflection" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Reflection.Extensions" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Reflection.TypeExtensions" version="4.7.0" exclude="Build,Analyzers" />
<dependency id="System.Runtime.Serialization.Primitives" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Runtime.Serialization.Xml" version="4.3.0" exclude="Build,Analyzers" />
<dependency id="System.Security.Permissions" version="[$PackageVersion_SystemSecurityPermissions$,)" exclude="Build,Analyzers" />
<dependency id="System.ServiceModel.Http" version="[$PackageVersion_System_ServiceModel_PostNet6$,)" exclude="Build,Analyzers" />
<dependency id="System.ServiceModel.Primitives" version="[$PackageVersion_System_ServiceModel_PostNet6$,)" exclude="Build,Analyzers" />
<dependency id="System.Text.Json" version="[$PackageVersion_SystemTextJson$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Http" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Logging" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Caching.Memory" version="[$PackageVersion_Microsoft_Extensions$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.Identity.Client.Extensions.Msal" version="[$PackageVersion_MSAL$,)" exclude="Build,Analyzers" />
<dependency id="Microsoft.VisualBasic" version="10.3.0" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="[$PackageVersion_Newtonsoft$,)" exclude="Build,Analyzers" />
<dependency id="System.Configuration.ConfigurationManager" version="[$PackageVersion_SystemConfigurationConfigurationManager$,)" exclude="Build,Analyzers" />
<dependency id="System.Runtime.Caching" version="4.7.0" exclude="Build,Analyzers" />
</group>
</dependencies>
<frameworkAssemblies>
<frameworkAssembly assemblyName="System.Web" targetFramework=".NETFramework4.6.2, .NETFramework4.7.2, .NETFramework4.8" />