/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Server;
///
/// Represents an immutable descriptor of an OpenIddict server event handler.
///
[DebuggerDisplay("{ServiceDescriptor?.ServiceType}")]
public class OpenIddictServerHandlerDescriptor
{
///
/// Creates a new instance of the class.
///
private OpenIddictServerHandlerDescriptor() { }
///
/// Gets the context type associated with the event.
///
public Type ContextType { get; private set; } = default!;
///
/// Gets the list of filters responsible for excluding the handler
/// from the activated handlers if it doesn't meet the criteria.
///
public ImmutableArray FilterTypes { get; private set; } = ImmutableArray.Create();
///
/// Gets the order assigned to the handler.
///
public int Order { get; private set; }
///
/// Gets the service descriptor associated with the handler.
///
public ServiceDescriptor ServiceDescriptor { get; private set; } = default!;
///
/// Gets the type associated with the handler.
///
public OpenIddictServerHandlerType Type { get; private set; }
///
/// Creates a builder allowing to initialize an immutable descriptor.
///
/// The event context type.
/// A new descriptor builder.
public static Builder CreateBuilder() where TContext : BaseContext
=> new Builder();
///
/// Contains methods allowing to build a descriptor instance.
///
/// The event context type.
public class Builder where TContext : BaseContext
{
private ServiceDescriptor? _descriptor;
private readonly List _filters = new();
private int _order;
private OpenIddictServerHandlerType _type;
///
/// Adds the type of a handler filter to the filters list.
///
/// The event handler filter type.
/// The builder instance, so that calls can be easily chained.
public Builder AddFilter(Type type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
if (!typeof(IOpenIddictServerHandlerFilter<>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0104));
}
_filters.Add(type);
return this;
}
///
/// Adds the type of a handler filter to the filters list.
///
/// The event handler filter type.
/// The builder instance, so that calls can be easily chained.
public Builder AddFilter()
where TFilter : IOpenIddictServerHandlerFilter
=> AddFilter(typeof(TFilter));
///
/// Imports the properties set on the specified descriptor.
///
/// The existing descriptor properties are copied from.
/// All the properties previously set on this instance are automatically replaced.
/// The builder instance, so that calls can be easily chained.
public Builder Import(OpenIddictServerHandlerDescriptor descriptor)
{
if (descriptor is null)
{
throw new ArgumentNullException(nameof(descriptor));
}
if (descriptor.ContextType != typeof(TContext))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0284));
}
_descriptor = descriptor.ServiceDescriptor;
_filters.Clear();
_filters.AddRange(descriptor.FilterTypes);
_order = descriptor.Order;
_type = descriptor.Type;
return this;
}
///
/// Sets the service descriptor.
///
/// The service descriptor.
/// The builder instance, so that calls can be easily chained.
public Builder SetServiceDescriptor(ServiceDescriptor descriptor)
{
if (descriptor is null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var type = descriptor.ServiceType;
if (!typeof(IOpenIddictServerHandler<>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0104));
}
_descriptor = descriptor;
return this;
}
///
/// Sets the order in which the event handler will be invoked.
///
/// The handler order.
/// The builder instance, so that calls can be easily chained.
public Builder SetOrder(int order)
{
_order = order;
return this;
}
///
/// Sets the type associated to the handler.
///
/// The handler type.
/// The builder instance, so that calls can be easily chained.
public Builder SetType(OpenIddictServerHandlerType type)
{
if (!Enum.IsDefined(typeof(OpenIddictServerHandlerType), type))
{
throw new InvalidEnumArgumentException(nameof(type), (int) type, typeof(OpenIddictServerHandlerType));
}
_type = type;
return this;
}
///
/// Configures the descriptor to use the specified inline handler.
///
/// The handler instance.
/// The builder instance, so that calls can be easily chained.
public Builder UseInlineHandler(Func handler)
{
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
return UseSingletonHandler(new OpenIddictServerHandler(handler));
}
///
/// Configures the descriptor to use the specified scoped handler.
///
/// The handler type.
/// The builder instance, so that calls can be easily chained.
public Builder UseScopedHandler()
where THandler : IOpenIddictServerHandler
=> SetServiceDescriptor(new ServiceDescriptor(
typeof(THandler), typeof(THandler), ServiceLifetime.Scoped));
///
/// Configures the descriptor to use the specified scoped handler.
///
/// The handler type.
/// The factory used to create the handler.
/// The builder instance, so that calls can be easily chained.
public Builder UseScopedHandler(Func factory)
where THandler : IOpenIddictServerHandler
{
if (factory is null)
{
throw new ArgumentNullException(nameof(factory));
}
return SetServiceDescriptor(new ServiceDescriptor(
typeof(THandler), factory, ServiceLifetime.Scoped));
}
///
/// Configures the descriptor to use the specified singleton handler.
///
/// The handler type.
/// The builder instance, so that calls can be easily chained.
public Builder UseSingletonHandler()
where THandler : IOpenIddictServerHandler
=> SetServiceDescriptor(new ServiceDescriptor(
typeof(THandler), typeof(THandler), ServiceLifetime.Singleton));
///
/// Configures the descriptor to use the specified singleton handler.
///
/// The handler type.
/// The factory used to create the handler.
/// The builder instance, so that calls can be easily chained.
public Builder UseSingletonHandler(Func factory)
where THandler : IOpenIddictServerHandler
{
if (factory is null)
{
throw new ArgumentNullException(nameof(factory));
}
return SetServiceDescriptor(new ServiceDescriptor(
typeof(THandler), factory, ServiceLifetime.Singleton));
}
///
/// Configures the descriptor to use the specified singleton handler.
///
/// The handler type.
/// The handler instance.
/// The builder instance, so that calls can be easily chained.
public Builder UseSingletonHandler(THandler handler)
where THandler : IOpenIddictServerHandler
{
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
return SetServiceDescriptor(new ServiceDescriptor(typeof(THandler), handler));
}
///
/// Build a new descriptor instance, based on the parameters that were previously set.
///
/// The builder instance, so that calls can be easily chained.
public OpenIddictServerHandlerDescriptor Build() => new OpenIddictServerHandlerDescriptor
{
ContextType = typeof(TContext),
FilterTypes = _filters.ToImmutableArray(),
Order = _order,
ServiceDescriptor = _descriptor ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0105)),
Type = _type
};
}
}