Browse Source

Refactored to services

pull/235/head
Galip Tolga Erdem 3 years ago
parent
commit
ba819c7452
  1. 37
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/CachedLocalizationService.cs
  2. 10
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/IRemoteLocalizationService.cs
  3. 92
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/RemoteLocalizationService.cs
  4. 113
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/LocalizationAggregation.cs
  5. 15
      gateways/web/src/EShopOnAbp.WebGateway/ReverseProxyBuilderExtensions.cs

37
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/CachedLocalizationService.cs

@ -0,0 +1,37 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.WebGateway.Aggregations.Localization;
public class CachedLocalizationService : ISingletonDependency
{
protected ConcurrentDictionary<string, ApplicationLocalizationDto> LocalizationDictionary { get; }
public CachedLocalizationService()
{
LocalizationDictionary = new ConcurrentDictionary<string, ApplicationLocalizationDto>(StringComparer.OrdinalIgnoreCase);
}
//TODO:skip url -> Use ServiceName_Culture as cache key
public void AddOrUpdate(string serviceNameWithCulture, ApplicationLocalizationDto localizationInfo)
{
LocalizationDictionary.AddOrUpdate(serviceNameWithCulture, localizationInfo, (key, value) => localizationInfo);
}
public ApplicationLocalizationDto Get(string serviceNameWithCulture)
{
return LocalizationDictionary.TryGetValue(serviceNameWithCulture, out var result) ? result : null;
}
public IDictionary<string, ApplicationLocalizationDto> GetMultipleLocalizationsAsync(string[] serviceNamesWithCulture)
{
return LocalizationDictionary
.Where(kv => serviceNamesWithCulture.Contains(kv.Key))
.ToImmutableDictionary();
}
}

10
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/IRemoteLocalizationService.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace EShopOnAbp.WebGateway.Aggregations.Localization;
public interface IRemoteLocalizationService
{
Task<Dictionary<string, ApplicationLocalizationDto>> GetMultipleLocalizationsAsync(Dictionary<string, string> serviceNameWithUrlDictionary);
}

92
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/RemoteLocalizationService.cs

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.WebGateway.Aggregations.Localization;
public class RemoteLocalizationService : IRemoteLocalizationService, ITransientDependency
{
private readonly ILogger<RemoteLocalizationService> _logger;
public RemoteLocalizationService(ILogger<RemoteLocalizationService> logger)
{
_logger = logger;
}
public async Task<Dictionary<string, ApplicationLocalizationDto>> GetMultipleLocalizationsAsync(Dictionary<string, string> serviceNameWithUrlDictionary)
{
Dictionary<string, Task<ApplicationLocalizationDto>> completedTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>();
Dictionary<string, Task<ApplicationLocalizationDto>> runningTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>();
Dictionary<string, ApplicationLocalizationDto> completedResult = new Dictionary<string, ApplicationLocalizationDto>();
using (HttpClient httpClient = new HttpClient())
{
foreach (var service in serviceNameWithUrlDictionary)
{
Task<ApplicationLocalizationDto> requestTask = MakeRequestAsync<ApplicationLocalizationDto>(httpClient, service.Value);
runningTasks.Add(service.Key, requestTask);
}
while (runningTasks.Count > 0)
{
KeyValuePair<string, Task<ApplicationLocalizationDto>> completedTask = await WaitForAnyTaskAsync(runningTasks);
runningTasks.Remove(completedTask.Key);
try
{
ApplicationLocalizationDto result = await completedTask.Value;
completedTasks.Add(completedTask.Key, completedTask.Value);
completedResult.Add(completedTask.Key, result);
_logger.LogInformation($"Key: {completedTask.Key}, Value: {result}");
}
catch (Exception ex)
{
_logger.LogInformation($"Error for the {completedTask.Key}: {ex.Message}");
}
}
}
return completedResult;
}
private async Task<KeyValuePair<string, Task<ApplicationLocalizationDto>>> WaitForAnyTaskAsync(Dictionary<string, Task<ApplicationLocalizationDto>> tasks)
{
Task<Task<ApplicationLocalizationDto>> completedTask = Task.WhenAny(tasks.Values);
Task<ApplicationLocalizationDto> result = await completedTask;
KeyValuePair<string, Task<ApplicationLocalizationDto>> completedTaskPair = tasks.First(kv => kv.Value == result);
return completedTaskPair;
}
private async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url)
{
try
{
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
return JsonSerializer.Deserialize<T>(content, options);
}
catch (Exception ex)
{
// Log or handle the exception, and return a default value or throw it again if needed
Console.WriteLine($"Error making request to {url}: {ex.Message}");
throw;
}
}
}

113
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/LocalizationAggregation.cs

@ -4,9 +4,11 @@ using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using EShopOnAbp.WebGateway.Aggregations.Localization;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.WebGateway.Aggregations;
@ -15,12 +17,14 @@ public class LocalizationAggregation : ILocalizationAggregation
//Yarp localization route name
public string LocalizationEndpoint { get; } = "EshopOnAbpLocalization";
private readonly ILogger<LocalizationAggregation> _logger;
private readonly IDistributedCache<LocalizationResponse> _localizationResponseCache;
private readonly CachedLocalizationService _cachedLocalizationService;
private readonly RemoteLocalizationService _remoteLocalizationService;
public LocalizationAggregation(ILogger<LocalizationAggregation> logger, IDistributedCache<LocalizationResponse> localizationResponseCache)
public LocalizationAggregation(ILogger<LocalizationAggregation> logger, CachedLocalizationService cachedLocalizationService, RemoteLocalizationService remoteLocalizationService)
{
_logger = logger;
_localizationResponseCache = localizationResponseCache;
_cachedLocalizationService = cachedLocalizationService;
_remoteLocalizationService = remoteLocalizationService;
}
public async Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input)
@ -28,23 +32,30 @@ public class LocalizationAggregation : ILocalizationAggregation
//Temp list
Dictionary<string, string> services = new Dictionary<string, string>()
{
{ "Administration", "https://localhost:44353/api/abp/application-localization?CultureName=en" },
{ "Catalog", "https://localhost:44354/api/abp/application-localization?CultureName=en" }
{ "Administration_tr", "https://localhost:44353/api/abp/application-localization?cultureName=tr&onlyDynamics=false" },
{ "Catalog_tr", "https://localhost:44354/api/abp/application-localization?cultureName=tr&onlyDynamics=false" }
};
// Check the cache
var cachedLocalization = _cachedLocalizationService.GetMultipleLocalizationsAsync(services.Keys.ToArray());
// Compare cache with input service list
Dictionary<string, ApplicationLocalizationDto> localizationResults = await GetCompletedLocalizationResultsAsync(services);
ApplicationLocalizationDto mergedResult = MergeLocalizations(localizationResults);
// Make request to remote localization service
var remoteLocalizationResults = await _remoteLocalizationService.GetMultipleLocalizationsAsync(services);
//merge result
ApplicationLocalizationDto mergedResult = MergeLocalizations(remoteLocalizationResults);
//return result
return mergedResult;
}
private ApplicationLocalizationDto MergeLocalizations(Dictionary<string, ApplicationLocalizationDto> localizationResults)
{
var localizationDto = new ApplicationLocalizationDto();
//TODO: fix
foreach (var localizationResult in localizationResults)
{
localizationDto.Resources.AddIfNotContains(localizationResult.Value.Resources);
@ -52,80 +63,6 @@ public class LocalizationAggregation : ILocalizationAggregation
return localizationDto;
}
private async Task<Dictionary<string, ApplicationLocalizationDto>> GetCompletedLocalizationResultsAsync(Dictionary<string, string> services)
{
Dictionary<string, Task<ApplicationLocalizationDto>> completedTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>();
Dictionary<string, Task<ApplicationLocalizationDto>> runningTasks = new Dictionary<string, Task<ApplicationLocalizationDto>>();
Dictionary<string, ApplicationLocalizationDto> completedResult = new Dictionary<string, ApplicationLocalizationDto>();
using (HttpClient httpClient = new HttpClient())
{
foreach (var service in services)
{
Task<ApplicationLocalizationDto> requestTask = MakeRequestAsync<ApplicationLocalizationDto>(httpClient, service.Value);
runningTasks.Add(service.Key, requestTask);
}
while (runningTasks.Count > 0)
{
KeyValuePair<string, Task<ApplicationLocalizationDto>> completedTask = await WaitForAnyTaskAsync(runningTasks);
runningTasks.Remove(completedTask.Key);
try
{
ApplicationLocalizationDto result = await completedTask.Value;
completedTasks.Add(completedTask.Key, completedTask.Value);
completedResult.Add(completedTask.Key, result);
_logger.LogInformation($"Key: {completedTask.Key}, Value: {result}");
}
catch (Exception ex)
{
_logger.LogInformation($"Error for the {completedTask.Key}: {ex.Message}");
}
}
}
return completedResult;
}
private async Task<KeyValuePair<string, Task<ApplicationLocalizationDto>>> WaitForAnyTaskAsync(
Dictionary<string, Task<ApplicationLocalizationDto>> tasks)
{
Task<Task<ApplicationLocalizationDto>> completedTask = Task.WhenAny(tasks.Values);
Task<ApplicationLocalizationDto> result = await completedTask;
// Find the key corresponding to the completed task
KeyValuePair<string, Task<ApplicationLocalizationDto>>
completedTaskPair = tasks.First(kv => kv.Value == result);
return completedTaskPair;
}
private async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url)
{
try
{
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
return JsonSerializer.Deserialize<T>(content, options);
}
catch (Exception ex)
{
// Log or handle the exception, and return a default value or throw it again if needed
Console.WriteLine($"Error making request to {url}: {ex.Message}");
throw;
}
}
}
public class LocalizationRequest
@ -133,20 +70,10 @@ public class LocalizationRequest
// Localization clusterName - localization endpoint pair
public Dictionary<string, string> LocalizationEndpoints { get; }
public string CultureName { get; set; }
public bool? OnlyDynamics { get; set; }
public LocalizationRequest(string cultureName)
{
CultureName = cultureName;
LocalizationEndpoints = new Dictionary<string, string>();
}
}
public class LocalizationResponse
{
// Localization url - localization resource pair
public Dictionary<string, ApplicationLocalizationDto> Localization { get; }
public string CultureName { get; set; }
public bool? OnlyDynamics { get; set; }
public string ServiceName { get; set; }
}

15
gateways/web/src/EShopOnAbp.WebGateway/ReverseProxyBuilderExtensions.cs

@ -8,13 +8,14 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Yarp.ReverseProxy.Configuration;
namespace EShopOnAbp.WebGateway;
public static class ReverseProxyBuilderExtensions
{
public static string LocalizationEndpoint = "api/abp/application-localization";
public static string LocalizationPath = "/api/abp/application-localization?cultureName=tr&onlyDynamics=false";
public static ReverseProxyConventionBuilder MapReverseProxyWithLocalization(this IEndpointRouteBuilder endpoints)
{
@ -50,22 +51,14 @@ public static class ReverseProxyBuilderExtensions
var proxyConfig = context.RequestServices.GetRequiredService<IProxyConfigProvider>();
context.Request.Query.TryGetValue("CultureName", out var cultureName);
context.Request.Query.TryGetValue("OnlyDynamics", out var onlyDynamics);
var input = new LocalizationRequest(cultureName);
StringBuilder stringBuilder = new StringBuilder(LocalizationEndpoint + $"?CultureName={cultureName}");
if (!string.IsNullOrEmpty(onlyDynamics))
{
input.OnlyDynamics = bool.Parse(onlyDynamics);
stringBuilder.Append($"&OnlyDynamics={onlyDynamics}");
}
var clusterList = GetClusters(proxyConfig);
foreach (var cluster in clusterList)
{
var hostUrl = new Uri(cluster.Value.Address) + $"{stringBuilder}";
input.LocalizationEndpoints.Add(cluster.Key, hostUrl);
var hostUrl = new Uri(cluster.Value.Address) + $"{LocalizationPath}";
input.LocalizationEndpoints.Add($"{cluster.Key}_{cultureName}", hostUrl);
}
return input;

Loading…
Cancel
Save