Skip to content
74 changes: 11 additions & 63 deletions src/Microsoft.Graph.Core/Exceptions/ErrorConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,30 @@ internal static class ErrorConstants
{
internal static class Codes
{
internal static string GeneralException = "generalException";

internal static string InvalidRequest = "invalidRequest";

internal static string ItemNotFound = "itemNotFound";

internal static string NotAllowed = "notAllowed";

internal static string Timeout = "timeout";

internal static string TooManyRedirects = "tooManyRedirects";

internal static string TooManyRetries = "tooManyRetries";

internal static string MaximumValueExceeded = "MaximumValueExceeded";

internal static string InvalidArgument = "invalidArgument";

internal const string TemporarilyUnavailable = "temporarily_unavailable";
internal const string GeneralException = "generalException";
}

internal static class Messages
{
internal static string AuthenticationProviderMissing = "Authentication provider is required before sending a request.";

internal static string BaseUrlMissing = "Base URL cannot be null or empty.";

internal static string InvalidTypeForDateConverter = "DateConverter can only serialize objects of type Date.";

internal static string InvalidTypeForDateTimeOffsetConverter = "DateTimeOffsetConverter can only serialize objects of type DateTimeOffset.";

internal static string LocationHeaderNotSetOnRedirect = "Location header not present in redirection response.";

internal static string OverallTimeoutCannotBeSet = "Overall timeout cannot be set after the first request is sent.";

internal static string RequestTimedOut = "The request timed out.";

internal static string RequestUrlMissing = "Request URL is required to send a request.";

internal static string TooManyRedirectsFormatString = "More than {0} redirects encountered while sending the request.";

internal static string TooManyRetriesFormatString = "More than {0} retries encountered while sending the request.";

internal static string UnableToCreateInstanceOfTypeFormatString = "Unable to create an instance of type {0}.";

internal static string UnableToDeserializeDate = "Unable to deserialize the returned Date.";

internal static string UnableToDeserializeDateTimeOffset = "Unable to deserialize the returned DateDateTimeOffset.";

internal static string UnexpectedExceptionOnSend = "An error occurred sending the request.";

internal static string UnexpectedExceptionResponse = "Unexpected exception returned from the service.";

internal static string MaximumValueExceeded = "{0} exceeds the maximum value of {1}.";

internal static string NullParameter = "{0} parameter cannot be null.";

internal static string UnableToDeserializeContent = "Unable to deserialize content.";
internal const string MaximumValueExceeded = "{0} exceeds the maximum value of {1}.";

internal static string InvalidDependsOnRequestId = "Corresponding batch request id not found for the specified dependsOn relation.";
internal const string NullParameter = "{0} parameter cannot be null.";

internal static string ExpiredUploadSession = "Upload session expired. Upload cannot resume";
internal const string UnableToDeserializeContent = "Unable to deserialize content.";

internal static string NoResponseForUpload = "No Response Received for upload.";
internal const string InvalidDependsOnRequestId = "Corresponding batch request id not found for the specified dependsOn relation.";

internal static string NullValue = "{0} cannot be null.";
internal const string ExpiredUploadSession = "Upload session expired. Upload cannot resume";

internal static string UnexpectedMsalException = "Unexpected exception returned from MSAL.";
internal const string NoResponseForUpload = "No Response Received for upload.";

internal static string UnexpectedException = "Unexpected exception occured while authenticating the request.";
internal const string MissingRetryAfterHeader = "Missing retry after header.";

internal static string MissingRetryAfterHeader = "Missing retry after header.";
internal const string PageIteratorRequestError = "Error occured when making a request with the page iterator. See inner exception for more details.";

internal static string PageIteratorRequestError = "Error occured when making a request with the page iterator. See inner exception for more details.";
internal const string BatchRequestError = "Error occured when making the batch request. See inner exception for more details.";

public static string InvalidProxyArgument = "Proxy cannot be set more once. Proxy can only be set on the proxy or defaultHttpHandler argument and not both.";
internal const string InvalidProxyArgument = "Proxy cannot be set more once. Proxy can only be set on the proxy or defaultHttpHandler argument and not both.";
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Graph.Core/Extensions/IParseNodeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Kiota.Abstractions.Serialization;

namespace Microsoft.Graph;

/// <summary>
/// Extension helpers for the <see cref="IParseNode"/>
/// </summary>
public static class ParseNodeExtensions
{
internal static string GetErrorMessage(this IParseNode responseParseNode)
{
var errorParseNode = responseParseNode.GetChildNode("error");
// concatenate the error code and message
return $"{errorParseNode?.GetChildNode("code")?.GetStringValue()} : {errorParseNode?.GetChildNode("message")?.GetStringValue()}";
}
}
35 changes: 34 additions & 1 deletion src/Microsoft.Graph.Core/Requests/BatchRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace Microsoft.Graph.Core.Requests
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Serialization.Json;

/// <summary>
/// The type BatchRequestBuilder
Expand Down Expand Up @@ -58,7 +60,9 @@ public async Task<BatchResponseContent> PostAsync(BatchRequestContent batchReque
var nativeResponseHandler = new NativeResponseHandler();
requestInfo.SetResponseHandler(nativeResponseHandler);
await this.RequestAdapter.SendNoContentAsync(requestInfo, cancellationToken: cancellationToken);
return new BatchResponseContent(nativeResponseHandler.Value as HttpResponseMessage, errorMappings);
var httpResponseMessage = nativeResponseHandler.Value as HttpResponseMessage;
await ThrowIfFailedResponseAsync(httpResponseMessage, cancellationToken);
return new BatchResponseContent(httpResponseMessage, errorMappings);
}

/// <summary>
Expand Down Expand Up @@ -99,5 +103,34 @@ public async Task<RequestInformation> ToPostRequestInformationAsync(BatchRequest
requestInfo.Headers.Add("Content-Type", CoreConstants.MimeTypeNames.Application.Json);
return requestInfo;
}

private static async Task ThrowIfFailedResponseAsync(HttpResponseMessage httpResponseMessage, CancellationToken cancellationToken)
{
if (httpResponseMessage.IsSuccessStatusCode) return;

if (httpResponseMessage is { Content.Headers.ContentType.MediaType: string contentTypeMediaType } && contentTypeMediaType.StartsWith(CoreConstants.MimeTypeNames.Application.Json, StringComparison.OrdinalIgnoreCase))
{
#if NET5_0_OR_GREATER
using var responseContent = await httpResponseMessage.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
#else
using var responseContent = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
#endif
using var document = await JsonDocument.ParseAsync(responseContent, cancellationToken: cancellationToken).ConfigureAwait(false);
var parsable = new JsonParseNode(document.RootElement);
throw new ServiceException(ErrorConstants.Messages.BatchRequestError, httpResponseMessage.Headers, (int)httpResponseMessage.StatusCode, new Exception(parsable.GetErrorMessage()));
}

var responseStringContent = string.Empty;
if (httpResponseMessage.Content != null)
{
#if NET5_0_OR_GREATER
responseStringContent = await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
responseStringContent = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
#endif
}

throw new ServiceException(ErrorConstants.Messages.BatchRequestError, httpResponseMessage.Headers, (int)httpResponseMessage.StatusCode, responseStringContent);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
public BatchResponseContent(HttpResponseMessage httpResponseMessage, Dictionary<string, ParsableFactory<IParsable>> errorMappings = null)
{
this.batchResponseMessage = httpResponseMessage ?? throw new ArgumentNullException(nameof(httpResponseMessage));
this.apiErrorMappings = errorMappings ?? new();
this.apiErrorMappings = errorMappings ?? new Dictionary<string, ParsableFactory<IParsable>>(StringComparer.OrdinalIgnoreCase) {
{"XXX", (parsable) => new ServiceException(ErrorConstants.Messages.BatchRequestError, new Exception(parsable.GetErrorMessage())) }
};
}

/// <summary>
Expand Down Expand Up @@ -88,7 +90,7 @@

if (jBatchResponseObject.RootElement.TryGetProperty(CoreConstants.BatchRequest.Responses, out JsonElement jResponses) && jResponses.ValueKind == JsonValueKind.Array)
{
foreach (var element in jResponses.EnumerateArray())

Check warning on line 93 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Loops should be simplified using the "Where" LINQ method

Check warning on line 93 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Loops should be simplified using the "Where" LINQ method
{
if (element.GetProperty(CoreConstants.BatchRequest.Id).GetString().Equals(requestId))
{
Expand Down Expand Up @@ -139,7 +141,7 @@
/// Gets the @NextLink of a batch response.
/// </summary>
/// <returns></returns>
[Obsolete("This method is deprecated as a batch response does not contain a next link", true)]

Check warning on line 144 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Do not forget to remove this deprecated code someday.

Check warning on line 144 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Do not forget to remove this deprecated code someday.
public async Task<string> GetNextLinkAsync()
{
jBatchResponseObject = jBatchResponseObject ?? await GetBatchResponseContentAsync().ConfigureAwait(false);
Expand Down Expand Up @@ -169,7 +171,7 @@
/// </summary>
/// <param name="jResponseItem">A single batch response item of type <see cref="JsonElement"/>.</param>
/// <returns>A single batch response as a <see cref="HttpResponseMessage"/>.</returns>
private HttpResponseMessage GetResponseMessageFromJObject(JsonElement jResponseItem)

Check warning on line 174 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'GetResponseMessageFromJObject' a static method.
{
HttpResponseMessage responseMessage = new HttpResponseMessage();

Expand Down Expand Up @@ -201,7 +203,7 @@
}


private HttpStatusCode GetStatusCodeFromJObject(JsonElement jResponseItem)

Check warning on line 206 in src/Microsoft.Graph.Core/Requests/Content/BatchResponseContent.cs

View workflow job for this annotation

GitHub Actions / Build

Make 'GetStatusCodeFromJObject' a static method.
{
if (jResponseItem.TryGetProperty(CoreConstants.BatchRequest.Status, out JsonElement status))
{
Expand Down
15 changes: 3 additions & 12 deletions src/Microsoft.Graph.Core/Tasks/PageIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
if (requestAdapter == null)
throw new ArgumentNullException(nameof(requestAdapter));

if (page == null)

Check warning on line 99 in src/Microsoft.Graph.Core/Tasks/PageIterator.cs

View workflow job for this annotation

GitHub Actions / Build

Use a comparison to 'default(TCollectionPage)' instead or add a constraint to 'TCollectionPage' so that it can't be a value type.

Check warning on line 99 in src/Microsoft.Graph.Core/Tasks/PageIterator.cs

View workflow job for this annotation

GitHub Actions / Build

Use a comparison to 'default(TCollectionPage)' instead or add a constraint to 'TCollectionPage' so that it can't be a value type.
throw new ArgumentNullException(nameof(page));

if (callback == null)
Expand All @@ -115,8 +115,7 @@
_processPageItemCallback = callback,
_requestConfigurator = requestConfigurator,
_errorMapping = errorMapping ?? new Dictionary<string, ParsableFactory<IParsable>>(StringComparer.OrdinalIgnoreCase) {
{"4XX", (parsable) => new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(GetErrorMessageFromParsable(parsable))) },
{"5XX", (parsable) => new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(GetErrorMessageFromParsable(parsable))) }
{"XXX", (parsable) => new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(parsable.GetErrorMessage())) }
},
State = PagingState.NotStarted
};
Expand Down Expand Up @@ -153,7 +152,7 @@
if (requestAdapter == null)
throw new ArgumentNullException(nameof(requestAdapter));

if (page == null)

Check warning on line 155 in src/Microsoft.Graph.Core/Tasks/PageIterator.cs

View workflow job for this annotation

GitHub Actions / Build

Use a comparison to 'default(TCollectionPage)' instead or add a constraint to 'TCollectionPage' so that it can't be a value type.

Check warning on line 155 in src/Microsoft.Graph.Core/Tasks/PageIterator.cs

View workflow job for this annotation

GitHub Actions / Build

Use a comparison to 'default(TCollectionPage)' instead or add a constraint to 'TCollectionPage' so that it can't be a value type.
throw new ArgumentNullException(nameof(page));

if (asyncCallback == null)
Expand All @@ -172,15 +171,14 @@
_asyncProcessPageItemCallback = asyncCallback,
_requestConfigurator = requestConfigurator,
_errorMapping = errorMapping ?? new Dictionary<string, ParsableFactory<IParsable>>(StringComparer.OrdinalIgnoreCase) {
{"4XX", (parsable) => new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(GetErrorMessageFromParsable(parsable))) },
{"5XX", (parsable) =>new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(GetErrorMessageFromParsable(parsable))) },
{"XXX", (parsable) => new ServiceException(ErrorConstants.Messages.PageIteratorRequestError,new Exception(parsable.GetErrorMessage())) }
},
State = PagingState.NotStarted
};
}

/// <summary>
/// Iterate across the content of a a single results page with the callback.
/// Iterate across the content of a single results page with the callback.
/// </summary>
/// <returns>A boolean value that indicates whether the callback cancelled
/// iterating across the page results or whether there are more pages to page.
Expand Down Expand Up @@ -393,13 +391,6 @@
// the next link property may not be defined in the response schema so we also check its presence in the additional data bag
return parsableCollection.AdditionalData.TryGetValue(CoreConstants.OdataInstanceAnnotations.NextLink, out var nextLink) ? nextLink.ToString() : string.Empty;
}

private static string GetErrorMessageFromParsable(IParseNode responseParseNode)
{
var errorParseNode = responseParseNode.GetChildNode("error");
// concatenate the error code and message
return $"{errorParseNode?.GetChildNode("code")?.GetStringValue()} : {errorParseNode?.GetChildNode("message")?.GetStringValue()}";
}
}

/// <summary>
Expand Down
Loading