Skip to content
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
19 changes: 5 additions & 14 deletions GenHTTP.slnx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
<Solution>

<Folder Name="/Adapters/">
<Project Path="Adapters\AspNetCore\GenHTTP.Adapters.AspNetCore.csproj" />
</Folder>

<Folder Name="/API/">
<Project Path="API\GenHTTP.Api.csproj" />
</Folder>

<Folder Name="/Engine/">
<Project Path="Engine\Internal\GenHTTP.Engine.Internal.csproj" />
<Project Path="Engine\Kestrel\GenHTTP.Engine.Kestrel.csproj" />
<Project Path="Engine\Shared\GenHTTP.Engine.Shared.csproj" />
</Folder>

<Folder Name="/Modules/">
<Project Path="Modules\ApiBrowsing\GenHTTP.Modules.ApiBrowsing.csproj" />
<Project Path="Modules\Authentication\GenHTTP.Modules.Authentication.csproj" />
Expand All @@ -23,6 +19,7 @@
<Project Path="Modules\Compression\GenHTTP.Modules.Compression.csproj" />
<Project Path="Modules\Controllers\GenHTTP.Modules.Controllers.csproj" />
<Project Path="Modules\Conversion\GenHTTP.Modules.Conversion.csproj" />
<Project Path="Modules\DependencyInjection\GenHTTP.Modules.DependencyInjection.csproj" />
<Project Path="Modules\DirectoryBrowsing\GenHTTP.Modules.DirectoryBrowsing.csproj" />
<Project Path="Modules\ErrorHandling\GenHTTP.Modules.ErrorHandling.csproj" />
<Project Path="Modules\Functional\GenHTTP.Modules.Functional.csproj" />
Expand All @@ -46,29 +43,23 @@
<Project Path="Modules\Webservices\GenHTTP.Modules.Webservices.csproj" />
<Project Path="Modules\Websockets\GenHTTP.Modules.Websockets.csproj" />
</Folder>

<Folder Name="/Playground/">
<Project Path="Playground\GenHTTP.Playground.csproj" />
</Folder>

<Folder Name="/Testing/">
<Project Path="Testing\Acceptance\GenHTTP.Testing.Acceptance.csproj" />
<Project Path="Testing\Testing\GenHTTP.Testing.csproj" />
</Folder>

<Folder Name="/Solution Items/">
<File Path="CONTRIBUTING.md" />
<File Path="LICENSE" />
<File Path="README.md" />
</Folder>

<Folder Name="/Solution Items/Packages/">
<File Path="Packages\GenHTTP.Core.Kestrel.nuspec" />
<File Path="Packages\GenHTTP.Core.nuspec" />
</Folder>

<Folder Name="/Solution Items/Resources/">
<File Path="Resources\icon.png" />
</Folder>

<Folder Name="/Testing/">
<Project Path="Testing\Acceptance\GenHTTP.Testing.Acceptance.csproj" />
<Project Path="Testing\Testing\GenHTTP.Testing.csproj" />
</Folder>
</Solution>
32 changes: 25 additions & 7 deletions Modules/Controllers/Provider/ControllerBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System.Diagnostics.CodeAnalysis;

using GenHTTP.Api.Content;
using GenHTTP.Api.Infrastructure;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.Conversion;
using GenHTTP.Modules.Conversion.Formatters;
using GenHTTP.Modules.Conversion.Serializers;
Expand All @@ -9,16 +12,18 @@

namespace GenHTTP.Modules.Controllers.Provider;

public sealed class ControllerBuilder : IHandlerBuilder<ControllerBuilder>
public sealed class ControllerBuilder : IHandlerBuilder<ControllerBuilder>, IRegistryBuilder<ControllerBuilder>
{
private readonly List<IConcernBuilder> _Concerns = [];

private Type? _Type;

private Func<IRequest, ValueTask<object>>? _InstanceProvider;

private IBuilder<FormatterRegistry>? _Formatters;

private IBuilder<InjectionRegistry>? _Injection;

private object? _Instance;

private IBuilder<SerializationRegistry>? _Serializers;

#region Functionality
Expand All @@ -42,14 +47,25 @@ public ControllerBuilder Formatters(IBuilder<FormatterRegistry> registry)
}

public ControllerBuilder Type<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>() where T : new()
=> Instance(new T());

public ControllerBuilder Type([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type)
{
_Instance = new T();
_Type = type;
return this;
}

public ControllerBuilder Instance(object instance)
{
_Instance = instance;
_Type = instance.GetType();
_InstanceProvider = (_) => ValueTask.FromResult(instance);

return this;
}

public ControllerBuilder InstanceProvider(Func<IRequest, ValueTask<object>> provider)
{
_InstanceProvider = provider;
return this;
}

Expand All @@ -67,11 +83,13 @@ public IHandler Build()

var formatters = (_Formatters ?? Formatting.Default()).Build();

var instance = _Instance ?? throw new BuilderMissingPropertyException("Instance or Type");
var instanceProvider = _InstanceProvider ?? throw new BuilderMissingPropertyException("Instance provider has not been set");

var type = _Type ?? throw new BuilderMissingPropertyException("Type has not been set");

var extensions = new MethodRegistry(serializers, injectors, formatters);

return Concerns.Chain(_Concerns, new ControllerHandler(instance, extensions));
return Concerns.Chain(_Concerns, new ControllerHandler(type, instanceProvider, extensions));
}

#endregion
Expand Down
55 changes: 31 additions & 24 deletions Modules/Controllers/Provider/ControllerHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,74 @@ public sealed partial class ControllerHandler : IHandler, IServiceMethodProvider
{
private static readonly Regex HyphenMatcher = CreateHyphenMatcher();

private MethodCollection? _Methods;

#region Get-/Setters

public MethodCollection Methods { get; }
private Type Type { get; }

private ResponseProvider ResponseProvider { get; }
private Func<IRequest, ValueTask<object>> InstanceProvider { get; }

private MethodRegistry Registry { get; }

private object Instance { get; }

#endregion

#region Initialization

public ControllerHandler(object instance, MethodRegistry registry)
public ControllerHandler(Type type, Func<IRequest, ValueTask<object>> instanceProvider, MethodRegistry registry)
{
Type = type;
InstanceProvider = instanceProvider;
Registry = registry;
}

#endregion

Instance = instance;
#region Functionality

ResponseProvider = new ResponseProvider(registry);
public ValueTask PrepareAsync() => ValueTask.CompletedTask;

Methods = new MethodCollection(AnalyzeMethods(instance.GetType(), registry));
}
public async ValueTask<IResponse?> HandleAsync(IRequest request) => await (await GetMethodsAsync(request)).HandleAsync(request);

private IEnumerable<MethodHandler> AnalyzeMethods(Type type, MethodRegistry registry)
public async ValueTask<MethodCollection> GetMethodsAsync(IRequest request)
{
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
if (_Methods != null) return _Methods;

var found = new List<MethodHandler>();


foreach (var method in Type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
var annotation = method.GetCustomAttribute<ControllerActionAttribute>(true) ?? new MethodAttribute();

var arguments = FindPathArguments(method);

var operation = CreateOperation(method, arguments);
var operation = CreateOperation(request, method, arguments, Registry);

yield return new MethodHandler(operation, Instance, annotation, registry);
found.Add(new MethodHandler(operation, InstanceProvider, annotation, Registry));
}

var result = new MethodCollection(found);

await result.PrepareAsync();

return _Methods = result;
}

private Operation CreateOperation(MethodInfo method, List<string> arguments)
private static Operation CreateOperation(IRequest request, MethodInfo method, List<string> arguments, MethodRegistry registry)
{
var pathArguments = string.Join('/', arguments.Select(a => $":{a}"));

if (method.Name == "Index")
{
return OperationBuilder.Create(pathArguments.Length > 0 ? $"/{pathArguments}/" : null, method, Registry, true);
return OperationBuilder.Create(request, pathArguments.Length > 0 ? $"/{pathArguments}/" : null, method, registry, true);
}

var name = HypenCase(method.Name);

var path = $"/{name}";

return OperationBuilder.Create(pathArguments.Length > 0 ? $"{path}/{pathArguments}/" : $"{path}/", method, Registry, true);
return OperationBuilder.Create(request, pathArguments.Length > 0 ? $"{path}/{pathArguments}/" : $"{path}/", method, registry, true);
}

private static List<string> FindPathArguments(MethodInfo method)
Expand Down Expand Up @@ -93,12 +108,4 @@ private static List<string> FindPathArguments(MethodInfo method)

#endregion

#region Functionality

public ValueTask PrepareAsync() => Methods.PrepareAsync();

public ValueTask<IResponse?> HandleAsync(IRequest request) => Methods.HandleAsync(request);

#endregion

}
38 changes: 38 additions & 0 deletions Modules/DependencyInjection/Basics/ConcernIntegration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.DependencyInjection.Infrastructure;

namespace GenHTTP.Modules.DependencyInjection.Basics;

internal class ConcernIntegration<T> : IConcern where T : class, IDependentConcern
{

#region Getters/Setters

public IHandler Content { get; }

#endregion

#region Initialization

internal ConcernIntegration(IHandler content)
{
Content = content;
}

#endregion

#region Functionality

public ValueTask PrepareAsync() => Content.PrepareAsync();

public async ValueTask<IResponse?> HandleAsync(IRequest request)
{
var instance = await InstanceProvider.ProvideAsync<T>(request);

return await instance.HandleAsync(Content, request);
}

#endregion

}
10 changes: 10 additions & 0 deletions Modules/DependencyInjection/Basics/ConcernIntegrationBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using GenHTTP.Api.Content;

namespace GenHTTP.Modules.DependencyInjection.Basics;

internal class ConcernIntegrationBuilder<T> : IConcernBuilder where T : class, IDependentConcern
{

public IConcern Build(IHandler content) => new ConcernIntegration<T>(content);

}
20 changes: 20 additions & 0 deletions Modules/DependencyInjection/Basics/HandlerIntegration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.DependencyInjection.Infrastructure;

namespace GenHTTP.Modules.DependencyInjection.Basics;

internal class HandlerIntegration<T> : IHandler where T: class, IDependentHandler
{

public ValueTask PrepareAsync() => ValueTask.CompletedTask;

public async ValueTask<IResponse?> HandleAsync(IRequest request)
{
var instance = await InstanceProvider.ProvideAsync<T>(request);

return await instance.HandleAsync(request);
}

}
10 changes: 10 additions & 0 deletions Modules/DependencyInjection/Basics/HandlerIntegrationBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using GenHTTP.Api.Content;

namespace GenHTTP.Modules.DependencyInjection.Basics;

internal class HandlerIntegrationBuilder<T> : IHandlerBuilder where T : class, IDependentHandler
{

public IHandler Build() => new HandlerIntegration<T>();

}
25 changes: 25 additions & 0 deletions Modules/DependencyInjection/Dependent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using GenHTTP.Api.Content;

using GenHTTP.Modules.DependencyInjection.Basics;

namespace GenHTTP.Modules.DependencyInjection;

/// <summary>
/// Allows to enable dependency injection for concerns and handlers.
/// </summary>
public static class Dependent
{

/// <summary>
/// Allows the given class to be used as a regular concern with dependency injection enabled.
/// </summary>
/// <typeparam name="T">The class implementing the concern</typeparam>
public static IConcernBuilder Concern<T>() where T : class, IDependentConcern => new ConcernIntegrationBuilder<T>();

/// <summary>
/// Allows the given class to be used as a regular handler with dependency injection enabled.
/// </summary>
/// <typeparam name="T">The class implementing the handler</typeparam>
public static IHandlerBuilder Handler<T>() where T : class, IDependentHandler => new HandlerIntegrationBuilder<T>();

}
39 changes: 39 additions & 0 deletions Modules/DependencyInjection/DependentController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Diagnostics.CodeAnalysis;

using GenHTTP.Api.Infrastructure;

using GenHTTP.Modules.Controllers.Provider;
using GenHTTP.Modules.Conversion.Formatters;
using GenHTTP.Modules.Conversion.Serializers;
using GenHTTP.Modules.DependencyInjection.Infrastructure;
using GenHTTP.Modules.Layouting.Provider;
using GenHTTP.Modules.Reflection.Injectors;

namespace GenHTTP.Modules.DependencyInjection;

public static class DependentController
{

/// <summary>
/// Adds the given controller to the layout with dependency injection enabled.
/// </summary>
/// <param name="layout">The layout to add the controller to</param>
/// <param name="path">The path to register the controller at</param>
/// <param name="injectors">Additional parameter injections to be used by the controller</param>
/// <param name="serializers">Additional serializers to be used by the controller</param>
/// <param name="formatters">Additional formatters to be used by the controller</param>
/// <typeparam name="T">The class implementing the controller</typeparam>
/// <returns>The given layout with the specified controller attached</returns>
public static LayoutBuilder AddDependentController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this LayoutBuilder layout, string path, InjectionRegistryBuilder? injectors = null, IBuilder<SerializationRegistry>? serializers = null, IBuilder<FormatterRegistry>? formatters = null) where T : class
{
var builder = new ControllerBuilder();

builder.Type(typeof(T));
builder.InstanceProvider(async (r) => await InstanceProvider.ProvideAsync<T>(r));

builder.Configure(injectors, serializers, formatters);

return layout.Add(path, builder);
}

}
Loading
Loading