Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #197 from microsoft/feature/XXXmapping
Browse files Browse the repository at this point in the history
Adds XXX mapping code to adapter.
  • Loading branch information
andrueastman authored Jan 23, 2024
2 parents f5a88f2 + 393984f commit b197059
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.5] - 2023-01-23

### Added

- Adds support for `XXX` status code error mapping to HttpClientRequestAdapter.

## [1.3.4] - 2023-12-29

### Added
Expand Down
19 changes: 19 additions & 0 deletions Microsoft.Kiota.Http.HttpClientLibrary.Tests/Mocks/MockError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Serialization;

namespace Microsoft.Kiota.Http.HttpClientLibrary.Tests.Mocks;

public class MockError(string message) : ApiException(message), IParsable
{
public IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>();
}

public void Serialize(ISerializationWriter writer)
{
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -446,5 +446,94 @@ public async void SetsTheApiExceptionStatusCode(HttpStatusCode statusCode)
Assert.True(e.ResponseHeaders.ContainsKey("request-id"));
}
}
[InlineData(HttpStatusCode.NotFound)]// 4XX
[InlineData(HttpStatusCode.BadGateway)]// 5XX
[Theory]
public async void SelectsTheXXXErrorMappingClassCorrectly(HttpStatusCode statusCode)
{
var mockHandler = new Mock<HttpMessageHandler>();
var client = new HttpClient(mockHandler.Object);
mockHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() =>
{
var responseMessage = new HttpResponseMessage
{
StatusCode = statusCode,
Content = new StringContent("{}",Encoding.UTF8,"application/json")
};
return responseMessage;
});
var mockParseNode = new Mock<IParseNode>();
mockParseNode.Setup<IParsable>(x => x.GetObjectValue(It.IsAny<ParsableFactory<IParsable>>()))
.Returns(new MockError("A general error occured"));
var mockParseNodeFactory = new Mock<IParseNodeFactory>();
mockParseNodeFactory.Setup<IParseNode>(x => x.GetRootParseNode(It.IsAny<string>(), It.IsAny<Stream>()))
.Returns(mockParseNode.Object);
var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client);
var requestInfo = new RequestInformation
{
HttpMethod = Method.GET,
UrlTemplate = "https://example.com"
};
try
{
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>()
{
{ "XXX", (parseNode) => new MockError("A general error occured")},
};
var response = await adapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping);
Assert.Fail("Expected an ApiException to be thrown");
}
catch(MockError mockError)
{
Assert.Equal((int)statusCode, mockError.ResponseStatusCode);
Assert.Equal("A general error occured", mockError.Message);
}
}
[InlineData(HttpStatusCode.BadGateway)]// 5XX
[Theory]
public async void ThrowsApiExceptionOnMissingMapping(HttpStatusCode statusCode)
{
var mockHandler = new Mock<HttpMessageHandler>();
var client = new HttpClient(mockHandler.Object);
mockHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() =>
{
var responseMessage = new HttpResponseMessage
{
StatusCode = statusCode,
Content = new StringContent("{}", Encoding.UTF8, "application/json")
};
return responseMessage;
});
var mockParseNode = new Mock<IParseNode>();
mockParseNode.Setup<IParsable>(x => x.GetObjectValue(It.IsAny<ParsableFactory<IParsable>>()))
.Returns(new MockError("A general error occured: "+ statusCode.ToString()));
var mockParseNodeFactory = new Mock<IParseNodeFactory>();
mockParseNodeFactory.Setup<IParseNode>(x => x.GetRootParseNode(It.IsAny<string>(), It.IsAny<Stream>()))
.Returns(mockParseNode.Object);
var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client);
var requestInfo = new RequestInformation
{
HttpMethod = Method.GET,
UrlTemplate = "https://example.com"
};
try
{
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>()
{
{ "4XX", (parseNode) => new MockError("A 4XX error occured") }//Only 4XX
};
var response = await adapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping);
Assert.Fail("Expected an ApiException to be thrown");
}
catch(ApiException apiException)
{
Assert.Equal((int)statusCode, apiException.ResponseStatusCode);
Assert.Contains("The server returned an unexpected status code and no error factory is registered for this code", apiException.Message);
}
}
}
}
5 changes: 3 additions & 2 deletions src/HttpClientRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public string? BaseUrl
get => baseUrl;
set => this.baseUrl = value?.TrimEnd('/');
}
private static readonly char[] charactersToDecodeForUriTemplate = new char[] { '$', '.', '-', '~' };
private static readonly char[] charactersToDecodeForUriTemplate = ['$', '.', '-', '~'];
private static readonly Regex queryParametersCleanupRegex = new(@"\{\?[^\}]+}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromMilliseconds(100));
private Activity? startTracingSpan(RequestInformation requestInfo, string methodName)
{
Expand Down Expand Up @@ -393,7 +393,8 @@ private async Task ThrowIfFailedResponse(HttpResponseMessage response, Dictionar
if(errorMapping == null ||
!errorMapping.TryGetValue(statusCodeAsString, out errorFactory) &&
!(statusCodeAsInt >= 400 && statusCodeAsInt < 500 && errorMapping.TryGetValue("4XX", out errorFactory)) &&
!(statusCodeAsInt >= 500 && statusCodeAsInt < 600 && errorMapping.TryGetValue("5XX", out errorFactory)))
!(statusCodeAsInt >= 500 && statusCodeAsInt < 600 && errorMapping.TryGetValue("5XX", out errorFactory)) &&
!errorMapping.TryGetValue("XXX", out errorFactory))
{
activityForAttributes?.SetTag(ErrorMappingFoundAttributeName, false);
throw new ApiException($"The server returned an unexpected status code and no error factory is registered for this code: {statusCodeAsString}")
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Kiota.Http.HttpClientLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.3.4</VersionPrefix>
<VersionPrefix>1.3.5</VersionPrefix>
<VersionSuffix></VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- Enable this line once we go live to prevent breaking changes -->
Expand Down

0 comments on commit b197059

Please sign in to comment.