Skip to content

Commit

Permalink
1.On BuilderExtensions.cs AddSwaggerEndPoints method InvalidOperation…
Browse files Browse the repository at this point in the history
…Exception throwing block refactored using IEndPointValidator.cs

2. created custom OnChange instead of IOptionsMonitor.OnChange to monipulate with Options which added Programmatically and don't effect to config options. Both works well
  • Loading branch information
rabdulatif committed Oct 16, 2024
1 parent da22005 commit 3d16d55
Show file tree
Hide file tree
Showing 16 changed files with 418 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using MMLib.SwaggerForOcelot.Aggregates;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Repositories.EndPointValidators;
using MMLib.SwaggerForOcelot.ServiceDiscovery.ConsulServiceDiscoveries;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
Expand Down Expand Up @@ -47,6 +48,8 @@ public static IServiceCollection AddSwaggerForOcelot(
{
services
.AddSingleton<IConsulServiceDiscovery, ConsulServiceDisvovery>()
.AddSingleton<ISwaggerEndpointsMonitor, SwaggerEndpointsMonitor>()
.AddSingleton<IEndPointValidator, EndPointValidator>()
.AddTransient<IRoutesDocumentationProvider, RoutesDocumentationProvider>()
.AddTransient<IDownstreamSwaggerDocsRepository, DownstreamSwaggerDocsRepository>()
.AddTransient<ISwaggerServiceDiscoveryProvider, SwaggerServiceDiscoveryProvider>()
Expand All @@ -62,7 +65,11 @@ public static IServiceCollection AddSwaggerForOcelot(
if (conf?.Type is ("Consul" or "PollConsul"))
{
services.AddConsulClient(conf);

services.AddSingleton<IConsulEndpointOptionsMonitor, ConsulEndpointOptionsMonitor>();
services.AddSingleton<ISwaggerEndpointsMonitor, ConsulSwaggerEndpointsMonitor>();
services.AddTransient<ISwaggerEndPointProvider, ConsulSwaggerEndpointProvider>();
services.AddSingleton<IEndPointValidator, ConsulEndPointValidator>();
}

services.AddHttpClient(IgnoreSslCertificate, c =>
Expand Down
27 changes: 13 additions & 14 deletions src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Repositories;
using MMLib.SwaggerForOcelot.Repositories.EndPointValidators;

namespace Microsoft.AspNetCore.Builder
{
Expand Down Expand Up @@ -41,7 +42,7 @@ public static IApplicationBuilder UseSwaggerForOcelotUI(
.ApplicationServices.GetService<ISwaggerEndPointProvider>().GetAll();

ChangeDetection(app, c, options);
AddSwaggerEndPoints(c, endPoints, options.DownstreamSwaggerEndPointBasePath);
AddSwaggerEndPoints(app, c, endPoints, options.DownstreamSwaggerEndPointBasePath);
});

return app;
Expand All @@ -50,14 +51,14 @@ public static IApplicationBuilder UseSwaggerForOcelotUI(
private static void ChangeDetection(IApplicationBuilder app, SwaggerUIOptions c,
SwaggerForOcelotUIOptions options)
{
IOptionsMonitor<List<SwaggerEndPointOptions>> endpointsChangeMonitor =
app.ApplicationServices.GetService<IOptionsMonitor<List<SwaggerEndPointOptions>>>();
var endpointsChangeMonitor =
app.ApplicationServices.GetService<ISwaggerEndpointsMonitor>();

endpointsChangeMonitor.OnChange((newEndpoints) =>
endpointsChangeMonitor.OptionsChanged += (s, newEndpoints) =>
{
c.ConfigObject.Urls = null;
AddSwaggerEndPoints(c, newEndpoints, options.DownstreamSwaggerEndPointBasePath);
});
AddSwaggerEndPoints(app, c, newEndpoints, options.DownstreamSwaggerEndPointBasePath);
};
}

/// <inheritdoc cref="UseSwaggerForOcelotUI(IApplicationBuilder,Action{SwaggerForOcelotUIOptions})"/>
Expand All @@ -79,25 +80,23 @@ private static void UseSwaggerForOcelot(IApplicationBuilder app, SwaggerForOcelo
=> app.Map(options.PathToSwaggerGenerator,
builder => builder.UseMiddleware<SwaggerForOcelotMiddleware>(options));

private static void AddSwaggerEndPoints(
SwaggerUIOptions c,
private static void AddSwaggerEndPoints(IApplicationBuilder app,
SwaggerUIOptions swaggerOptions,
IReadOnlyList<SwaggerEndPointOptions> endPoints,
string basePath)
{
static string GetDescription(SwaggerEndPointConfig config)
=> config.IsGatewayItSelf ? config.Name : $"{config.Name} - {config.Version}";

// if (endPoints is null || endPoints.Count == 0)
// {
// throw new InvalidOperationException(
// $"{SwaggerEndPointOptions.ConfigurationSectionName} configuration section is missing or empty.");
// }
var validator = app.ApplicationServices.GetRequiredService<IEndPointValidator>();
validator.Validate(endPoints);

foreach (SwaggerEndPointOptions endPoint in endPoints)
{
foreach (SwaggerEndPointConfig config in endPoint.Config)
{
c.SwaggerEndpoint($"{basePath}/{config.Version}/{endPoint.KeyToPath}", GetDescription(config));
swaggerOptions.SwaggerEndpoint($"{basePath}/{config.Version}/{endPoint.KeyToPath}",
GetDescription(config));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ConsulSwaggerEndpointProvider : ISwaggerEndPointProvider
/// <summary>
///
/// </summary>
public IConsulServiceDiscovery _service { get; set; }
private IConsulServiceDiscovery _service;

/// <summary>
///
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using MMLib.SwaggerForOcelot.Configuration;
using System.Collections.Generic;

namespace MMLib.SwaggerForOcelot.Repositories.EndPointValidators;

/// <summary>
///
/// </summary>
public class ConsulEndPointValidator : IEndPointValidator
{
/// <summary>
///
/// </summary>
/// <param name="endPoints"></param>
public void Validate(IReadOnlyList<SwaggerEndPointOptions> endPoints)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using MMLib.SwaggerForOcelot.Configuration;
using System;
using System.Collections.Generic;

namespace MMLib.SwaggerForOcelot.Repositories.EndPointValidators;

/// <summary>
///
/// </summary>
public class EndPointValidator : IEndPointValidator
{
/// <summary>
///
/// </summary>
/// <param name="endPoints"></param>
public void Validate(IReadOnlyList<SwaggerEndPointOptions> endPoints)
{
if (endPoints is null || endPoints.Count == 0)
{
throw new InvalidOperationException(
$"{SwaggerEndPointOptions.ConfigurationSectionName} configuration section is missing or empty.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MMLib.SwaggerForOcelot.Configuration;
using System.Collections.Generic;

namespace MMLib.SwaggerForOcelot.Repositories.EndPointValidators;

/// <summary>
///
/// </summary>
public interface IEndPointValidator
{
/// <summary>
///
/// </summary>
/// <param name="endPoints"></param>
void Validate(IReadOnlyList<SwaggerEndPointOptions> endPoints);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#nullable enable
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Configuration;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MMLib.SwaggerForOcelot.Repositories;

/// <summary>
///
/// </summary>
public class ConsulSwaggerEndpointsMonitor : ISwaggerEndpointsMonitor
{
/// <summary>
///
/// </summary>
private readonly IOptionsMonitor<List<SwaggerEndPointOptions>> _optionsMonitor;

/// <summary>
///
/// </summary>
private readonly IConsulEndpointOptionsMonitor _consulOptionsMonitor;

/// <summary>
///
/// </summary>
public event EventHandler<List<SwaggerEndPointOptions>> OptionsChanged;

/// <summary>
///
/// </summary>
/// <param name="optionsMonitor"></param>
/// <param name="consulOptionsMonitor"></param>
public ConsulSwaggerEndpointsMonitor(IOptionsMonitor<List<SwaggerEndPointOptions>> optionsMonitor,

Check warning on line 36 in src/MMLib.SwaggerForOcelot/Repositories/EndPointsMonitor/ConsulSwaggerEndpointsMonitor.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Non-nullable event 'OptionsChanged' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.
IConsulEndpointOptionsMonitor consulOptionsMonitor)
{
_optionsMonitor = optionsMonitor;
_consulOptionsMonitor = consulOptionsMonitor;
_optionsMonitor.OnChange(ConfigChanged);
_consulOptionsMonitor.OptionsChanged +=(s,e) => ConfigChanged(e);
}

/// <summary>
///
/// </summary>
/// <param name="configOptions"></param>
/// <returns></returns>
private void ConfigChanged(List<SwaggerEndPointOptions> configOptions)
{
var options = ConcatOptions(_optionsMonitor.CurrentValue, _consulOptionsMonitor.CurrentValue);
CallOptionsChanged(options);
}

/// <summary>
///
/// </summary>
/// <param name="configOptions"></param>
/// <param name="localOptions"></param>
/// <returns></returns>
protected virtual List<SwaggerEndPointOptions> ConcatOptions(List<SwaggerEndPointOptions> configOptions,
List<SwaggerEndPointOptions> localOptions)
{
return configOptions
.Concat(localOptions)
.DistinctBy(s => s.Key)
.ToList();
}

/// <summary>
///
/// </summary>
/// <param name="options"></param>
protected virtual void CallOptionsChanged(List<SwaggerEndPointOptions> options)
{
OptionsChanged?.Invoke(this, options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MMLib.SwaggerForOcelot.Configuration;
using System;
using System.Collections.Generic;

namespace MMLib.SwaggerForOcelot.Repositories;

/// <summary>
///
/// </summary>
public interface ISwaggerEndpointsMonitor
{
/// <summary>
///
/// </summary>
event EventHandler<List<SwaggerEndPointOptions>> OptionsChanged;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#nullable enable
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Configuration;
using System;
using System.Collections.Generic;

namespace MMLib.SwaggerForOcelot.Repositories;

/// <summary>
///
/// </summary>
public class SwaggerEndpointsMonitor : ISwaggerEndpointsMonitor
{
/// <summary>
///
/// </summary>
private readonly IOptionsMonitor<List<SwaggerEndPointOptions>> _optionsMonitor;

/// <summary>
///
/// </summary>
public event EventHandler<List<SwaggerEndPointOptions>> OptionsChanged;

/// <summary>
///
/// </summary>
/// <param name="optionsMonitor"></param>
public SwaggerEndpointsMonitor(IOptionsMonitor<List<SwaggerEndPointOptions>> optionsMonitor)

Check warning on line 28 in src/MMLib.SwaggerForOcelot/Repositories/EndPointsMonitor/SwaggerEndpointsMonitor.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Non-nullable event 'OptionsChanged' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.
{
_optionsMonitor = optionsMonitor;
_optionsMonitor.OnChange(ConfigChanged);
}

/// <summary>
///
/// </summary>
/// <param name="configOptions"></param>
/// <returns></returns>
private void ConfigChanged(List<SwaggerEndPointOptions> configOptions)
{
CallOptionsChanged(_optionsMonitor.CurrentValue);
}

/// <summary>
///
/// </summary>
/// <param name="options"></param>
protected virtual void CallOptionsChanged(List<SwaggerEndPointOptions> options)
{
OptionsChanged?.Invoke(this, options);
}
}
Loading

0 comments on commit 3d16d55

Please sign in to comment.