diff --git a/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs b/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs index 6a6551f..67e9631 100644 --- a/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs +++ b/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs @@ -18,7 +18,7 @@ public static void GenerateSource(SourceProductionContext context, ImmutableSort context.CancellationToken.ThrowIfCancellationRequested(); var nonStaticClassNames = grouped.Keys - .Where(x => !x.IsStatic) + .Where(x => !x.IsStatic && !x.IsAbstract) .Select(x => x.Name) .ToList(); var source = new StringBuilder(); diff --git a/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs new file mode 100644 index 0000000..25c413a --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs @@ -0,0 +1,100 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string AcceptsAttributeName = "AcceptsAttribute"; + internal const string AcceptsAttributeHint = $"{AcceptsAttributeFullyQualifiedName}.gs.cs"; + + private const string AcceptsAttributeFullyQualifiedName = $"{AttributesNamespace}.{AcceptsAttributeName}"; + + internal static readonly SourceText AcceptsAttributeSourceText = CreateAcceptsAttributeSourceText(); + + private static SourceText CreateAcceptsAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies the request type and content types accepted by the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{AcceptsAttributeName}} : global::System.Attribute + { + /// + /// Gets the CLR type of the endpoint filter. + /// + public global::System.Type Type { get; } + + /// + /// Gets the primary content type accepted by the endpoint. + /// + public string ContentType { get; } + + /// + /// Gets the additional content types accepted by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Gets a value indicating whether the request body is optional. + /// + public bool IsOptional { get; init; } + + /// + /// Initializes a new instance of the class. + /// + /// The CLR type of the request body. + /// The primary content type accepted by the endpoint. + /// Additional content types accepted by the endpoint. + public {{AcceptsAttributeName}}(global::System.Type type, string contentType = "application/json", params string[] additionalContentTypes) + { + Type = type; + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes; + } + } + + /// + /// Specifies the request type using a generic argument and the content types accepted by the annotated endpoint or class. + /// + /// The CLR type of the request body. + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{AcceptsAttributeName}} : global::System.Attribute + { + /// + /// Gets the CLR type of the endpoint filter. + /// + public global::System.Type Type => typeof(TRequest); + + /// + /// Gets the primary content type accepted by the endpoint. + /// + public string ContentType { get; } + + /// + /// Gets the additional content types accepted by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Gets a value indicating whether the request body is optional. + /// + public bool IsOptional { get; init; } + + /// + /// Initializes a new instance of the generic Accepts attribute class. + /// + /// The primary content type accepted by the endpoint. + /// Additional content types accepted by the endpoint. + public {{AcceptsAttributeName}}(string contentType = "application/json", params string[] additionalContentTypes) + { + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs new file mode 100644 index 0000000..7973b79 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs @@ -0,0 +1,29 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string DisableAntiforgeryAttributeName = "DisableAntiforgeryAttribute"; + internal const string DisableAntiforgeryAttributeHint = $"{DisableAntiforgeryAttributeFullyQualifiedName}.gs.cs"; + + private const string DisableAntiforgeryAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableAntiforgeryAttributeName}"; + + internal static readonly SourceText DisableAntiforgeryAttributeSourceText = CreateDisableAntiforgeryAttributeSourceText(); + + private static SourceText CreateDisableAntiforgeryAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Disables antiforgery protection for the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{DisableAntiforgeryAttributeName}} : global::System.Attribute + { + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs new file mode 100644 index 0000000..99225ef --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs @@ -0,0 +1,29 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string DisableRequestTimeoutAttributeName = "DisableRequestTimeoutAttribute"; + internal const string DisableRequestTimeoutAttributeHint = $"{DisableRequestTimeoutAttributeFullyQualifiedName}.gs.cs"; + + private const string DisableRequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableRequestTimeoutAttributeName}"; + + internal static readonly SourceText DisableRequestTimeoutAttributeSourceText = CreateDisableRequestTimeoutAttributeSourceText(); + + private static SourceText CreateDisableRequestTimeoutAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Disables the request timeout for the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{DisableRequestTimeoutAttributeName}} : global::System.Attribute + { + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs new file mode 100644 index 0000000..f98194e --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs @@ -0,0 +1,31 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string DisableValidationAttributeName = "DisableValidationAttribute"; + internal const string DisableValidationAttributeHint = $"{DisableValidationAttributeFullyQualifiedName}.gs.cs"; + + private const string DisableValidationAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableValidationAttributeName}"; + + internal static readonly SourceText DisableValidationAttributeSourceText = CreateDisableValidationAttributeSourceText(); + + private static SourceText CreateDisableValidationAttributeSourceText() => SourceText.From($$""" + #if NET10_0_OR_GREATER + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Disables request validation for the annotated endpoint or class when targeting .NET 10 or later. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{DisableValidationAttributeName}} : global::System.Attribute + { + } + #endif + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs new file mode 100644 index 0000000..71fd661 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs @@ -0,0 +1,55 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string EndpointFilterAttributeName = "EndpointFilterAttribute"; + internal const string EndpointFilterAttributeHint = $"{EndpointFilterAttributeFullyQualifiedName}.gs.cs"; + + private const string EndpointFilterAttributeFullyQualifiedName = $"{AttributesNamespace}.{EndpointFilterAttributeName}"; + + internal static readonly SourceText EndpointFilterAttributeSourceText = CreateEndpointFilterAttributeSourceText(); + + private static SourceText CreateEndpointFilterAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies an endpoint filter type to apply to the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute + { + /// + /// Gets the CLR type of the endpoint filter. + /// + public global::System.Type Type { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The CLR type of the endpoint filter. + public {{EndpointFilterAttributeName}}(global::System.Type type) + { + Type = type; + } + } + + /// + /// Specifies an endpoint filter type using a generic argument. + /// + /// The CLR type of the endpoint filter. + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute + { + /// + /// Gets the CLR type of the endpoint filter. + /// + public global::System.Type Type => typeof(TFilter); + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs index 1b90733..9748a57 100644 --- a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs +++ b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs @@ -6,19 +6,6 @@ namespace GeneratedEndpoints.Common; internal static partial class Constants { - internal static readonly string FileHeader = $""" - //----------------------------------------------------------------------------- - // - // This code was generated by {nameof(MinimalApiGenerator)} which can be found - // in the {typeof(MinimalApiGenerator).Namespace} namespace. - // - // Changes to this file may cause incorrect behavior - // and will be lost if the code is regenerated. - // - //----------------------------------------------------------------------------- - - #nullable enable - """; internal static readonly ImmutableArray HttpAttributeDefinitions = [ @@ -38,632 +25,6 @@ internal static partial class Constants internal static readonly ImmutableDictionary HttpAttributeDefinitionsByName = HttpAttributeDefinitions.ToImmutableDictionary(static definition => definition.Name); - internal static readonly SourceText RequireAuthorizationAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies that authorization is required for the annotated endpoint or class. - /// Optionally restricts access to the specified authorization policies. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{RequireAuthorizationAttributeName}} : global::System.Attribute - { - /// - /// Gets the policy names that the endpoint or class requires. - /// - public string[] PolicyNames { get; } - - /// - /// Marks the endpoint or class as requiring authorization. - /// - public {{RequireAuthorizationAttributeName}}() - { - PolicyNames = []; - } - - /// - /// Marks the endpoint or class as requiring authorization with one or more policies. - /// - public {{RequireAuthorizationAttributeName}}(params string[] policyNames) - { - PolicyNames = policyNames ?? []; - } - } - """, Encoding.UTF8 - ); - - internal static readonly SourceText RequireCorsAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies that the annotated endpoint requires a configured CORS policy. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{RequireCorsAttributeName}} : global::System.Attribute - { - /// - /// Gets the optional CORS policy name. - /// - public string? PolicyName { get; } - - /// - /// Marks the endpoint or class as requiring the default CORS policy. - /// - public {{RequireCorsAttributeName}}() - { - } - - /// - /// Marks the endpoint or class as requiring the specified named CORS policy. - /// - public {{RequireCorsAttributeName}}(string policyName) - { - PolicyName = policyName; - } - } - """, Encoding.UTF8 - ); - - internal static readonly SourceText RequireRateLimitingAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies that the annotated endpoint requires the provided rate limiting policy. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{RequireRateLimitingAttributeName}} : global::System.Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The rate limiting policy to apply. - public {{RequireRateLimitingAttributeName}}(string policyName) - { - PolicyName = policyName; - } - - /// - /// Gets the rate limiting policy name. - /// - public string PolicyName { get; } - } - """, Encoding.UTF8 - ); - - internal static readonly SourceText RequireHostAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies the allowed hosts for the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{RequireHostAttributeName}} : global::System.Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The hosts that are allowed to access the endpoint. - public {{RequireHostAttributeName}}(params string[] hosts) - { - Hosts = hosts ?? []; - } - - /// - /// Gets the allowed hosts. - /// - public string[] Hosts { get; } - } - """, Encoding.UTF8 - ); - - internal static readonly SourceText DisableAntiforgeryAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Disables antiforgery protection for the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{DisableAntiforgeryAttributeName}} : global::System.Attribute - { - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText ShortCircuitAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Marks the annotated endpoint or class to short-circuit the request pipeline. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{ShortCircuitAttributeName}} : global::System.Attribute - { - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText DisableRequestTimeoutAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Disables the request timeout for the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{DisableRequestTimeoutAttributeName}} : global::System.Attribute - { - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText DisableValidationAttributeSourceText = SourceText.From($$""" - #if NET10_0_OR_GREATER - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Disables request validation for the annotated endpoint or class when targeting .NET 10 or later. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{DisableValidationAttributeName}} : global::System.Attribute - { - } - #endif - - """, Encoding.UTF8 - ); - - internal static readonly SourceText RequestTimeoutAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Applies the request timeout metadata to the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{RequestTimeoutAttributeName}} : global::System.Attribute - { - /// - /// Gets the optional request timeout policy name. - /// - public string? PolicyName { get; init; } - - /// - /// Applies the default request timeout behavior. - /// - public {{RequestTimeoutAttributeName}}() - { - } - - /// - /// Applies the specified request timeout policy. - /// - /// The request timeout policy name. - public {{RequestTimeoutAttributeName}}(string policyName) - { - PolicyName = policyName; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText OrderAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies the order for the annotated endpoint when building conventions. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{OrderAttributeName}} : global::System.Attribute - { - /// - /// Gets the order that will be applied to the endpoint. - /// - public int Order { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The order value to apply to the endpoint. - public {{OrderAttributeName}}(int order) - { - Order = order; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText MapGroupAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies the route group for the annotated class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - internal sealed class {{MapGroupAttributeName}} : global::System.Attribute - { - /// - /// Gets the route group pattern. - /// - public string Pattern { get; } - - /// - /// Gets or sets the endpoint group name. - /// - public string? Name { get; init; } - - /// - /// Initializes a new instance of the class. - /// - /// The route group pattern to apply. - public {{MapGroupAttributeName}}(string pattern) - { - Pattern = pattern; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText SummaryAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies the summary metadata for the annotated endpoint. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - internal sealed class {{SummaryAttributeName}} : global::System.Attribute - { - /// - /// Gets the summary value for the endpoint. - /// - public string Summary { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The summary to apply to the endpoint. - public {{SummaryAttributeName}}(string summary) - { - Summary = summary; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText AcceptsAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies the request type and content types accepted by the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{AcceptsAttributeName}} : global::System.Attribute - { - /// - /// Gets the CLR type of the endpoint filter. - /// - public global::System.Type Type { get; } - - /// - /// Gets the primary content type accepted by the endpoint. - /// - public string ContentType { get; } - - /// - /// Gets the additional content types accepted by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Gets a value indicating whether the request body is optional. - /// - public bool IsOptional { get; init; } - - /// - /// Initializes a new instance of the class. - /// - /// The CLR type of the request body. - /// The primary content type accepted by the endpoint. - /// Additional content types accepted by the endpoint. - public {{AcceptsAttributeName}}(global::System.Type type, string contentType = "application/json", params string[] additionalContentTypes) - { - Type = type; - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes; - } - } - - /// - /// Specifies the request type using a generic argument and the content types accepted by the annotated endpoint or class. - /// - /// The CLR type of the request body. - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{AcceptsAttributeName}} : global::System.Attribute - { - /// - /// Gets the CLR type of the endpoint filter. - /// - public global::System.Type Type => typeof(TRequest); - - /// - /// Gets the primary content type accepted by the endpoint. - /// - public string ContentType { get; } - - /// - /// Gets the additional content types accepted by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Gets a value indicating whether the request body is optional. - /// - public bool IsOptional { get; init; } - - /// - /// Initializes a new instance of the generic Accepts attribute class. - /// - /// The primary content type accepted by the endpoint. - /// Additional content types accepted by the endpoint. - public {{AcceptsAttributeName}}(string contentType = "application/json", params string[] additionalContentTypes) - { - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText EndpointFilterAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies an endpoint filter type to apply to the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute - { - /// - /// Gets the CLR type of the endpoint filter. - /// - public global::System.Type Type { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The CLR type of the endpoint filter. - public {{EndpointFilterAttributeName}}(global::System.Type type) - { - Type = type; - } - } - - /// - /// Specifies an endpoint filter type using a generic argument. - /// - /// The CLR type of the endpoint filter. - [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute - { - /// - /// Gets the CLR type of the endpoint filter. - /// - public global::System.Type Type => typeof(TFilter); - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText ProducesResponseAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies a response type, status code, and content types produced by the annotated endpoint or class. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute - { - /// - /// Gets the response type produced by the endpoint. - /// - public global::System.Type Type { get; } - - /// - /// Gets the HTTP status code returned by the endpoint. - /// - public int StatusCode { get; } - - /// - /// Gets the primary content type produced by the endpoint. - /// - public string? ContentType { get; } - - /// - /// Gets the additional content types produced by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The CLR type of the response body. - /// The HTTP status code returned by the endpoint. - /// The primary content type produced by the endpoint. - /// Additional content types produced by the endpoint. - public {{ProducesResponseAttributeName}}(global::System.Type type, int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes) - { - Type = type; - StatusCode = statusCode; - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes ?? []; - } - } - - /// - /// Specifies a response type using a generic argument along with status code and content types produced by the annotated endpoint or class. - /// - /// The CLR type of the response body. - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute - { - /// - /// Gets the response type produced by the endpoint. - /// - public global::System.Type Type => typeof(TResponse); - - /// - /// Gets the HTTP status code returned by the endpoint. - /// - public int StatusCode { get; } - - /// - /// Gets the primary content type produced by the endpoint. - /// - public string? ContentType { get; } - - /// - /// Gets the additional content types produced by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Initializes a new instance of the generic Produces attribute class. - /// - /// The HTTP status code returned by the endpoint. - /// The primary content type produced by the endpoint. - /// Additional content types produced by the endpoint. - public {{ProducesResponseAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes) - { - StatusCode = statusCode; - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes ?? []; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText ProducesProblemAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies that the endpoint produces a problem details payload. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{ProducesProblemAttributeName}} : global::System.Attribute - { - /// - /// Gets the HTTP status code returned by the endpoint. - /// - public int StatusCode { get; } - - /// - /// Gets the primary content type produced by the endpoint. - /// - public string? ContentType { get; } - - /// - /// Gets the additional content types produced by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The HTTP status code returned by the endpoint. - /// The primary content type produced by the endpoint. - /// Additional content types produced by the endpoint. - public {{ProducesProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError, string? contentType = null, params string[] additionalContentTypes) - { - StatusCode = statusCode; - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes ?? []; - } - } - - """, Encoding.UTF8 - ); - - internal static readonly SourceText ProducesValidationProblemAttributeSourceText = SourceText.From($$""" - {{FileHeader}} - - namespace {{AttributesNamespace}}; - - /// - /// Specifies that the endpoint produces a validation problem details payload. - /// - [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - internal sealed class {{ProducesValidationProblemAttributeName}} : global::System.Attribute - { - /// - /// Gets the HTTP status code returned by the endpoint. - /// - public int StatusCode { get; } - - /// - /// Gets the primary content type produced by the endpoint. - /// - public string? ContentType { get; } - - /// - /// Gets the additional content types produced by the endpoint. - /// - public string[] AdditionalContentTypes { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The HTTP status code returned by the endpoint. - /// The primary content type produced by the endpoint. - /// Additional content types produced by the endpoint. - public {{ProducesValidationProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status400BadRequest, string? contentType = null, params string[] additionalContentTypes) - { - StatusCode = statusCode; - ContentType = contentType; - AdditionalContentTypes = additionalContentTypes ?? []; - } - } - - """, Encoding.UTF8 - ); - private static HttpAttributeDefinition CreateHttpAttributeDefinition(string attributeName, string verb, bool allowOptionalPattern = false) { var fullyQualifiedName = $"{AttributesNamespace}.{attributeName}"; diff --git a/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs new file mode 100644 index 0000000..4b61e0b --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs @@ -0,0 +1,47 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string MapGroupAttributeName = "MapGroupAttribute"; + internal const string MapGroupAttributeHint = $"{MapGroupAttributeFullyQualifiedName}.gs.cs"; + + private const string MapGroupAttributeFullyQualifiedName = $"{AttributesNamespace}.{MapGroupAttributeName}"; + + internal static readonly SourceText MapGroupAttributeSourceText = CreateMapGroupAttributeSourceText(); + + private static SourceText CreateMapGroupAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies the route group for the annotated class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + internal sealed class {{MapGroupAttributeName}} : global::System.Attribute + { + /// + /// Gets the route group pattern. + /// + public string Pattern { get; } + + /// + /// Gets or sets the endpoint group name. + /// + public string? Name { get; init; } + + /// + /// Initializes a new instance of the class. + /// + /// The route group pattern to apply. + public {{MapGroupAttributeName}}(string pattern) + { + Pattern = pattern; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs new file mode 100644 index 0000000..a52e2fe --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs @@ -0,0 +1,42 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string OrderAttributeName = "OrderAttribute"; + internal const string OrderAttributeHint = $"{OrderAttributeFullyQualifiedName}.gs.cs"; + + private const string OrderAttributeFullyQualifiedName = $"{AttributesNamespace}.{OrderAttributeName}"; + + internal static readonly SourceText OrderAttributeSourceText = CreateOrderAttributeSourceText(); + + private static SourceText CreateOrderAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies the order for the annotated endpoint when building conventions. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{OrderAttributeName}} : global::System.Attribute + { + /// + /// Gets the order that will be applied to the endpoint. + /// + public int Order { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The order value to apply to the endpoint. + public {{OrderAttributeName}}(int order) + { + Order = order; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs new file mode 100644 index 0000000..80a6d5f --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs @@ -0,0 +1,56 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string ProducesProblemAttributeName = "ProducesProblemAttribute"; + internal const string ProducesProblemAttributeHint = $"{ProducesProblemAttributeFullyQualifiedName}.gs.cs"; + + private const string ProducesProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesProblemAttributeName}"; + + internal static readonly SourceText ProducesProblemAttributeSourceText = CreateProducesProblemAttributeSourceText(); + + private static SourceText CreateProducesProblemAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies that the endpoint produces a problem details payload. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{ProducesProblemAttributeName}} : global::System.Attribute + { + /// + /// Gets the HTTP status code returned by the endpoint. + /// + public int StatusCode { get; } + + /// + /// Gets the primary content type produced by the endpoint. + /// + public string? ContentType { get; } + + /// + /// Gets the additional content types produced by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP status code returned by the endpoint. + /// The primary content type produced by the endpoint. + /// Additional content types produced by the endpoint. + public {{ProducesProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError, string? contentType = null, params string[] additionalContentTypes) + { + StatusCode = statusCode; + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes ?? []; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs new file mode 100644 index 0000000..a7bee4b --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs @@ -0,0 +1,104 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string ProducesResponseAttributeName = "ProducesResponseAttribute"; + internal const string ProducesResponseAttributeHint = $"{ProducesResponseAttributeFullyQualifiedName}.gs.cs"; + + private const string ProducesResponseAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesResponseAttributeName}"; + + internal static readonly SourceText ProducesResponseAttributeSourceText = CreateProducesResponseAttributeSourceText(); + + private static SourceText CreateProducesResponseAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies a response type, status code, and content types produced by the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute + { + /// + /// Gets the response type produced by the endpoint. + /// + public global::System.Type Type { get; } + + /// + /// Gets the HTTP status code returned by the endpoint. + /// + public int StatusCode { get; } + + /// + /// Gets the primary content type produced by the endpoint. + /// + public string? ContentType { get; } + + /// + /// Gets the additional content types produced by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The CLR type of the response body. + /// The HTTP status code returned by the endpoint. + /// The primary content type produced by the endpoint. + /// Additional content types produced by the endpoint. + public {{ProducesResponseAttributeName}}(global::System.Type type, int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes) + { + Type = type; + StatusCode = statusCode; + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes ?? []; + } + } + + /// + /// Specifies a response type using a generic argument along with status code and content types produced by the annotated endpoint or class. + /// + /// The CLR type of the response body. + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute + { + /// + /// Gets the response type produced by the endpoint. + /// + public global::System.Type Type => typeof(TResponse); + + /// + /// Gets the HTTP status code returned by the endpoint. + /// + public int StatusCode { get; } + + /// + /// Gets the primary content type produced by the endpoint. + /// + public string? ContentType { get; } + + /// + /// Gets the additional content types produced by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Initializes a new instance of the generic Produces attribute class. + /// + /// The HTTP status code returned by the endpoint. + /// The primary content type produced by the endpoint. + /// Additional content types produced by the endpoint. + public {{ProducesResponseAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes) + { + StatusCode = statusCode; + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes ?? []; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs new file mode 100644 index 0000000..784e5c0 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs @@ -0,0 +1,56 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string ProducesValidationProblemAttributeName = "ProducesValidationProblemAttribute"; + internal const string ProducesValidationProblemAttributeHint = $"{ProducesValidationProblemAttributeFullyQualifiedName}.gs.cs"; + + private const string ProducesValidationProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesValidationProblemAttributeName}"; + + internal static readonly SourceText ProducesValidationProblemAttributeSourceText = CreateProducesValidationProblemAttributeSourceText(); + + private static SourceText CreateProducesValidationProblemAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies that the endpoint produces a validation problem details payload. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class {{ProducesValidationProblemAttributeName}} : global::System.Attribute + { + /// + /// Gets the HTTP status code returned by the endpoint. + /// + public int StatusCode { get; } + + /// + /// Gets the primary content type produced by the endpoint. + /// + public string? ContentType { get; } + + /// + /// Gets the additional content types produced by the endpoint. + /// + public string[] AdditionalContentTypes { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP status code returned by the endpoint. + /// The primary content type produced by the endpoint. + /// Additional content types produced by the endpoint. + public {{ProducesValidationProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status400BadRequest, string? contentType = null, params string[] additionalContentTypes) + { + StatusCode = statusCode; + ContentType = contentType; + AdditionalContentTypes = additionalContentTypes ?? []; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs new file mode 100644 index 0000000..7c7831e --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs @@ -0,0 +1,49 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string RequestTimeoutAttributeName = "RequestTimeoutAttribute"; + internal const string RequestTimeoutAttributeHint = $"{RequestTimeoutAttributeFullyQualifiedName}.gs.cs"; + + private const string RequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequestTimeoutAttributeName}"; + + internal static readonly SourceText RequestTimeoutAttributeSourceText = CreateRequestTimeoutAttributeSourceText(); + + private static SourceText CreateRequestTimeoutAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Applies the request timeout metadata to the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{RequestTimeoutAttributeName}} : global::System.Attribute + { + /// + /// Gets the optional request timeout policy name. + /// + public string? PolicyName { get; init; } + + /// + /// Applies the default request timeout behavior. + /// + public {{RequestTimeoutAttributeName}}() + { + } + + /// + /// Applies the specified request timeout policy. + /// + /// The request timeout policy name. + public {{RequestTimeoutAttributeName}}(string policyName) + { + PolicyName = policyName; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs new file mode 100644 index 0000000..bc54ccc --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs @@ -0,0 +1,49 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string RequireAuthorizationAttributeName = "RequireAuthorizationAttribute"; + internal const string RequireAuthorizationAttributeHint = $"{RequireAuthorizationAttributeFullyQualifiedName}.gs.cs"; + + private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}"; + + internal static readonly SourceText RequireAuthorizationAttributeSourceText = CreateRequireAuthorizationAttributeSourceText(); + + private static SourceText CreateRequireAuthorizationAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies that authorization is required for the annotated endpoint or class. + /// Optionally restricts access to the specified authorization policies. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{RequireAuthorizationAttributeName}} : global::System.Attribute + { + /// + /// Gets the policy names that the endpoint or class requires. + /// + public string[] PolicyNames { get; } + + /// + /// Marks the endpoint or class as requiring authorization. + /// + public {{RequireAuthorizationAttributeName}}() + { + PolicyNames = []; + } + + /// + /// Marks the endpoint or class as requiring authorization with one or more policies. + /// + public {{RequireAuthorizationAttributeName}}(params string[] policyNames) + { + PolicyNames = policyNames ?? []; + } + } + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs new file mode 100644 index 0000000..52fd3e8 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs @@ -0,0 +1,47 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string RequireCorsAttributeName = "RequireCorsAttribute"; + internal const string RequireCorsAttributeHint = $"{RequireCorsAttributeFullyQualifiedName}.gs.cs"; + + private const string RequireCorsAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireCorsAttributeName}"; + + internal static readonly SourceText RequireCorsAttributeSourceText = CreateRequireCorsAttributeSourceText(); + + private static SourceText CreateRequireCorsAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies that the annotated endpoint requires a configured CORS policy. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{RequireCorsAttributeName}} : global::System.Attribute + { + /// + /// Gets the optional CORS policy name. + /// + public string? PolicyName { get; } + + /// + /// Marks the endpoint or class as requiring the default CORS policy. + /// + public {{RequireCorsAttributeName}}() + { + } + + /// + /// Marks the endpoint or class as requiring the specified named CORS policy. + /// + public {{RequireCorsAttributeName}}(string policyName) + { + PolicyName = policyName; + } + } + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs new file mode 100644 index 0000000..7a040a9 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs @@ -0,0 +1,41 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string RequireHostAttributeName = "RequireHostAttribute"; + internal const string RequireHostAttributeHint = $"{RequireHostAttributeFullyQualifiedName}.gs.cs"; + + private const string RequireHostAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireHostAttributeName}"; + + internal static readonly SourceText RequireHostAttributeSourceText = CreateRequireHostAttributeSourceText(); + + private static SourceText CreateRequireHostAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies the allowed hosts for the annotated endpoint or class. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{RequireHostAttributeName}} : global::System.Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The hosts that are allowed to access the endpoint. + public {{RequireHostAttributeName}}(params string[] hosts) + { + Hosts = hosts ?? []; + } + + /// + /// Gets the allowed hosts. + /// + public string[] Hosts { get; } + } + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs new file mode 100644 index 0000000..fa4f88d --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs @@ -0,0 +1,41 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string RequireRateLimitingAttributeName = "RequireRateLimitingAttribute"; + internal const string RequireRateLimitingAttributeHint = $"{RequireRateLimitingAttributeFullyQualifiedName}.gs.cs"; + + private const string RequireRateLimitingAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireRateLimitingAttributeName}"; + + internal static readonly SourceText RequireRateLimitingAttributeSourceText = CreateRequireRateLimitingAttributeSourceText(); + + private static SourceText CreateRequireRateLimitingAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies that the annotated endpoint requires the provided rate limiting policy. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{RequireRateLimitingAttributeName}} : global::System.Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The rate limiting policy to apply. + public {{RequireRateLimitingAttributeName}}(string policyName) + { + PolicyName = policyName; + } + + /// + /// Gets the rate limiting policy name. + /// + public string PolicyName { get; } + } + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs new file mode 100644 index 0000000..37e2b5c --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs @@ -0,0 +1,29 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string ShortCircuitAttributeName = "ShortCircuitAttribute"; + internal const string ShortCircuitAttributeHint = $"{ShortCircuitAttributeFullyQualifiedName}.gs.cs"; + + private const string ShortCircuitAttributeFullyQualifiedName = $"{AttributesNamespace}.{ShortCircuitAttributeName}"; + + internal static readonly SourceText ShortCircuitAttributeSourceText = CreateShortCircuitAttributeSourceText(); + + private static SourceText CreateShortCircuitAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Marks the annotated endpoint or class to short-circuit the request pipeline. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{ShortCircuitAttributeName}} : global::System.Attribute + { + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs new file mode 100644 index 0000000..ab45af4 --- /dev/null +++ b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs @@ -0,0 +1,42 @@ +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace GeneratedEndpoints.Common; + +internal static partial class Constants +{ + internal const string SummaryAttributeName = "SummaryAttribute"; + internal const string SummaryAttributeHint = $"{SummaryAttributeFullyQualifiedName}.gs.cs"; + + private const string SummaryAttributeFullyQualifiedName = $"{AttributesNamespace}.{SummaryAttributeName}"; + + internal static readonly SourceText SummaryAttributeSourceText = CreateSummaryAttributeSourceText(); + + private static SourceText CreateSummaryAttributeSourceText() => SourceText.From($$""" + {{FileHeader}} + + namespace {{AttributesNamespace}}; + + /// + /// Specifies the summary metadata for the annotated endpoint. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class {{SummaryAttributeName}} : global::System.Attribute + { + /// + /// Gets the summary value for the endpoint. + /// + public string Summary { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The summary to apply to the endpoint. + public {{SummaryAttributeName}}(string summary) + { + Summary = summary; + } + } + + """, Encoding.UTF8); +} diff --git a/src/GeneratedEndpoints/Common/Constants.cs b/src/GeneratedEndpoints/Common/Constants.cs index d7cb149..b170d78 100644 --- a/src/GeneratedEndpoints/Common/Constants.cs +++ b/src/GeneratedEndpoints/Common/Constants.cs @@ -2,62 +2,24 @@ namespace GeneratedEndpoints.Common; internal static partial class Constants { + internal const string FileHeader = + "//-----------------------------------------------------------------------------\n" + + "// \n" + + "// This code was generated by MinimalApiGenerator which can be found\n" + + "// in the GeneratedEndpoints namespace.\n" + + "//\n" + + "// Changes to this file may cause incorrect behavior\n" + + "// and will be lost if the code is regenerated.\n" + + "// \n" + + "//-----------------------------------------------------------------------------\n" + + "\n" + + "#nullable enable"; + internal const string FallbackHttpMethod = "__FALLBACK__"; internal const string NameAttributeNamedParameter = "Name"; internal const string IsOptionalAttributeNamedParameter = "IsOptional"; - internal const string RequireAuthorizationAttributeName = "RequireAuthorizationAttribute"; - internal const string RequireAuthorizationAttributeHint = $"{RequireAuthorizationAttributeFullyQualifiedName}.gs.cs"; - - internal const string RequireCorsAttributeName = "RequireCorsAttribute"; - internal const string RequireCorsAttributeHint = $"{RequireCorsAttributeFullyQualifiedName}.gs.cs"; - - internal const string RequireRateLimitingAttributeName = "RequireRateLimitingAttribute"; - internal const string RequireRateLimitingAttributeHint = $"{RequireRateLimitingAttributeFullyQualifiedName}.gs.cs"; - - internal const string RequireHostAttributeName = "RequireHostAttribute"; - internal const string RequireHostAttributeHint = $"{RequireHostAttributeFullyQualifiedName}.gs.cs"; - - internal const string DisableAntiforgeryAttributeName = "DisableAntiforgeryAttribute"; - internal const string DisableAntiforgeryAttributeHint = $"{DisableAntiforgeryAttributeFullyQualifiedName}.gs.cs"; - - internal const string ShortCircuitAttributeName = "ShortCircuitAttribute"; - internal const string ShortCircuitAttributeHint = $"{ShortCircuitAttributeFullyQualifiedName}.gs.cs"; - - internal const string DisableRequestTimeoutAttributeName = "DisableRequestTimeoutAttribute"; - internal const string DisableRequestTimeoutAttributeHint = $"{DisableRequestTimeoutAttributeFullyQualifiedName}.gs.cs"; - - internal const string DisableValidationAttributeName = "DisableValidationAttribute"; - internal const string DisableValidationAttributeHint = $"{DisableValidationAttributeFullyQualifiedName}.gs.cs"; - - internal const string RequestTimeoutAttributeName = "RequestTimeoutAttribute"; - internal const string RequestTimeoutAttributeHint = $"{RequestTimeoutAttributeFullyQualifiedName}.gs.cs"; - - internal const string OrderAttributeName = "OrderAttribute"; - internal const string OrderAttributeHint = $"{OrderAttributeFullyQualifiedName}.gs.cs"; - - internal const string MapGroupAttributeName = "MapGroupAttribute"; - internal const string MapGroupAttributeHint = $"{MapGroupAttributeFullyQualifiedName}.gs.cs"; - - internal const string SummaryAttributeName = "SummaryAttribute"; - internal const string SummaryAttributeHint = $"{SummaryAttributeFullyQualifiedName}.gs.cs"; - - internal const string EndpointFilterAttributeName = "EndpointFilterAttribute"; - internal const string EndpointFilterAttributeHint = $"{EndpointFilterAttributeFullyQualifiedName}.gs.cs"; - - internal const string AcceptsAttributeName = "AcceptsAttribute"; - internal const string AcceptsAttributeHint = $"{AcceptsAttributeFullyQualifiedName}.gs.cs"; - - internal const string ProducesResponseAttributeName = "ProducesResponseAttribute"; - internal const string ProducesResponseAttributeHint = $"{ProducesResponseAttributeFullyQualifiedName}.gs.cs"; - - internal const string ProducesProblemAttributeName = "ProducesProblemAttribute"; - internal const string ProducesProblemAttributeHint = $"{ProducesProblemAttributeFullyQualifiedName}.gs.cs"; - - internal const string ProducesValidationProblemAttributeName = "ProducesValidationProblemAttribute"; - internal const string ProducesValidationProblemAttributeHint = $"{ProducesValidationProblemAttributeFullyQualifiedName}.gs.cs"; - internal const string RoutingNamespace = $"{BaseNamespace}.Routing"; internal const string AddEndpointHandlersClassName = "EndpointServicesExtensions"; @@ -74,24 +36,7 @@ internal static partial class Constants internal const string GlobalPrefix = "global::"; private const string BaseNamespace = "Microsoft.AspNetCore.Generated"; - private const string AttributesNamespace = $"{BaseNamespace}.Attributes"; - private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}"; - private const string RequireCorsAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireCorsAttributeName}"; - private const string RequireRateLimitingAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireRateLimitingAttributeName}"; - private const string RequireHostAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireHostAttributeName}"; - private const string DisableAntiforgeryAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableAntiforgeryAttributeName}"; - private const string ShortCircuitAttributeFullyQualifiedName = $"{AttributesNamespace}.{ShortCircuitAttributeName}"; - private const string DisableRequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableRequestTimeoutAttributeName}"; - private const string DisableValidationAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableValidationAttributeName}"; - private const string RequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequestTimeoutAttributeName}"; - private const string OrderAttributeFullyQualifiedName = $"{AttributesNamespace}.{OrderAttributeName}"; - private const string MapGroupAttributeFullyQualifiedName = $"{AttributesNamespace}.{MapGroupAttributeName}"; - private const string SummaryAttributeFullyQualifiedName = $"{AttributesNamespace}.{SummaryAttributeName}"; - private const string EndpointFilterAttributeFullyQualifiedName = $"{AttributesNamespace}.{EndpointFilterAttributeName}"; - private const string AcceptsAttributeFullyQualifiedName = $"{AttributesNamespace}.{AcceptsAttributeName}"; - private const string ProducesResponseAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesResponseAttributeName}"; - private const string ProducesProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesProblemAttributeName}"; - private const string ProducesValidationProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesValidationProblemAttributeName}"; + internal const string AttributesNamespace = $"{BaseNamespace}.Attributes"; private const string AddEndpointHandlersMethodFullyQualifiedName = $"{RoutingNamespace}.{AddEndpointHandlersMethodName}"; private const string UseEndpointHandlersMethodFullyQualifiedName = $"{RoutingNamespace}.{UseEndpointHandlersMethodName}"; } diff --git a/src/GeneratedEndpoints/Common/DeclarationExtensions.cs b/src/GeneratedEndpoints/Common/DeclarationExtensions.cs index aa020a6..5b65c18 100644 --- a/src/GeneratedEndpoints/Common/DeclarationExtensions.cs +++ b/src/GeneratedEndpoints/Common/DeclarationExtensions.cs @@ -1,5 +1,3 @@ -using System.Text; - namespace GeneratedEndpoints.Common; /// Provides extension methods for working with declarations. @@ -10,7 +8,7 @@ internal static class DeclarationExtensions /// The namespace represented by the declarations. public static string ToNamespace(this EquatableImmutableArray declarations) { - var builder = new StringBuilder(); + var builder = StringBuilderPool.Get(); for (var index = 0; index < declarations.Count; index++) { @@ -23,7 +21,7 @@ public static string ToNamespace(this EquatableImmutableArray decla builder.Append(declaration.Name); } - return builder.ToString(); + return StringBuilderPool.ToStringAndReturn(builder); } /// Converts a list of declarations to their fully qualified name. @@ -31,7 +29,7 @@ public static string ToNamespace(this EquatableImmutableArray decla /// The fully qualified name represented by the declarations. public static string ToFullyQualifiedName(this EquatableImmutableArray declarations) { - var builder = new StringBuilder(); + var builder = StringBuilderPool.Get(); for (var index = 0; index < declarations.Count; index++) { @@ -48,7 +46,7 @@ public static string ToFullyQualifiedName(this EquatableImmutableArray Where(this EquatableImmutableArray declarations, Func predicate) diff --git a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs index f8dccd5..a03f476 100644 --- a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs +++ b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs @@ -170,12 +170,7 @@ public static EndpointConfiguration Create(ISymbol symbol) RequestTimeoutPolicyName = requestTimeoutPolicyName, Order = order, Group = groupIdentifier is not null && groupPattern is not null - ? new EndpointGroup - { - Identifier = groupIdentifier, - Pattern = groupPattern, - Name = groupName, - } + ? new EndpointGroup { Identifier = groupIdentifier, Pattern = groupPattern, Name = groupName } : null, }; } @@ -207,8 +202,10 @@ public static EndpointConfiguration Create(ISymbol symbol) private static void TryAddAcceptsMetadata(AttributeData attribute, INamedTypeSymbol attributeClass, ref List? accepts) { + var isGenericAttribute = attributeClass is { IsGenericType: true, TypeArguments.Length: 1 }; + string? requestType; - if (attributeClass is { IsGenericType: true, TypeArguments.Length: 1 }) + if (isGenericAttribute) requestType = attributeClass.TypeArguments[0] .ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); else @@ -218,8 +215,11 @@ private static void TryAddAcceptsMetadata(AttributeData attribute, INamedTypeSym if (requestType is null) return; - var contentType = attribute.GetConstructorStringValue() ?? ApplicationJsonContentType; - var additionalContentTypes = attribute.GetConstructorStringArray(1); + var contentTypeIndex = isGenericAttribute ? 0 : 1; + var additionalContentTypesIndex = isGenericAttribute ? 1 : 2; + + var contentType = attribute.GetConstructorStringValue(contentTypeIndex) ?? ApplicationJsonContentType; + var additionalContentTypes = attribute.GetConstructorStringArray(additionalContentTypesIndex); var isOptional = attribute.GetNamedBoolValue(IsOptionalAttributeNamedParameter); var acceptMetadata = new AcceptsMetadata(requestType, contentType, additionalContentTypes, isOptional); @@ -235,7 +235,7 @@ private static void TryAddProducesMetadata(AttributeData attribute, INamedTypeSy { var responseType = attributeClass.TypeArguments[0] .ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var statusCode = attribute.GetConstructorIntValue(0) ?? 200; + var statusCode = attribute.GetConstructorIntValue() ?? 200; var contentType = attribute.GetConstructorStringValue(1); var additionalContentTypes = attribute.GetConstructorStringArray(2); producesMetadata = new ProducesMetadata(responseType, statusCode, contentType, additionalContentTypes); diff --git a/src/GeneratedEndpoints/Common/RequestHandlerClass.cs b/src/GeneratedEndpoints/Common/RequestHandlerClass.cs index e3c8336..17ac27a 100644 --- a/src/GeneratedEndpoints/Common/RequestHandlerClass.cs +++ b/src/GeneratedEndpoints/Common/RequestHandlerClass.cs @@ -4,6 +4,7 @@ namespace GeneratedEndpoints.Common; { public required string Name { get; init; } public required bool IsStatic { get; init; } + public required bool IsAbstract { get; init; } public required bool HasConfigureMethod { get; init; } public required bool ConfigureMethodAcceptsServiceProvider { get; init; } public required EndpointConfiguration Configuration { get; init; } diff --git a/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs b/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs index 806a254..08e6d93 100644 --- a/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs +++ b/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs @@ -15,6 +15,7 @@ internal static class RequestHandlerClassHelper var name = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var isStatic = classSymbol.IsStatic; + var isAbstract = classSymbol.IsAbstract; var configureMethodDetails = GetConfigureMethodDetails(classSymbol, cancellationToken); var classConfiguration = EndpointConfigurationFactory.Create(classSymbol); @@ -22,6 +23,7 @@ internal static class RequestHandlerClassHelper { Name = name, IsStatic = isStatic, + IsAbstract = isAbstract, HasConfigureMethod = configureMethodDetails.HasConfigureMethod, ConfigureMethodAcceptsServiceProvider = configureMethodDetails.ConfigureMethodAcceptsServiceProvider, Configuration = classConfiguration, diff --git a/src/GeneratedEndpoints/GeneratedEndpoints.csproj b/src/GeneratedEndpoints/GeneratedEndpoints.csproj index efddba0..a5c4bae 100644 --- a/src/GeneratedEndpoints/GeneratedEndpoints.csproj +++ b/src/GeneratedEndpoints/GeneratedEndpoints.csproj @@ -27,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -36,9 +36,9 @@ GeneratedEndpoints - 10.0.2 - 10.0.2.0 - 10.0.2.0 + 10.0.3 + 10.0.3.0 + 10.0.3.0 en-US false diff --git a/src/GeneratedEndpoints/MinimalApiGenerator.cs b/src/GeneratedEndpoints/MinimalApiGenerator.cs index 873acfd..b6051b7 100644 --- a/src/GeneratedEndpoints/MinimalApiGenerator.cs +++ b/src/GeneratedEndpoints/MinimalApiGenerator.cs @@ -105,6 +105,9 @@ private static bool RequestHandlerFilter(SyntaxNode syntaxNode, CancellationToke var requestHandlerMethod = RequestHandlerMethodHelper.Create(methodSymbol, cancellationToken); + if (requestHandlerClass.Value.IsAbstract && !requestHandlerMethod.IsStatic) + return null; + var (httpMethod, pattern, name) = GetRequestHandlerAttribute(methodSymbol, attribute, cancellationToken); var requestHandler = new RequestHandler diff --git a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs index 035ce8d..5611197 100644 --- a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs +++ b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs @@ -29,6 +29,20 @@ public static string BuildFallbackSource(bool includeDefault, bool includeCustom return builder.ToString(); } + public static string BuildAbstractEndpointsSource() + { + return """ + internal abstract class AbstractEndpoints + { + [MapGet("/abstract/static")] + public static Ok Static() => TypedResults.Ok(); + + [MapPost("/abstract/instance")] + public Ok Instance() => TypedResults.Ok(); + } + """; + } + public static string BuildAuthorizationMatrixSource( bool classAllowAnonymous, bool methodAllowAnonymous, @@ -370,7 +384,7 @@ public static string BuildContractsAndBindingSource( if (includeAccepts) { var secondContentType = string.IsNullOrWhiteSpace(acceptsContentType2) ? "" : $", \"{acceptsContentType2}\""; - builder.AppendLine($" [Accepts(\"{acceptsContentType1 ?? "application/json"}\"{secondContentType})]"); + builder.AppendLine($" [Accepts(typeof(RequestRecord), \"{acceptsContentType1 ?? "application/json"}\"{secondContentType})]"); } if (includeGenericAccepts) diff --git a/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj b/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj index 1bd4374..8d28c01 100644 --- a/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj +++ b/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj @@ -14,7 +14,7 @@ - + all diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt index c9e81bb..33f7099 100644 --- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt +++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt @@ -26,6 +26,7 @@ internal static class EndpointRouteBuilderExtensions .WithName("Handle") .WithDisplayName("Contract endpoint") .WithTags("Contracts", "Bindings") + .Accepts("application/custom") .Produces(200, "application/problem+json") .ProducesProblem(500, "application/problem+json") .AllowAnonymous(); diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt index d535349..ee24747 100644 --- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt +++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt @@ -26,6 +26,7 @@ internal static class EndpointRouteBuilderExtensions .WithName("Handle") .WithDisplayName("Contract endpoint") .ExcludeFromDescription() + .Accepts("application/xml") .Produces(200, "application/json") .ProducesProblem(500, "application/json") .RequireAuthorization("ContractsPolicy"); diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt index 74ba401..0930bfa 100644 --- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt +++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt @@ -28,6 +28,7 @@ internal static class EndpointRouteBuilderExtensions .WithSummary("Gets detailed content.") .WithDescription("Shows binding and contract combinations.") .WithTags("Contracts", "Bindings") + .Accepts("application/xml", "text/xml") .Accepts("application/xml") .Produces(200, "application/json", "text/json") .Produces(200, "application/json") diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt new file mode 100644 index 0000000..e27630b --- /dev/null +++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// +// This code was generated by MinimalApiGenerator which can be found +// in the GeneratedEndpoints namespace. +// +// Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated. +// +//----------------------------------------------------------------------------- + +#nullable enable + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.AspNetCore.Generated.Routing; + +internal static class EndpointServicesExtensions +{ + internal static void AddEndpointHandlers(this IServiceCollection services) + { + } +} diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt new file mode 100644 index 0000000..eacb7f5 --- /dev/null +++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// +// This code was generated by MinimalApiGenerator which can be found +// in the GeneratedEndpoints namespace. +// +// Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated. +// +//----------------------------------------------------------------------------- + +#nullable enable + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Generated.Routing; + +internal static class EndpointRouteBuilderExtensions +{ + internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder) + { + builder.MapGet("/abstract/static", global::GeneratedEndpointsTests.AbstractEndpoints.Static) + .WithName("Static"); + + return builder; + } +} diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt index d8a37e9..be1b5fd 100644 --- a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt +++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt @@ -23,7 +23,8 @@ internal static class EndpointRouteBuilderExtensions internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder) { builder.MapGet("/contracts/{id:int}", global::GeneratedEndpointsTests.ContractEndpoints.Handle) - .WithName("Handle"); + .WithName("Handle") + .Accepts("application/custom"); return builder; } diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt index d8a37e9..104e71d 100644 --- a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt +++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt @@ -23,7 +23,8 @@ internal static class EndpointRouteBuilderExtensions internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder) { builder.MapGet("/contracts/{id:int}", global::GeneratedEndpointsTests.ContractEndpoints.Handle) - .WithName("Handle"); + .WithName("Handle") + .Accepts("application/json", "text/json"); return builder; } diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.cs b/tests/GeneratedEndpoints.Tests/IndividualTests.cs index bd0d6c3..4cf7e63 100644 --- a/tests/GeneratedEndpoints.Tests/IndividualTests.cs +++ b/tests/GeneratedEndpoints.Tests/IndividualTests.cs @@ -172,6 +172,13 @@ public async Task OrderMetadata() await VerifyIndividualAsync(source, nameof(OrderMetadata)); } + [Fact] + public async Task AbstractClassSkipsInstanceHandlers() + { + var source = AbstractEndpoints(); + await VerifyIndividualAsync(source, nameof(AbstractClassSkipsInstanceHandlers)); + } + [Fact] public async Task ClassMapGroup() { @@ -472,6 +479,11 @@ private static string FallbackScenario(bool includeDefault = false, bool include return SourceFactory.BuildFallbackSource(includeDefault, includeCustom, customRoute); } + private static string AbstractEndpoints() + { + return SourceFactory.BuildAbstractEndpointsSource(); + } + private static string AuthorizationScenario( bool classAllowAnonymous = false, bool methodAllowAnonymous = false,