23 changed files with 636 additions and 10 deletions
@ -0,0 +1,94 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public class AppConfigurationAggregation : AggregateServiceBase<ApplicationConfigurationDto>, |
|||
IAppConfigurationAggregation, ITransientDependency |
|||
{ |
|||
public string AppConfigRouteName => "EshopOnAbpApplicationConfiguration"; |
|||
public string AppConfigEndpoint => "api/abp/application-configuration"; |
|||
|
|||
protected IAppConfigurationRemoteService AppConfigurationRemoteService { get; } |
|||
|
|||
public AppConfigurationAggregation( |
|||
IAppConfigurationRemoteService appConfigurationRemoteService) : base( |
|||
appConfigurationRemoteService) |
|||
{ |
|||
AppConfigurationRemoteService = appConfigurationRemoteService; |
|||
} |
|||
|
|||
public async Task<ApplicationConfigurationDto> GetAppConfigurationAsync(AppConfigurationRequest input) |
|||
{ |
|||
var remoteAppConfigurationResults = |
|||
await AppConfigurationRemoteService.GetMultipleAsync(input.Endpoints); |
|||
|
|||
//merge only application configuration settings data
|
|||
var mergedResult = MergeAppConfigurationSettingsData(remoteAppConfigurationResults); |
|||
|
|||
//return result
|
|||
return mergedResult; |
|||
} |
|||
|
|||
private static ApplicationConfigurationDto MergeAppConfigurationSettingsData( |
|||
IDictionary<string, ApplicationConfigurationDto> appConfigurations) |
|||
{ |
|||
var appConfigurationDto = CreateInitialAppConfigDto(appConfigurations); |
|||
|
|||
foreach (var (_, appConfig) in appConfigurations) |
|||
{ |
|||
foreach (var resource in appConfig.Setting.Values) |
|||
{ |
|||
appConfigurationDto.Setting.Values.TryAdd(resource.Key, resource.Value); |
|||
} |
|||
} |
|||
|
|||
return appConfigurationDto; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks "Administration" clusterId to set the initial data from the AdministrationService.
|
|||
/// Otherwise uses the first available service for the initial application configuration data
|
|||
/// </summary>
|
|||
/// <param name="appConfigurations"></param>
|
|||
/// <returns></returns>
|
|||
private static ApplicationConfigurationDto CreateInitialAppConfigDto( |
|||
IDictionary<string, ApplicationConfigurationDto> appConfigurations |
|||
) |
|||
{ |
|||
if (appConfigurations.Count == 0) |
|||
{ |
|||
return new ApplicationConfigurationDto(); |
|||
} |
|||
|
|||
if (appConfigurations.TryGetValue("Administration_AppConfig", out var administrationServiceData)) |
|||
{ |
|||
return MapServiceData(administrationServiceData); |
|||
} |
|||
|
|||
return MapServiceData(appConfigurations.First().Value); |
|||
} |
|||
|
|||
private static ApplicationConfigurationDto MapServiceData(ApplicationConfigurationDto appConfiguration) |
|||
{ |
|||
return new ApplicationConfigurationDto |
|||
{ |
|||
Localization = appConfiguration.Localization, |
|||
Auth = appConfiguration.Auth, |
|||
Clock = appConfiguration.Clock, |
|||
Setting = appConfiguration.Setting, |
|||
Features = appConfiguration.Features, |
|||
Timing = appConfiguration.Timing, |
|||
CurrentTenant = appConfiguration.CurrentTenant, |
|||
CurrentUser = appConfiguration.CurrentUser, |
|||
ExtraProperties = appConfiguration.ExtraProperties, |
|||
GlobalFeatures = appConfiguration.GlobalFeatures, |
|||
MultiTenancy = appConfiguration.MultiTenancy, |
|||
ObjectExtensions = appConfiguration.ObjectExtensions |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public class AppConfigurationCachedService(IMemoryCache applicationConfigurationCache) |
|||
: CachedServiceBase<ApplicationConfigurationDto>(applicationConfigurationCache), ISingletonDependency; |
|||
@ -0,0 +1,15 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public class AppConfigurationRemoteService( |
|||
IHttpContextAccessor httpContextAccessor, |
|||
IJsonSerializer jsonSerializer, |
|||
ILogger<AggregateRemoteServiceBase<ApplicationConfigurationDto>> logger) |
|||
: AggregateRemoteServiceBase<ApplicationConfigurationDto>(httpContextAccessor, jsonSerializer, logger), |
|||
IAppConfigurationRemoteService, ITransientDependency; |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public class AppConfigurationRequest : IRequestInput |
|||
{ |
|||
public Dictionary<string, string> Endpoints { get; } = new(); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public interface IAppConfigurationAggregation |
|||
{ |
|||
string AppConfigRouteName { get; } |
|||
string AppConfigEndpoint { get; } |
|||
Task<ApplicationConfigurationDto> GetAppConfigurationAsync(AppConfigurationRequest input); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
|
|||
public interface IAppConfigurationRemoteService : IAggregateRemoteService<ApplicationConfigurationDto>; |
|||
@ -0,0 +1,110 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class AggregateRemoteServiceBase<TDto> : IAggregateRemoteService<TDto> |
|||
{ |
|||
private readonly IHttpContextAccessor _httpContextAccessor; |
|||
private readonly ILogger<AggregateRemoteServiceBase<TDto>> _logger; |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
|
|||
protected AggregateRemoteServiceBase(IHttpContextAccessor httpContextAccessor, IJsonSerializer jsonSerializer, |
|||
ILogger<AggregateRemoteServiceBase<TDto>> logger) |
|||
{ |
|||
_httpContextAccessor = httpContextAccessor; |
|||
JsonSerializer = jsonSerializer; |
|||
_logger = logger; |
|||
} |
|||
|
|||
public async Task<Dictionary<string, TDto>> GetMultipleAsync( |
|||
Dictionary<string, string> serviceNameWithUrlDictionary) |
|||
{ |
|||
Dictionary<string, Task<TDto>> completedTasks = new Dictionary<string, Task<TDto>>(); |
|||
Dictionary<string, Task<TDto>> runningTasks = new Dictionary<string, Task<TDto>>(); |
|||
Dictionary<string, TDto> completedResult = new Dictionary<string, TDto>(); |
|||
|
|||
using (HttpClient httpClient = CreateHttpClient()) |
|||
{ |
|||
foreach (var service in serviceNameWithUrlDictionary) |
|||
{ |
|||
Task<TDto> requestTask = |
|||
MakeRequestAsync<TDto>(httpClient, service.Value); |
|||
runningTasks.Add(service.Key, requestTask); |
|||
} |
|||
|
|||
while (runningTasks.Count > 0) |
|||
{ |
|||
KeyValuePair<string, Task<TDto>> completedTask = await WaitForAnyTaskAsync(runningTasks); |
|||
|
|||
runningTasks.Remove(completedTask.Key); |
|||
|
|||
try |
|||
{ |
|||
TDto result = await completedTask.Value; |
|||
|
|||
completedTasks.Add(completedTask.Key, completedTask.Value); |
|||
completedResult.Add(completedTask.Key, result); |
|||
|
|||
_logger.LogInformation("Localization Key: {0}, Value: {1}", completedTask.Key, result); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogInformation("Error for the {0}: {1}", completedTask.Key, ex.Message); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return completedResult; |
|||
} |
|||
|
|||
private HttpClient CreateHttpClient() |
|||
{ |
|||
var httpClient = new HttpClient(); |
|||
|
|||
var headers = _httpContextAccessor.HttpContext?.Request.Headers; |
|||
if (headers != null) |
|||
{ |
|||
foreach (var header in headers) |
|||
{ |
|||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value.ToArray()); |
|||
} |
|||
} |
|||
|
|||
return httpClient; |
|||
} |
|||
|
|||
public async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url) |
|||
{ |
|||
try |
|||
{ |
|||
HttpResponseMessage response = await httpClient.GetAsync(url); |
|||
response.EnsureSuccessStatusCode(); |
|||
|
|||
var content = await response.Content.ReadAsStringAsync(); |
|||
return JsonSerializer.Deserialize<T>(content); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
_logger.LogInformation("Error making request to {0}: {1}", url, e.Message); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
public async Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>( |
|||
Dictionary<TKey, Task<TValue>> tasks) |
|||
{ |
|||
var completedTask = Task.WhenAny(tasks.Values); |
|||
var result = await completedTask; |
|||
|
|||
var completedTaskPair = tasks.First(kv => kv.Value == result); |
|||
|
|||
return completedTaskPair; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class AggregateServiceBase<TDto> |
|||
{ |
|||
private readonly IAggregateRemoteService<TDto> _remoteService; |
|||
|
|||
public AggregateServiceBase(IAggregateRemoteService<TDto> remoteService) |
|||
{ |
|||
_remoteService = remoteService; |
|||
} |
|||
|
|||
public virtual async Task<Dictionary<string, TDto>> GetMultipleFromRemoteAsync(List<string> missingKeys, |
|||
Dictionary<string, string> endpoints) |
|||
{ |
|||
return await _remoteService |
|||
.GetMultipleAsync(endpoints |
|||
.Where(kv => missingKeys.Contains(kv.Key)) |
|||
.ToDictionary(k => k.Key, v => v.Value)); |
|||
} |
|||
|
|||
public List<string> GetMissingServiceKeys( |
|||
IDictionary<string, TDto> serviceNamesWithData, |
|||
Dictionary<string, string> serviceNamesWithUrls) |
|||
{ |
|||
List<string> missingKeysInCache = serviceNamesWithUrls.Keys.Except(serviceNamesWithData.Keys).ToList(); |
|||
List<string> missingKeysInUrls = serviceNamesWithData.Keys.Except(serviceNamesWithUrls.Keys).ToList(); |
|||
|
|||
return missingKeysInCache.Concat(missingKeysInUrls).ToList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public abstract class CachedServiceBase<TCacheValue> : ICachedServiceBase<TCacheValue> |
|||
{ |
|||
private readonly IMemoryCache _cache; |
|||
|
|||
protected MemoryCacheEntryOptions CacheEntryOptions { get; } = new() |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24), |
|||
SlidingExpiration = TimeSpan.FromHours(4) |
|||
}; |
|||
|
|||
protected CachedServiceBase(IMemoryCache cache) |
|||
{ |
|||
_cache = cache ?? throw new ArgumentNullException(nameof(cache)); |
|||
} |
|||
|
|||
public void Add(string serviceName, TCacheValue data) |
|||
{ |
|||
_cache.Set(serviceName, data, CacheEntryOptions); |
|||
} |
|||
|
|||
public IDictionary<string, TCacheValue> GetManyAsync(IEnumerable<string> serviceNames) |
|||
{ |
|||
var result = new Dictionary<string, TCacheValue>(); |
|||
|
|||
foreach (var serviceName in serviceNames) |
|||
{ |
|||
if (_cache.TryGetValue(serviceName, out TCacheValue data)) |
|||
{ |
|||
result.Add(serviceName, data); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface IAggregateRemoteService<TDto> |
|||
{ |
|||
Task<Dictionary<string, TDto>> GetMultipleAsync(Dictionary<string, string> serviceNameWithUrlDictionary); |
|||
Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url); |
|||
Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>(Dictionary<TKey, Task<TValue>> tasks); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface ICachedServiceBase<TValue> |
|||
{ |
|||
void Add(string serviceName, TValue data); |
|||
IDictionary<string, TValue> GetManyAsync(IEnumerable<string> serviceNames); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
public interface IRequestInput |
|||
{ |
|||
Dictionary<string, string> Endpoints { get; } |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public interface ILocalizationAggregation |
|||
{ |
|||
string LocalizationRouteName { get; } |
|||
string LocalizationEndpoint { get; } |
|||
Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public interface ILocalizationRemoteService : IAggregateRemoteService<ApplicationLocalizationDto>; |
|||
@ -0,0 +1,72 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationAggregation : AggregateServiceBase<ApplicationLocalizationDto>, ILocalizationAggregation, |
|||
ITransientDependency |
|||
{ |
|||
public string LocalizationRouteName => "EshopOnAbpLocalization"; |
|||
public string LocalizationEndpoint => "api/abp/application-localization"; |
|||
protected LocalizationCachedService LocalizationCachedService { get; } |
|||
|
|||
public LocalizationAggregation( |
|||
LocalizationCachedService localizationCachedService, |
|||
ILocalizationRemoteService localizationRemoteService) |
|||
: base(localizationRemoteService) |
|||
{ |
|||
LocalizationCachedService = localizationCachedService; |
|||
} |
|||
|
|||
public async Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input) |
|||
{ |
|||
// Check the cache service
|
|||
var cachedLocalization = LocalizationCachedService |
|||
.GetManyAsync(input.Endpoints.Keys.ToArray()); |
|||
|
|||
// Compare cache with input service list
|
|||
var missingLocalizationKeys = GetMissingServiceKeys(cachedLocalization, input.Endpoints); |
|||
|
|||
if (missingLocalizationKeys.Count != 0) |
|||
{ |
|||
// Make request to remote localization service to get missing localizations
|
|||
var remoteLocalizationResults = |
|||
await GetMultipleFromRemoteAsync(missingLocalizationKeys, input.Endpoints); |
|||
|
|||
// Update localization cache
|
|||
foreach (var result in remoteLocalizationResults) |
|||
{ |
|||
LocalizationCachedService.Add(result.Key, result.Value); |
|||
} |
|||
|
|||
cachedLocalization = LocalizationCachedService |
|||
.GetManyAsync(input.Endpoints.Keys.ToArray()); |
|||
} |
|||
|
|||
//merge result
|
|||
var mergedResult = MergeLocalizationData(cachedLocalization); |
|||
|
|||
//return result
|
|||
return mergedResult; |
|||
} |
|||
|
|||
private static ApplicationLocalizationDto MergeLocalizationData( |
|||
IDictionary<string, ApplicationLocalizationDto> resourceDictionary) |
|||
{ |
|||
var localizationDto = new ApplicationLocalizationDto(); |
|||
|
|||
foreach (var localization in resourceDictionary) |
|||
{ |
|||
foreach (var resource in localization.Value.Resources) |
|||
{ |
|||
localizationDto.Resources.TryAdd(resource.Key, resource.Value); |
|||
} |
|||
} |
|||
|
|||
return localizationDto; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationCachedService(IMemoryCache localizationCache) |
|||
: CachedServiceBase<ApplicationLocalizationDto>(localizationCache), ISingletonDependency; |
|||
@ -0,0 +1,20 @@ |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationRemoteService : AggregateRemoteServiceBase<ApplicationLocalizationDto>, |
|||
ILocalizationRemoteService, ITransientDependency |
|||
{ |
|||
public LocalizationRemoteService( |
|||
IHttpContextAccessor httpContextAccessor, |
|||
IJsonSerializer jsonSerializer, |
|||
ILogger<AggregateRemoteServiceBase<ApplicationLocalizationDto>> logger) |
|||
: base(httpContextAccessor, jsonSerializer, logger) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using EShopOnAbp.WebGateway.Aggregations.Base; |
|||
|
|||
namespace EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
|
|||
public class LocalizationRequest : IRequestInput |
|||
{ |
|||
public Dictionary<string, string> Endpoints { get; } = new(); |
|||
public string CultureName { get; set; } |
|||
|
|||
public LocalizationRequest(string cultureName) |
|||
{ |
|||
CultureName = cultureName; |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration; |
|||
using EShopOnAbp.WebGateway.Aggregations.Localization; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Routing; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Yarp.ReverseProxy.Configuration; |
|||
|
|||
namespace EShopOnAbp.WebGateway; |
|||
|
|||
public static class ReverseProxyBuilderExtensions |
|||
{ |
|||
public static ReverseProxyConventionBuilder MapReverseProxyWithLocalization(this IEndpointRouteBuilder endpoints) |
|||
{ |
|||
return endpoints.MapReverseProxy(proxyBuilder => |
|||
{ |
|||
proxyBuilder.Use(async (context, next) => |
|||
{ |
|||
var endpoint = context.GetEndpoint(); |
|||
|
|||
var localizationAggregation = context.RequestServices |
|||
.GetRequiredService<ILocalizationAggregation>(); |
|||
|
|||
var appConfigurationAggregation = context.RequestServices |
|||
.GetRequiredService<IAppConfigurationAggregation>(); |
|||
|
|||
// The "/api/abp/application-localization" endpoint
|
|||
if (localizationAggregation.LocalizationRouteName == endpoint?.DisplayName) |
|||
{ |
|||
var localizationRequestInput = |
|||
CreateLocalizationRequestInput(context, localizationAggregation.LocalizationEndpoint); |
|||
|
|||
var result = await localizationAggregation.GetLocalizationAsync(localizationRequestInput); |
|||
await context.Response.WriteAsJsonAsync(result); |
|||
return; |
|||
} |
|||
|
|||
// The "/api/abp/application-configuration" endpoint
|
|||
if (appConfigurationAggregation.AppConfigRouteName == endpoint?.DisplayName) |
|||
{ |
|||
var appConfigurationRequestInput = |
|||
CreateAppConfigurationRequestInput(context, appConfigurationAggregation.AppConfigEndpoint); |
|||
|
|||
var result = |
|||
await appConfigurationAggregation.GetAppConfigurationAsync(appConfigurationRequestInput); |
|||
await context.Response.WriteAsJsonAsync(result); |
|||
return; |
|||
} |
|||
|
|||
await next(); |
|||
}); |
|||
|
|||
proxyBuilder.UseLoadBalancing(); |
|||
}); |
|||
} |
|||
|
|||
private static AppConfigurationRequest CreateAppConfigurationRequestInput(HttpContext context, |
|||
string appConfigurationPath) |
|||
{ |
|||
var proxyConfig = context.RequestServices.GetRequiredService<IProxyConfigProvider>(); |
|||
|
|||
var input = new AppConfigurationRequest(); |
|||
string path = $"{appConfigurationPath}?includeLocalizationResources=false"; |
|||
|
|||
var clusterList = GetClusters(proxyConfig); |
|||
foreach (var cluster in clusterList) |
|||
{ |
|||
var hostUrl = new Uri(cluster.Value.Address) + $"{path}"; |
|||
// CacheKey/Endpoint dictionary key -> ex: ("Administration_AppConfig")
|
|||
input.Endpoints.Add($"{cluster.Key}_AppConfig", hostUrl); |
|||
} |
|||
|
|||
return input; |
|||
} |
|||
|
|||
private static LocalizationRequest CreateLocalizationRequestInput(HttpContext context, string localizationPath) |
|||
{ |
|||
var proxyConfig = context.RequestServices.GetRequiredService<IProxyConfigProvider>(); |
|||
|
|||
context.Request.Query.TryGetValue("CultureName", out var cultureName); |
|||
|
|||
var input = new LocalizationRequest(cultureName); |
|||
string path = $"{localizationPath}?cultureName={cultureName}&onlyDynamics=false"; |
|||
|
|||
var clusterList = GetClusters(proxyConfig); |
|||
foreach (var cluster in clusterList) |
|||
{ |
|||
var hostUrl = new Uri(cluster.Value.Address) + $"{path}"; |
|||
// Endpoint dictionary key -> ex: ("Administration_en")
|
|||
input.Endpoints.Add($"{cluster.Key}_{cultureName}", hostUrl); |
|||
} |
|||
|
|||
return input; |
|||
} |
|||
|
|||
private static Dictionary<string, DestinationConfig> GetClusters(IProxyConfigProvider proxyConfig) |
|||
{ |
|||
var yarpConfig = proxyConfig.GetConfig(); |
|||
|
|||
var routedClusters = yarpConfig.Clusters |
|||
.SelectMany(t => t.Destinations, |
|||
(clusterId, destination) => new { clusterId.ClusterId, destination.Value }); |
|||
|
|||
return routedClusters |
|||
.GroupBy(q => q.Value.Address) |
|||
.Select(t => t.First()) |
|||
.Distinct() |
|||
.ToDictionary(k => k.ClusterId, v => v.Value); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue