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

Commit

Permalink
Add uri replacement handler (#158)
Browse files Browse the repository at this point in the history
* Add uri replacement handler

* Bump minor version

* Add changelog entry.
  • Loading branch information
calebkiage authored Nov 2, 2023
1 parent 3ddf8e0 commit 01e734e
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 1 deletion.
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.0] - 2023-11-02

### Added

- Added uri replacement handler.

## [1.2.0] - 2023-10-23

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;
using Moq;
using Xunit;

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

public class UriReplacementOptionTests {
[Fact]
public void Does_Nothing_When_Url_Replacement_Is_Disabled()
{
var uri = new Uri("http://localhost/test");
var disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>());

Assert.False(disabled.IsEnabled());
Assert.Equal(uri, disabled.Replace(uri));

disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>{
{"test", ""}
});

Assert.Equal(uri, disabled.Replace(uri));
}

[Fact]
public void Returns_Null_When_Url_Provided_Is_Null()
{
var disabled = new UriReplacementHandlerOption(false, new Dictionary<string, string>());

Assert.False(disabled.IsEnabled());
Assert.Null(disabled.Replace(null));
}

[Fact]
public void Replaces_Key_In_Path_With_Value()
{
var uri = new Uri("http://localhost/test");
var option = new UriReplacementHandlerOption(true, new Dictionary<string, string>{{"test", ""}});

Assert.True(option.IsEnabled());
Assert.Equal("http://localhost/", option.Replace(uri)!.ToString());
}
}

public class UriReplacementHandlerTests
{
[Fact]
public async Task Calls_Uri_ReplacementAsync()
{
var mockReplacement = new Mock<IUriReplacementHandlerOption>();
mockReplacement.Setup(static x => x.IsEnabled()).Returns(true);
mockReplacement.Setup(static x => x.Replace(It.IsAny<Uri>())).Returns(new Uri("http://changed"));

var handler = new UriReplacementHandler<IUriReplacementHandlerOption>(mockReplacement.Object)
{
InnerHandler = new FakeSuccessHandler()
};
var msg = new HttpRequestMessage(HttpMethod.Get, "http://localhost");
var client = new HttpClient(handler);
await client.SendAsync(msg);

mockReplacement.Verify(static x=> x.Replace(It.IsAny<Uri>()), Times.Once());
}
}
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.2.0</VersionPrefix>
<VersionPrefix>1.3.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- Enable this line once we go live to prevent breaking changes -->
Expand Down
70 changes: 70 additions & 0 deletions src/Middleware/Options/UriReplacementHandlerOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using Microsoft.Kiota.Abstractions;

namespace Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;

/// <summary>
/// Interface for making URI replacements.
/// </summary>
public interface IUriReplacementHandlerOption : IRequestOption
{
/// <summary>
/// Check if URI replacement is enabled for the option.
/// </summary>
/// <returns>true if replacement is enabled or false otherwise.</returns>
bool IsEnabled();

/// <summary>
/// Accepts a URI and returns a new URI with all replacements applied.
/// </summary>
/// <param name="original">The URI to apply replacements to</param>
/// <returns>A new URI with all replacements applied.</returns>
Uri? Replace(Uri? original);
}

/// <summary>
/// Url replacement options.
/// </summary>
public class UriReplacementHandlerOption : IUriReplacementHandlerOption
{
private readonly bool isEnabled;

private readonly IEnumerable<KeyValuePair<string, string>> replacementPairs;

/// <summary>
/// Creates a new instance of UriReplacementOption.
/// </summary>
/// <param name="isEnabled">Whether replacement is enabled.</param>
/// <param name="replacementPairs">Replacements with the key being a string to match against and the value being the replacement.</param>
public UriReplacementHandlerOption(bool isEnabled, IEnumerable<KeyValuePair<string, string>> replacementPairs)
{
this.isEnabled = isEnabled;
this.replacementPairs = replacementPairs;
}

/// <inheritdoc/>
public bool IsEnabled()
{
return isEnabled;
}

/// <inheritdoc/>
public Uri? Replace(Uri? original)
{
if(original is null) return null;

if(!isEnabled)
{
return original;
}

var newUrl = new UriBuilder(original);
foreach(var pair in replacementPairs)
{
newUrl.Path = newUrl.Path.Replace(pair.Key, pair.Value);
}

return newUrl.Uri;
}
}
56 changes: 56 additions & 0 deletions src/Middleware/UriReplacementHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Http.HttpClientLibrary.Extensions;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;

namespace Microsoft.Kiota.Http.HttpClientLibrary.Middleware;

/// <summary>
/// Replaces a portion of the URL.
/// </summary>
/// <typeparam name="TUriReplacementHandlerOption">A type with the rules used to perform a URI replacement.</typeparam>
public class UriReplacementHandler<TUriReplacementHandlerOption> : DelegatingHandler where TUriReplacementHandlerOption : IUriReplacementHandlerOption
{
private readonly TUriReplacementHandlerOption uriReplacement;

/// <summary>
/// Creates a new UriReplacementHandler.
/// </summary>
/// <param name="uriReplacement">An object with the URI replacement rules.</param>
public UriReplacementHandler(TUriReplacementHandlerOption uriReplacement)
{
this.uriReplacement = uriReplacement;
}

/// <inheritdoc/>
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource.StartActivity($"{nameof(UriReplacementHandler<TUriReplacementHandlerOption>)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.uri_replacement.enable", uriReplacement.IsEnabled());
}
else
{
activity = null;
activitySource = null;
}

try
{
request.RequestUri = uriReplacement.Replace(request.RequestUri);
return await base.SendAsync(request, cancellationToken);
}
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}
}

0 comments on commit 01e734e

Please sign in to comment.