Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added error output handling for Graph calls #1923

Merged
merged 3 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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 [#1923](https://github.com/pnp/powershell/pull/1923)
- Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922)

### Fixed
Expand Down
27 changes: 1 addition & 26 deletions src/Commands/Graph/InvokeGraphMethod.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -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<GraphException>(errorContent, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
exception.AccessToken = AccessToken;
throw exception;
}
}

private object Deserialize(string result)
{
var element = JsonSerializer.Deserialize<JsonElement>(result);
Expand Down Expand Up @@ -250,31 +229,27 @@ 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);
}

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);
}

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);
}

private void DeleteRequest()
{
var response = GraphHelper.DeleteAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult();
ThrowIfNoSuccess(response);
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
WriteGraphResult(result);
}
Expand Down
16 changes: 1 addition & 15 deletions src/Commands/Teams/CopyTeamsTeam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
40 changes: 18 additions & 22 deletions src/Commands/Utilities/REST/GraphHelper.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -249,26 +248,8 @@ public static async Task<HttpResponseMessage> PatchAsync(HttpClient httpClient,
return await GetResponseMessageAsync(httpClient, message);
}



// public static async Task<T> PatchAsync<T>(HttpClient httpClient, string accessToken, string url, T content,IDictionary<string, string> 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<T>(returnValue, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
// }
// else
// {
// return default;
// }
// }
#endregion


public static async Task<T> PostAsync<T>(HttpClient httpClient, string url, HttpContent content, string accessToken, IDictionary<string, string> additionalHeaders = null, bool propertyNameCaseInsensitive = false)
{
return await PostInternalAsync<T>(httpClient, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive);
Expand Down Expand Up @@ -369,8 +350,6 @@ private static async Task<string> SendMessageAsync(HttpClient httpClient, HttpRe
}
}



public static async Task<HttpResponseMessage> GetResponseMessageAsync(HttpClient httpClient, HttpRequestMessage message)
{
var response = await httpClient.SendAsync(message);
Expand All @@ -381,6 +360,23 @@ public static async Task<HttpResponseMessage> 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;
}

Expand Down
15 changes: 2 additions & 13 deletions src/Commands/Utilities/TeamsUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -913,19 +913,8 @@ public static async Task<TeamApp> 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<TeamApp>(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
}
return null;
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return JsonSerializer.Deserialize<TeamApp>(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
}

public static async Task<HttpResponseMessage> UpdateAppAsync(HttpClient httpClient, string accessToken, byte[] bytes, string appId)
Expand Down