From a51adcbaf8fa864b163870092a2a1493ff6a7c3b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 00:25:20 +0200 Subject: [PATCH 1/2] Added error output handling for Graph calls --- CHANGELOG.md | 1 + src/Commands/Graph/InvokeGraphMethod.cs | 27 +-------------- src/Commands/Teams/CopyTeamsTeam.cs | 16 +-------- src/Commands/Utilities/REST/GraphHelper.cs | 40 ++++++++++------------ src/Commands/Utilities/TeamsUtility.cs | 15 ++------ 5 files changed, 23 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5932c10a..912628285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) - Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) +- Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails ### Fixed diff --git a/src/Commands/Graph/InvokeGraphMethod.cs b/src/Commands/Graph/InvokeGraphMethod.cs index 5429a421c..d82781e12 100644 --- a/src/Commands/Graph/InvokeGraphMethod.cs +++ b/src/Commands/Graph/InvokeGraphMethod.cs @@ -1,22 +1,12 @@ - -using Newtonsoft.Json.Linq; -using PnP.Framework.Utilities; -using PnP.PowerShell.Commands.Attributes; -using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Enums; -using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Utilities.REST; -using PnP.PowerShell.Commands.Utilities.JSON; using System; -using System.Linq; using System.Management.Automation; using System.Text.Json; -using System.Collections; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; -using PnP.PowerShell.Commands.Model.Graph; namespace PnP.PowerShell.Commands.Base { @@ -160,17 +150,6 @@ private void SendRequest() } } - private void ThrowIfNoSuccess(HttpResponseMessage response) - { - if (!response.IsSuccessStatusCode) - { - var errorContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - var exception = JsonSerializer.Deserialize(errorContent, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - exception.AccessToken = AccessToken; - throw exception; - } - } - private object Deserialize(string result) { var element = JsonSerializer.Deserialize(result); @@ -250,7 +229,6 @@ private void GetRequest() private void PostRequest() { var response = GraphHelper.PostAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -258,7 +236,6 @@ private void PostRequest() private void PutRequest() { var response = GraphHelper.PutAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -266,7 +243,6 @@ private void PutRequest() private void PatchRequest() { var response = GraphHelper.PatchAsync(HttpClient, AccessToken, GetHttpContent(), Url, AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -274,7 +250,6 @@ private void PatchRequest() private void DeleteRequest() { var response = GraphHelper.DeleteAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } diff --git a/src/Commands/Teams/CopyTeamsTeam.cs b/src/Commands/Teams/CopyTeamsTeam.cs index ce2830066..48be4d2bf 100644 --- a/src/Commands/Teams/CopyTeamsTeam.cs +++ b/src/Commands/Teams/CopyTeamsTeam.cs @@ -70,21 +70,7 @@ protected override void ExecuteCmdlet() * but currently ignored and can't be set by user */ teamClone.MailNickName = DisplayName; teamClone.Visibility = (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()); - var response = TeamsUtility.CloneTeamAsync(AccessToken, HttpClient, groupId, teamClone).GetAwaiter().GetResult(); - if (!response.IsSuccessStatusCode) - { - if (GraphHelper.TryGetGraphException(response, out GraphException ex)) - { - if (ex.Error != null) - { - throw new PSInvalidOperationException(ex.Error.Message); - } - } - else - { - WriteError(new ErrorRecord(new Exception($"Team clone failed"), "CLONEFAILED", ErrorCategory.InvalidResult, this)); - } - } + TeamsUtility.CloneTeamAsync(AccessToken, HttpClient, groupId, teamClone).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index 500246f48..4a37d1a93 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -1,14 +1,13 @@ using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Model.Graph; -using PnP.PowerShell.Commands.Model.Teams; using System; using System.Collections.Generic; using System.Linq; +using System.Management.Automation; using System.Net; using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading; using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Utilities.REST @@ -249,26 +248,8 @@ public static async Task PatchAsync(HttpClient httpClient, return await GetResponseMessageAsync(httpClient, message); } - - - // public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content,IDictionary additionalHeaders = null) - // { - // var requestContent = new StringContent(JsonSerializer.Serialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); - // requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - // var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, requestContent, additionalHeaders); - // var returnValue = await SendMessageAsync(httpClient, message); - // if (!string.IsNullOrEmpty(returnValue)) - // { - // return JsonSerializer.Deserialize(returnValue, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - // } - // else - // { - // return default; - // } - // } #endregion - public static async Task PostAsync(HttpClient httpClient, string url, HttpContent content, string accessToken, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { return await PostInternalAsync(httpClient, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive); @@ -369,8 +350,6 @@ private static async Task SendMessageAsync(HttpClient httpClient, HttpRe } } - - public static async Task GetResponseMessageAsync(HttpClient httpClient, HttpRequestMessage message) { var response = await httpClient.SendAsync(message); @@ -381,6 +360,23 @@ public static async Task GetResponseMessageAsync(HttpClient await Task.Delay(retryAfter.Delta.Value.Seconds * 1000); response = await httpClient.SendAsync(CloneMessage(message)); } + + // Validate if the response was successful, if not throw an exception + if (!response.IsSuccessStatusCode) + { + if (GraphHelper.TryGetGraphException(response, out GraphException ex)) + { + if (ex.Error != null) + { + throw new PSInvalidOperationException(ex.Error.Message); + } + } + else + { + throw new PSInvalidOperationException($"Call to Microsoft Graph URL {message.RequestUri} failed with status code {response.StatusCode}"); + } + } + return response; } diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 04467a9e3..ced2b994d 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -913,19 +913,8 @@ public static async Task AddAppAsync(HttpClient httpClient, string acce var byteArrayContent = new ByteArrayContent(bytes); byteArrayContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip"); var response = await GraphHelper.PostAsync(httpClient, "v1.0/appCatalogs/teamsApps", accessToken, byteArrayContent); - if (!response.IsSuccessStatusCode) - { - if (GraphHelper.TryGetGraphException(response, out GraphException exception)) - { - throw exception; - } - } - else - { - var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - return JsonSerializer.Deserialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - } - return null; + var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + return JsonSerializer.Deserialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); } public static async Task UpdateAppAsync(HttpClient httpClient, string accessToken, byte[] bytes, string appId) From d2f4afe1d0daf702e27176d5e86384c1ae1a2c10 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 00:27:49 +0200 Subject: [PATCH 2/2] Added PR reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 912628285..90ccda4c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) - Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) -- Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails +- Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails [#1923](https://github.com/pnp/powershell/pull/1923) ### Fixed