Skip to content

Commit

Permalink
Various improvements and added Put and Post Verbs to router
Browse files Browse the repository at this point in the history
  • Loading branch information
fancyDevelopment committed Sep 26, 2023
1 parent 908da0d commit ca4abc6
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.0.4</Version>
<Version>0.0.5</Version>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.0.4</Version>
<Version>0.0.5</Version>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
193 changes: 141 additions & 52 deletions src/Fancy.ResourceLinker.Gateway/Routing/GatewayRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,64 +94,108 @@ public GatewayRouter(GatewayRoutingSettings settings, IHttpForwarder forwarder,
_serializerOptions.AddResourceConverter();
}

private async Task SetTokenToRequest(HttpRequestMessage request)
{
string accessToken;
if (_tokenService != null)
{
// A user session exists, get token from token service
accessToken = await _tokenService.GetAccessTokenAsync();
}
else if (_tokenClient != null)
{
// Fall back to client credentials token directly
ClientCredentialsTokenResponse? tokenResponse = await _tokenClient.GetTokenViaClientCredentialsAsync();
if (tokenResponse == null) throw new InvalidOperationException("Could not retrieve token via client credentials.");
accessToken = tokenResponse.AccessToken;
}
else
{
throw new InvalidOperationException($"If you want to send access tokens, gateway authentication must be configured.");
}

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}

/// <summary>
/// Gets data from a url and deserializes it into a given type.
/// Sends a request and deserializes the response into a given type.
/// </summary>
/// <typeparam name="TResource">The type of the resource.</typeparam>
/// <param name="requestUri">The uri of the data to get.</param>
/// <param name="request">The request to send.</param>
/// <param name="sendAccessToken">I true, the request will be enriched with an access token.</param>
/// <returns>The result deserialized into the specified resource type.</returns>
private async Task<TResource> GetAsync<TResource>(Uri requestUri, bool sendAccessToken) where TResource : class
private async Task<TResource?> SendAsync<TResource>(HttpRequestMessage request, bool sendAccessToken) where TResource : class
{
// Set up request
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = requestUri,
Method = HttpMethod.Get
};

if (_settings.ResourceProxy != null)
{
request.Headers.Add("X-Forwarded-Host", _settings.ResourceProxy);
}

if(sendAccessToken)
{
string accessToken;
if (_tokenService != null)
{
// A user session exists, get token from token service
accessToken = await _tokenService.GetAccessTokenAsync();
}
else if(_tokenClient != null)
{
// Fall back to client credentials token directly
ClientCredentialsTokenResponse? tokenResponse = await _tokenClient.GetTokenViaClientCredentialsAsync();
if (tokenResponse == null) throw new InvalidOperationException("Could not retrieve token via client credentials.");
accessToken = tokenResponse.AccessToken;
}
else
{
throw new InvalidOperationException($"If '{nameof(sendAccessToken)}' is 'true', gateway authentication must be configured.");
}

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
await SetTokenToRequest(request);
}

// Get data from microservice
HttpResponseMessage responseMessage = await _httpClient.SendAsync(request);
responseMessage.EnsureSuccessStatusCode();

if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
if (responseMessage.Content.Headers.ContentLength > 0)// responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
string jsonResponse = await responseMessage.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<TResource>(jsonResponse, _serializerOptions) ?? throw new Exception("Error on deserialization of result");
}
else
{
throw new Exception("No Content was provided by the server"); ;
return default;// throw new Exception("No Content was provided by the server");
}
}

/// <summary>
/// Sends a request.
/// </summary>
/// <param name="request">The request to send.</param>
/// <param name="sendAccessToken">I true, the request will be enriched with an access token.</param>
private async Task SendAsync(HttpRequestMessage request, bool sendAccessToken)
{
if (_settings.ResourceProxy != null)
{
request.Headers.Add("X-Forwarded-Host", _settings.ResourceProxy);
}

if (sendAccessToken)
{
await SetTokenToRequest(request);
}

// Get data from microservice
HttpResponseMessage responseMessage = await _httpClient.SendAsync(request);
responseMessage.EnsureSuccessStatusCode();
}

/// <summary>
/// Gets data from a url and deserializes it into a given type.
/// </summary>
/// <typeparam name="TResource">The type of the resource.</typeparam>
/// <param name="requestUri">The uri of the data to get.</param>
/// <param name="sendAccessToken">I true, the request will be enriched with an access token.</param>
/// <returns>The result deserialized into the specified resource type.</returns>
private async Task<TResource> GetAsync<TResource>(Uri requestUri, bool sendAccessToken) where TResource : class
{
// Set up request
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = requestUri,
Method = HttpMethod.Get,
};

var result = await SendAsync<TResource>(request, sendAccessToken);

if(result == null) throw new ApplicationException("No Content was provided by the server");

return result;
}

/// <summary>
/// Get data from a microservice specified by its key of a provided route and deserializes it into a given type.
/// </summary>
Expand All @@ -165,6 +209,68 @@ public Task<TResource> GetAsync<TResource>(string routeKey, string relativeUrl)
return GetAsync<TResource>(requestUri, _settings.Routes[routeKey].EnforceAuthentication);
}

/// <summary>
/// Puts data to a specific uri.
/// </summary>
/// <param name="requestUri">The uri to send to.</param>
/// <param name="content">The content to send - will be serialized as json.</param>
/// <param name="sendAccessToken">I true, the request will be enriched with an access token.</param>
private Task PutAsync(Uri requestUri, object content, bool sendAccessToken)
{
// Set up request
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = requestUri,
Method = HttpMethod.Put,
Content = new StringContent(JsonSerializer.Serialize(content), Encoding.UTF8, "application/json")
};

return SendAsync(request, sendAccessToken);
}

/// <summary>
/// Puts data to a specific uri.
/// </summary>
/// <param name="routeKey">The key of the route to use.</param>
/// <param name="relativeUrl">The relative url to the endpoint.</param>
/// <param name="content">The content to send - will be serialized as json.</param>
public Task PutAsync(string routeKey, string relativeUrl, object content)
{
Uri requestUri = CombineUris(GetBaseUrl(routeKey), relativeUrl);
return PutAsync(requestUri, content, _settings.Routes[routeKey].EnforceAuthentication);
}

/// <summary>
/// Post data to a specific uri.
/// </summary>
/// <param name="requestUri">The uri to send to.</param>
/// <param name="content">The content to send - will be serialized as json.</param>
/// <param name="sendAccessToken">I true, the request will be enriched with an access token.</param>
private Task PostAsync(Uri requestUri, object content, bool sendAccessToken)
{
// Set up request
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = requestUri,
Method = HttpMethod.Post,
Content = new StringContent(JsonSerializer.Serialize(content), Encoding.UTF8, "application/json")
};

return SendAsync(request, sendAccessToken);
}

/// <summary>
/// Post data to a specific uri.
/// </summary>
/// <param name="routeKey">The key of the route to use.</param>
/// <param name="relativeUrl">The relative url to the endpoint.</param>
/// <param name="content">The content to send - will be serialized as json.</param>
public Task PostAsync(string routeKey, string relativeUrl, object content)
{
Uri requestUri = CombineUris(GetBaseUrl(routeKey), relativeUrl);
return PostAsync(requestUri, content, _settings.Routes[routeKey].EnforceAuthentication);
}

/// <summary>
/// Gets data from a url and deserializes it into a given type. If data is available in the cache and not older
/// as the age specified the data is returned from the chache, if not data is retrieved from the origin and written to the cache.
Expand All @@ -175,15 +281,15 @@ public Task<TResource> GetAsync<TResource>(string routeKey, string relativeUrl)
/// <returns>
/// The result deserialized into the specified resource type.
/// </returns>
public async Task<TResource> GetCachedAsync<TResource>(Uri requestUri, TimeSpan maxResourceAge, bool sendAccessToken) where TResource : ResourceBase
public async Task<TResource> GetCachedAsync<TResource>(Uri requestUri, TimeSpan maxResourceAge, bool sendAccessToken) where TResource : class
{
string cacheKey = requestUri.ToString();

// Check if we can get the resource from cache
TResource? data;
if (_resourceCache.TryRead(cacheKey, maxResourceAge, out data))
{
return data ?? throw new Exception("Error on reading item from cache");
return data ?? throw new ApplicationException("Error on reading item from cache");
}
else
{
Expand All @@ -206,7 +312,7 @@ public async Task<TResource> GetCachedAsync<TResource>(Uri requestUri, TimeSpan
/// <returns>
/// The result deserialized into the specified resource type.
/// </returns>
public Task<TResource> GetCachedAsync<TResource>(string routeKey, string relativeUrl, TimeSpan maxResourceAge) where TResource : ResourceBase
public Task<TResource> GetCachedAsync<TResource>(string routeKey, string relativeUrl, TimeSpan maxResourceAge) where TResource : class
{
Uri requestUri = CombineUris(GetBaseUrl(routeKey), relativeUrl);
return GetCachedAsync<TResource>(requestUri, maxResourceAge, _settings.Routes[routeKey].EnforceAuthentication);
Expand Down Expand Up @@ -246,24 +352,7 @@ public async Task<IActionResult> ProxyAsync(HttpContext httpContext, string rout

if (_settings.Routes[routeKey].EnforceAuthentication)
{
string accessToken;
if (_tokenService != null)
{
// A user session exists, get token from token service
accessToken = await _tokenService.GetAccessTokenAsync();
}
else if (_tokenClient != null)
{
// Fall back to client credentials token directly
ClientCredentialsTokenResponse? tokenResponse = await _tokenClient.GetTokenViaClientCredentialsAsync();
if (tokenResponse == null) throw new InvalidOperationException("Could not retrieve token via client credentials.");
accessToken = tokenResponse.AccessToken;
}
else
{
throw new InvalidOperationException($"If 'EnforceAuthentication' is 'true', gateway authentication must be configured.");
}
proxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
await SetTokenToRequest(proxyRequest);
}

HttpResponseMessage proxyResponse = await _httpClient.SendAsync(proxyRequest);
Expand Down
4 changes: 2 additions & 2 deletions src/Fancy.ResourceLinker.Gateway/Routing/IResourceCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface IResourceCache
/// <typeparam name="TResource">The type of the resource.</typeparam>
/// <param name="key">The key to save the resource under.</param>
/// <param name="resource">The resource instance to save.</param>
void Write<TResource>(string key, TResource resource) where TResource : ResourceBase;
void Write<TResource>(string key, TResource resource) where TResource : class;

/// <summary>
/// Tries to read a resource from the cache.
Expand All @@ -23,5 +23,5 @@ public interface IResourceCache
/// <param name="maxResourceAge">The maximum age of the resource.</param>
/// <param name="resource">The resource.</param>
/// <returns>True if the cache was able to read and provide a valid resource instance; otherwise, false.</returns>
bool TryRead<TResource>(string key, TimeSpan maxResourceAge, out TResource? resource) where TResource : ResourceBase;
bool TryRead<TResource>(string key, TimeSpan maxResourceAge, out TResource? resource) where TResource : class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class InMemoryResourceCache : IResourceCache
/// <typeparam name="TResource">The type of the resource.</typeparam>
/// <param name="key">The key to save the resource under.</param>
/// <param name="resource">The resource instance to save.</param>
public void Write<TResource>(string key, TResource resource) where TResource : ResourceBase
public void Write<TResource>(string key, TResource resource) where TResource : class
{
_cache[key] = new Tuple<DateTime, object>(DateTime.Now, resource);
}
Expand All @@ -35,9 +35,9 @@ public void Write<TResource>(string key, TResource resource) where TResource : R
/// <returns>
/// True if the cache was able to read and provide a valid resource instance; otherwise, false.
/// </returns>
public bool TryRead<TResource>(string key, TimeSpan maxResourceAge, out TResource? resource) where TResource : ResourceBase
public bool TryRead<TResource>(string key, TimeSpan maxResourceAge, out TResource? resource) where TResource : class
{
resource = null;
resource = default;

// Check if the key exists within the cache
if (_cache.ContainsKey(key))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.0.4</Version>
<Version>0.0.5</Version>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.0.4</Version>
<Version>0.0.5</Version>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down

0 comments on commit ca4abc6

Please sign in to comment.