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

Add uri replacement handler #158

Merged
merged 7 commits into from
Nov 2, 2023
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
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();
calebkiage marked this conversation as resolved.
Show resolved Hide resolved

/// <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();
}
}
}
Loading