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

Commit 01e734e

Browse files
authored
Add uri replacement handler (#158)
* Add uri replacement handler * Bump minor version * Add changelog entry.
1 parent 3ddf8e0 commit 01e734e

File tree

5 files changed

+201
-1
lines changed

5 files changed

+201
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.3.0] - 2023-11-02
11+
12+
### Added
13+
14+
- Added uri replacement handler.
15+
1016
## [1.2.0] - 2023-10-23
1117

1218
### Added
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
6+
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;
7+
using Moq;
8+
using Xunit;
9+
10+
namespace Microsoft.Kiota.Http.HttpClientLibrary.Tests.Middleware;
11+
12+
public class UriReplacementOptionTests {
13+
[Fact]
14+
public void Does_Nothing_When_Url_Replacement_Is_Disabled()
15+
{
16+
var uri = new Uri("http://localhost/test");
17+
var disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>());
18+
19+
Assert.False(disabled.IsEnabled());
20+
Assert.Equal(uri, disabled.Replace(uri));
21+
22+
disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>{
23+
{"test", ""}
24+
});
25+
26+
Assert.Equal(uri, disabled.Replace(uri));
27+
}
28+
29+
[Fact]
30+
public void Returns_Null_When_Url_Provided_Is_Null()
31+
{
32+
var disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>());
33+
34+
Assert.False(disabled.IsEnabled());
35+
Assert.Null(disabled.Replace(null));
36+
}
37+
38+
[Fact]
39+
public void Replaces_Key_In_Path_With_Value()
40+
{
41+
var uri = new Uri("http://localhost/test");
42+
var option = new UriReplacementHandlerOption(true, new Dictionary<string, string>{{"test", ""}});
43+
44+
Assert.True(option.IsEnabled());
45+
Assert.Equal("http://localhost/", option.Replace(uri)!.ToString());
46+
}
47+
}
48+
49+
public class UriReplacementHandlerTests
50+
{
51+
[Fact]
52+
public async Task Calls_Uri_ReplacementAsync()
53+
{
54+
var mockReplacement = new Mock<IUriReplacementHandlerOption>();
55+
mockReplacement.Setup(static x => x.IsEnabled()).Returns(true);
56+
mockReplacement.Setup(static x => x.Replace(It.IsAny<Uri>())).Returns(new Uri("http://changed"));
57+
58+
var handler = new UriReplacementHandler<IUriReplacementHandlerOption>(mockReplacement.Object)
59+
{
60+
InnerHandler = new FakeSuccessHandler()
61+
};
62+
var msg = new HttpRequestMessage(HttpMethod.Get, "http://localhost");
63+
var client = new HttpClient(handler);
64+
await client.SendAsync(msg);
65+
66+
mockReplacement.Verify(static x=> x.Replace(It.IsAny<Uri>()), Times.Once());
67+
}
68+
}

src/Microsoft.Kiota.Http.HttpClientLibrary.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
1515
<EmbedUntrackedSources>true</EmbedUntrackedSources>
1616
<Deterministic>true</Deterministic>
17-
<VersionPrefix>1.2.0</VersionPrefix>
17+
<VersionPrefix>1.3.0</VersionPrefix>
1818
<VersionSuffix></VersionSuffix>
1919
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
2020
<!-- Enable this line once we go live to prevent breaking changes -->
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Kiota.Abstractions;
4+
5+
namespace Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;
6+
7+
/// <summary>
8+
/// Interface for making URI replacements.
9+
/// </summary>
10+
public interface IUriReplacementHandlerOption : IRequestOption
11+
{
12+
/// <summary>
13+
/// Check if URI replacement is enabled for the option.
14+
/// </summary>
15+
/// <returns>true if replacement is enabled or false otherwise.</returns>
16+
bool IsEnabled();
17+
18+
/// <summary>
19+
/// Accepts a URI and returns a new URI with all replacements applied.
20+
/// </summary>
21+
/// <param name="original">The URI to apply replacements to</param>
22+
/// <returns>A new URI with all replacements applied.</returns>
23+
Uri? Replace(Uri? original);
24+
}
25+
26+
/// <summary>
27+
/// Url replacement options.
28+
/// </summary>
29+
public class UriReplacementHandlerOption : IUriReplacementHandlerOption
30+
{
31+
private readonly bool isEnabled;
32+
33+
private readonly IEnumerable<KeyValuePair<string, string>> replacementPairs;
34+
35+
/// <summary>
36+
/// Creates a new instance of UriReplacementOption.
37+
/// </summary>
38+
/// <param name="isEnabled">Whether replacement is enabled.</param>
39+
/// <param name="replacementPairs">Replacements with the key being a string to match against and the value being the replacement.</param>
40+
public UriReplacementHandlerOption(bool isEnabled, IEnumerable<KeyValuePair<string, string>> replacementPairs)
41+
{
42+
this.isEnabled = isEnabled;
43+
this.replacementPairs = replacementPairs;
44+
}
45+
46+
/// <inheritdoc/>
47+
public bool IsEnabled()
48+
{
49+
return isEnabled;
50+
}
51+
52+
/// <inheritdoc/>
53+
public Uri? Replace(Uri? original)
54+
{
55+
if(original is null) return null;
56+
57+
if(!isEnabled)
58+
{
59+
return original;
60+
}
61+
62+
var newUrl = new UriBuilder(original);
63+
foreach(var pair in replacementPairs)
64+
{
65+
newUrl.Path = newUrl.Path.Replace(pair.Key, pair.Value);
66+
}
67+
68+
return newUrl.Uri;
69+
}
70+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Diagnostics;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Microsoft.Kiota.Abstractions;
5+
using Microsoft.Kiota.Http.HttpClientLibrary.Extensions;
6+
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;
7+
8+
namespace Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
9+
10+
/// <summary>
11+
/// Replaces a portion of the URL.
12+
/// </summary>
13+
/// <typeparam name="TUriReplacementHandlerOption">A type with the rules used to perform a URI replacement.</typeparam>
14+
public class UriReplacementHandler<TUriReplacementHandlerOption> : DelegatingHandler where TUriReplacementHandlerOption : IUriReplacementHandlerOption
15+
{
16+
private readonly TUriReplacementHandlerOption uriReplacement;
17+
18+
/// <summary>
19+
/// Creates a new UriReplacementHandler.
20+
/// </summary>
21+
/// <param name="uriReplacement">An object with the URI replacement rules.</param>
22+
public UriReplacementHandler(TUriReplacementHandlerOption uriReplacement)
23+
{
24+
this.uriReplacement = uriReplacement;
25+
}
26+
27+
/// <inheritdoc/>
28+
protected override async Task<HttpResponseMessage> SendAsync(
29+
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
30+
{
31+
ActivitySource? activitySource;
32+
Activity? activity;
33+
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
34+
{
35+
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
36+
activity = activitySource.StartActivity($"{nameof(UriReplacementHandler<TUriReplacementHandlerOption>)}_{nameof(SendAsync)}");
37+
activity?.SetTag("com.microsoft.kiota.handler.uri_replacement.enable", uriReplacement.IsEnabled());
38+
}
39+
else
40+
{
41+
activity = null;
42+
activitySource = null;
43+
}
44+
45+
try
46+
{
47+
request.RequestUri = uriReplacement.Replace(request.RequestUri);
48+
return await base.SendAsync(request, cancellationToken);
49+
}
50+
finally
51+
{
52+
activity?.Dispose();
53+
activitySource?.Dispose();
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)