Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#2) add extensions #4

Merged
merged 2 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion scripts/pack-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ echo "$divider"
echo "Starting packaging process"
echo "$divider"

# Function to check if a directory has changes
directory_contains_changes() {
local dir="$1"
[[ "$FORCE_PACK_ALL" == true ]] || [[ $(echo "$CHANGED_FILES" | grep -c "$dir") -gt 0 ]]
Expand Down
333 changes: 333 additions & 0 deletions src/Nuar/src/Nuar/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetJSON;
using Nuar.Auth;
using Nuar.Configuration;
using Nuar.Extensions;
using Nuar.Handlers;
using Nuar.Options;
using Nuar.Requests;
using Nuar.Routing;
using Nuar.WebApi;
using Polly;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc;
using System.Text;
using Microsoft.AspNetCore.Http;

[assembly: InternalsVisibleTo("Nuar.Tests.Unit")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

namespace Nuar
{
public static class NuarExtensions
{
private const string Logo = @"

.-----------------. .----------------. .----------------. .----------------.
| .--------------. || .--------------. || .--------------. || .--------------. |
| | ____ _____ | || | _____ _____ | || | __ | || | _______ | |
| ||_ \|_ _| | || ||_ _||_ _|| || | / \ | || | |_ __ \ | |
| | | \ | | | || | | | | | | || | / /\ \ | || | | |__) | | |
| | | |\ \| | | || | | ' ' | | || | / ____ \ | || | | __ / | |
| | _| |_\ |_ | || | \ `--' / | || | _/ / \ \_ | || | _| | \ \_ | |
| ||_____|\____| | || | `.__.' | || ||____| |____|| || | |____| |___| | |
| | | || | | || | | || | | |
| '--------------' || '--------------' || '--------------' || '--------------' |
'----------------' '----------------' '----------------' '----------------'

_ _____ _
/\ (_)/ ____| | |
/ \ _ __ _| | __ __ _| |_ _____ ____ _ _ _
/ /\ \ | '_ \| | | |_ |/ _` | __/ _ \ \ /\ / / _` | | | |
/ ____ \| |_) | | |__| | (_| | || __/\ V V / (_| | |_| |
/_/ \_\ .__/|_|\_____|\__,_|\__\___| \_/\_/ \__,_|\__, |
| | __/ |
|_| |___/



/#### Nuar API Gateway ####\
@#~ *Innovative & Fast* ~#@

";


public static IServiceCollection AddNuar(this IServiceCollection services)
{
var (configuration, optionsProvider) = BuildConfiguration(services);

return services.AddCoreServices()
.ConfigureLogging(configuration)
.ConfigureHttpClient(configuration)
.AddNuarServices()
.AddExtensions(optionsProvider);
}

private static (NuarOptions, OptionsProvider) BuildConfiguration(IServiceCollection services)
{
IConfiguration config;
using (var scope = services.BuildServiceProvider().CreateScope())
{
config = scope.ServiceProvider.GetService<IConfiguration>();
}

var optionsProvider = new OptionsProvider(config);
services.AddSingleton<IOptionsProvider>(optionsProvider);
var options = optionsProvider.Get<NuarOptions>();
services.AddSingleton(options);

return (options, optionsProvider);
}

// Step 2: Add custom JSON formatter using NetJSON
private static IServiceCollection AddCoreServices(this IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.InputFormatters.Clear();
options.OutputFormatters.Clear();
options.InputFormatters.Add(new NetJsonInputFormatter());
options.OutputFormatters.Add(new NetJsonOutputFormatter());
})
.AddApiExplorer();

return services;
}

private static IServiceCollection ConfigureLogging(this IServiceCollection services, NuarOptions options)
{
services.AddLogging(builder =>
{
builder.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddConsole();
});

return services;
}

private static IServiceCollection ConfigureHttpClient(this IServiceCollection services, NuarOptions options)
{
var http = options.Http ?? new Http();
var httpClientBuilder = services.AddHttpClient("nuar");

httpClientBuilder.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(http.Retries, retryAttempt =>
{
var interval = http.Exponential
? Math.Pow(http.Interval, retryAttempt)
: http.Interval;

return TimeSpan.FromSeconds(interval);
}));

return services;
}

private static IServiceCollection AddNuarServices(this IServiceCollection services)
{
// Register core Nuar services
services.AddSingleton<IAuthenticationManager, AuthenticationManager>();
services.AddSingleton<IAuthorizationManager, AuthorizationManager>();
services.AddSingleton<IPolicyManager, PolicyManager>();
services.AddSingleton<IDownstreamBuilder, DownstreamBuilder>();
services.AddSingleton<IPayloadBuilder, PayloadBuilder>();
services.AddSingleton<IPayloadManager, PayloadManager>();
services.AddSingleton<IPayloadTransformer, PayloadTransformer>();
services.AddSingleton<IPayloadValidator, PayloadValidator>();
services.AddSingleton<IRequestExecutionValidator, RequestExecutionValidator>();
services.AddSingleton<IRequestHandlerManager, RequestHandlerManager>();
services.AddSingleton<IRequestProcessor, RequestProcessor>();
services.AddSingleton<IRouteConfigurator, RouteConfigurator>();
services.AddSingleton<IRouteProvider, RouteProvider>();
services.AddSingleton<ISchemaValidator, SchemaValidator>();
services.AddSingleton<IUpstreamBuilder, UpstreamBuilder>();
services.AddSingleton<IValueProvider, ValueProvider>();
services.AddSingleton<DownstreamHandler>();
services.AddSingleton<ReturnValueHandler>();
services.AddSingleton<WebApiEndpointDefinitions>();

return services;
}

private static IServiceCollection AddExtensions(this IServiceCollection services, IOptionsProvider optionsProvider)
{
var options = optionsProvider.Get<NuarOptions>();
var extensionProvider = new ExtensionProvider(options);
services.AddSingleton<IExtensionProvider>(extensionProvider);

foreach (var extension in extensionProvider.GetAll())
{
if (extension.Options.Enabled == false)
{
continue;
}

extension.Extension.Add(services, optionsProvider);
}

return services;
}

public static IApplicationBuilder UseNuar(this IApplicationBuilder app)
{
var newLine = Environment.NewLine;
var logger = app.ApplicationServices.GetRequiredService<ILogger<Nuar>>();
logger.LogInformation($"{newLine}{Logo}{newLine}");
var options = app.ApplicationServices.GetRequiredService<NuarOptions>();

if (options.Auth?.Enabled == true)
{
logger.LogInformation("Authentication is enabled.");
app.UseAuthentication();
}
else
{
logger.LogInformation("Authentication is disabled.");
}

if (options.UseForwardedHeaders)
{
logger.LogInformation("Headers forwarding is enabled.");
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All
});
}

app.UseExtensions();
app.RegisterRequestHandlers();
app.AddRoutes();

return app;
}

private static void RegisterRequestHandlers(this IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Nuar>>();
var options = app.ApplicationServices.GetRequiredService<NuarOptions>();
var requestHandlerManager = app.ApplicationServices.GetRequiredService<IRequestHandlerManager>();

requestHandlerManager.AddHandler("downstream",
app.ApplicationServices.GetRequiredService<DownstreamHandler>());
requestHandlerManager.AddHandler("return_value",
app.ApplicationServices.GetRequiredService<ReturnValueHandler>());

if (options.Modules == null)
{
return;
}

var handlers = options.Modules
.SelectMany(m => m.Value.Routes)
.Select(r => r.Use)
.Distinct()
.ToArray();

foreach (var handler in handlers)
{
if (requestHandlerManager.GetHandler(handler) == null)
{
throw new Exception($"Handler: '{handler}' was not defined.");
}

logger.LogInformation($"Added handler: '{handler}'");
}
}

private class Nuar
{
}

private static void AddRoutes(this IApplicationBuilder app)
{
var routeProvider = app.ApplicationServices.GetRequiredService<IRouteProvider>();
app.UseRouting();
app.UseEndpoints(routeProvider.Build());
}

private static void UseExtensions(this IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Nuar>>();
var optionsProvider = app.ApplicationServices.GetRequiredService<IOptionsProvider>();
var extensionProvider = app.ApplicationServices.GetRequiredService<IExtensionProvider>();

foreach (var extension in extensionProvider.GetAll())
{
if (extension.Options.Enabled == false)
{
continue;
}

extension.Extension.Use(app, optionsProvider);
logger.LogInformation($"Enabled extension: '{extension.Extension.Name}'");
}
}

public static IApplicationBuilder UseRequestHandler<T>(this IApplicationBuilder app, string name)
where T : IHandler
{
var requestHandlerManager = app.ApplicationServices.GetRequiredService<IRequestHandlerManager>();
var handler = app.ApplicationServices.GetRequiredService<T>();
requestHandlerManager.AddHandler(name, handler);

return app;
}
}

// Step 3: Define custom NetJSON input and output formatters
public class NetJsonInputFormatter : TextInputFormatter
{
public NetJsonInputFormatter()
{
SupportedMediaTypes.Add("application/json");
SupportedEncodings.Add(Encoding.UTF8);
}

protected override bool CanReadType(Type type)
{
return type != null;
}

public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var request = context.HttpContext.Request;
using (var reader = new StreamReader(request.Body, encoding))
{
var body = await reader.ReadToEndAsync();
var result = NetJSON.NetJSON.Deserialize(context.ModelType, body);
return await InputFormatterResult.SuccessAsync(result);
}
}
}

public class NetJsonOutputFormatter : TextOutputFormatter
{
public NetJsonOutputFormatter()
{
SupportedMediaTypes.Add("application/json");
SupportedEncodings.Add(Encoding.UTF8);
}

protected override bool CanWriteType(Type type)

Check warning on line 321 in src/Nuar/src/Nuar/Extensions.cs

View workflow job for this annotation

GitHub Actions / publish

Nullability of type of parameter 'type' doesn't match overridden member (possibly because of nullability attributes).
{
return type != null;
}

public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var response = context.HttpContext.Response;
var json = NetJSON.NetJSON.Serialize(context.Object);
return response.WriteAsync(json);
}
}
}
1 change: 1 addition & 0 deletions src/Nuar/src/Nuar/Nuar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
<PackageReference Include="NetJSON" Version="1.4.4" />
<PackageReference Include="NJsonSchema.Yaml" Version="11.0.2" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="YamlDotNet" Version="16.1.2" />
Expand Down
2 changes: 1 addition & 1 deletion src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
[assembly: System.Reflection.AssemblyCompanyAttribute("Nuar")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+e76dfca658910f3e2e5da1f22f3d2c0d79b63cf8")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+bc199865745675ed80a20d35ace3ea7bbf0c147e")]
[assembly: System.Reflection.AssemblyProductAttribute("Nuar")]
[assembly: System.Reflection.AssemblyTitleAttribute("Nuar")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0699f35eb30bcc75f1b7c42f15054c46358d062513981ff92a515d52ab637ac6
cc57279673fc1b89c73b578fb3711056f666df951e27b30316d27382dbcf31be
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Nuar
build_property.ProjectDir = /home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/Nuar/
build_property.ProjectDir = /home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 9.0
Expand Down
Binary file modified src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.assets.cache
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
19465b6224201d81c032783e4f31c93eafd12929d3752b32413591d208cd04f6
ed69f814a871661be305c58ce4f57e8f8ce0b045bad32677e9b3a17800ae112e
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,12 @@
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/Nuar/obj/Debug/net9.0/refint/Nuar.dll
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/Nuar/obj/Debug/net9.0/Nuar.pdb
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/Nuar/obj/Debug/net9.0/ref/Nuar.dll
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.csproj.AssemblyReference.cache
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.GeneratedMSBuildEditorConfig.editorconfig
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.AssemblyInfoInputs.cache
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.AssemblyInfo.cs
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.csproj.CoreCompileInputs.cache
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.sourcelink.json
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.dll
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/refint/Nuar.dll
/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.pdb
2 changes: 1 addition & 1 deletion src/Nuar/src/Nuar/obj/Debug/net9.0/Nuar.sourcelink.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"documents":{"/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/*":"https://raw.githubusercontent.com/itsharppro/Nuar/54355918ed69f82870d0ff6530e1153b5479f2af/*"}}
{"documents":{"/home/kaliuser/Documents/portfolio/commercial_apps/crmlxrms/nuar_framework/*":"https://raw.githubusercontent.com/itsharppro/Nuar/bc199865745675ed80a20d35ace3ea7bbf0c147e/*"}}
Loading
Loading