Files

108 lines
4.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace Octokit
{
/// <summary>
/// Extensions for working with Uris
/// </summary>
public static class UriExtensions
{
/// <summary>
/// Returns a Uri where any existing relative Uri component is stripped
/// eg https://example.com/some/path becomes https://example.com
/// </summary>
/// <param name="uri">Base Uri</param>
/// <returns></returns>
public static Uri StripRelativeUri(this Uri uri)
{
return new Uri(uri, "/");
}
/// <summary>
/// Returns a Uri where any existing relative Uri component is replaced with the respective value
/// eg https://example.com/some/path becomes https://example.com/replacement/path
/// </summary>
/// <param name="uri">Base Uri</param>
/// <param name="relativeUri">Relative Uri to add to the base Uri, replacing any existing relative Uri component</param>
/// <returns></returns>
public static Uri ReplaceRelativeUri(this Uri uri, Uri relativeUri)
{
// Prepending a forward slash to the relative Uri causes it to replace any that is existing
return new Uri(StripRelativeUri(uri), relativeUri);
}
/// <summary>
/// Merge a dictionary of values with an existing <see cref="Uri"/>
/// </summary>
/// <param name="uri">Original request Uri</param>
/// <param name="parameters">Collection of key-value pairs</param>
/// <returns>Updated request Uri</returns>
public static Uri ApplyParameters(this Uri uri, IDictionary<string, string> parameters)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
if (parameters == null || !parameters.Any()) return uri;
// to prevent values being persisted across requests
// use a temporary dictionary which combines new and existing parameters
IDictionary<string, string> p = new Dictionary<string, string>(parameters);
var hasQueryString = uri.OriginalString.IndexOf("?", StringComparison.Ordinal);
string uriWithoutQuery = hasQueryString == -1
? uri.ToString()
: uri.OriginalString.Substring(0, hasQueryString);
string queryString;
if (uri.IsAbsoluteUri)
{
queryString = uri.Query;
}
else
{
queryString = hasQueryString == -1
? ""
: uri.OriginalString.Substring(hasQueryString);
}
var values = queryString.Replace("?", "")
.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
var existingParameters = values.ToDictionary(
key => key.Substring(0, key.IndexOf('=')),
value => value.Substring(value.IndexOf('=') + 1));
foreach (var existing in existingParameters)
{
if (existing.Key == "page")
{
// See https://github.com/octokit/octokit.net/issues/1955
// See https://github.com/octokit/octokit.net/issues/2602
// Desired behavior: don't replace page number in nextPageUri
p[existing.Key] = existing.Value;
}
else if (!p.ContainsKey(existing.Key))
{
p.Add(existing);
}
}
Func<string, string, string> mapValueFunc = (key, value) => (key == "q" || key =="before" || key=="after") ? value : Uri.EscapeDataString(value);
string query = string.Join("&", p.Select(kvp => kvp.Key + "=" + mapValueFunc(kvp.Key, kvp.Value)));
if (uri.IsAbsoluteUri)
{
var uriBuilder = new UriBuilder(uri)
{
Query = query
};
return uriBuilder.Uri;
}
return new Uri(uriWithoutQuery + "?" + query, UriKind.Relative);
}
}
}