From aa44de31690a7310499a4b5674ac20635edf6ac6 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Fri, 26 May 2023 16:11:57 -0700 Subject: [PATCH 1/5] Make service errors more discoverable. --- src/readme.graph.md | 40 +++++++++++++++++++++++++ tools/Custom/HttpMessageLogFormatter.cs | 17 +++++++---- tools/Custom/PSCmdletExtensions.cs | 16 ++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/readme.graph.md b/src/readme.graph.md index 399f225729c..9b5cc5ab7ff 100644 --- a/src/readme.graph.md +++ b/src/readme.graph.md @@ -246,6 +246,27 @@ directive: } } } +# Mark '@odata.id' as required properties for /$ref. + - from: 'openapi-document' + where: $.components.schemas['microsoft.graph.ODataErrors.InnerError'] + transform: >- + return { + "type": "object", + "properties": { + "date" : { + "type" : 'string' + }, + "request-id" : { + "type" : 'string' + }, + "client-request-id" : { + "type" : 'string' + } + }, + "additionalProperties": { + "type" : "object" + } + } # Mark '@odata.id' as required properties for /$ref. - from: 'openapi-document' where: $.components.schemas.ReferenceCreate @@ -394,6 +415,10 @@ directive: } } + // Format error details. + let errorDetailsRegex = /(ErrorDetails\s*=\s*)(new.*ErrorDetails\(message\).*)/gmi + $ = $.replace(errorDetailsRegex, '$1this.GetErrorDetails((await response)?.Error, responseMessage)'); + return $; } @@ -542,6 +567,21 @@ directive: return $; } +# Modify generated JsonObject class. + - from: source-file-csharp + where: $ + transform: > + if (!$documentPath.match(/generated%2Fruntime%2FNodes%2FJsonObject.cs/gm)) + { + return $; + } else { + // Make JsonObject's items dictionary case insensitive. + let dictionaryInitRegex = /(\s*=\s*new\s*Dictionary\()(\);)/gm + $ = $.replace(dictionaryInitRegex, '$1StringComparer.InvariantCultureIgnoreCase$2'); + + return $; + } + # Modify API class. - from: source-file-csharp where: $ diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 6b1b5a4cdb3..16bff521cde 100644 --- a/tools/Custom/HttpMessageLogFormatter.cs +++ b/tools/Custom/HttpMessageLogFormatter.cs @@ -63,9 +63,9 @@ public static async Task GetHttpRequestLogAsync(HttpRequestMessage reque StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"============================ HTTP REQUEST ============================{Environment.NewLine}"); - stringBuilder.AppendLine($"HTTP Method:{Environment.NewLine}{requestClone.Method.ToString()}{Environment.NewLine}"); - stringBuilder.AppendLine($"Absolute Uri:{Environment.NewLine}{requestClone.RequestUri.ToString()}{Environment.NewLine}"); - stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(ConvertHttpHeadersToCollection(requestClone.Headers))}{Environment.NewLine}"); + stringBuilder.AppendLine($"HTTP Method:{Environment.NewLine}{requestClone.Method}{Environment.NewLine}"); + stringBuilder.AppendLine($"Absolute Uri:{Environment.NewLine}{requestClone.RequestUri}{Environment.NewLine}"); + stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(requestClone.Headers)}{Environment.NewLine}"); stringBuilder.AppendLine($"Body:{Environment.NewLine}{SanitizeBody(body)}{Environment.NewLine}"); return stringBuilder.ToString(); } @@ -84,12 +84,17 @@ public static async Task GetHttpResponseLogAsync(HttpResponseMessage res StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"============================ HTTP RESPONSE ============================{Environment.NewLine}"); stringBuilder.AppendLine($"Status Code:{Environment.NewLine}{response.StatusCode}{Environment.NewLine}"); - stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(ConvertHttpHeadersToCollection(response.Headers))}{Environment.NewLine}"); + stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(response.Headers)}{Environment.NewLine}"); stringBuilder.AppendLine($"Body:{Environment.NewLine}{SanitizeBody(body)}{Environment.NewLine}"); return stringBuilder.ToString(); } - private static Regex regexPattern = new Regex("(\\s*\"access_token\"\\s*:\\s*)\"[^\"]+\"", RegexOptions.Compiled); + internal static string HeadersToString(HttpHeaders headers) + { + return HeadersToString(ConvertHttpHeadersToCollection(headers)); + } + + private static readonly Regex regexPattern = new Regex("(\\s*\"access_token\"\\s*:\\s*)\"[^\"]+\"", RegexOptions.Compiled); private static object SanitizeBody(string body) { IList regexList = new List(); @@ -110,7 +115,7 @@ private static IDictionary> ConvertHttpHeadersToColl return headers.ToDictionary(a => a.Key, a => a.Value); } - private static object HeadersToString(IDictionary> headers) + private static string HeadersToString(IDictionary> headers) { StringBuilder stringBuilder = headers.Aggregate(new StringBuilder(), (sb, kvp) => sb.AppendLine(string.Format("{0,-30}: {1}", kvp.Key, String.Join(",", kvp.Value.ToArray())))); diff --git a/tools/Custom/PSCmdletExtensions.cs b/tools/Custom/PSCmdletExtensions.cs index 5ea97a405c9..f3d08395592 100644 --- a/tools/Custom/PSCmdletExtensions.cs +++ b/tools/Custom/PSCmdletExtensions.cs @@ -5,6 +5,7 @@ namespace NamespacePrefixPlaceholder.PowerShell { using Microsoft.Graph.PowerShell.Authentication; using Microsoft.Graph.PowerShell.Authentication.Common; + using NamespacePrefixPlaceholder.PowerShell.Models; using System; using System.Collections.ObjectModel; using System.IO; @@ -105,6 +106,21 @@ internal static void WriteToFile(this PSCmdlet cmdlet, HttpResponseMessage respo } } + internal static ErrorDetails GetErrorDetails(this PSCmdlet cmdlet, IMicrosoftGraphODataErrorsMainError odataError, HttpResponseMessage responseMessage) + { + var serviceErrorDoc = "https://learn.microsoft.com/graph/errors"; + var recommendedAction = $"See service error codes: {serviceErrorDoc}"; + var requestUri = $"{responseMessage.RequestMessage?.Method} {responseMessage.RequestMessage?.RequestUri}"; + string date = odataError?.Innererror?.Date; + string requestId = odataError?.Innererror?.RequestId; + string clientRequestId = odataError?.Innererror?.ClientRequestId; + string errorDetailsMessage = $"{odataError?.Message}\n\n{requestUri}\nStatusCode: {responseMessage.StatusCode}\nErrorCode: {odataError?.Code}\nDate: {date}\nRequestId: {requestId}\nClientRequestId: {clientRequestId}\n\n{recommendedAction}"; + return new ErrorDetails(errorDetailsMessage) + { + RecommendedAction = recommendedAction + }; + } + /// /// Writes an input stream to an output stream. /// From d2d0e2ec372faa3cd07ff34166263c463049f30c Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 20 Jun 2023 11:27:55 -0700 Subject: [PATCH 2/5] Log response content. --- src/readme.graph.md | 2 +- tools/Custom/HttpMessageLogFormatter.cs | 18 ++++++++++++++++++ tools/Custom/PSCmdletExtensions.cs | 8 ++------ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/readme.graph.md b/src/readme.graph.md index c349a23eebe..8820d24d24d 100644 --- a/src/readme.graph.md +++ b/src/readme.graph.md @@ -422,7 +422,7 @@ directive: // Format error details. let errorDetailsRegex = /(ErrorDetails\s*=\s*)(new.*ErrorDetails\(message\).*)/gmi - $ = $.replace(errorDetailsRegex, '$1this.GetErrorDetails((await response)?.Error, responseMessage)'); + $ = $.replace(errorDetailsRegex, '$1await this.GetErrorDetailsAsync((await response)?.Error, responseMessage)'); return $; } diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 16bff521cde..6b3387cbad9 100644 --- a/tools/Custom/HttpMessageLogFormatter.cs +++ b/tools/Custom/HttpMessageLogFormatter.cs @@ -89,6 +89,24 @@ public static async Task GetHttpResponseLogAsync(HttpResponseMessage res return stringBuilder.ToString(); } + public static async Task GetErrorLogAsync(HttpResponseMessage response) + { + if (response == null) return string.Empty; + + string body = string.Empty; + try + { + body = (response.Content == null) ? string.Empty : FormatString(await response.Content.ReadAsStringAsync()); + } + catch { } + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Status:{((int)response.StatusCode)} ({response.StatusCode}){Environment.NewLine}"); + stringBuilder.AppendLine($"Content:{Environment.NewLine}{body}{Environment.NewLine}"); + stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(response.Headers)}{Environment.NewLine}"); + return stringBuilder.ToString(); + } + internal static string HeadersToString(HttpHeaders headers) { return HeadersToString(ConvertHttpHeadersToCollection(headers)); diff --git a/tools/Custom/PSCmdletExtensions.cs b/tools/Custom/PSCmdletExtensions.cs index f3d08395592..ec27bf16669 100644 --- a/tools/Custom/PSCmdletExtensions.cs +++ b/tools/Custom/PSCmdletExtensions.cs @@ -106,15 +106,11 @@ internal static void WriteToFile(this PSCmdlet cmdlet, HttpResponseMessage respo } } - internal static ErrorDetails GetErrorDetails(this PSCmdlet cmdlet, IMicrosoftGraphODataErrorsMainError odataError, HttpResponseMessage responseMessage) + internal static async Task GetErrorDetailsAsync(this PSCmdlet cmdlet, IMicrosoftGraphODataErrorsMainError odataError, HttpResponseMessage response) { var serviceErrorDoc = "https://learn.microsoft.com/graph/errors"; var recommendedAction = $"See service error codes: {serviceErrorDoc}"; - var requestUri = $"{responseMessage.RequestMessage?.Method} {responseMessage.RequestMessage?.RequestUri}"; - string date = odataError?.Innererror?.Date; - string requestId = odataError?.Innererror?.RequestId; - string clientRequestId = odataError?.Innererror?.ClientRequestId; - string errorDetailsMessage = $"{odataError?.Message}\n\n{requestUri}\nStatusCode: {responseMessage.StatusCode}\nErrorCode: {odataError?.Code}\nDate: {date}\nRequestId: {requestId}\nClientRequestId: {clientRequestId}\n\n{recommendedAction}"; + var errorDetailsMessage = await HttpMessageLogFormatter.GetErrorLogAsync(response); return new ErrorDetails(errorDetailsMessage) { RecommendedAction = recommendedAction From 80c5cab6d874f2acad95b582a0403207f6de67f4 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 20 Jun 2023 12:05:51 -0700 Subject: [PATCH 3/5] Include error message in line one. --- tools/Custom/HttpMessageLogFormatter.cs | 2 +- tools/Custom/PSCmdletExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 6b3387cbad9..97e70dcc542 100644 --- a/tools/Custom/HttpMessageLogFormatter.cs +++ b/tools/Custom/HttpMessageLogFormatter.cs @@ -96,7 +96,7 @@ public static async Task GetErrorLogAsync(HttpResponseMessage response) string body = string.Empty; try { - body = (response.Content == null) ? string.Empty : FormatString(await response.Content.ReadAsStringAsync()); + body = (response.Content == null) ? string.Empty : await response.Content.ReadAsStringAsync(); } catch { } diff --git a/tools/Custom/PSCmdletExtensions.cs b/tools/Custom/PSCmdletExtensions.cs index ec27bf16669..8b4e16e3658 100644 --- a/tools/Custom/PSCmdletExtensions.cs +++ b/tools/Custom/PSCmdletExtensions.cs @@ -111,7 +111,7 @@ internal static async Task GetErrorDetailsAsync(this PSCmdlet cmdl var serviceErrorDoc = "https://learn.microsoft.com/graph/errors"; var recommendedAction = $"See service error codes: {serviceErrorDoc}"; var errorDetailsMessage = await HttpMessageLogFormatter.GetErrorLogAsync(response); - return new ErrorDetails(errorDetailsMessage) + return new ErrorDetails($"{odataError?.Message}{Environment.NewLine}{errorDetailsMessage}") { RecommendedAction = recommendedAction }; From 296ba2dfd06c5a8d5e629e1839b5abb5f0b2c91a Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 20 Jun 2023 12:07:10 -0700 Subject: [PATCH 4/5] Add Microsoft Graph error properties to InnerError. --- src/readme.graph.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readme.graph.md b/src/readme.graph.md index 8820d24d24d..a5cf8ab0d67 100644 --- a/src/readme.graph.md +++ b/src/readme.graph.md @@ -246,7 +246,7 @@ directive: } } } -# Mark '@odata.id' as required properties for /$ref. +# Add Microsoft Graph error properties to InnerError. - from: 'openapi-document' where: $.components.schemas['microsoft.graph.ODataErrors.InnerError'] transform: >- From 3cffc9fb053098398b1fc6dd6f9a1b593192b309 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 20 Jun 2023 16:02:05 -0700 Subject: [PATCH 5/5] Log Microsoft Graph error codes. --- tools/Custom/HttpMessageLogFormatter.cs | 16 ++++++---------- tools/Custom/PSCmdletExtensions.cs | 6 +++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/tools/Custom/HttpMessageLogFormatter.cs b/tools/Custom/HttpMessageLogFormatter.cs index 97e70dcc542..7aaf1e1247c 100644 --- a/tools/Custom/HttpMessageLogFormatter.cs +++ b/tools/Custom/HttpMessageLogFormatter.cs @@ -4,6 +4,7 @@ namespace NamespacePrefixPlaceholder.PowerShell { + using NamespacePrefixPlaceholder.PowerShell.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -89,20 +90,15 @@ public static async Task GetHttpResponseLogAsync(HttpResponseMessage res return stringBuilder.ToString(); } - public static async Task GetErrorLogAsync(HttpResponseMessage response) + public static async Task GetErrorLogAsync(HttpResponseMessage response, IMicrosoftGraphODataErrorsMainError odataError) { if (response == null) return string.Empty; - string body = string.Empty; - try - { - body = (response.Content == null) ? string.Empty : await response.Content.ReadAsStringAsync(); - } - catch { } - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Status:{((int)response.StatusCode)} ({response.StatusCode}){Environment.NewLine}"); - stringBuilder.AppendLine($"Content:{Environment.NewLine}{body}{Environment.NewLine}"); + stringBuilder.AppendLine($"{odataError?.Message}{Environment.NewLine}"); + stringBuilder.AppendLine($"Status: {((int)response.StatusCode)} ({response.StatusCode})"); + stringBuilder.AppendLine($"ErrorCode: {odataError?.Code}"); + stringBuilder.AppendLine($"Date: {odataError?.Innererror?.Date}{Environment.NewLine}"); stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(response.Headers)}{Environment.NewLine}"); return stringBuilder.ToString(); } diff --git a/tools/Custom/PSCmdletExtensions.cs b/tools/Custom/PSCmdletExtensions.cs index 8b4e16e3658..f34a234d9f2 100644 --- a/tools/Custom/PSCmdletExtensions.cs +++ b/tools/Custom/PSCmdletExtensions.cs @@ -105,13 +105,13 @@ internal static void WriteToFile(this PSCmdlet cmdlet, HttpResponseMessage respo cmdlet.WriteToStream(inputStream, fileProvider.Stream, downloadUrl, cancellationToken); } } - + internal static async Task GetErrorDetailsAsync(this PSCmdlet cmdlet, IMicrosoftGraphODataErrorsMainError odataError, HttpResponseMessage response) { var serviceErrorDoc = "https://learn.microsoft.com/graph/errors"; var recommendedAction = $"See service error codes: {serviceErrorDoc}"; - var errorDetailsMessage = await HttpMessageLogFormatter.GetErrorLogAsync(response); - return new ErrorDetails($"{odataError?.Message}{Environment.NewLine}{errorDetailsMessage}") + var errorDetailsMessage = await HttpMessageLogFormatter.GetErrorLogAsync(response, odataError); + return new ErrorDetails(errorDetailsMessage) { RecommendedAction = recommendedAction };