Skip to content

Commit 1fe9bf8

Browse files
authored
Merge pull request #107 from microsoft/release/update/210308110047
Queued up bug fixes from pre Rename. See notes for updates.
2 parents 7df20ec + 439cd9b commit 1fe9bf8

14 files changed

+422
-91
lines changed

src/GeneralTools/DataverseClient/Client/ConnectionService.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,9 @@ internal sealed class ConnectionService : IConnectionService, IDisposable
9393
private string _ServiceCACHEName = "Microsoft.PowerPlatform.Dataverse.Client.Service"; // this is the base cache key name that will be used to cache the service.
9494

9595
//OAuth Params
96-
private string _clientId; // client id to register your application for OAuth
97-
private Uri _redirectUri; // uri specifying the redirection uri post OAuth auth
9896
private PromptBehavior _promptBehavior; // prompt behavior
9997
private string _tokenCachePath; // user specified token cache file path
100-
private string _resource; // Resource to connect to
10198
private bool _isOnPremOAuth = false; // Identifies whether the connection is for OnPrem or Online Deployment for OAuth
102-
private static string _authority; //cached authority reading from credential manager
10399
private static string _userId = null; //cached userid reading from config file
104100
private bool _isCalledbyExecuteRequest = false; //Flag indicating that the an request called by Execute_Command
105101
private bool _isDefaultCredsLoginForOAuth = false; //Flag indicating that the user is trying to login with the current user id.
@@ -119,6 +115,27 @@ internal sealed class ConnectionService : IConnectionService, IDisposable
119115
/// if Set to true then the connection is for one use and should be cleand out of cache when completed.
120116
/// </summary>
121117
private bool unqueInstance = false;
118+
119+
/// <summary>
120+
/// Client or App Id to use.
121+
/// </summary>
122+
private string _clientId;
123+
124+
/// <summary>
125+
/// uri specifying the redirection uri post OAuth auth
126+
/// </summary>
127+
private Uri _redirectUri;
128+
129+
/// <summary>
130+
/// Resource to connect to
131+
/// </summary>
132+
private string _resource;
133+
134+
/// <summary>
135+
/// cached authority reading from credential manager
136+
/// </summary>
137+
internal static string _authority;
138+
122139
/// <summary>
123140
/// when certificate Auth is used, this is the certificate that is used to execute the connection.
124141
/// </summary>
@@ -1370,6 +1387,12 @@ internal void SetClonedProperties(ServiceClient sourceClient)
13701387
TenantId = sourceClient.TenantId;
13711388
EnvironmentId = sourceClient.EnvironmentId;
13721389
GetAccessTokenAsync = sourceClient.GetAccessToken;
1390+
_clientId = sourceClient._connectionSvc._clientId;
1391+
_certificateStoreLocation = sourceClient._connectionSvc._certificateStoreLocation;
1392+
_certificateThumbprint = sourceClient._connectionSvc._certificateThumbprint;
1393+
_certificateOfConnection = sourceClient._connectionSvc._certificateOfConnection;
1394+
_redirectUri = sourceClient._connectionSvc._redirectUri;
1395+
_resource = sourceClient._connectionSvc._resource;
13731396
}
13741397

13751398
#region WebAPI Interface Utilities

src/GeneralTools/DataverseClient/Client/DataverseTraceLogger.cs

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
using Microsoft.Xrm.Sdk;
88
using System.Collections.Generic;
99
using System.Collections.Concurrent;
10+
using System.Linq;
1011
using Microsoft.Rest;
1112
using Newtonsoft.Json.Linq;
1213
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
14+
using Microsoft.Extensions.Logging;
1315

1416
namespace Microsoft.PowerPlatform.Dataverse.Client
1517
{
@@ -20,15 +22,17 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
2022
internal sealed class DataverseTraceLogger : TraceLoggerBase
2123
{
2224
// Internal connection of exceptions since last clear.
23-
private List<Exception> _ActiveExceptionsList;
25+
private List<Exception> _ActiveExceptionsList;
26+
27+
private ILogger _logger;
2428

2529
#region Properties
2630
/// <summary>
2731
/// Last Error from CRM
2832
/// </summary>
2933
public new string LastError
3034
{
31-
get { return base.LastError.ToString(); }
35+
get { return base.LastError; }
3236
}
3337

3438
/// <summary>
@@ -80,6 +84,15 @@ public DataverseTraceLogger(string traceSourceName = "")
8084
base.Initialize();
8185
}
8286

87+
public DataverseTraceLogger(ILogger logger)
88+
{
89+
_logger = logger;
90+
TraceSourceName = DefaultTraceSourceName;
91+
_ActiveExceptionsList = new List<Exception>();
92+
base.Initialize();
93+
}
94+
95+
8396
public override void ResetLastError()
8497
{
8598
if (base.LastError.Length > 0 )
@@ -105,7 +118,7 @@ public void ClearLogCache()
105118
/// <param name="message"></param>
106119
public override void Log(string message)
107120
{
108-
Source.TraceEvent(TraceEventType.Information, (int)TraceEventType.Information, message);
121+
TraceEvent(TraceEventType.Information, (int)TraceEventType.Information, message, null);
109122
}
110123

111124
/// <summary>
@@ -115,11 +128,14 @@ public override void Log(string message)
115128
/// <param name="eventType"></param>
116129
public override void Log(string message, TraceEventType eventType)
117130
{
118-
TraceEvent(eventType, (int)eventType, message);
119131
if (eventType == TraceEventType.Error)
120132
{
121133
Log(message, eventType, new Exception(message));
122134
}
135+
else
136+
{
137+
TraceEvent(eventType, (int)eventType, message, null);
138+
}
123139
}
124140

125141
/// <summary>
@@ -144,10 +160,10 @@ public override void Log(string message, TraceEventType eventType, Exception exc
144160
if (!(exception != null && _ActiveExceptionsList.Contains(exception))) // Skip this line if its already been done.
145161
GetExceptionDetail(exception, detailedDump, 0, lastMessage);
146162

147-
TraceEvent(eventType, (int)eventType, detailedDump.ToString());
163+
TraceEvent(eventType, (int)eventType, detailedDump.ToString(), exception);
148164
if (eventType == TraceEventType.Error)
149165
{
150-
base.LastError.Append(lastMessage.ToString());
166+
base.LastError += lastMessage.ToString();
151167
if (!(exception != null && _ActiveExceptionsList.Contains(exception))) // Skip this line if its already been done.
152168
{
153169
// check and or alter the exception is its and HTTPOperationExecption.
@@ -175,13 +191,13 @@ public override void Log(string message, TraceEventType eventType, Exception exc
175191
public override void Log(Exception exception)
176192
{
177193
if (exception != null && _ActiveExceptionsList.Contains(exception))
178-
return; // allready logged this one .
194+
return; // already logged this one .
179195

180196
StringBuilder detailedDump = new StringBuilder();
181197
StringBuilder lastMessage = new StringBuilder();
182198
GetExceptionDetail(exception, detailedDump, 0, lastMessage);
183-
TraceEvent(TraceEventType.Error, (int)TraceEventType.Error, detailedDump.ToString());
184-
base.LastError.Append(lastMessage.ToString());
199+
TraceEvent(TraceEventType.Error, (int)TraceEventType.Error, detailedDump.ToString(), exception);
200+
base.LastError += lastMessage.ToString();
185201
LastException = exception;
186202

187203
_ActiveExceptionsList.Add(exception);
@@ -196,10 +212,17 @@ public override void Log(Exception exception)
196212
/// <param name="eventType"></param>
197213
/// <param name="id"></param>
198214
/// <param name="message"></param>
199-
private void TraceEvent(TraceEventType eventType, int id, string message)
215+
/// <param name="ex"></param>
216+
private void TraceEvent(TraceEventType eventType, int id, string message , Exception ex)
200217
{
201218
Source.TraceEvent(eventType, id, message);
202219

220+
LogLevel logLevel = TranslateTraceEventType(eventType);
221+
if (_logger != null && _logger.IsEnabled(logLevel))
222+
{
223+
_logger.Log(logLevel, id, ex, message);
224+
}
225+
203226
if (EnabledInMemoryLogCapture)
204227
{
205228
Logs.Enqueue(Tuple.Create<DateTime, string>(DateTime.UtcNow,
@@ -253,7 +276,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
253276
FormatExceptionMessage(
254277
OrgFault.Source != null ? OrgFault.Source.ToString().Trim() : "Not Provided",
255278
OrgFault.TargetSite != null ? OrgFault.TargetSite.Name.ToString() : "Not Provided",
256-
OrgFault.Detail != null ? string.Format(CultureInfo.InvariantCulture, "Message: {0}\nErrorCode: {1}\nTrace: {2}{3}", OrgFault.Detail.Message, OrgFault.Detail.ErrorCode, OrgFault.Detail.TraceText, string.IsNullOrEmpty(ErrorDetail) ? "" : $"\n{ErrorDetail}") :
279+
OrgFault.Detail != null ? string.Format(CultureInfo.InvariantCulture, "Message: {0}\nErrorCode: {1}{4}\nTrace: {2}{3}", OrgFault.Detail.Message, OrgFault.Detail.ErrorCode, OrgFault.Detail.TraceText, string.IsNullOrEmpty(ErrorDetail) ? "" : $"\n{ErrorDetail}" , OrgFault.Detail.ActivityId == null ? "" : $"\nActivityId: {OrgFault.Detail.ActivityId}") :
257280
string.IsNullOrEmpty(OrgFault.Message) ? "Not Provided" : OrgFault.Message.ToString().Trim(),
258281
string.IsNullOrEmpty(OrgFault.HelpLink) ? "Not Provided" : OrgFault.HelpLink.ToString().Trim(),
259282
string.IsNullOrEmpty(OrgFault.StackTrace) ? "Not Provided" : OrgFault.StackTrace.ToString().Trim()
@@ -280,7 +303,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
280303
OrganizationServiceFault oFault = (OrganizationServiceFault)objException;
281304
string ErrorDetail = GenerateOrgErrorDetailsInfo(oFault.ErrorDetails);
282305
FormatOrgFaultMessage(
283-
string.Format(CultureInfo.InvariantCulture, "Message: {0}\nErrorCode: {1}\nTrace: {2}{3}", oFault.Message, oFault.ErrorCode, oFault.TraceText, string.IsNullOrEmpty(ErrorDetail) ? "" : $"\n{ErrorDetail}"),
306+
string.Format(CultureInfo.InvariantCulture, "Message: {0}\nErrorCode: {1}{4}\nTrace: {2}{3}", oFault.Message, oFault.ErrorCode, oFault.TraceText, string.IsNullOrEmpty(ErrorDetail) ? "" : $"\n{ErrorDetail}", oFault.ActivityId == null ? "" : $"\nActivityId: {oFault.ActivityId}"),
284307
oFault.Timestamp.ToString(),
285308
oFault.ErrorCode.ToString(),
286309
string.IsNullOrEmpty(oFault.HelpLink) ? "Not Provided" : oFault.HelpLink.ToString().Trim(),
@@ -302,39 +325,41 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
302325
{
303326
if (objException is HttpOperationException httpOperationException)
304327
{
305-
JObject contentBody = JObject.Parse(httpOperationException.Response.Content);
328+
JObject contentBody = null;
329+
if (!string.IsNullOrEmpty(httpOperationException.Response.Content))
330+
contentBody = JObject.Parse(httpOperationException.Response.Content);
306331

307-
var ErrorBlock = contentBody["error"];
332+
var ErrorBlock = contentBody?["error"];
308333
FormatExceptionMessage(
309334
httpOperationException.Source != null ? httpOperationException.Source.ToString().Trim() : "Not Provided",
310335
httpOperationException.TargetSite != null ? httpOperationException.TargetSite.Name?.ToString() : "Not Provided",
311-
string.IsNullOrEmpty(ErrorBlock["message"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(ErrorBlock["message"]?.ToString()).Trim(),
336+
string.IsNullOrEmpty(ErrorBlock?["message"]?.ToString()) ? "Not Provided" : string.Format("Message: {0}{1}\n", GetFirstLineFromString(ErrorBlock?["message"]?.ToString()).Trim(), httpOperationException.Response != null && httpOperationException.Response.Headers.ContainsKey("REQ_ID") ? $"\nActivityId: {ExtractString(httpOperationException.Response.Headers["REQ_ID"])}" : "") ,
312337
string.IsNullOrEmpty(httpOperationException.HelpLink) ? "Not Provided" : httpOperationException.HelpLink.ToString().Trim(),
313-
string.IsNullOrEmpty(ErrorBlock["stacktrace"]?.ToString()) ? "Not Provided" : ErrorBlock["stacktrace"]?.ToString().Trim()
338+
string.IsNullOrEmpty(ErrorBlock?["stacktrace"]?.ToString()) ? "Not Provided" : ErrorBlock["stacktrace"]?.ToString().Trim()
314339
, sw, level);
315340

316341
lastErrorMsg.Append(string.IsNullOrEmpty(httpOperationException.Message) ? "Not Provided" : httpOperationException.Message.ToString().Trim());
317342

318-
// WebEx currently only returns 1 leve of error.
319-
var InnerError = contentBody["error"]["innererror"];
343+
// WebEx currently only returns 1 level of error.
344+
var InnerError = contentBody?["error"]["innererror"];
320345
if (lastErrorMsg.Length > 0 && InnerError != null)
321346
{
322347
level++;
323348
lastErrorMsg.Append(" => ");
324349
FormatExceptionMessage(
325350
httpOperationException.Source != null ? httpOperationException.Source.ToString().Trim() : "Not Provided",
326351
httpOperationException.TargetSite != null ? httpOperationException.TargetSite.Name?.ToString() : "Not Provided",
327-
string.IsNullOrEmpty(InnerError["message"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(InnerError["message"]?.ToString()).Trim(),
328-
string.IsNullOrEmpty(InnerError["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(InnerError["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString()).Trim(),
329-
string.IsNullOrEmpty(InnerError["stacktrace"]?.ToString()) ? "Not Provided" : InnerError["stacktrace"]?.ToString().Trim()
352+
string.IsNullOrEmpty(InnerError?["message"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(InnerError?["message"]?.ToString()).Trim(),
353+
string.IsNullOrEmpty(InnerError?["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString()) ? "Not Provided" : GetFirstLineFromString(InnerError?["@Microsoft.PowerApps.CDS.HelpLink"]?.ToString()).Trim(),
354+
string.IsNullOrEmpty(InnerError?["stacktrace"]?.ToString()) ? "Not Provided" : InnerError?["stacktrace"]?.ToString().Trim()
330355
, sw, level);
331356
}
332357
}
333358
else
334359
{
335360
if (objException is DataverseOperationException cdsOpExecp)
336361
{
337-
FormatCdsSvcFaultMessage(
362+
FormatSvcFaultMessage(
338363
string.IsNullOrEmpty(cdsOpExecp.Message) ? "Not Provided" : cdsOpExecp.Message.ToString().Trim(),
339364
string.IsNullOrEmpty(cdsOpExecp.Source) ? "Not Provided" : cdsOpExecp.Source.ToString().Trim(),
340365
cdsOpExecp.HResult == -1 ? "Not Provided" : cdsOpExecp.HResult.ToString().Trim(),
@@ -382,12 +407,30 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
382407
return;
383408
}
384409

385-
/// <summary>
386-
/// returns the first line from the text block.
387-
/// </summary>
388-
/// <param name="textBlock"></param>
389-
/// <returns></returns>
390-
internal static string GetFirstLineFromString(string textBlock)
410+
private static string ExtractString(IEnumerable<string> enumerable)
411+
{
412+
string sOut = string.Empty;
413+
if (enumerable != null)
414+
{
415+
List<string> lst = new List<string>(enumerable);
416+
417+
foreach (var itm in lst.Distinct())
418+
{
419+
if (string.IsNullOrEmpty(sOut))
420+
sOut += $"{itm}";
421+
else
422+
sOut += $"|{itm}";
423+
}
424+
}
425+
return sOut;
426+
}
427+
428+
/// <summary>
429+
/// returns the first line from the text block.
430+
/// </summary>
431+
/// <param name="textBlock"></param>
432+
/// <returns></returns>
433+
internal static string GetFirstLineFromString(string textBlock)
391434
{
392435
if (!string.IsNullOrEmpty(textBlock))
393436
{
@@ -477,11 +520,11 @@ private static void FormatOrgFaultMessage(string message, string timeOfEvent, st
477520
/// <param name="helpLink">Help Link</param>
478521
/// <param name="sw">Writer to write too</param>
479522
/// <param name="level">Depth</param>
480-
private static void FormatCdsSvcFaultMessage(string message, string source, string errorCode, System.Collections.IDictionary dataItems , string helpLink, StringBuilder sw, int level)
523+
private static void FormatSvcFaultMessage(string message, string source, string errorCode, System.Collections.IDictionary dataItems , string helpLink, StringBuilder sw, int level)
481524
{
482525
if (level != 0)
483526
sw.AppendLine($"Inner Exception Level {level}\t: ");
484-
sw.AppendLine("==CdsClientOperationException Info=======================================================================================");
527+
sw.AppendLine("==DataverseOperationException Info=======================================================================================");
485528
sw.AppendLine($"Source: {source}");
486529
sw.AppendLine("Error: " + message);
487530
sw.AppendLine("ErrorCode: " + errorCode);
@@ -490,14 +533,34 @@ private static void FormatCdsSvcFaultMessage(string message, string source, stri
490533
sw.AppendLine($"HelpLink Url: {helpLink}");
491534
if (dataItems != null && dataItems.Count > 0)
492535
{
493-
sw.AppendLine("CdsErrorDetail:");
536+
sw.AppendLine("DataverseErrorDetail:");
494537
foreach (System.Collections.DictionaryEntry itm in dataItems)
495538
{
496539
sw.AppendLine($"\t{itm.Key}: {itm.Value}");
497540
}
498541
}
499542
sw.AppendLine("======================================================================================================================");
500543
}
544+
545+
private static LogLevel TranslateTraceEventType(TraceEventType traceLevel)
546+
{
547+
switch (traceLevel)
548+
{
549+
case TraceEventType.Critical:
550+
return LogLevel.Critical;
551+
case TraceEventType.Error:
552+
return LogLevel.Error;
553+
case TraceEventType.Warning:
554+
return LogLevel.Warning;
555+
case TraceEventType.Information:
556+
return LogLevel.Information;
557+
case TraceEventType.Verbose:
558+
return LogLevel.Trace;
559+
default:
560+
return LogLevel.None;
561+
}
562+
}
563+
501564
}
502565

503566
/// <summary>

src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public interface IOrganizationServiceAsync
5151
/// <param name="entityName">Logical name of entity</param>
5252
/// <param name="id">Id of entity</param>
5353
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
54-
void DeleteAsync(string entityName, Guid id, CancellationToken cancellationToken);
54+
Task DeleteAsync(string entityName, Guid id, CancellationToken cancellationToken);
5555

5656
/// <summary>
5757
/// Perform an action in an organization specified by the request.
@@ -69,7 +69,7 @@ public interface IOrganizationServiceAsync
6969
/// <param name="relationship"></param>
7070
/// <param name="relatedEntities"></param>
7171
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
72-
void AssociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken);
72+
Task AssociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken);
7373

7474
/// <summary>
7575
/// Disassociate an entity with a set of entities
@@ -79,7 +79,7 @@ public interface IOrganizationServiceAsync
7979
/// <param name="relationship"></param>
8080
/// <param name="relatedEntities"></param>
8181
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
82-
void DisassociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken);
82+
Task DisassociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken);
8383

8484
/// <summary>
8585
/// Retrieves a collection of entities

0 commit comments

Comments
 (0)