├── tests ├── Reprise.Benchmarks.Api │ ├── Global.cs │ ├── appsettings.json │ ├── Reprise.Benchmarks.Api.csproj │ ├── Properties │ │ └── launchSettings.json │ └── Program.cs ├── Reprise.UnitTests │ ├── Features │ │ ├── Endpoints │ │ │ ├── EndpointTypeProcessorTests.Process.verified.txt │ │ │ ├── EndpointMapperTests.Add.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_EmptyRoute.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_EmptyHttpMethod.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_NoHandleMethod.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_MultipleHandleMethods.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_NoMethodRouteAttribute.verified.txt │ │ │ ├── EndpointMapperTests.MapEndpoints_DuplicateRoute.verified.txt │ │ │ └── EndpointMapperTests.MapEndpoints_MultipleMethodRouteAttributes.verified.txt │ │ ├── ExceptionHandling │ │ │ ├── DefaultExceptionLoggerTests.Log_ValidationException.verified.txt │ │ │ ├── ErrorContextTests.Request.verified.txt │ │ │ ├── ErrorContextTests.TraceIdentifier.verified.txt │ │ │ ├── ExceptionHandlerTests.InvokeAsync.verified.txt │ │ │ ├── DefaultExceptionLoggerTests.Log_Exception.verified.txt │ │ │ ├── ExceptionLoggerTypeProcessorTests.Process_DuplicateExceptionLogger.verified.txt │ │ │ ├── ErrorResponseFactoryTypeProcessorTests.Process_DuplicateErrorResponseFactory.verified.txt │ │ │ ├── DefaultExceptionLoggerTests.Log_BadHttpRequestException.verified.txt │ │ │ ├── ExceptionHandlerTests.Ctor_NoExceptionLogger.verified.txt │ │ │ ├── ExceptionHandlerTests.Ctor_NoErrorResponseFactory.verified.txt │ │ │ ├── ExceptionHandlerTests.InvokeAsync_Exception.verified.txt │ │ │ ├── ExceptionHandlerTests.InvokeAsync_ValidationException.verified.txt │ │ │ └── DefaultErrorResponseFactoryTests.cs │ │ ├── OpenApi │ │ │ ├── Name │ │ │ │ ├── NameProcessorTests.Process_EmptyName.verified.txt │ │ │ │ ├── NameProcessorTests.Process_Duplicate.verified.txt │ │ │ │ ├── NameProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt │ │ │ │ ├── NameProcessorTests.Process.DotNet6_0.verified.txt │ │ │ │ ├── NameProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt │ │ │ │ └── NameProcessorTests.Process.DotNet7_0.verified.txt │ │ │ ├── Tags │ │ │ │ ├── TagsProcessorTests.Process_EmptyTag.verified.txt │ │ │ │ ├── TagsProcessorTests.Process.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route= .DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-{id}.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route={id}.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api-{id}.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-users.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api-users.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-users-{id}.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-{id}-users.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api-users-{id}.DotNet6_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route= .DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-{id}.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route={id}.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api-{id}.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-users.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-api-users.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-users-{id}.DotNet7_0.verified.txt │ │ │ │ ├── TagsProcessorTests.Process_NoAttribute_route=-{id}-users.DotNet7_0.verified.txt │ │ │ │ └── TagsProcessorTests.Process_NoAttribute_route=-api-users-{id}.DotNet7_0.verified.txt │ │ │ ├── Produces │ │ │ │ ├── ProducesProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt │ │ │ │ ├── ProducesProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt │ │ │ │ └── ProducesProcessorTests.Process.DotNet6_0.verified.txt │ │ │ └── ExcludeFromDescription │ │ │ │ ├── ExcludeFromDescriptionProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt │ │ │ │ ├── ExcludeFromDescriptionProcessorTests.Process.DotNet6_0.verified.txt │ │ │ │ ├── ExcludeFromDescriptionProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt │ │ │ │ └── ExcludeFromDescriptionProcessorTests.Process.DotNet7_0.verified.txt │ │ ├── Events │ │ │ └── EventBusTests │ │ │ │ ├── Publish.PayloadNull.verified.txt │ │ │ │ ├── PublishAndWait.PayloadNull.verified.txt │ │ │ │ ├── Publish.MultipleHandlers.verified.txt │ │ │ │ └── PublishAndWait.MultipleHandlers.verified.txt │ │ ├── Filters │ │ │ ├── EndpointFilterTests.EndpointFilter_NoImplementation.verified.txt │ │ │ ├── ValidationFilterTests.NullableTypeNull.verified.txt │ │ │ ├── EndpointFilterTests.EndpointFilter.verified.txt │ │ │ ├── EndpointFilterTests.GlobalEndpointFilters.verified.txt │ │ │ ├── EndpointFilterTests.GlobalEndpointFilters_EndpointFilter.verified.txt │ │ │ ├── EndpointFilterTests.GlobalEndpointFilters_EndpointMultipleFilters.verified.txt │ │ │ ├── ValidationFilterTests.NoValidator.verified.txt │ │ │ ├── ValidationFilterTests.NullableType.verified.txt │ │ │ ├── ValidationFilterTests.ValidRequest.verified.txt │ │ │ ├── ValidationFilterTests.MultipleTypes.verified.txt │ │ │ ├── ValidationFilterTests.InvalidRequest.verified.txt │ │ │ ├── ValidationFilterTests.NullableTypeInvalidRequest.verified.txt │ │ │ └── ValidationFilterTests.MultipleTypesInvalidRequest.verified.txt │ │ ├── Configurations │ │ │ ├── ConfigurationTypeProcessorTests.Process_EmptySubSectionKey.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_InvalidSubSectionKey.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_DuplicateConfiguration.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_MissingProperty.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_ComplexMissingProperty.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_NoParameterlessCtor.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_InvalidValue.DotNet6_0.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_ComplexInvalidValue.DotNet6_0.verified.txt │ │ │ ├── ConfigurationTypeProcessorTests.Process_InvalidValue.DotNet7_0.verified.txt │ │ │ └── ConfigurationTypeProcessorTests.Process_ComplexInvalidValue.DotNet7_0.verified.txt │ │ ├── Jobs │ │ │ ├── JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobNoTriggers.verified.txt │ │ │ ├── JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobEmptyCron.verified.txt │ │ │ ├── JobRunnerTests │ │ │ │ ├── StopAsync.CallsTaskRunner.verified.txt │ │ │ │ ├── StopAsync.cs │ │ │ │ └── StartAsync.BeforeStartJobs.verified.txt │ │ │ ├── DateTimeProviderTests.cs │ │ │ ├── JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobInvalidCron.verified.txt │ │ │ └── JobStateRegistryTests.cs │ │ ├── Validation │ │ │ └── ValidatorTypeProcessorTests.Process_DuplicateValidator.verified.txt │ │ ├── Mappers │ │ │ ├── MapperTypeProcessorTests.Process_DuplicateMapper.verified.txt │ │ │ └── MapperTypeProcessorTests.Process_DuplicateMirrorMapper.verified.txt │ │ ├── Authorization │ │ │ ├── AuthorizationProcessorTests.Process_NoAuthorization.DotNet6_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process.DotNet6_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process_PolicyName.DotNet6_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process_NoAuthorization.DotNet7_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process.DotNet7_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process_MultiplePolicyNames.DotNet6_0.verified.txt │ │ │ ├── AuthorizationProcessorTests.Process_PolicyName.DotNet7_0.verified.txt │ │ │ └── AuthorizationProcessorTests.Process_MultiplePolicyNames.DotNet7_0.verified.txt │ │ └── RateLimiting │ │ │ ├── RateLimitingProcessorTests.Process_NoRateLimiting.DotNet7_0.verified.txt │ │ │ └── RateLimitingProcessorTests.Process.DotNet7_0.verified.txt │ ├── stryker-config.yaml │ ├── Common │ │ ├── ExtensionMethodsTests.CreateInstance_Nullable.verified.txt │ │ ├── ExtensionMethodsTests.GetInternalDependency_MissingService.verified.txt │ │ ├── ExtensionMethodsTests.CreateInstance_NoCtor.verified.txt │ │ └── TaskRunnerTests │ │ │ ├── WhenAll.MultipleMethodsThrow.verified.txt │ │ │ ├── CreateTimer.cs │ │ │ ├── TaskRunnerTestBase.cs │ │ │ ├── Dispose.cs │ │ │ └── StopTimers.cs │ ├── ExtensionMethods │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpoints_AppNull.verified.txt │ │ ├── ApplicationBuilderExtensionsTests.UseExceptionHandling_AppNull.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_AppNull.verified.txt │ │ ├── WebApplicationBuilderExtensionsTests.ConfigureServices_BuilderNull.verified.txt │ │ ├── WebApplicationBuilderExtensionsTests.ConfigureServicesAssembly_BuilderNull.verified.txt │ │ ├── WebApplicationBuilderExtensionsTests.ConfigureServicesAssembly_AssemblyNull.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpoints.DotNet6_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions.DotNet6_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_OptionsNull.DotNet6_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpoints.DotNet7_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions.DotNet7_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_OptionsNull.DotNet7_0.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpoints_NoMapper.verified.txt │ │ ├── EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_NoMapper.verified.txt │ │ ├── WebApplicationBuilderExtensionsTests.Processors.verified.txt │ │ ├── ApplicationBuilderExtensionsTests.UseExceptionHandling.verified.txt │ │ └── ApplicationBuilderExtensionsTests.cs │ ├── TestHelpers │ │ └── ServiceScopeIdentifier.cs │ ├── ReflectionTests.PublicTypes.DotNet6_0.verified.txt │ ├── ReflectionTests.PublicTypes.DotNet7_0.verified.txt │ ├── ReflectionTests.NonPublicTypes.DotNet6_0.verified.txt │ └── Global.cs ├── Reprise.Benchmarks.Api.Carter │ ├── appsettings.json │ ├── Global.cs │ ├── Reprise.Benchmarks.Api.Carter.csproj │ ├── Properties │ │ └── launchSettings.json │ └── Program.cs ├── Reprise.Benchmarks.Api.FastEndpoints │ ├── Global.cs │ ├── appsettings.json │ ├── Reprise.Benchmarks.Api.FastEndpoints.csproj │ ├── Properties │ │ └── launchSettings.json │ └── Program.cs ├── Reprise.Benchmarks.Api.MinimalApis │ ├── appsettings.json │ ├── Reprise.Benchmarks.Api.MinimalApis.csproj │ └── Properties │ │ └── launchSettings.json ├── Reprise.IntegrationTests │ ├── RateLimitingTests.GetLimited.verified.txt │ ├── RateLimitingTests.Get.verified.txt │ ├── GreetingsTests.PostWaitCancel.DotNet6_0.verified.txt │ ├── GreetingsTests.PostWaitCancel.DotNet7_0.verified.txt │ ├── UsersTests.Get_NotFound.DotNet6_0.verified.txt │ ├── WeatherTests.Get_origin=https---example.com.DotNet6_0.verified.txt │ ├── UsersTests.Delete.DotNet6_0.verified.txt │ ├── UsersTests.Delete_NotFound.DotNet6_0.verified.txt │ ├── UsersTests.Get_NotFound.DotNet7_0.verified.txt │ ├── UsersTests.Delete.DotNet7_0.verified.txt │ ├── TokenTests.GetToken.DotNet6_0.verified.txt │ ├── UsersTests.Delete_NotFound.DotNet7_0.verified.txt │ ├── HeartbeatJobTests.cs │ ├── WeatherTests.Get_origin=https---contoso.com.DotNet6_0.verified.txt │ ├── WeatherTests.Get_origin=https---example.com.DotNet7_0.verified.txt │ ├── GreetingsTests.Get.DotNet6_0.verified.txt │ ├── OpenApiTests.cs │ ├── ThrowTests.cs │ ├── ThrowTests.Get.DotNet6_0.verified.txt │ ├── WeatherTests.Get_origin=https---contoso.com.DotNet7_0.verified.txt │ ├── GreetingsTests.Get.DotNet7_0.verified.txt │ ├── TokenTests.GetToken.DotNet7_0.verified.txt │ ├── ThrowTests.Get.DotNet7_0.verified.txt │ ├── UsersTests.Get.DotNet6_0.verified.txt │ ├── UsersTests.Update.DotNet6_0.verified.txt │ ├── UsersTests.Update_NotFound.DotNet6_0.verified.txt │ ├── UsersTests.Update.DotNet7_0.verified.txt │ ├── UsersTests.Get.DotNet7_0.verified.txt │ ├── UsersTests.Update_NotFound.DotNet7_0.verified.txt │ ├── TokenTests.cs │ ├── Global.cs │ ├── GreetingsTests.Post.DotNet6_0.verified.txt │ ├── GreetingsTests.PostWait.DotNet6_0.verified.txt │ ├── UsersTests.Create.DotNet6_0.verified.txt │ ├── GreetingsTests.Post.DotNet7_0.verified.txt │ ├── GreetingsTests.PostWait.DotNet7_0.verified.txt │ ├── UsersTests.Create.DotNet7_0.verified.txt │ ├── WeatherTests.cs │ ├── TestBase.cs │ ├── UsersTests.Create_BadRequest.DotNet6_0.verified.txt │ ├── UsersTests.Update_BadRequest.DotNet6_0.verified.txt │ ├── UsersTests.Create_BadRequest.DotNet7_0.verified.txt │ ├── UsersTests.Update_BadRequest.DotNet7_0.verified.txt │ ├── RateLimitingTests.cs │ ├── UsersTests.GetAll.DotNet6_0.verified.txt │ └── UsersTests.GetAll.DotNet7_0.verified.txt └── Reprise.Benchmarks │ ├── Global.cs │ ├── Program.cs │ ├── BenchmarksBase.cs │ ├── Reprise.Benchmarks.csproj │ └── StartupBenchmarks.cs ├── src └── Reprise │ ├── icon.png │ ├── DefaultDocumentation.json │ ├── Features │ ├── Jobs │ │ ├── DateTimeProvider.cs │ │ ├── IJob.cs │ │ ├── LoggerExtensions.cs │ │ ├── JobStateRegistry.cs │ │ ├── JobState.cs │ │ └── Attributes.cs │ ├── Endpoints │ │ ├── EndpointOptions.cs │ │ └── EndpointTypeProcessor.cs │ ├── OpenApi │ │ ├── ExcludeFromDescription │ │ │ ├── ExcludeFromDescriptionAttribute.cs │ │ │ └── ExcludeFromDescriptionProcessor.cs │ │ ├── Name │ │ │ ├── NameAttribute.cs │ │ │ └── NameProcessor.cs │ │ ├── Tags │ │ │ └── TagsAttribute.cs │ │ └── Produces │ │ │ ├── ProducesProcessor.cs │ │ │ └── ProducesAttribute.cs │ ├── ServiceConfigurators │ │ ├── IServiceConfigurator.cs │ │ └── ServiceConfiguratorTypeProcessor.cs │ ├── Authorization │ │ ├── EndpointOptions.cs │ │ └── AuthorizationProcessor.cs │ ├── RateLimiting │ │ ├── EndpointOptions.cs │ │ └── RateLimitingProcessor.cs │ ├── ExceptionHandling │ │ ├── DefaultErrorResponseFactory.cs │ │ ├── DefaultExceptionLogger.cs │ │ ├── ExceptionLoggerTypeProcessor.cs │ │ ├── ErrorResponseFactoryTypeProcessor.cs │ │ └── ErrorContext.cs │ ├── Events │ │ ├── LoggerExtensions.cs │ │ └── EventHandlerTypeProcessor.cs │ ├── Mappers │ │ ├── IMapper.cs │ │ └── MapperTypeProcessor.cs │ ├── Configurations │ │ └── ConfigurationAttribute.cs │ ├── Filters │ │ ├── EndpointOptions.cs │ │ └── FilterAttribute.cs │ └── Validation │ │ └── ValidatorTypeProcessor.cs │ ├── Common │ ├── IRouteHandlerBuilderProcessor.cs │ └── AbstractTypeProcessor.cs │ ├── Global.cs │ └── ExtensionMethods │ └── ApplicationBuilderExtensions.cs ├── .github ├── dependabot.yml ├── workflows │ ├── codeql.yml │ └── cd.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── docs ├── Reprise.IEvent.md ├── Reprise.RunOnStartAttribute.md ├── Reprise.RunBeforeStartAttribute.md ├── Reprise.ExcludeFromDescriptionAttribute.md ├── Reprise.IJob.md ├── Reprise.CronAttribute.md ├── Reprise.EndpointAttribute.md ├── Reprise.NameAttribute.md ├── Reprise.GetAttribute.md ├── Reprise.PutAttribute.md ├── Reprise.PostAttribute.md ├── Reprise.PatchAttribute.md ├── Reprise.DeleteAttribute.md ├── Reprise.IServiceConfigurator.md ├── Reprise.TagsAttribute.md └── Reprise.ConfigurationAttribute.md ├── samples ├── Reprise.SampleApi.WeatherService │ └── Reprise.SampleApi.WeatherService.csproj └── Reprise.SampleApi │ ├── ErrorHandling │ ├── ProducesBadRequest.cs │ ├── ExceptionLogger.cs │ └── ErrorResponseFactory.cs │ ├── Endpoints │ ├── GetThrowEndpoint.cs │ ├── Users │ │ ├── GetAllUsersEndpoint.cs │ │ ├── DeleteUserEndpoint.cs │ │ ├── GetUserEndpoint.cs │ │ ├── UpdateUserEndpoint.cs │ │ ├── CreateUserEndpoint.cs │ │ └── Common.cs │ ├── Greetings │ │ ├── PostGreetingsEndpoint.cs │ │ ├── GreetingsEventBus.cs │ │ ├── PostGreetingsWaitEndpoint.cs │ │ └── GetGreetingsEndpoint.cs │ ├── GetTokenEndpoint.cs │ ├── GetLimitedEndpoint.cs │ └── GetWeatherEndpoint.cs │ ├── Properties │ └── launchSettings.json │ ├── Filters │ ├── GreetingFilter.cs │ └── TraceIdFilter.cs │ ├── Global.cs │ ├── Data │ └── DataContext.cs │ ├── Jobs │ └── BeforeStartJob.cs │ └── Reprise.SampleApi.csproj ├── Directory.Build.props └── LICENSE /tests/Reprise.Benchmarks.Api/Global.cs: -------------------------------------------------------------------------------- 1 | global using FluentValidation; 2 | -------------------------------------------------------------------------------- /src/Reprise/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yavorfingarov/Reprise/HEAD/src/Reprise/icon.png -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointTypeProcessorTests.Process.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | StubEndpointWithAttribute 3 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/stryker-config.yaml: -------------------------------------------------------------------------------- 1 | stryker-config: 2 | target-framework: 'net7.0' 3 | reporters: [ 'html', 'markdown' ] 4 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Greeting": { 4 | "Message": "Hello, world!" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/DefaultExceptionLoggerTests.Log_ValidationException.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: {}, 3 | logs: [] 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ErrorContextTests.Request.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Method: HttpContext.get_Request() 4 | } 5 | ] -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.Carter/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Greeting": { 4 | "Message": "Hello, world!" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.FastEndpoints/Global.cs: -------------------------------------------------------------------------------- 1 | global using FastEndpoints; 2 | global using FluentValidation; 3 | global using Microsoft.Extensions.Options; 4 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.FastEndpoints/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Greeting": { 4 | "Message": "Hello, world!" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.MinimalApis/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Greeting": { 4 | "Message": "Hello, world!" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ErrorContextTests.TraceIdentifier.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Method: HttpContext.get_TraceIdentifier() 4 | } 5 | ] -------------------------------------------------------------------------------- /src/Reprise/DefaultDocumentation.json: -------------------------------------------------------------------------------- 1 | { 2 | "OutputDirectoryPath": "..\\..\\docs", 3 | "GeneratedPages": "Namespaces,Types", 4 | "Markdown.IgnoreLineBreak": true 5 | } 6 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/RateLimitingTests.GetLimited.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 503 Service Unavailable, 4 | RequestMessage: http://localhost/limited 5 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: / 5 | schedule: 6 | interval: daily 7 | assignees: 8 | - yavorfingarov 9 | -------------------------------------------------------------------------------- /docs/Reprise.IEvent.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## IEvent Interface 4 | 5 | Marker interface to represent an event. 6 | 7 | ```csharp 8 | public interface IEvent 9 | ``` -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.Carter/Global.cs: -------------------------------------------------------------------------------- 1 | global using Carter; 2 | global using Carter.ModelBinding; 3 | global using FluentValidation; 4 | global using Microsoft.Extensions.Options; 5 | -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/DateTimeProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal class DateTimeProvider 4 | { 5 | public virtual DateTime UtcNow => DateTime.UtcNow; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionHandlerTests.InvokeAsync.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | _MockHttpContext: [], 4 | nextInvoked: true 5 | }, 6 | logs: [] 7 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.Add.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | Reprise.UnitTests.Features.Endpoints.StubGetEndpoint, 3 | Reprise.UnitTests.Features.Endpoints.StubPostEndpoint 4 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/ExtensionMethodsTests.CreateInstance_Nullable.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: System.Nullable`1[System.Int32] could not be instantiated. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpoints_AppNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: app 5 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/RateLimitingTests.Get.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: http://localhost/limited 8 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/ApplicationBuilderExtensionsTests.UseExceptionHandling_AppNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: app 5 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_AppNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: app 5 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/WebApplicationBuilderExtensionsTests.ConfigureServices_BuilderNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: builder 5 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/WebApplicationBuilderExtensionsTests.ConfigureServicesAssembly_BuilderNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: builder 5 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process_EmptyName.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.OpenApi.Name.StubNameEndpoint has an empty name. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.PostWaitCancel.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Log: [ 3 | [GreetingHandler] Received greeting with message: Hello, world!, 4 | [SlowerGreetingHandler] Task was cancelled. 5 | ] 6 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.PostWaitCancel.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Log: [ 3 | [GreetingHandler] Received greeting with message: Hello, world!, 4 | [SlowerGreetingHandler] Task was cancelled. 5 | ] 6 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/WebApplicationBuilderExtensionsTests.ConfigureServicesAssembly_AssemblyNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: ArgumentNullException, 3 | Message: Value cannot be null., 4 | ParamName: assembly 5 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Events/EventBusTests/Publish.PayloadNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: ArgumentNullException, 4 | Message: Value cannot be null., 5 | ParamName: payload 6 | }, 7 | logs: [] 8 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_EmptyTag.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.OpenApi.Tags.StubTagType.WithEmptyTag has an empty tag. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/TestHelpers/ServiceScopeIdentifier.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.TestHelpers 2 | { 3 | internal class ServiceScopeIdentifier 4 | { 5 | public Guid ScopeId { get; } = Guid.NewGuid(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_EmptyRoute.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointEmptyRoute.Handle has an empty route. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Events/EventBusTests/PublishAndWait.PayloadNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: ArgumentNullException, 4 | Message: Value cannot be null., 5 | ParamName: payload 6 | }, 7 | logs: [] 8 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_EmptyHttpMethod.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointEmptyHttpMethod.Handle has an empty HTTP method. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_NoHandleMethod.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointNoHandleMethod has no public static Handle method. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/EndpointFilterTests.EndpointFilter_NoImplementation.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Filters.StubFilterNoImplementation does not implement IEndpointFilter. 4 | } -------------------------------------------------------------------------------- /src/Reprise/Common/IRouteHandlerBuilderProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal interface IRouteHandlerBuilderProcessor 4 | { 5 | void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks/Global.cs: -------------------------------------------------------------------------------- 1 | global using BenchmarkDotNet.Attributes; 2 | 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "BenchmarkDotNet doesn't support static methods.")] 6 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Get_NotFound.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | RequestMessage: { 5 | Uri: http://localhost/users/7, 6 | Headers: { 7 | Authorization: {Scrubbed} 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/WeatherTests.Get_origin=https---example.com.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Request: { 5 | Uri: http://localhost/weather, 6 | Headers: { 7 | Origin: https://example.com 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_EmptySubSectionKey.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Configurations.StubConfigurationEmptySubSectionKey has an empty sub-section key. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_MultipleHandleMethods.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointMultipleHandleMethod has more than one public static Handle method. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobNoTriggers.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Jobs.StubJobNoTriggers has no run triggers. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_NoMethodRouteAttribute.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointNoMethodRouteAttribute.Handle has no HTTP method and route attribute. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobEmptyCron.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Jobs.StubJobEmptyCron has an empty cron expression. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Delete.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 204 No Content, 4 | RequestMessage: { 5 | Method: DELETE, 6 | Uri: http://localhost/users/2, 7 | Headers: { 8 | Authorization: {Scrubbed} 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Delete_NotFound.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | RequestMessage: { 5 | Method: DELETE, 6 | Uri: http://localhost/users/7, 7 | Headers: { 8 | Authorization: {Scrubbed} 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_DuplicateRoute.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: GET / is handled by both Reprise.UnitTests.Features.Endpoints.StubDuplicateGetEndpoint and Reprise.UnitTests.Features.Endpoints.StubGetEndpoint. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Endpoints/EndpointMapperTests.MapEndpoints_MultipleMethodRouteAttributes.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Endpoints.StubEndpointMultipleMethodRouteAttributes.Handle has multiple HTTP method and route attributes. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process_Duplicate.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Name 'Test' is used by both Reprise.UnitTests.Features.OpenApi.Name.StubNameEndpointDuplicate and Reprise.UnitTests.Features.OpenApi.Name.StubNameEndpoint. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_InvalidSubSectionKey.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Configurations.StubConfigurationInvalidSubSection has a sub-section key that doesn't match any configuration data. 4 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Endpoints/EndpointOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Provides configuration for API endpoints. 5 | /// 6 | public sealed partial class EndpointOptions 7 | { 8 | internal EndpointOptions() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Get_NotFound.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: { 8 | Uri: http://localhost/users/7, 9 | Headers: { 10 | Authorization: {Scrubbed} 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobRunnerTests/StopAsync.CallsTaskRunner.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | MockTaskRunner: [ 4 | { 5 | Method: TaskRunner.StopTimers(), 6 | ReturnValue: { 7 | Status: RanToCompletion 8 | } 9 | } 10 | ] 11 | }, 12 | logs: [] 13 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api/Reprise.Benchmarks.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.Carter/Reprise.Benchmarks.Api.Carter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/DefaultExceptionLoggerTests.Log_Exception.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: {}, 3 | logs: [ 4 | { 5 | Error: An exception was thrown while executing the request., 6 | Exception: { 7 | Type: Exception, 8 | Message: Test message 9 | } 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/Reprise/Common/AbstractTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal abstract class AbstractTypeProcessor 4 | { 5 | public virtual void PostProcess(WebApplicationBuilder builder) 6 | { 7 | } 8 | 9 | public abstract void Process(WebApplicationBuilder builder, Type type); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Delete.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 204 No Content, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: { 8 | Method: DELETE, 9 | Uri: http://localhost/users/2, 10 | Headers: { 11 | Authorization: {Scrubbed} 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /samples/Reprise.SampleApi.WeatherService/Reprise.SampleApi.WeatherService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/TokenTests.GetToken.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | type: Bearer, 10 | token: {Scrubbed} 11 | } 12 | }, 13 | Request: http://localhost/token 14 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Delete_NotFound.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: { 8 | Method: DELETE, 9 | Uri: http://localhost/users/7, 10 | Headers: { 11 | Authorization: {Scrubbed} 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.NullableTypeNull.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: text/plain; charset=utf-8 7 | }, 8 | Value: No message 9 | }, 10 | Request: { 11 | Method: POST, 12 | Uri: http://localhost/ 13 | } 14 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Validation/ValidatorTypeProcessorTests.Process_DuplicateValidator.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Validation.StubValidatedType is validated by both Reprise.UnitTests.Features.Validation.StubDuplicateValidator and Reprise.UnitTests.Features.Validation.StubValidator. 4 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/IJob.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies the contract for running jobs. 5 | /// 6 | public interface IJob 7 | { 8 | /// 9 | /// Runs the job. 10 | /// 11 | Task Run(CancellationToken cancellationToken); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.MinimalApis/Reprise.Benchmarks.Api.MinimalApis.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/HeartbeatJobTests.cs: -------------------------------------------------------------------------------- 1 | using Reprise.SampleApi.Jobs; 2 | 3 | namespace Reprise.IntegrationTests 4 | { 5 | public class HeartbeatJobTests : TestBase 6 | { 7 | [Fact] 8 | public void Invocations() 9 | { 10 | Assert.True(BeforeStartJob.Invocations > 0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/WeatherTests.Get_origin=https---contoso.com.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | Access-Control-Allow-Origin: https://contoso.com 6 | }, 7 | Request: { 8 | Uri: http://localhost/weather, 9 | Headers: { 10 | Origin: https://contoso.com 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/WeatherTests.Get_origin=https---example.com.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | greeting: Hello!, 6 | trace-id: {Scrubbed} 7 | }, 8 | Request: { 9 | Uri: http://localhost/weather, 10 | Headers: { 11 | Origin: https://example.com 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpoints.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.ExcludeFromDescriptionProcessor, 6 | Reprise.NameProcessor, 7 | Reprise.ProducesProcessor, 8 | Reprise.TagsProcessor 9 | ] 10 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_DuplicateConfiguration.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Sub-section key 'TestConfiguration' is bound to both Reprise.UnitTests.Features.Configurations.StubConfigurationDuplicate and Reprise.UnitTests.Features.Configurations.StubConfiguration. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.FastEndpoints/Reprise.Benchmarks.Api.FastEndpoints.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.ExcludeFromDescriptionProcessor, 6 | Reprise.NameProcessor, 7 | Reprise.ProducesProcessor, 8 | Reprise.TagsProcessor 9 | ] 10 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionLoggerTypeProcessorTests.Process_DuplicateExceptionLogger.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: IExceptionLogger is implemented by both Reprise.UnitTests.Features.ExceptionHandling.StubExceptionLogger and Reprise.UnitTests.Features.ExceptionHandling.StubDuplicateExceptionLogger. 4 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | enable 6 | True 7 | True 8 | latest-recommended 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/ErrorHandling/ProducesBadRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.ErrorHandling 2 | { 3 | public class ProducesBadRequestAttribute : ProducesAttribute 4 | { 5 | public ProducesBadRequestAttribute() : 6 | base(StatusCodes.Status400BadRequest, typeof(ErrorResponse), null) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_OptionsNull.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.ExcludeFromDescriptionProcessor, 6 | Reprise.NameProcessor, 7 | Reprise.ProducesProcessor, 8 | Reprise.TagsProcessor 9 | ] 10 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_MissingProperty.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: 'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of Reprise.UnitTests.Features.Configurations.StubConfiguration: 'AnotherNumber' 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/EndpointFilterTests.EndpointFilter.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | filter-invocations: StubFilterA 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: text/plain; charset=utf-8 10 | }, 11 | Value: Hello, world! 12 | }, 13 | Request: http://localhost/ 14 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.Get.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: [ 9 | Hello, Alice!, 10 | Hello, John!, 11 | Hello, Skylar! 12 | ] 13 | }, 14 | Request: http://localhost/greetings 15 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_ComplexMissingProperty.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: 'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of Reprise.UnitTests.Features.Configurations.StubConfigurationSection: 'Number' 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Mappers/MapperTypeProcessorTests.Process_DuplicateMapper.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Mappers.StubType1 and Reprise.UnitTests.Features.Mappers.StubType2 are mapped by both Reprise.UnitTests.Features.Mappers.StubTypeDuplicateMapper and Reprise.UnitTests.Features.Mappers.StubTypeMapper. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace Reprise.Benchmarks 4 | { 5 | public class Program 6 | { 7 | public static void Main(string[] args) 8 | { 9 | BenchmarkRunner.Run(args: args); 10 | BenchmarkRunner.Run(args: args); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/OpenApiTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.IntegrationTests 2 | { 3 | public class OpenApiTests : TestBase 4 | { 5 | [Fact] 6 | public async Task SwaggerJson() 7 | { 8 | await Verify(await Client.GetAsync("/swagger/v1/swagger.json")) 9 | .UniqueForRuntimeAndVersion(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies an API endpoint that is excluded from the OpenAPI description. 5 | /// 6 | [AttributeUsage(AttributeTargets.Method)] 7 | public sealed class ExcludeFromDescriptionAttribute : Attribute 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ErrorResponseFactoryTypeProcessorTests.Process_DuplicateErrorResponseFactory.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: IErrorResponseFactory is implemented by both Reprise.UnitTests.Features.ExceptionHandling.StubErrorResponseFactory and Reprise.UnitTests.Features.ExceptionHandling.StubDuplicateErrorResponseFactory. 4 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Mappers/MapperTypeProcessorTests.Process_DuplicateMirrorMapper.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Mappers.StubType2 and Reprise.UnitTests.Features.Mappers.StubType1 are mapped by both Reprise.UnitTests.Features.Mappers.StubTypeDuplicateMirrorMapper and Reprise.UnitTests.Features.Mappers.StubTypeMapper. 4 | } -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | schedule: 10 | - cron: 0 3 * * 1 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | permissions: 16 | security-events: write 17 | uses: yavorfingarov/Workflows/.github/workflows/codeql.yml@master 18 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/ThrowTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.IntegrationTests 2 | { 3 | public class ThrowTests : TestBase 4 | { 5 | [Fact] 6 | public async Task Get() 7 | { 8 | await Verify(await Client.GetAsync("/throw")) 9 | .ScrubMember("trace-id") 10 | .UniqueForRuntimeAndVersion(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/ThrowTests.Get.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 500 Internal Server Error, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | message: The method or operation is not implemented., 10 | details: null 11 | } 12 | }, 13 | Request: http://localhost/throw 14 | } -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/GetThrowEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints 2 | { 3 | [Endpoint] 4 | public class GetThrowEndpoint 5 | { 6 | [Get("/throw")] 7 | [AllowAnonymous] 8 | [ExcludeFromDescription] 9 | public static IResult Handle() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Development": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5039", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/EndpointFilterTests.GlobalEndpointFilters.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | filter-invocations: StubFilterA / StubFilterB / StubFilterC 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: text/plain; charset=utf-8 10 | }, 11 | Value: Hello, world! 12 | }, 13 | Request: http://localhost/ 14 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.Carter/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Development": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5039", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/WeatherTests.Get_origin=https---contoso.com.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | Access-Control-Allow-Origin: https://contoso.com, 6 | greeting: Hello!, 7 | trace-id: {Scrubbed} 8 | }, 9 | Request: { 10 | Uri: http://localhost/weather, 11 | Headers: { 12 | Origin: https://contoso.com 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.FastEndpoints/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Development": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5039", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.MinimalApis/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Development": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5039", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/EndpointFilterTests.GlobalEndpointFilters_EndpointFilter.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | filter-invocations: StubFilterB / StubFilterC / StubFilterA 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: text/plain; charset=utf-8 10 | }, 11 | Value: Hello, world! 12 | }, 13 | Request: http://localhost/ 14 | } -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Development": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "https://localhost:7008;http://localhost:5008", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpoints.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.FilterProcessor, 6 | Reprise.ExcludeFromDescriptionProcessor, 7 | Reprise.NameProcessor, 8 | Reprise.ProducesProcessor, 9 | Reprise.TagsProcessor, 10 | Reprise.RateLimitingProcessor 11 | ] 12 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.Get.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: [ 12 | Hello, Alice!, 13 | Hello, John!, 14 | Hello, Skylar! 15 | ] 16 | }, 17 | Request: http://localhost/greetings 18 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/TokenTests.GetToken.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | greeting: Hello!, 6 | trace-id: {Scrubbed} 7 | }, 8 | Content: { 9 | Headers: { 10 | Content-Type: application/json; charset=utf-8 11 | }, 12 | Value: { 13 | type: Bearer, 14 | token: {Scrubbed} 15 | } 16 | }, 17 | Request: http://localhost/token 18 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.FilterProcessor, 6 | Reprise.ExcludeFromDescriptionProcessor, 7 | Reprise.NameProcessor, 8 | Reprise.ProducesProcessor, 9 | Reprise.TagsProcessor, 10 | Reprise.RateLimitingProcessor 11 | ] 12 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/DefaultExceptionLoggerTests.Log_BadHttpRequestException.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: {}, 3 | logs: [ 4 | { 5 | Error: BadHttpRequestException: Test message, 6 | State: [ 7 | { 8 | Message: Test message 9 | }, 10 | { 11 | {OriginalFormat}: BadHttpRequestException: {Message} 12 | } 13 | ] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks/BenchmarksBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Testing; 2 | 3 | namespace Reprise.Benchmarks 4 | { 5 | public abstract class BenchmarksBase 6 | { 7 | public static HttpClient CreateClient() where T : class 8 | { 9 | var webApplicationFactory = new WebApplicationFactory(); 10 | 11 | return webApplicationFactory.CreateClient(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/EndpointFilterTests.GlobalEndpointFilters_EndpointMultipleFilters.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | filter-invocations: StubFilterD / StubFilterC / StubFilterB / StubFilterA 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: text/plain; charset=utf-8 10 | }, 11 | Value: Hello, world! 12 | }, 13 | Request: http://localhost/ 14 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_OptionsNull.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | EndpointOptions: {}, 3 | Processors: [ 4 | Reprise.AuthorizationProcessor, 5 | Reprise.FilterProcessor, 6 | Reprise.ExcludeFromDescriptionProcessor, 7 | Reprise.NameProcessor, 8 | Reprise.ProducesProcessor, 9 | Reprise.TagsProcessor, 10 | Reprise.RateLimitingProcessor 11 | ] 12 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/DateTimeProviderTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Features.Jobs 2 | { 3 | public class DateTimeProviderTests 4 | { 5 | [Fact] 6 | public void UtcNow() 7 | { 8 | var dateTimeProvider = new DateTimeProvider(); 9 | 10 | Assert.Equal(DateTime.UtcNow, dateTimeProvider.UtcNow, precision: TimeSpan.FromMilliseconds(50)); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpoints_NoMapper.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise could not resolve an internal dependency. Have you called 'builder.ConfigureServices()'?, 4 | InnerException: { 5 | $type: InvalidOperationException, 6 | Type: InvalidOperationException, 7 | Message: No service for type 'Reprise.EndpointMapper' has been registered. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/ThrowTests.Get.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 500 Internal Server Error, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: { 12 | message: The method or operation is not implemented., 13 | details: null 14 | } 15 | }, 16 | Request: http://localhost/throw 17 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/ExtensionMethodsTests.GetInternalDependency_MissingService.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise could not resolve an internal dependency. Have you called 'builder.ConfigureServices()'?, 4 | InnerException: { 5 | $type: InvalidOperationException, 6 | Type: InvalidOperationException, 7 | Message: No service for type 'Reprise.UnitTests.Common.StubType' has been registered. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Get.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | id: 2, 10 | firstName: Cornell, 11 | lastName: Ratke 12 | } 13 | }, 14 | Request: { 15 | Uri: http://localhost/users/2, 16 | Headers: { 17 | Authorization: {Scrubbed} 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 204 No Content, 4 | RequestMessage: { 5 | Method: PUT, 6 | Uri: http://localhost/users/2, 7 | Headers: { 8 | Authorization: {Scrubbed} 9 | }, 10 | Content: { 11 | Headers: { 12 | Content-Length: 55, 13 | Content-Type: application/json; charset=utf-8 14 | }, 15 | Value: 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/ExtensionMethodsTests.CreateInstance_NoCtor.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Common.StubTypeNoCtor could not be instantiated. Check the inner exception for more details., 4 | InnerException: { 5 | $type: MissingMethodException, 6 | Type: MissingMethodException, 7 | Message: No parameterless constructor defined for type 'Reprise.UnitTests.Common.StubTypeNoCtor'. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/TaskRunnerTests/WhenAll.MultipleMethodsThrow.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | exception: { 3 | Type: AggregateException, 4 | InnerExceptions: [ 5 | { 6 | Type: Exception, 7 | Message: Test message 8 | }, 9 | { 10 | Type: Exception, 11 | Message: Test message 12 | }, 13 | { 14 | Type: Exception, 15 | Message: Test message 16 | } 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/EndpointRouteBuilderExtensionsTests.MapEndpointsOptions_NoMapper.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise could not resolve an internal dependency. Have you called 'builder.ConfigureServices()'?, 4 | InnerException: { 5 | $type: InvalidOperationException, 6 | Type: InvalidOperationException, 7 | Message: No service for type 'Reprise.EndpointMapper' has been registered. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update_NotFound.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | RequestMessage: { 5 | Method: PUT, 6 | Uri: http://localhost/users/7, 7 | Headers: { 8 | Authorization: {Scrubbed} 9 | }, 10 | Content: { 11 | Headers: { 12 | Content-Length: 55, 13 | Content-Type: application/json; charset=utf-8 14 | }, 15 | Value: 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/WebApplicationBuilderExtensionsTests.Processors.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | Reprise.ConfigurationTypeProcessor, 3 | Reprise.EndpointTypeProcessor, 4 | Reprise.EventHandlerTypeProcessor, 5 | Reprise.ErrorResponseFactoryTypeProcessor, 6 | Reprise.ExceptionLoggerTypeProcessor, 7 | Reprise.JobTypeProcessor, 8 | Reprise.MapperTypeProcessor, 9 | Reprise.ServiceConfiguratorTypeProcessor, 10 | Reprise.ValidatorTypeProcessor 11 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobRunnerTests/StopAsync.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Features.Jobs.JobRunnerTests 2 | { 3 | public class StopAsync : JobRunnerTestBase 4 | { 5 | [Fact] 6 | public async Task CallsTaskRunner() 7 | { 8 | ConfigureServices(); 9 | 10 | await JobRunner.StopAsync(CancellationToken.None); 11 | 12 | await Verify(new { MockTaskRunner }); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Filters/GreetingFilter.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise.SampleApi.Filters 3 | { 4 | public class GreetingFilter : IEndpointFilter 5 | { 6 | public ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 7 | { 8 | context.HttpContext.Response.Headers.Add("greeting", "Hello!"); 9 | 10 | return next(context); 11 | } 12 | } 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Global.cs: -------------------------------------------------------------------------------- 1 | global using FluentValidation; 2 | global using Microsoft.AspNetCore.Authorization; 3 | global using Reprise.SampleApi.Data; 4 | global using Reprise.SampleApi.ErrorHandling; 5 | #if NET7_0 6 | global using Reprise.SampleApi.Filters; 7 | #endif 8 | 9 | using System.Diagnostics.CodeAnalysis; 10 | 11 | [assembly: SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates", Justification = "Logging performance is not a concern.")] 12 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/GetAllUsersEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | [Endpoint] 4 | public class GetAllUsersEndpoint 5 | { 6 | [Get("/users")] 7 | [Produces(StatusCodes.Status200OK, typeof(IEnumerable))] 8 | public static IEnumerable Handle(DataContext context, IMapper mapper) 9 | { 10 | return context.Users.Select(mapper.Map); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Filters/TraceIdFilter.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise.SampleApi.Filters 3 | { 4 | public class TraceIdFilter : IEndpointFilter 5 | { 6 | public ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 7 | { 8 | context.HttpContext.Response.Headers.Add("trace-id", context.HttpContext.TraceIdentifier); 9 | 10 | return next(context); 11 | } 12 | } 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /src/Reprise/Features/ServiceConfigurators/IServiceConfigurator.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies the contract to configure services at application startup. 5 | /// 6 | public interface IServiceConfigurator 7 | { 8 | /// 9 | /// Configures services for the specified . 10 | /// 11 | void ConfigureServices(WebApplicationBuilder builder); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Reprise/Features/Authorization/EndpointOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | public partial class EndpointOptions 4 | { 5 | internal string[]? AuthorizationPolicyNames { get; private set; } 6 | 7 | /// 8 | /// Enables authorization for all API endpoints. 9 | /// 10 | public void RequireAuthorization(params string[] policyNames) 11 | { 12 | AuthorizationPolicyNames = policyNames; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/Reprise.RunOnStartAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## RunOnStartAttribute Class 4 | 5 | Identifies a job that runs after the server is started. 6 | 7 | ```csharp 8 | public sealed class RunOnStartAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 RunOnStartAttribute -------------------------------------------------------------------------------- /src/Reprise/Features/RateLimiting/EndpointOptions.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise 3 | { 4 | public partial class EndpointOptions 5 | { 6 | internal string? RateLimitingPolicy { get; private set; } 7 | 8 | /// 9 | /// Enables rate limiting for all API endpoints. 10 | /// 11 | public void RequireRateLimiting(string policyName) 12 | { 13 | RateLimitingPolicy = policyName; 14 | } 15 | } 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 204 No Content, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: { 8 | Method: PUT, 9 | Uri: http://localhost/users/2, 10 | Headers: { 11 | Authorization: {Scrubbed} 12 | }, 13 | Content: { 14 | Headers: { 15 | Content-Length: 55, 16 | Content-Type: application/json; charset=utf-8 17 | }, 18 | Value: 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionHandlerTests.Ctor_NoExceptionLogger.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: InvalidOperationException, 4 | Message: Reprise could not resolve an internal dependency. Have you called 'builder.ConfigureServices()'?, 5 | InnerException: { 6 | $type: InvalidOperationException, 7 | Type: InvalidOperationException, 8 | Message: No service for type 'Reprise.IExceptionLogger' has been registered. 9 | } 10 | }, 11 | logs: [] 12 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Get.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: { 12 | id: 2, 13 | firstName: Cornell, 14 | lastName: Ratke 15 | } 16 | }, 17 | Request: { 18 | Uri: http://localhost/users/2, 19 | Headers: { 20 | Authorization: {Scrubbed} 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /docs/Reprise.RunBeforeStartAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## RunBeforeStartAttribute Class 4 | 5 | Identifies a job that runs before the server is started. 6 | 7 | ```csharp 8 | public sealed class RunBeforeStartAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 RunBeforeStartAttribute -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update_NotFound.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 404 Not Found, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | RequestMessage: { 8 | Method: PUT, 9 | Uri: http://localhost/users/7, 10 | Headers: { 11 | Authorization: {Scrubbed} 12 | }, 13 | Content: { 14 | Headers: { 15 | Content-Length: 55, 16 | Content-Type: application/json; charset=utf-8 17 | }, 18 | Value: 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionHandlerTests.Ctor_NoErrorResponseFactory.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Type: InvalidOperationException, 4 | Message: Reprise could not resolve an internal dependency. Have you called 'builder.ConfigureServices()'?, 5 | InnerException: { 6 | $type: InvalidOperationException, 7 | Type: InvalidOperationException, 8 | Message: No service for type 'Reprise.IErrorResponseFactory' has been registered. 9 | } 10 | }, 11 | logs: [] 12 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Authorization/AuthorizationProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class AuthorizationProcessor : IRouteHandlerBuilderProcessor 4 | { 5 | public void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route) 6 | { 7 | if (options.AuthorizationPolicyNames != null) 8 | { 9 | builder.RequireAuthorization(options.AuthorizationPolicyNames); 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Greetings/PostGreetingsEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Greetings 2 | { 3 | [Endpoint] 4 | public class PostGreetingsEndpoint 5 | { 6 | [Post("/greetings")] 7 | [AllowAnonymous] 8 | [Produces(StatusCodes.Status202Accepted)] 9 | public static IResult Handle(Greeting greeting, IEventBus eventBus) 10 | { 11 | eventBus.Publish(greeting); 12 | 13 | return Results.Accepted(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/TokenTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.IntegrationTests 2 | { 3 | [UsesVerify] 4 | public class TokenTests : TestBase 5 | { 6 | [Fact] 7 | public async Task GetToken() 8 | { 9 | await Verify(await Client.GetAsync("/token")) 10 | .ScrubMember("token") 11 | .ScrubMember("trace-id") 12 | .UniqueForRuntimeAndVersion(); 13 | } 14 | } 15 | 16 | public record TokenResponse(string Type, string Token); 17 | } 18 | -------------------------------------------------------------------------------- /src/Reprise/Features/RateLimiting/RateLimitingProcessor.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise 3 | { 4 | internal sealed class RateLimitingProcessor : IRouteHandlerBuilderProcessor 5 | { 6 | public void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route) 7 | { 8 | if (options.RateLimitingPolicy != null) 9 | { 10 | builder.RequireRateLimiting(options.RateLimitingPolicy); 11 | } 12 | } 13 | } 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobTypeProcessorTests.Process_InvalidTriggers_jobType=Reprise.UnitTests.Features.Jobs.StubJobInvalidCron.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Jobs.StubJobInvalidCron has an invalid cron expression., 4 | InnerException: { 5 | $type: CrontabException, 6 | Type: CrontabException, 7 | Message: 'foobar' is an invalid crontab expression. It must contain 5 components of a schedule in the sequence of minutes, hours, days, months, and days of week. 8 | } 9 | } -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ExcludeFromDescriptionProcessor : IRouteHandlerBuilderProcessor 4 | { 5 | public void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route) 6 | { 7 | if (handlerInfo.IsDefined(typeof(ExcludeFromDescriptionAttribute))) 8 | { 9 | builder.ExcludeFromDescription(); 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Greetings/GreetingsEventBus.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Greetings 2 | { 3 | public static class GreetingsEventBus 4 | { 5 | public static IReadOnlyList Log => _Log; 6 | 7 | private static readonly List _Log = new(); 8 | 9 | public static void LogInformation(string message) 10 | { 11 | _Log.Add(message); 12 | } 13 | 14 | public static void ClearLog() 15 | { 16 | _Log.Clear(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/Reprise.ExcludeFromDescriptionAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## ExcludeFromDescriptionAttribute Class 4 | 5 | Specifies an API endpoint that is excluded from the OpenAPI description. 6 | 7 | ```csharp 8 | public sealed class ExcludeFromDescriptionAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 ExcludeFromDescriptionAttribute -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: yavorfingarov 7 | --- 8 | 9 | **Problem** 10 | A clear and concise description of what the problem is. 11 | 12 | **Description** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Alternatives** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context about the feature request here. 20 | -------------------------------------------------------------------------------- /src/Reprise/Features/ServiceConfigurators/ServiceConfiguratorTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ServiceConfiguratorTypeProcessor : AbstractTypeProcessor 4 | { 5 | public override void Process(WebApplicationBuilder builder, Type type) 6 | { 7 | if (type.IsAssignableTo(typeof(IServiceConfigurator))) 8 | { 9 | var configurator = (IServiceConfigurator)type.CreateInstance(); 10 | configurator.ConfigureServices(builder); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.Benchmarks.Api 2 | { 3 | public class Program 4 | { 5 | public static void Main() 6 | { 7 | var builder = WebApplication.CreateBuilder(); 8 | 9 | builder.Logging.ClearProviders(); 10 | 11 | builder.ConfigureServices(); 12 | 13 | var app = builder.Build(); 14 | 15 | app.UseExceptionHandling(); 16 | 17 | app.MapEndpoints(options => options.AddValidationFilter()); 18 | 19 | app.Run(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobStateRegistryTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Features.Jobs 2 | { 3 | public class JobStateRegistryTests 4 | { 5 | [Fact] 6 | public void Add() 7 | { 8 | var jobState = new JobState(GetType(), default, default, default); 9 | var jobStateRegistry = new JobStateRegistry(); 10 | 11 | Assert.Empty(jobStateRegistry); 12 | 13 | jobStateRegistry.Add(jobState); 14 | 15 | Assert.Same(jobState, jobStateRegistry.Single()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/ApplicationBuilderExtensionsTests.UseExceptionHandling.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Method: IApplicationBuilder.get_ApplicationServices() 4 | }, 5 | { 6 | Method: IApplicationBuilder.Use(Func middleware), 7 | Arguments: [ 8 | { 9 | Type: Func, 10 | Target: UseMiddlewareExtensions.<>c__DisplayClass5_0, 11 | Method: Microsoft.AspNetCore.Http.RequestDelegate UseMiddleware(Microsoft.AspNetCore.Http.RequestDelegate) 12 | } 13 | ] 14 | } 15 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.NoValidator.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: text/plain; charset=utf-8 7 | }, 8 | Value: No message 9 | }, 10 | Request: { 11 | Method: POST, 12 | Uri: http://localhost/, 13 | Headers: { 14 | Transfer-Encoding: chunked 15 | }, 16 | Content: { 17 | Headers: { 18 | Content-Type: application/json; charset=utf-8 19 | }, 20 | Value: { 21 | message: null 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/Global.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Runtime.CompilerServices; 3 | 4 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 5 | 6 | [assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Test names can contain underscores.")] 7 | 8 | namespace Reprise.IntegrationTests 9 | { 10 | public class ModuleInit 11 | { 12 | [ModuleInitializer] 13 | public static void Initialize() 14 | { 15 | VerifyHttp.Initialize(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: '' 5 | labels: bug 6 | assignees: yavorfingarov 7 | --- 8 | 9 | **Description** 10 | A clear and concise description of what the bug is. 11 | 12 | **Steps to reproduce** 13 | A clear and concise description of what led to the problem. 14 | 15 | **Expected behavior** 16 | A clear and concise description of what you expected to happen. 17 | 18 | **Package version** 19 | E.g. 3.3.1 20 | 21 | **.NET version** 22 | E.g. 7.0.2 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /src/Reprise/Features/ExceptionHandling/DefaultErrorResponseFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class DefaultErrorResponseFactory : IErrorResponseFactory 4 | { 5 | public object? Create(ErrorContext context) 6 | { 7 | return null; 8 | } 9 | 10 | public object? Create(ErrorContext context) 11 | { 12 | return null; 13 | } 14 | 15 | public object? Create(ErrorContext context) 16 | { 17 | return null; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_NoParameterlessCtor.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Reprise.UnitTests.Features.Configurations.StubConfigurationNoParameterlessCtor could not be instantiated. Check the inner exception for more details., 4 | InnerException: { 5 | $type: MissingMethodException, 6 | Type: MissingMethodException, 7 | Message: Cannot dynamically create an instance of type 'Reprise.UnitTests.Features.Configurations.StubConfigurationNoParameterlessCtor'. Reason: No parameterless constructor defined. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.NullableType.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: text/plain; charset=utf-8 7 | }, 8 | Value: Hello, world! 9 | }, 10 | Request: { 11 | Method: POST, 12 | Uri: http://localhost/, 13 | Headers: { 14 | Transfer-Encoding: chunked 15 | }, 16 | Content: { 17 | Headers: { 18 | Content-Type: application/json; charset=utf-8 19 | }, 20 | Value: { 21 | message: Hello, world! 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.ValidRequest.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: text/plain; charset=utf-8 7 | }, 8 | Value: Hello, world! 9 | }, 10 | Request: { 11 | Method: POST, 12 | Uri: http://localhost/, 13 | Headers: { 14 | Transfer-Encoding: chunked 15 | }, 16 | Content: { 17 | Headers: { 18 | Content-Type: application/json; charset=utf-8 19 | }, 20 | Value: { 21 | message: Hello, world! 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_InvalidValue.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Failed to convert configuration value at 'TestConfiguration:Number' to type 'System.Int32'., 4 | InnerException: { 5 | $type: ArgumentException, 6 | Type: ArgumentException, 7 | Message: True is not a valid value for Int32., 8 | ParamName: value, 9 | InnerException: { 10 | $type: FormatException, 11 | Type: FormatException, 12 | Message: Input string was not in a correct format. 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Greetings/PostGreetingsWaitEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Greetings 2 | { 3 | [Endpoint] 4 | public class PostGreetingsWaitEndpoint 5 | { 6 | [Post("/greetings/wait")] 7 | [AllowAnonymous] 8 | [Produces(StatusCodes.Status202Accepted)] 9 | public static async Task Handle(Greeting greeting, IEventBus eventBus, CancellationToken cancellationToken) 10 | { 11 | await eventBus.PublishAndWait(greeting, cancellationToken); 12 | 13 | return Results.Accepted(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_ComplexInvalidValue.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Failed to convert configuration value at 'TestConfiguration:Number' to type 'System.Int32'., 4 | InnerException: { 5 | $type: ArgumentException, 6 | Type: ArgumentException, 7 | Message: True is not a valid value for Int32., 8 | ParamName: value, 9 | InnerException: { 10 | $type: FormatException, 11 | Type: FormatException, 12 | Message: Input string was not in a correct format. 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_InvalidValue.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Failed to convert configuration value at 'TestConfiguration:Number' to type 'System.Int32'., 4 | InnerException: { 5 | $type: ArgumentException, 6 | Type: ArgumentException, 7 | Message: True is not a valid value for Int32., 8 | ParamName: value, 9 | InnerException: { 10 | $type: FormatException, 11 | Type: FormatException, 12 | Message: The input string 'True' was not in a correct format. 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal static partial class LoggerExtensions 4 | { 5 | private static readonly Action _ScheduledJobException = LoggerMessage.Define( 6 | LogLevel.Error, default, 7 | "An exception was thrown while executing {ScheduledJobType}."); 8 | 9 | public static void LogScheduledJobException(this ILogger logger, Exception exception, string? scheduledJobType) 10 | { 11 | _ScheduledJobException(logger, scheduledJobType, exception); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Reprise/Features/Events/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal static partial class LoggerExtensions 4 | { 5 | private static readonly Action _MessageHandlerException = LoggerMessage.Define( 6 | LogLevel.Error, default, 7 | "An exception was thrown while executing {EventHandlerType}."); 8 | 9 | public static void LogEventHandlerException(this ILogger logger, Exception exception, string? eventHandlerType) 10 | { 11 | _MessageHandlerException(logger, eventHandlerType, exception); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Configurations/ConfigurationTypeProcessorTests.Process_ComplexInvalidValue.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Type: InvalidOperationException, 3 | Message: Failed to convert configuration value at 'TestConfiguration:Number' to type 'System.Int32'., 4 | InnerException: { 5 | $type: ArgumentException, 6 | Type: ArgumentException, 7 | Message: True is not a valid value for Int32., 8 | ParamName: value, 9 | InnerException: { 10 | $type: FormatException, 11 | Type: FormatException, 12 | Message: The input string 'True' was not in a correct format. 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/Name/NameAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies a name that is used for link generation and is treated as the operation ID in the OpenAPI description. 5 | /// 6 | [AttributeUsage(AttributeTargets.Method)] 7 | public sealed class NameAttribute : Attribute 8 | { 9 | internal string Name { get; } 10 | 11 | /// 12 | /// Creates a new . 13 | /// 14 | public NameAttribute(string name) 15 | { 16 | Name = name; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/Tags/TagsAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies custom OpenAPI tags for the API endpoint. 5 | /// 6 | [AttributeUsage(AttributeTargets.Method)] 7 | public sealed class TagsAttribute : Attribute 8 | { 9 | internal string[] Tags { get; } 10 | 11 | /// 12 | /// Creates a new . 13 | /// 14 | public TagsAttribute(string tag, params string[] additionalTags) 15 | { 16 | Tags = additionalTags.Prepend(tag).ToArray(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Reprise/Global.cs: -------------------------------------------------------------------------------- 1 | global using System.Reflection; 2 | global using FluentValidation; 3 | global using Microsoft.AspNetCore.Builder; 4 | global using Microsoft.AspNetCore.Http; 5 | global using Microsoft.Extensions.Configuration; 6 | global using Microsoft.Extensions.DependencyInjection; 7 | global using Microsoft.Extensions.DependencyInjection.Extensions; 8 | global using Microsoft.Extensions.Hosting; 9 | global using Microsoft.Extensions.Logging; 10 | global using NCrontab; 11 | 12 | using System.Runtime.CompilerServices; 13 | 14 | [assembly: InternalsVisibleTo("Reprise.UnitTests")] 15 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] 16 | -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/JobStateRegistry.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Reprise 4 | { 5 | internal sealed class JobStateRegistry : IEnumerable 6 | { 7 | private readonly List _JobStates = new(); 8 | 9 | public void Add(JobState jobState) 10 | { 11 | _JobStates.Add(jobState); 12 | } 13 | 14 | public IEnumerator GetEnumerator() 15 | { 16 | return _JobStates.GetEnumerator(); 17 | } 18 | 19 | IEnumerator IEnumerable.GetEnumerator() 20 | { 21 | return GetEnumerator(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.Post.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | response: { 3 | Version: 1.1, 4 | Status: 202 Accepted, 5 | Content: { 6 | Headers: {} 7 | }, 8 | Request: { 9 | Method: POST, 10 | Uri: http://localhost/greetings, 11 | Content: { 12 | Headers: { 13 | Content-Length: 27, 14 | Content-Type: application/json; charset=utf-8 15 | }, 16 | Value: 17 | } 18 | } 19 | }, 20 | Log: [ 21 | [GreetingHandler] Received greeting with message: Hello, world!, 22 | [SlowerGreetingHandler] Received greeting with message: Hello, world! 23 | ] 24 | } -------------------------------------------------------------------------------- /src/Reprise/Features/ExceptionHandling/DefaultExceptionLogger.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class DefaultExceptionLogger : IExceptionLogger 4 | { 5 | public void Log(ILogger logger, ErrorContext context) 6 | { 7 | logger.LogBadHttpRequestException(context.Exception.Message); 8 | } 9 | 10 | public void Log(ILogger logger, ErrorContext context) 11 | { 12 | } 13 | 14 | public void Log(ILogger logger, ErrorContext context) 15 | { 16 | logger.LogException(context.Exception); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Data/DataContext.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Data 2 | { 3 | public class ServiceConfigurator : IServiceConfigurator 4 | { 5 | public void ConfigureServices(WebApplicationBuilder builder) 6 | { 7 | builder.Services.AddSingleton(); 8 | } 9 | } 10 | 11 | public class DataContext 12 | { 13 | public List Users { get; } = new(); 14 | } 15 | 16 | public class User 17 | { 18 | public int Id { get; init; } 19 | 20 | public string FirstName { get; set; } = null!; 21 | 22 | public string LastName { get; set; } = null!; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.PostWait.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | response: { 3 | Version: 1.1, 4 | Status: 202 Accepted, 5 | Content: { 6 | Headers: {} 7 | }, 8 | Request: { 9 | Method: POST, 10 | Uri: http://localhost/greetings/wait, 11 | Content: { 12 | Headers: { 13 | Content-Length: 27, 14 | Content-Type: application/json; charset=utf-8 15 | }, 16 | Value: 17 | } 18 | } 19 | }, 20 | Log: [ 21 | [GreetingHandler] Received greeting with message: Hello, world!, 22 | [SlowerGreetingHandler] Received greeting with message: Hello, world! 23 | ] 24 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Endpoints/EndpointTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class EndpointTypeProcessor : AbstractTypeProcessor 4 | { 5 | internal EndpointMapper EndpointMapper { get; } = new(); 6 | 7 | public override void Process(WebApplicationBuilder builder, Type type) 8 | { 9 | if (type.IsDefined(typeof(EndpointAttribute))) 10 | { 11 | EndpointMapper.Add(type); 12 | } 13 | } 14 | 15 | public override void PostProcess(WebApplicationBuilder builder) 16 | { 17 | builder.Services.AddSingleton(EndpointMapper); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/JobState.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class JobState 4 | { 5 | public Type JobType { get; } 6 | 7 | public bool RunBeforeStart { get; } 8 | 9 | public bool RunOnStart { get; } 10 | 11 | public CrontabSchedule? Schedule { get; } 12 | 13 | public Timer? Timer { get; set; } 14 | 15 | public JobState(Type jobType, bool runBeforeStart, bool runOnStart, CrontabSchedule? schedule) 16 | { 17 | JobType = jobType; 18 | RunBeforeStart = runBeforeStart; 19 | RunOnStart = runOnStart; 20 | Schedule = schedule; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.MultipleTypes.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: text/plain; charset=utf-8 7 | }, 8 | Value: Hello, world! 9 | }, 10 | Request: { 11 | Method: POST, 12 | Uri: { 13 | Path: http://localhost/, 14 | Query: { 15 | audience: world 16 | } 17 | }, 18 | Headers: { 19 | Transfer-Encoding: chunked 20 | }, 21 | Content: { 22 | Headers: { 23 | Content-Type: application/json; charset=utf-8 24 | }, 25 | Value: { 26 | message: Hello 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.InvalidRequest.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | errors: { 10 | Message: 'Message' must not be empty. 11 | } 12 | } 13 | }, 14 | Request: { 15 | Method: POST, 16 | Uri: http://localhost/, 17 | Headers: { 18 | Transfer-Encoding: chunked 19 | }, 20 | Content: { 21 | Headers: { 22 | Content-Type: application/json; charset=utf-8 23 | }, 24 | Value: { 25 | message: null 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.NullableTypeInvalidRequest.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | errors: { 10 | Message: 'Message' must not be empty. 11 | } 12 | } 13 | }, 14 | Request: { 15 | Method: POST, 16 | Uri: http://localhost/, 17 | Headers: { 18 | Transfer-Encoding: chunked 19 | }, 20 | Content: { 21 | Headers: { 22 | Content-Type: application/json; charset=utf-8 23 | }, 24 | Value: { 25 | message: null 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Create.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 201 Created, 4 | Headers: { 5 | Location: /users/0 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: { 12 | id: 0, 13 | firstName: Leanna, 14 | lastName: Schmeler 15 | } 16 | }, 17 | Request: { 18 | Method: POST, 19 | Uri: http://localhost/users, 20 | Headers: { 21 | Authorization: {Scrubbed} 22 | }, 23 | Content: { 24 | Headers: { 25 | Content-Length: 54, 26 | Content-Type: application/json; charset=utf-8 27 | }, 28 | Value: 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/DeleteUserEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | [Endpoint] 4 | public class DeleteUserEndpoint 5 | { 6 | [Delete("/users/{id}")] 7 | [Produces(StatusCodes.Status204NoContent)] 8 | [Produces(StatusCodes.Status404NotFound)] 9 | public static IResult Handle(int id, DataContext context) 10 | { 11 | var index = context.Users.FindIndex(x => x.Id == id); 12 | if (index == -1) 13 | { 14 | return Results.NotFound(); 15 | } 16 | context.Users.RemoveAt(index); 17 | 18 | return Results.NoContent(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.Post.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | response: { 3 | Version: 1.1, 4 | Status: 202 Accepted, 5 | Headers: { 6 | trace-id: {Scrubbed} 7 | }, 8 | Content: { 9 | Headers: {} 10 | }, 11 | Request: { 12 | Method: POST, 13 | Uri: http://localhost/greetings, 14 | Content: { 15 | Headers: { 16 | Content-Length: 27, 17 | Content-Type: application/json; charset=utf-8 18 | }, 19 | Value: 20 | } 21 | } 22 | }, 23 | Log: [ 24 | [GreetingHandler] Received greeting with message: Hello, world!, 25 | [SlowerGreetingHandler] Received greeting with message: Hello, world! 26 | ] 27 | } -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | uses: yavorfingarov/Workflows/.github/workflows/build.yml@master 14 | with: 15 | unit-tests-path: ./tests/Reprise.UnitTests 16 | status-gist-id: 552110af4a546bfef6adfd60e60163c3 17 | secrets: inherit 18 | 19 | publish: 20 | name: Publish 21 | needs: build 22 | if: ${{ needs.build.outputs.run-next == 'true' }} 23 | uses: yavorfingarov/Workflows/.github/workflows/publish-nuget.yml@master 24 | with: 25 | csproj-path: ./src/Reprise/Reprise.csproj 26 | secrets: inherit 27 | -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/Produces/ProducesProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ProducesProcessor : IRouteHandlerBuilderProcessor 4 | { 5 | public void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route) 6 | { 7 | var producesAttributes = handlerInfo.GetCustomAttributes(); 8 | foreach (var producesAttribute in producesAttributes) 9 | { 10 | builder.Produces(producesAttribute.StatusCode, producesAttribute.ResponseType, 11 | producesAttribute.ContentType, producesAttribute.AdditionalContentTypes); 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/GreetingsTests.PostWait.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | response: { 3 | Version: 1.1, 4 | Status: 202 Accepted, 5 | Headers: { 6 | trace-id: {Scrubbed} 7 | }, 8 | Content: { 9 | Headers: {} 10 | }, 11 | Request: { 12 | Method: POST, 13 | Uri: http://localhost/greetings/wait, 14 | Content: { 15 | Headers: { 16 | Content-Length: 27, 17 | Content-Type: application/json; charset=utf-8 18 | }, 19 | Value: 20 | } 21 | } 22 | }, 23 | Log: [ 24 | [GreetingHandler] Received greeting with message: Hello, world!, 25 | [SlowerGreetingHandler] Received greeting with message: Hello, world! 26 | ] 27 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Events/EventHandlerTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class EventHandlerTypeProcessor : AbstractTypeProcessor 4 | { 5 | public override void Process(WebApplicationBuilder builder, Type type) 6 | { 7 | if (type.TryGetGenericInterfaceType(typeof(IEventHandler<>), out var interfaceType)) 8 | { 9 | builder.Services.AddScoped(interfaceType, type); 10 | } 11 | } 12 | 13 | public override void PostProcess(WebApplicationBuilder builder) 14 | { 15 | builder.Services.TryAddSingleton(); 16 | builder.Services.AddScoped(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/GetTokenEndpoint.cs: -------------------------------------------------------------------------------- 1 | using Reprise.SampleApi.Security; 2 | 3 | namespace Reprise.SampleApi.Endpoints 4 | { 5 | [Endpoint] 6 | public class GetTokenEndpoint 7 | { 8 | [Get("/token")] 9 | [AllowAnonymous] 10 | #if NET7_0 11 | [Filter(typeof(GreetingFilter))] 12 | #endif 13 | [Produces(StatusCodes.Status200OK, typeof(TokenResponse))] 14 | [Tags("Public")] 15 | public static TokenResponse Handle(IJwtGenerator generator) 16 | { 17 | var (type, token) = generator.Generate(); 18 | 19 | return new TokenResponse(type, token); 20 | } 21 | } 22 | 23 | public record TokenResponse(string Type, string Token); 24 | } 25 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Create.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 201 Created, 4 | Headers: { 5 | Location: /users/0, 6 | trace-id: {Scrubbed} 7 | }, 8 | Content: { 9 | Headers: { 10 | Content-Type: application/json; charset=utf-8 11 | }, 12 | Value: { 13 | id: 0, 14 | firstName: Leanna, 15 | lastName: Schmeler 16 | } 17 | }, 18 | Request: { 19 | Method: POST, 20 | Uri: http://localhost/users, 21 | Headers: { 22 | Authorization: {Scrubbed} 23 | }, 24 | Content: { 25 | Headers: { 26 | Content-Length: 54, 27 | Content-Type: application/json; charset=utf-8 28 | }, 29 | Value: 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/WeatherTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.IntegrationTests 2 | { 3 | public class WeatherTests : TestBase 4 | { 5 | [Theory] 6 | [InlineData("https://example.com")] 7 | [InlineData("https://contoso.com")] 8 | public async Task Get(string origin) 9 | { 10 | var request = new HttpRequestMessage(HttpMethod.Get, "/weather"); 11 | request.Headers.Add("Origin", origin); 12 | 13 | await Verify(await Client.SendAsync(request)) 14 | .IgnoreMembersWithType() 15 | .ScrubMember("trace-id") 16 | .UniqueForRuntimeAndVersion() 17 | .UseParameters(origin); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/TaskRunnerTests/CreateTimer.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Common.TaskRunnerTests 2 | { 3 | public class CreateTimer : TaskRunnerTestBase 4 | { 5 | [Fact] 6 | public async Task MultipleTimers() 7 | { 8 | var states = Enumerable.Range(0, 5) 9 | .Select(_ => new TimerCallbackMethodState()) 10 | .ToList(); 11 | 12 | foreach (var state in states) 13 | { 14 | TaskRunner.CreateTimer(TimerCallbackMethod, state, TimeSpan.Zero, TimeSpan.FromMilliseconds(100)); 15 | } 16 | 17 | await Task.Delay(250); 18 | Assert.True(states.Sum(s => s.Invocations) >= 10); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Reprise/ExtensionMethods/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Extension methods for adding custom middleware. 5 | /// 6 | public static class ApplicationBuilderExtensions 7 | { 8 | /// 9 | /// Adds the Reprise exception handler. Its behavior can be customized by implementing 10 | /// and/or . 11 | /// 12 | public static IApplicationBuilder UseExceptionHandling(this IApplicationBuilder app) 13 | { 14 | ArgumentNullException.ThrowIfNull(app); 15 | 16 | return app.UseMiddleware(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Jobs/BeforeStartJob.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Jobs 2 | { 3 | [RunBeforeStart] 4 | public class BeforeStartJob : IJob 5 | { 6 | public static int Invocations => _Invocations; 7 | 8 | private static int _Invocations; 9 | 10 | private readonly ILogger _Logger; 11 | 12 | public BeforeStartJob(ILogger logger) 13 | { 14 | _Logger = logger; 15 | } 16 | 17 | public Task Run(CancellationToken cancellationToken) 18 | { 19 | Interlocked.Increment(ref _Invocations); 20 | _Logger.LogInformation("BeforeStartJob invoked."); 21 | 22 | return Task.CompletedTask; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/TestBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Testing; 2 | using Reprise.SampleApi; 3 | 4 | namespace Reprise.IntegrationTests 5 | { 6 | [UsesVerify] 7 | public abstract class TestBase : IDisposable 8 | { 9 | public HttpClient Client { get; } 10 | 11 | private readonly WebApplicationFactory _WebApplicationFactory; 12 | 13 | public TestBase() 14 | { 15 | _WebApplicationFactory = new WebApplicationFactory(); 16 | Client = _WebApplicationFactory.CreateClient(); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | _WebApplicationFactory.Dispose(); 22 | GC.SuppressFinalize(this); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ExtensionMethods/ApplicationBuilderExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.ExtensionMethods 2 | { 3 | [UsesVerify] 4 | public class ApplicationBuilderExtensionsTests 5 | { 6 | [Fact] 7 | public Task UseExceptionHandling() 8 | { 9 | var mockApplicationBuilder = new Mock(); 10 | 11 | mockApplicationBuilder.Object.UseExceptionHandling(); 12 | 13 | return Verify(mockApplicationBuilder); 14 | } 15 | 16 | [Fact] 17 | public Task UseExceptionHandling_AppNull() 18 | { 19 | return Throws(() => ApplicationBuilderExtensions.UseExceptionHandling(null!)) 20 | .IgnoreStackTrace(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Create_BadRequest.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | message: Validation failed, 10 | details: { 11 | FirstName: 'First Name' must not be empty., 12 | LastName: 'Last Name' must not be empty. 13 | } 14 | } 15 | }, 16 | Request: { 17 | Method: POST, 18 | Uri: http://localhost/users, 19 | Headers: { 20 | Authorization: {Scrubbed} 21 | }, 22 | Content: { 23 | Headers: { 24 | Content-Length: 42, 25 | Content-Type: application/json; charset=utf-8 26 | }, 27 | Value: 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update_BadRequest.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | message: Validation failed, 10 | details: { 11 | FirstName: 'First Name' must not be empty., 12 | LastName: 'Last Name' must not be empty. 13 | } 14 | } 15 | }, 16 | Request: { 17 | Method: PUT, 18 | Uri: http://localhost/users/7, 19 | Headers: { 20 | Authorization: {Scrubbed} 21 | }, 22 | Content: { 23 | Headers: { 24 | Content-Length: 42, 25 | Content-Type: application/json; charset=utf-8 26 | }, 27 | Value: 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | NameProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | } 20 | ], 21 | RequestDelegate: { 22 | Type: RequestDelegate, 23 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 24 | } 25 | } 26 | ] 27 | } 28 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Filters/ValidationFilterTests.MultipleTypesInvalidRequest.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: { 9 | errors: { 10 | Audience: 'Audience' must not be empty. 11 | } 12 | } 13 | }, 14 | Request: { 15 | Method: POST, 16 | Uri: { 17 | Path: http://localhost/, 18 | Query: { 19 | audience: 20 | } 21 | }, 22 | Headers: { 23 | Transfer-Encoding: chunked 24 | }, 25 | Content: { 26 | Headers: { 27 | Content-Type: application/json; charset=utf-8 28 | }, 29 | Value: { 30 | message: Hello 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Produces/ProducesProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ProducesProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | } 20 | ], 21 | RequestDelegate: { 22 | Type: RequestDelegate, 23 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 24 | } 25 | } 26 | ] 27 | } 28 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/TaskRunnerTests/TaskRunnerTestBase.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Common.TaskRunnerTests 2 | { 3 | [UsesVerify] 4 | public abstract class TaskRunnerTestBase : IDisposable 5 | { 6 | internal TaskRunner TaskRunner { get; } = new(); 7 | 8 | public void Dispose() 9 | { 10 | TaskRunner.Dispose(); 11 | GC.SuppressFinalize(this); 12 | } 13 | 14 | internal static void TimerCallbackMethod(object? state) 15 | { 16 | var timerCallbackMethodState = (state as TimerCallbackMethodState)!; 17 | timerCallbackMethodState.Invocations++; 18 | } 19 | } 20 | 21 | internal class TimerCallbackMethodState 22 | { 23 | public int Invocations { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/GetUserEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | [Endpoint] 4 | public class GetUserEndpoint 5 | { 6 | public const string Id = "GetUser"; 7 | 8 | [Get("/users/{id}")] 9 | [Produces(StatusCodes.Status200OK, typeof(User))] 10 | [Produces(StatusCodes.Status404NotFound)] 11 | [Name(Id)] 12 | public static IResult Handle(int id, DataContext context, IMapper mapper) 13 | { 14 | var user = context.Users.FirstOrDefault(u => u.Id == id); 15 | if (user == null) 16 | { 17 | return Results.NotFound(); 18 | } 19 | var userDto = mapper.Map(user); 20 | 21 | return Results.Ok(userDto); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_NoAuthorization.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__5_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | } 20 | ], 21 | RequestDelegate: { 22 | Type: RequestDelegate, 23 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 24 | } 25 | } 26 | ] 27 | } 28 | ] -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Create_BadRequest.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: { 12 | message: Validation failed, 13 | details: { 14 | FirstName: 'First Name' must not be empty., 15 | LastName: 'Last Name' must not be empty. 16 | } 17 | } 18 | }, 19 | Request: { 20 | Method: POST, 21 | Uri: http://localhost/users, 22 | Headers: { 23 | Authorization: {Scrubbed} 24 | }, 25 | Content: { 26 | Headers: { 27 | Content-Length: 42, 28 | Content-Type: application/json; charset=utf-8 29 | }, 30 | Value: 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.Update_BadRequest.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 400 Bad Request, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: { 12 | message: Validation failed, 13 | details: { 14 | FirstName: 'First Name' must not be empty., 15 | LastName: 'Last Name' must not be empty. 16 | } 17 | } 18 | }, 19 | Request: { 20 | Method: PUT, 21 | Uri: http://localhost/users/7, 22 | Headers: { 23 | Authorization: {Scrubbed} 24 | }, 25 | Content: { 26 | Headers: { 27 | Content-Length: 42, 28 | Content-Type: application/json; charset=utf-8 29 | }, 30 | Value: 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionProcessorTests.Process_NoAttribute.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ExcludeFromDescriptionProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | } 20 | ], 21 | RequestDelegate: { 22 | Type: RequestDelegate, 23 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 24 | } 25 | } 26 | ] 27 | } 28 | ] -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/ErrorHandling/ExceptionLogger.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.ErrorHandling 2 | { 3 | public class ExceptionLogger : IExceptionLogger 4 | { 5 | public void Log(ILogger logger, ErrorContext context) 6 | { 7 | logger.LogError("BadHttpRequestException: {Message}", context.Exception.Message); 8 | } 9 | 10 | public void Log(ILogger logger, ErrorContext context) 11 | { 12 | logger.LogError("ValidationException: {Message}", context.Exception.Message); 13 | } 14 | 15 | public void Log(ILogger logger, ErrorContext context) 16 | { 17 | logger.LogError(context.Exception, "An unhandled exception has occurred while executing the request."); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Greetings/GetGreetingsEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace Reprise.SampleApi.Endpoints.Greetings 4 | { 5 | [Endpoint] 6 | public class GetGreetingsEndpoint 7 | { 8 | [Get("/greetings")] 9 | [AllowAnonymous] 10 | [Produces(StatusCodes.Status200OK, typeof(IEnumerable))] 11 | public static IEnumerable Handle(GreetingConfiguration configuration) 12 | { 13 | return configuration.Names.Select(name => string.Format(CultureInfo.InvariantCulture, configuration.Message, name)); 14 | } 15 | } 16 | 17 | [Configuration("Greeting")] 18 | public class GreetingConfiguration 19 | { 20 | public string Message { get; set; } = null!; 21 | 22 | public List Names { get; set; } = null!; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__2_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | TypeId: AuthorizeAttribute 22 | } 23 | ], 24 | RequestDelegate: { 25 | Type: RequestDelegate, 26 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 27 | } 28 | } 29 | ] 30 | } 31 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/TaskRunnerTests/Dispose.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Common.TaskRunnerTests 2 | { 3 | public class Dispose : TaskRunnerTestBase 4 | { 5 | [Fact] 6 | public void NoTimers() 7 | { 8 | TaskRunner.Dispose(); 9 | } 10 | 11 | [Fact] 12 | public void MultipleTimers() 13 | { 14 | var timers = Enumerable.Range(0, 5) 15 | .Select(_ => TaskRunner.CreateTimer(TimerCallbackMethod, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan)) 16 | .ToList(); 17 | 18 | TaskRunner.Dispose(); 19 | 20 | foreach (var timer in timers) 21 | { 22 | Assert.Throws(() => timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Reprise/Features/Mappers/IMapper.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Specifies the contract to map objects. 5 | /// 6 | public interface IMapper 7 | { 8 | /// 9 | /// Maps a source object to a new destination object. 10 | /// 11 | T1 Map(T2 source); 12 | 13 | /// 14 | /// Maps a source object to a new destination object. 15 | /// 16 | T2 Map(T1 source); 17 | 18 | /// 19 | /// Maps a source object to an existing destination object. 20 | /// 21 | void Map(T2 source, T1 destination); 22 | 23 | /// 24 | /// Maps a source object to an existing destination object. 25 | /// 26 | void Map(T1 source, T2 destination); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Reprise/Features/Configurations/ConfigurationAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Identifies a strongly-typed hierarchical configuration model bound at application startup. 5 | /// 6 | /// 7 | /// A configuration class should be public non-abstract with a public parameterless constructor. 8 | /// All public read-write properties of the type are bound. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class)] 11 | public sealed class ConfigurationAttribute : Attribute 12 | { 13 | internal string SubSectionKey { get; } 14 | 15 | /// 16 | /// Creates a new . 17 | /// 18 | public ConfigurationAttribute(string subSectionKey) 19 | { 20 | SubSectionKey = subSectionKey; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/ErrorHandling/ErrorResponseFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.ErrorHandling 2 | { 3 | public class ErrorResponseFactory : IErrorResponseFactory 4 | { 5 | public object? Create(ErrorContext context) 6 | { 7 | return new ErrorResponse(context.Exception.Message); 8 | } 9 | 10 | public object? Create(ErrorContext context) 11 | { 12 | return new ErrorResponse("Validation failed", 13 | context.Exception.Errors.ToDictionary(f => f.PropertyName, f => f.ErrorMessage)); 14 | } 15 | 16 | public object? Create(ErrorContext context) 17 | { 18 | return new ErrorResponse(context.Exception.Message); 19 | } 20 | } 21 | 22 | public record ErrorResponse(string Message, object? Details = null); 23 | } 24 | -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/RateLimitingTests.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise.IntegrationTests 3 | { 4 | [UsesVerify] 5 | public class RateLimitingTests : TestBase 6 | { 7 | [Fact] 8 | public async Task Get() 9 | { 10 | var response = await Client.GetAsync("/limited"); 11 | response.EnsureSuccessStatusCode(); 12 | await Task.Delay(1_100); 13 | 14 | await Verify(await Client.GetAsync("/limited")) 15 | .ScrubMember("trace-id"); 16 | } 17 | 18 | [Fact] 19 | public async Task GetLimited() 20 | { 21 | var response = await Client.GetAsync("/limited"); 22 | response.EnsureSuccessStatusCode(); 23 | 24 | await Verify(await Client.GetAsync("/limited")) 25 | .ScrubMember("trace-id"); 26 | } 27 | } 28 | } 29 | #endif 30 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | NameProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | EndpointName: Test 22 | }, 23 | { 24 | RouteName: Test 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /docs/Reprise.IJob.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## IJob Interface 4 | 5 | Specifies the contract for running jobs. 6 | 7 | ```csharp 8 | public interface IJob 9 | ``` 10 | ### Methods 11 | 12 | 13 | 14 | ## IJob.Run(CancellationToken) Method 15 | 16 | Runs the job. 17 | 18 | ```csharp 19 | System.Threading.Tasks.Task Run(System.Threading.CancellationToken cancellationToken); 20 | ``` 21 | #### Parameters 22 | 23 | 24 | 25 | `cancellationToken` [System.Threading.CancellationToken](https://docs.microsoft.com/en-us/dotnet/api/System.Threading.CancellationToken 'System.Threading.CancellationToken') 26 | 27 | #### Returns 28 | [System.Threading.Tasks.Task](https://docs.microsoft.com/en-us/dotnet/api/System.Threading.Tasks.Task 'System.Threading.Tasks.Task') -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.GetAll.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Content: { 5 | Headers: { 6 | Content-Type: application/json; charset=utf-8 7 | }, 8 | Value: [ 9 | { 10 | id: 0, 11 | firstName: Leanna, 12 | lastName: Schmeler 13 | }, 14 | { 15 | id: 1, 16 | firstName: Nestor, 17 | lastName: Moen 18 | }, 19 | { 20 | id: 2, 21 | firstName: Cornell, 22 | lastName: Ratke 23 | }, 24 | { 25 | id: 3, 26 | firstName: Danika, 27 | lastName: Hettinger 28 | }, 29 | { 30 | id: 4, 31 | firstName: Everette, 32 | lastName: Gerhold 33 | } 34 | ] 35 | }, 36 | Request: { 37 | Uri: http://localhost/users, 38 | Headers: { 39 | Authorization: {Scrubbed} 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Test 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks/Reprise.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks/StartupBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Engines; 2 | 3 | namespace Reprise.Benchmarks 4 | { 5 | [MemoryDiagnoser] 6 | [SimpleJob(RunStrategy.ColdStart, invocationCount: 32)] 7 | public class StartupBenchmarks : BenchmarksBase 8 | { 9 | [Benchmark] 10 | public HttpClient Reprise() 11 | { 12 | return CreateClient(); 13 | } 14 | 15 | [Benchmark] 16 | public HttpClient Carter() 17 | { 18 | return CreateClient(); 19 | } 20 | 21 | [Benchmark] 22 | public HttpClient FastEndpoints() 23 | { 24 | return CreateClient(); 25 | } 26 | 27 | [Benchmark] 28 | public HttpClient MinimalApis() 29 | { 30 | return CreateClient(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_PolicyName.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Policy: Policy1, 22 | TypeId: AuthorizeAttribute 23 | } 24 | ], 25 | RequestDelegate: { 26 | Type: RequestDelegate, 27 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 28 | } 29 | } 30 | ] 31 | } 32 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | NameProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route= .DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-{id}.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route={id}.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /src/Reprise/Features/Filters/EndpointOptions.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise 3 | { 4 | public partial class EndpointOptions 5 | { 6 | internal List<(int Order, Type FilterType)> FilterTypes { get; } = new(); 7 | 8 | private int _CurrentFilterOrder; 9 | 10 | /// 11 | /// Adds a filter for all API endpoints. 12 | /// 13 | public void AddEndpointFilter(int? order = null) where TFilter : IEndpointFilter 14 | { 15 | FilterTypes.Add((order ?? _CurrentFilterOrder++, typeof(TFilter))); 16 | } 17 | 18 | /// 19 | /// Adds a validation filter for all API endpoints. 20 | /// 21 | public void AddValidationFilter(int? order = null) 22 | { 23 | FilterTypes.Add((order ?? _CurrentFilterOrder++, typeof(ValidationFilterFactory))); 24 | } 25 | } 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-{id}.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | / 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-users.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Users 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.Carter/Program.cs: -------------------------------------------------------------------------------- 1 | using Reprise.Benchmarks.Api.Carter.Modules; 2 | 3 | namespace Reprise.Benchmarks.Api.Carter 4 | { 5 | public class Program 6 | { 7 | public static void Main() 8 | { 9 | var builder = WebApplication.CreateBuilder(); 10 | 11 | builder.Logging.ClearProviders(); 12 | 13 | builder.Services.AddCarter(new DependencyContextAssemblyCatalog(new[] { typeof(Program).Assembly }), options => 14 | { 15 | options.WithEmptyResponseNegotiators(); 16 | }); 17 | 18 | builder.Services.Configure( 19 | builder.Configuration.GetSection("Greeting"), 20 | config => config.ErrorOnUnknownConfiguration = true); 21 | 22 | var app = builder.Build(); 23 | 24 | app.MapCarter(); 25 | 26 | app.Run(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Produces/ProducesProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ProducesProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-users.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Users 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-users-{id}.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Users 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-{id}-users.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Users 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-users-{id}.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Tags: [ 22 | Users 23 | ], 24 | TypeId: TagsAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Common/TaskRunnerTests/StopTimers.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Common.TaskRunnerTests 2 | { 3 | public class StopTimers : TaskRunnerTestBase 4 | { 5 | [Fact] 6 | public void NoTimers() 7 | { 8 | TaskRunner.StopTimers(); 9 | } 10 | 11 | [Fact] 12 | public async Task MultipleTimers() 13 | { 14 | var states = Enumerable.Range(0, 5) 15 | .Select(_ => new TimerCallbackMethodState()) 16 | .ToList(); 17 | foreach (var state in states) 18 | { 19 | TaskRunner.CreateTimer(TimerCallbackMethod, state, TimeSpan.Zero, TimeSpan.FromMilliseconds(100)); 20 | } 21 | await Task.Delay(50); 22 | 23 | await TaskRunner.StopTimers(); 24 | 25 | await Task.Delay(150); 26 | Assert.True(states.Sum(s => s.Invocations) <= 5); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionProcessorTests.Process.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ExcludeFromDescriptionProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | ExcludeFromDescription: true, 22 | TypeId: ExcludeFromDescriptionAttribute 23 | } 24 | ], 25 | RequestDelegate: { 26 | Type: RequestDelegate, 27 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 28 | } 29 | } 30 | ] 31 | } 32 | ] -------------------------------------------------------------------------------- /docs/Reprise.CronAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## CronAttribute Class 4 | 5 | Identifies a job that runs on a schedule. 6 | 7 | ```csharp 8 | public class CronAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 CronAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## CronAttribute(string) Constructor 17 | 18 | Creates a new [CronAttribute](Reprise.CronAttribute.md 'Reprise.CronAttribute'). 19 | 20 | ```csharp 21 | public CronAttribute(string expression); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `expression` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/RateLimiting/RateLimitingProcessorTests.Process_NoRateLimiting.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | RateLimitingProcessorTests.<>c.b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.IntegrationTests/UsersTests.GetAll.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Version: 1.1, 3 | Status: 200 OK, 4 | Headers: { 5 | trace-id: {Scrubbed} 6 | }, 7 | Content: { 8 | Headers: { 9 | Content-Type: application/json; charset=utf-8 10 | }, 11 | Value: [ 12 | { 13 | id: 0, 14 | firstName: Leanna, 15 | lastName: Schmeler 16 | }, 17 | { 18 | id: 1, 19 | firstName: Nestor, 20 | lastName: Moen 21 | }, 22 | { 23 | id: 2, 24 | firstName: Cornell, 25 | lastName: Ratke 26 | }, 27 | { 28 | id: 3, 29 | firstName: Danika, 30 | lastName: Hettinger 31 | }, 32 | { 33 | id: 4, 34 | firstName: Everette, 35 | lastName: Gerhold 36 | } 37 | ] 38 | }, 39 | Request: { 40 | Uri: http://localhost/users, 41 | Headers: { 42 | Authorization: {Scrubbed} 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_NoAuthorization.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__5_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/Reprise.Benchmarks.Api.FastEndpoints/Program.cs: -------------------------------------------------------------------------------- 1 | using Reprise.Benchmarks.Api.FastEndpoints.Endpoints; 2 | 3 | namespace Reprise.Benchmarks.Api.FastEndpoints 4 | { 5 | public class Program 6 | { 7 | public static void Main() 8 | { 9 | var builder = WebApplication.CreateBuilder(); 10 | 11 | builder.Logging.ClearProviders(); 12 | 13 | builder.Services.AddFastEndpoints(options => 14 | { 15 | options.DisableAutoDiscovery = true; 16 | options.Assemblies = new[] { typeof(Program).Assembly }; 17 | }); 18 | 19 | builder.Services.Configure( 20 | builder.Configuration.GetSection("Greeting"), 21 | config => config.ErrorOnUnknownConfiguration = true); 22 | 23 | var app = builder.Build(); 24 | 25 | app.UseFastEndpoints(); 26 | 27 | app.Run(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Reprise/Features/Validation/ValidatorTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ValidatorTypeProcessor : AbstractTypeProcessor 4 | { 5 | private readonly Dictionary _Validators = new(); 6 | 7 | public override void Process(WebApplicationBuilder builder, Type type) 8 | { 9 | if (type.TryGetGenericInterfaceType(typeof(IValidator<>), out var interfaceType)) 10 | { 11 | if (_Validators.TryGetValue(interfaceType.GenericTypeArguments[0], out var existingType)) 12 | { 13 | throw new InvalidOperationException( 14 | $"{interfaceType.GenericTypeArguments[0]} is validated by both {type} and {existingType}."); 15 | } 16 | builder.Services.AddSingleton(interfaceType, type); 17 | _Validators[interfaceType.GenericTypeArguments[0]] = type; 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionProcessorTests.Process_NoAttribute.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ExcludeFromDescriptionProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | } 26 | ], 27 | RequestDelegate: { 28 | Type: RequestDelegate, 29 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 30 | } 31 | } 32 | ] 33 | } 34 | ] -------------------------------------------------------------------------------- /docs/Reprise.EndpointAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## EndpointAttribute Class 4 | 5 | Identifies a type that is an API endpoint. 6 | 7 | ```csharp 8 | public sealed class EndpointAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 EndpointAttribute 12 | 13 | ### Remarks 14 | An API endpoint contains a public static `Handle` method 15 | that is decorated with a [GetAttribute](Reprise.GetAttribute.md 'Reprise.GetAttribute'), [PostAttribute](Reprise.PostAttribute.md 'Reprise.PostAttribute'), 16 | [PutAttribute](Reprise.PutAttribute.md 'Reprise.PutAttribute'), [PatchAttribute](Reprise.PatchAttribute.md 'Reprise.PatchAttribute'), [DeleteAttribute](Reprise.DeleteAttribute.md 'Reprise.DeleteAttribute') 17 | or [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute'). -------------------------------------------------------------------------------- /src/Reprise/Features/Filters/FilterAttribute.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | namespace Reprise 3 | { 4 | /// 5 | /// Specifies a filter for the API endpoint. 6 | /// 7 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 8 | public sealed class FilterAttribute : Attribute 9 | { 10 | internal int Order { get; } 11 | 12 | internal Type FilterType { get; } 13 | 14 | /// 15 | /// Creates a new with default order value. 16 | /// 17 | public FilterAttribute(Type filterType) : this(filterType, int.MaxValue) 18 | { 19 | } 20 | 21 | /// 22 | /// Creates a new with custom order value. 23 | /// 24 | public FilterAttribute(Type filterType, int order) 25 | { 26 | FilterType = filterType; 27 | Order = order; 28 | } 29 | } 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/UpdateUserEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | [Endpoint] 4 | public class UpdateUserEndpoint 5 | { 6 | [Put("/users/{id}")] 7 | [Produces(StatusCodes.Status204NoContent)] 8 | [Produces(StatusCodes.Status404NotFound)] 9 | [ProducesBadRequest] 10 | public static IResult Handle( 11 | int id, 12 | UserDto userDto, 13 | #if NET6_0 14 | IValidator validator, 15 | #endif 16 | IMapper mapper, 17 | DataContext context) 18 | { 19 | #if NET6_0 20 | validator.ValidateAndThrow(userDto); 21 | #endif 22 | var user = context.Users.FirstOrDefault(u => u.Id == id); 23 | if (user == null) 24 | { 25 | return Results.NotFound(); 26 | } 27 | mapper.Map(userDto, user); 28 | 29 | return Results.NoContent(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__2_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | TypeId: AuthorizeAttribute 28 | } 29 | ], 30 | RequestDelegate: { 31 | Type: RequestDelegate, 32 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 33 | } 34 | } 35 | ] 36 | } 37 | ] -------------------------------------------------------------------------------- /docs/Reprise.NameAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## NameAttribute Class 4 | 5 | Specifies a name that is used for link generation and is treated as the operation ID in the OpenAPI description. 6 | 7 | ```csharp 8 | public sealed class NameAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 NameAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## NameAttribute(string) Constructor 17 | 18 | Creates a new [NameAttribute](Reprise.NameAttribute.md 'Reprise.NameAttribute'). 19 | 20 | ```csharp 21 | public NameAttribute(string name); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `name` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/CreateUserEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | [Endpoint] 4 | public class CreateUserEndpoint 5 | { 6 | [Post("/users")] 7 | [Produces(StatusCodes.Status201Created)] 8 | [ProducesBadRequest] 9 | public static IResult Handle( 10 | UserDto userDto, 11 | #if NET6_0 12 | IValidator validator, 13 | #endif 14 | IMapper mapper, 15 | DataContext context, 16 | LinkGenerator linker) 17 | { 18 | #if NET6_0 19 | validator.ValidateAndThrow(userDto); 20 | #endif 21 | var user = new User() 22 | { 23 | Id = !context.Users.Any() ? 0 : context.Users.Max(u => u.Id) + 1 24 | }; 25 | mapper.Map(userDto, user); 26 | context.Users.Add(user); 27 | 28 | return Results.Created(linker.GetPathByName(GetUserEndpoint.Id, values: new { user.Id })!, user); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/Reprise.GetAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## GetAttribute Class 4 | 5 | Identifies a public static `Handle` method that supports the HTTP GET method. 6 | 7 | ```csharp 8 | public sealed class GetAttribute : Reprise.MapAttribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute') 🡒 GetAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## GetAttribute(string) Constructor 17 | 18 | Creates a new [GetAttribute](Reprise.GetAttribute.md 'Reprise.GetAttribute'). 19 | 20 | ```csharp 21 | public GetAttribute(string route); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `route` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /docs/Reprise.PutAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## PutAttribute Class 4 | 5 | Identifies a public static `Handle` method that supports the HTTP PUT method. 6 | 7 | ```csharp 8 | public sealed class PutAttribute : Reprise.MapAttribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute') 🡒 PutAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## PutAttribute(string) Constructor 17 | 18 | Creates a new [PutAttribute](Reprise.PutAttribute.md 'Reprise.PutAttribute'). 19 | 20 | ```csharp 21 | public PutAttribute(string route); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `route` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_MultiplePolicyNames.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__4_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Policy: Policy1, 22 | TypeId: AuthorizeAttribute 23 | }, 24 | { 25 | Policy: Policy2, 26 | TypeId: AuthorizeAttribute 27 | } 28 | ], 29 | RequestDelegate: { 30 | Type: RequestDelegate, 31 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 32 | } 33 | } 34 | ] 35 | } 36 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Name/NameProcessorTests.Process.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | NameProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | EndpointName: Test 28 | }, 29 | { 30 | RouteName: Test 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Test 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/GetLimitedEndpoint.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0 2 | using Microsoft.AspNetCore.RateLimiting; 3 | 4 | namespace Reprise.SampleApi.Endpoints 5 | { 6 | [Endpoint] 7 | public class GetLimitedEndpoint : IServiceConfigurator 8 | { 9 | public void ConfigureServices(WebApplicationBuilder builder) 10 | { 11 | builder.Services.AddRateLimiter(options => 12 | { 13 | options.AddFixedWindowLimiter("FixedWindowOneSecond", fixedWindowOptions => 14 | { 15 | fixedWindowOptions.PermitLimit = 1; 16 | fixedWindowOptions.Window = TimeSpan.FromSeconds(1); 17 | }); 18 | }); 19 | } 20 | 21 | [Get("/limited")] 22 | [AllowAnonymous] 23 | [EnableRateLimiting("FixedWindowOneSecond")] 24 | [Produces(StatusCodes.Status200OK)] 25 | [Tags("Public")] 26 | public static IResult Handle() 27 | { 28 | return Results.Ok(); 29 | } 30 | } 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/RateLimiting/RateLimitingProcessorTests.Process.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | RateLimitingProcessorTests.<>c.b__2_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | PolicyName: TestPolicy, 28 | TypeId: EnableRateLimitingAttribute 29 | } 30 | ], 31 | RequestDelegate: { 32 | Type: RequestDelegate, 33 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 34 | } 35 | } 36 | ] 37 | } 38 | ] -------------------------------------------------------------------------------- /docs/Reprise.PostAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## PostAttribute Class 4 | 5 | Identifies a public static `Handle` method that supports the HTTP POST method. 6 | 7 | ```csharp 8 | public sealed class PostAttribute : Reprise.MapAttribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute') 🡒 PostAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## PostAttribute(string) Constructor 17 | 18 | Creates a new [PostAttribute](Reprise.PostAttribute.md 'Reprise.PostAttribute'). 19 | 20 | ```csharp 21 | public PostAttribute(string route); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `route` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /src/Reprise/Features/Jobs/Attributes.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Identifies a job that runs before the server is started. 5 | /// 6 | [AttributeUsage(AttributeTargets.Class)] 7 | public sealed class RunBeforeStartAttribute : Attribute 8 | { 9 | } 10 | 11 | /// 12 | /// Identifies a job that runs after the server is started. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class)] 15 | public sealed class RunOnStartAttribute : Attribute 16 | { 17 | } 18 | 19 | /// 20 | /// Identifies a job that runs on a schedule. 21 | /// 22 | [AttributeUsage(AttributeTargets.Class)] 23 | public class CronAttribute : Attribute 24 | { 25 | internal string Expression { get; } 26 | 27 | /// 28 | /// Creates a new . 29 | /// 30 | public CronAttribute(string expression) 31 | { 32 | Expression = expression; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_PolicyName.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Policy: Policy1, 28 | TypeId: AuthorizeAttribute 29 | } 30 | ], 31 | RequestDelegate: { 32 | Type: RequestDelegate, 33 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 34 | } 35 | } 36 | ] 37 | } 38 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route= .DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-{id}.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route={id}.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /docs/Reprise.PatchAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## PatchAttribute Class 4 | 5 | Identifies a public static `Handle` method that supports the HTTP PATCH method. 6 | 7 | ```csharp 8 | public sealed class PatchAttribute : Reprise.MapAttribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute') 🡒 PatchAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## PatchAttribute(string) Constructor 17 | 18 | Creates a new [PatchAttribute](Reprise.PatchAttribute.md 'Reprise.PatchAttribute'). 19 | 20 | ```csharp 21 | public PatchAttribute(string route); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `route` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Reprise.SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-{id}.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | / 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-users.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Users 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-users.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Users 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-users-{id}.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Users 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-{id}-users.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Users 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/Produces/ProducesAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | /// 4 | /// Describes a response returned from an API endpoint. 5 | /// 6 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 7 | public class ProducesAttribute : Attribute 8 | { 9 | internal int StatusCode { get; } 10 | 11 | internal Type? ResponseType { get; } 12 | 13 | internal string? ContentType { get; } 14 | 15 | internal string[] AdditionalContentTypes { get; } 16 | 17 | /// 18 | /// Creates a new . 19 | /// 20 | public ProducesAttribute(int statusCode, 21 | Type? responseType = null, 22 | string? contentType = null, 23 | params string[] additionalContentTypes) 24 | { 25 | StatusCode = statusCode; 26 | ResponseType = responseType; 27 | ContentType = contentType; 28 | AdditionalContentTypes = additionalContentTypes; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Jobs/JobRunnerTests/StartAsync.BeforeStartJobs.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | Jobs: [ 4 | { 5 | ServiceScopeId: Guid_1, 6 | WorkerStatus: Done, 7 | IsDisposed: true 8 | }, 9 | { 10 | ServiceScopeId: Guid_2, 11 | WorkerStatus: Done, 12 | IsDisposed: true 13 | }, 14 | { 15 | ServiceScopeId: Guid_3, 16 | WorkerStatus: Done, 17 | IsDisposed: true 18 | } 19 | ], 20 | MockTaskRunner: [ 21 | { 22 | Method: TaskRunner.WhenAll(IEnumerable tasks), 23 | Arguments: [ 24 | [ 25 | { 26 | Status: WaitingForActivation 27 | }, 28 | { 29 | Status: WaitingForActivation 30 | }, 31 | { 32 | Status: WaitingForActivation 33 | } 34 | ] 35 | ], 36 | ReturnValue: { 37 | Status: RanToCompletion 38 | } 39 | } 40 | ], 41 | MockDateTimeProvider: [] 42 | }, 43 | logs: [] 44 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Tags/TagsProcessorTests.Process_NoAttribute_route=-api-users-{id}.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | TagsProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Tags: [ 28 | Users 29 | ], 30 | TypeId: TagsAttribute 31 | } 32 | ], 33 | RequestDelegate: { 34 | Type: RequestDelegate, 35 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 36 | } 37 | } 38 | ] 39 | } 40 | ] -------------------------------------------------------------------------------- /docs/Reprise.DeleteAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## DeleteAttribute Class 4 | 5 | Identifies a public static `Handle` method that supports the HTTP DELETE method. 6 | 7 | ```csharp 8 | public sealed class DeleteAttribute : Reprise.MapAttribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 [MapAttribute](Reprise.MapAttribute.md 'Reprise.MapAttribute') 🡒 DeleteAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## DeleteAttribute(string) Constructor 17 | 18 | Creates a new [DeleteAttribute](Reprise.DeleteAttribute.md 'Reprise.DeleteAttribute'). 19 | 20 | ```csharp 21 | public DeleteAttribute(string route); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `route` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/ExcludeFromDescription/ExcludeFromDescriptionProcessorTests.Process.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ExcludeFromDescriptionProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | ExcludeFromDescription: true, 28 | TypeId: ExcludeFromDescriptionAttribute 29 | } 30 | ], 31 | RequestDelegate: { 32 | Type: RequestDelegate, 33 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 34 | } 35 | } 36 | ] 37 | } 38 | ] -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Events/EventBusTests/Publish.MultipleHandlers.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | RequestScopeIdentifier: { 4 | ScopeId: Guid_1 5 | }, 6 | EventHandlers: [ 7 | { 8 | ServiceScopeId: Guid_2, 9 | WorkerStatus: Done, 10 | IsDisposed: true 11 | }, 12 | { 13 | ServiceScopeId: Guid_2, 14 | WorkerStatus: Done, 15 | IsDisposed: true 16 | }, 17 | { 18 | ServiceScopeId: Guid_2, 19 | WorkerStatus: Done, 20 | IsDisposed: true 21 | } 22 | ], 23 | MockTaskRunner: [ 24 | { 25 | Method: TaskRunner.WhenAll(IEnumerable tasks), 26 | Arguments: [ 27 | [ 28 | { 29 | Status: WaitingForActivation 30 | }, 31 | { 32 | Status: WaitingForActivation 33 | }, 34 | { 35 | Status: WaitingForActivation 36 | } 37 | ] 38 | ], 39 | ReturnValue: { 40 | Status: RanToCompletion 41 | } 42 | } 43 | ] 44 | }, 45 | logs: [] 46 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ReflectionTests.PublicTypes.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Interface: [ 3 | Reprise.IEvent, 4 | Reprise.IEventHandler`1, 5 | Reprise.IEventBus, 6 | Reprise.IExceptionLogger, 7 | Reprise.IErrorResponseFactory, 8 | Reprise.IJob, 9 | Reprise.IMapper`2, 10 | Reprise.IServiceConfigurator 11 | ], 12 | Sealed: [ 13 | Reprise.EndpointOptions, 14 | Reprise.ConfigurationAttribute, 15 | Reprise.EndpointAttribute, 16 | Reprise.GetAttribute, 17 | Reprise.PostAttribute, 18 | Reprise.PutAttribute, 19 | Reprise.PatchAttribute, 20 | Reprise.DeleteAttribute, 21 | Reprise.ErrorContext`1, 22 | Reprise.RunBeforeStartAttribute, 23 | Reprise.RunOnStartAttribute, 24 | Reprise.ExcludeFromDescriptionAttribute, 25 | Reprise.NameAttribute, 26 | Reprise.TagsAttribute 27 | ], 28 | Open: [ 29 | Reprise.MapAttribute, 30 | Reprise.CronAttribute, 31 | Reprise.ProducesAttribute 32 | ], 33 | Static: [ 34 | Reprise.ApplicationBuilderExtensions, 35 | Reprise.EndpointRouteBuilderExtensions, 36 | Reprise.WebApplicationBuilderExtensions 37 | ] 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yavor Fingarov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Events/EventBusTests/PublishAndWait.MultipleHandlers.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | RequestScopeIdentifier: { 4 | ScopeId: Guid_1 5 | }, 6 | EventHandlers: [ 7 | { 8 | ServiceScopeId: Guid_1, 9 | WorkerStatus: Done, 10 | IsDisposed: false 11 | }, 12 | { 13 | ServiceScopeId: Guid_1, 14 | WorkerStatus: Done, 15 | IsDisposed: false 16 | }, 17 | { 18 | ServiceScopeId: Guid_1, 19 | WorkerStatus: Done, 20 | IsDisposed: false 21 | } 22 | ], 23 | MockTaskRunner: [ 24 | { 25 | Method: TaskRunner.WhenAll(IEnumerable tasks), 26 | Arguments: [ 27 | [ 28 | { 29 | Status: WaitingForActivation 30 | }, 31 | { 32 | Status: WaitingForActivation 33 | }, 34 | { 35 | Status: WaitingForActivation 36 | } 37 | ] 38 | ], 39 | ReturnValue: { 40 | Status: RanToCompletion 41 | } 42 | } 43 | ] 44 | }, 45 | logs: [] 46 | } -------------------------------------------------------------------------------- /src/Reprise/Features/ExceptionHandling/ExceptionLoggerTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ExceptionLoggerTypeProcessor : AbstractTypeProcessor 4 | { 5 | private Type? _ExceptionLoggerType; 6 | 7 | public override void Process(WebApplicationBuilder builder, Type type) 8 | { 9 | if (type.IsAssignableTo(typeof(IExceptionLogger))) 10 | { 11 | if (_ExceptionLoggerType != null) 12 | { 13 | throw new InvalidOperationException( 14 | $"{nameof(IExceptionLogger)} is implemented by both {_ExceptionLoggerType} and {type}."); 15 | } 16 | builder.Services.AddSingleton(typeof(IExceptionLogger), type); 17 | _ExceptionLoggerType = type; 18 | } 19 | } 20 | 21 | public override void PostProcess(WebApplicationBuilder builder) 22 | { 23 | if (_ExceptionLoggerType == null) 24 | { 25 | builder.Services.AddSingleton(); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ReflectionTests.PublicTypes.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Interface: [ 3 | Reprise.IEvent, 4 | Reprise.IEventHandler`1, 5 | Reprise.IEventBus, 6 | Reprise.IExceptionLogger, 7 | Reprise.IErrorResponseFactory, 8 | Reprise.IJob, 9 | Reprise.IMapper`2, 10 | Reprise.IServiceConfigurator 11 | ], 12 | Sealed: [ 13 | Reprise.EndpointOptions, 14 | Reprise.ConfigurationAttribute, 15 | Reprise.EndpointAttribute, 16 | Reprise.GetAttribute, 17 | Reprise.PostAttribute, 18 | Reprise.PutAttribute, 19 | Reprise.PatchAttribute, 20 | Reprise.DeleteAttribute, 21 | Reprise.ErrorContext`1, 22 | Reprise.FilterAttribute, 23 | Reprise.RunBeforeStartAttribute, 24 | Reprise.RunOnStartAttribute, 25 | Reprise.ExcludeFromDescriptionAttribute, 26 | Reprise.NameAttribute, 27 | Reprise.TagsAttribute 28 | ], 29 | Open: [ 30 | Reprise.MapAttribute, 31 | Reprise.CronAttribute, 32 | Reprise.ProducesAttribute 33 | ], 34 | Static: [ 35 | Reprise.ApplicationBuilderExtensions, 36 | Reprise.EndpointRouteBuilderExtensions, 37 | Reprise.WebApplicationBuilderExtensions 38 | ] 39 | } -------------------------------------------------------------------------------- /docs/Reprise.IServiceConfigurator.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## IServiceConfigurator Interface 4 | 5 | Specifies the contract to configure services at application startup. 6 | 7 | ```csharp 8 | public interface IServiceConfigurator 9 | ``` 10 | ### Methods 11 | 12 | 13 | 14 | ## IServiceConfigurator.ConfigureServices(WebApplicationBuilder) Method 15 | 16 | Configures services for the specified [Microsoft.AspNetCore.Builder.WebApplicationBuilder](https://docs.microsoft.com/en-us/dotnet/api/Microsoft.AspNetCore.Builder.WebApplicationBuilder 'Microsoft.AspNetCore.Builder.WebApplicationBuilder'). 17 | 18 | ```csharp 19 | void ConfigureServices(Microsoft.AspNetCore.Builder.WebApplicationBuilder builder); 20 | ``` 21 | #### Parameters 22 | 23 | 24 | 25 | `builder` [Microsoft.AspNetCore.Builder.WebApplicationBuilder](https://docs.microsoft.com/en-us/dotnet/api/Microsoft.AspNetCore.Builder.WebApplicationBuilder 'Microsoft.AspNetCore.Builder.WebApplicationBuilder') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/Authorization/AuthorizationProcessorTests.Process_MultiplePolicyNames.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | AuthorizationProcessorTests.<>c.b__4_0(), 11 | { 12 | AcceptCorsPreflight: false, 13 | HttpMethods: [ 14 | GET 15 | ] 16 | }, 17 | { 18 | StatusCode: 200, 19 | ContentTypes: [ 20 | text/plain 21 | ] 22 | }, 23 | { 24 | TypeId: NullableContextAttribute 25 | }, 26 | { 27 | Policy: Policy1, 28 | TypeId: AuthorizeAttribute 29 | }, 30 | { 31 | Policy: Policy2, 32 | TypeId: AuthorizeAttribute 33 | } 34 | ], 35 | RequestDelegate: { 36 | Type: RequestDelegate, 37 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 38 | } 39 | } 40 | ] 41 | } 42 | ] -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/Users/Common.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.SampleApi.Endpoints.Users 2 | { 3 | public record UserDto(int? Id, string FirstName, string LastName); 4 | 5 | public class UserDtoValidator : AbstractValidator 6 | { 7 | public UserDtoValidator() 8 | { 9 | RuleFor(u => u.FirstName) 10 | .NotEmpty(); 11 | RuleFor(u => u.LastName) 12 | .NotEmpty(); 13 | } 14 | } 15 | 16 | public class UserMapper : IMapper 17 | { 18 | public User Map(UserDto source) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public UserDto Map(User source) 24 | { 25 | return new UserDto(source.Id, source.FirstName, source.LastName); 26 | } 27 | 28 | public void Map(UserDto source, User destination) 29 | { 30 | destination.FirstName = source.FirstName; 31 | destination.LastName = source.LastName; 32 | } 33 | 34 | public void Map(User source, UserDto destination) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Reprise/Features/ExceptionHandling/ErrorResponseFactoryTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class ErrorResponseFactoryTypeProcessor : AbstractTypeProcessor 4 | { 5 | private Type? _ErrorResponseFactoryType; 6 | 7 | public override void Process(WebApplicationBuilder builder, Type type) 8 | { 9 | if (type.IsAssignableTo(typeof(IErrorResponseFactory))) 10 | { 11 | if (_ErrorResponseFactoryType != null) 12 | { 13 | throw new InvalidOperationException( 14 | $"{nameof(IErrorResponseFactory)} is implemented by both {_ErrorResponseFactoryType} and {type}."); 15 | } 16 | builder.Services.AddSingleton(typeof(IErrorResponseFactory), type); 17 | _ErrorResponseFactoryType = type; 18 | } 19 | } 20 | 21 | public override void PostProcess(WebApplicationBuilder builder) 22 | { 23 | if (_ErrorResponseFactoryType == null) 24 | { 25 | builder.Services.AddSingleton(); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionHandlerTests.InvokeAsync_Exception.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | _MockHttpResponse: [ 4 | { 5 | Method: HttpResponse.get_HasStarted(), 6 | ReturnValue: false 7 | }, 8 | { 9 | Method: HttpResponse.set_StatusCode(int value), 10 | Arguments: [ 11 | 500 12 | ] 13 | } 14 | ], 15 | _MockExceptionLogger: [ 16 | { 17 | Method: IExceptionLogger.Log(ILogger logger, ErrorContext context), 18 | Arguments: [ 19 | {}, 20 | { 21 | Exception: { 22 | $type: Exception, 23 | Type: Exception, 24 | Message: Test message 25 | } 26 | } 27 | ] 28 | } 29 | ], 30 | _MockErrorResponseFactory: [ 31 | { 32 | Method: IErrorResponseFactory.Create(ErrorContext context), 33 | Arguments: [ 34 | { 35 | Exception: { 36 | $type: Exception, 37 | Type: Exception, 38 | Message: Test message 39 | } 40 | } 41 | ] 42 | } 43 | ] 44 | }, 45 | logs: [] 46 | } -------------------------------------------------------------------------------- /src/Reprise/Features/Mappers/MapperTypeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class MapperTypeProcessor : AbstractTypeProcessor 4 | { 5 | private readonly Dictionary<(Type, Type), Type> _Mappings = new(); 6 | 7 | public override void Process(WebApplicationBuilder builder, Type type) 8 | { 9 | if (type.TryGetGenericInterfaceType(typeof(IMapper<,>), out var interfaceType)) 10 | { 11 | if (_Mappings.TryGetValue((interfaceType.GenericTypeArguments[0], interfaceType.GenericTypeArguments[1]), out var existingType) || 12 | _Mappings.TryGetValue((interfaceType.GenericTypeArguments[1], interfaceType.GenericTypeArguments[0]), out existingType)) 13 | { 14 | throw new InvalidOperationException( 15 | $"{interfaceType.GenericTypeArguments[0]} and {interfaceType.GenericTypeArguments[1]} are mapped by both {type} and {existingType}."); 16 | } 17 | builder.Services.AddSingleton(interfaceType, type); 18 | _Mappings[(interfaceType.GenericTypeArguments[0], interfaceType.GenericTypeArguments[1])] = type; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Reprise/Features/OpenApi/Name/NameProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise 2 | { 3 | internal sealed class NameProcessor : IRouteHandlerBuilderProcessor 4 | { 5 | private readonly Dictionary _NamedEndpoints = new(); 6 | 7 | public void Process(RouteHandlerBuilder builder, MethodInfo handlerInfo, EndpointOptions options, string route) 8 | { 9 | var nameAttribute = handlerInfo.GetCustomAttribute(); 10 | if (nameAttribute != null) 11 | { 12 | if (nameAttribute.Name.IsEmpty()) 13 | { 14 | throw new InvalidOperationException($"{handlerInfo.DeclaringType} has an empty name."); 15 | } 16 | if (_NamedEndpoints.TryGetValue(nameAttribute.Name, out var existingType)) 17 | { 18 | throw new InvalidOperationException( 19 | $"Name '{nameAttribute.Name}' is used by both {handlerInfo.DeclaringType} and {existingType}."); 20 | } 21 | builder.WithName(nameAttribute.Name); 22 | _NamedEndpoints.Add(nameAttribute.Name, handlerInfo.DeclaringType!); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/Reprise.SampleApi/Endpoints/GetWeatherEndpoint.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Cors; 2 | using Reprise.SampleApi.WeatherService; 3 | 4 | namespace Reprise.SampleApi.Endpoints 5 | { 6 | [Endpoint] 7 | public class GetWeatherEndpoint : IServiceConfigurator 8 | { 9 | private const string _CorsPolicy = "WeatherCorsPolicy"; 10 | 11 | public void ConfigureServices(WebApplicationBuilder builder) 12 | { 13 | builder.Services.AddWeatherService(); 14 | 15 | builder.Services.AddCors(options => 16 | { 17 | options.AddPolicy(_CorsPolicy, builder => 18 | { 19 | builder.WithOrigins("https://contoso.com"); 20 | }); 21 | }); 22 | } 23 | 24 | [Get("/weather")] 25 | [AllowAnonymous] 26 | [EnableCors(_CorsPolicy)] 27 | #if NET7_0 28 | [Filter(typeof(GreetingFilter))] 29 | #endif 30 | [Produces(StatusCodes.Status200OK, typeof(WeatherForecast[]))] 31 | [Tags("Public")] 32 | public static async Task Handle(IWeatherService weatherService) 33 | { 34 | return await weatherService.GetForecast(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/OpenApi/Produces/ProducesProcessorTests.Process.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | Endpoints: [ 4 | { 5 | RoutePattern: { 6 | RawText: / 7 | }, 8 | DisplayName: HTTP: GET /, 9 | Metadata: [ 10 | ProducesProcessorTests.<>c.<.ctor>b__3_0(), 11 | { 12 | TypeId: NullableContextAttribute 13 | }, 14 | { 15 | AcceptCorsPreflight: false, 16 | HttpMethods: [ 17 | GET 18 | ] 19 | }, 20 | { 21 | Type: StubProducesResponse, 22 | StatusCode: 202, 23 | ContentTypes: [ 24 | application/json, 25 | application/xml 26 | ] 27 | }, 28 | { 29 | Type: StubProducesErrorResponse, 30 | StatusCode: 418, 31 | ContentTypes: [ 32 | application/json, 33 | application/xml 34 | ] 35 | } 36 | ], 37 | RequestDelegate: { 38 | Type: RequestDelegate, 39 | Method: System.Threading.Tasks.Task Create(Microsoft.AspNetCore.Http.HttpContext) 40 | } 41 | } 42 | ] 43 | } 44 | ] -------------------------------------------------------------------------------- /src/Reprise/Features/ExceptionHandling/ErrorContext.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Reprise 4 | { 5 | /// 6 | /// Encapsulates the exception context. 7 | /// 8 | public sealed class ErrorContext where TException : Exception 9 | { 10 | /// 11 | /// Gets the exception. 12 | /// 13 | public TException Exception { get; } 14 | 15 | /// 16 | /// Gets the . 17 | /// 18 | public HttpRequest Request => _HttpContext.Request; 19 | 20 | /// 21 | /// Gets the . 22 | /// 23 | public string TraceIdentifier => _HttpContext.TraceIdentifier; 24 | 25 | /// 26 | /// Gets the current . 27 | /// 28 | public string? ActivityId => Activity.Current?.Id; 29 | 30 | private readonly HttpContext _HttpContext; 31 | 32 | internal ErrorContext(HttpContext httpContext, TException exception) 33 | { 34 | _HttpContext = httpContext; 35 | Exception = exception; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/ReflectionTests.NonPublicTypes.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Interface: [ 3 | Reprise.IRouteHandlerBuilderProcessor 4 | ], 5 | Abstract: [ 6 | Reprise.AbstractTypeProcessor 7 | ], 8 | Sealed: [ 9 | Reprise.AuthorizationProcessor, 10 | Reprise.ConfigurationTypeProcessor, 11 | Reprise.EndpointTypeProcessor, 12 | Reprise.EventBus, 13 | Reprise.EventHandlerTypeProcessor, 14 | Reprise.DefaultErrorResponseFactory, 15 | Reprise.DefaultExceptionLogger, 16 | Reprise.ErrorResponseFactoryTypeProcessor, 17 | Reprise.ExceptionHandler, 18 | Reprise.ExceptionLoggerTypeProcessor, 19 | Reprise.JobRunner, 20 | Reprise.JobState, 21 | Reprise.JobStateRegistry, 22 | Reprise.JobTypeProcessor, 23 | Reprise.MapperTypeProcessor, 24 | Reprise.ExcludeFromDescriptionProcessor, 25 | Reprise.NameProcessor, 26 | Reprise.ProducesProcessor, 27 | Reprise.TagsProcessor, 28 | Reprise.ServiceConfiguratorTypeProcessor, 29 | Reprise.ValidatorTypeProcessor 30 | ], 31 | Open: [ 32 | Reprise.TaskRunner, 33 | Reprise.TypeProcessorFactory, 34 | Reprise.EndpointMapper, 35 | Reprise.DateTimeProvider 36 | ], 37 | Static: [ 38 | Reprise.ExtensionMethods, 39 | Reprise.LoggerExtensions 40 | ] 41 | } -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/ExceptionHandlerTests.InvokeAsync_ValidationException.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | target: { 3 | _MockHttpResponse: [ 4 | { 5 | Method: HttpResponse.get_HasStarted(), 6 | ReturnValue: false 7 | }, 8 | { 9 | Method: HttpResponse.set_StatusCode(int value), 10 | Arguments: [ 11 | 400 12 | ] 13 | } 14 | ], 15 | _MockExceptionLogger: [ 16 | { 17 | Method: IExceptionLogger.Log(ILogger logger, ErrorContext context), 18 | Arguments: [ 19 | {}, 20 | { 21 | Exception: { 22 | $type: ValidationException, 23 | Type: ValidationException, 24 | Message: Test message 25 | } 26 | } 27 | ] 28 | } 29 | ], 30 | _MockErrorResponseFactory: [ 31 | { 32 | Method: IErrorResponseFactory.Create(ErrorContext context), 33 | Arguments: [ 34 | { 35 | Exception: { 36 | $type: ValidationException, 37 | Type: ValidationException, 38 | Message: Test message 39 | } 40 | } 41 | ] 42 | } 43 | ] 44 | }, 45 | logs: [] 46 | } -------------------------------------------------------------------------------- /docs/Reprise.TagsAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## TagsAttribute Class 4 | 5 | Specifies custom OpenAPI tags for the API endpoint. 6 | 7 | ```csharp 8 | public sealed class TagsAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 TagsAttribute 12 | ### Constructors 13 | 14 | 15 | 16 | ## TagsAttribute(string, string[]) Constructor 17 | 18 | Creates a new [TagsAttribute](Reprise.TagsAttribute.md 'Reprise.TagsAttribute'). 19 | 20 | ```csharp 21 | public TagsAttribute(string tag, params string[] additionalTags); 22 | ``` 23 | #### Parameters 24 | 25 | 26 | 27 | `tag` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') 28 | 29 | 30 | 31 | `additionalTags` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String')[[]](https://docs.microsoft.com/en-us/dotnet/api/System.Array 'System.Array') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Features/ExceptionHandling/DefaultErrorResponseFactoryTests.cs: -------------------------------------------------------------------------------- 1 | namespace Reprise.UnitTests.Features.ExceptionHandling 2 | { 3 | public class DefaultErrorResponseFactoryTests 4 | { 5 | private readonly DefaultErrorResponseFactory _DefaultFactory = new(); 6 | 7 | [Fact] 8 | public void Create_BadHttpRequestException() 9 | { 10 | var exception = new BadHttpRequestException("Test message"); 11 | var errorContext = new ErrorContext(null!, exception); 12 | 13 | Assert.Null(_DefaultFactory.Create(errorContext)); 14 | } 15 | 16 | [Fact] 17 | public void Create_ValidationException() 18 | { 19 | var exception = new ValidationException("Test message"); 20 | var errorContext = new ErrorContext(null!, exception); 21 | 22 | Assert.Null(_DefaultFactory.Create(errorContext)); 23 | } 24 | 25 | [Fact] 26 | public void Create_Exception() 27 | { 28 | var exception = new Exception("Test message"); 29 | var errorContext = new ErrorContext(null!, exception); 30 | 31 | Assert.Null(_DefaultFactory.Create(errorContext)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/Reprise.ConfigurationAttribute.md: -------------------------------------------------------------------------------- 1 | ### [Reprise](Reprise.md 'Reprise') 2 | 3 | ## ConfigurationAttribute Class 4 | 5 | Identifies a strongly-typed hierarchical configuration model bound at application startup. 6 | 7 | ```csharp 8 | public sealed class ConfigurationAttribute : System.Attribute 9 | ``` 10 | 11 | Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Attribute](https://docs.microsoft.com/en-us/dotnet/api/System.Attribute 'System.Attribute') 🡒 ConfigurationAttribute 12 | 13 | ### Remarks 14 | A configuration class should be public non-abstract with a public parameterless constructor. 15 | All public read-write properties of the type are bound. 16 | ### Constructors 17 | 18 | 19 | 20 | ## ConfigurationAttribute(string) Constructor 21 | 22 | Creates a new [ConfigurationAttribute](Reprise.ConfigurationAttribute.md 'Reprise.ConfigurationAttribute'). 23 | 24 | ```csharp 25 | public ConfigurationAttribute(string subSectionKey); 26 | ``` 27 | #### Parameters 28 | 29 | 30 | 31 | `subSectionKey` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') -------------------------------------------------------------------------------- /tests/Reprise.UnitTests/Global.cs: -------------------------------------------------------------------------------- 1 | global using FluentValidation; 2 | global using Microsoft.AspNetCore.Builder; 3 | global using Microsoft.AspNetCore.Http; 4 | global using Microsoft.AspNetCore.Routing; 5 | global using Microsoft.Extensions.DependencyInjection; 6 | global using Microsoft.Extensions.Logging; 7 | global using Moq; 8 | global using NCrontab; 9 | global using Reprise.UnitTests.TestHelpers; 10 | 11 | using System.Diagnostics.CodeAnalysis; 12 | using System.Runtime.CompilerServices; 13 | 14 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 15 | 16 | [assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Test names can contain underscores.")] 17 | [assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Plain exception is used in tests.")] 18 | [assembly: SuppressMessage("Build", "CA1852", Justification = "Using unsealed internal types in tests is not problematic.")] 19 | 20 | namespace Reprise.UnitTests 21 | { 22 | public class ModuleInit 23 | { 24 | [ModuleInitializer] 25 | public static void Initialize() 26 | { 27 | VerifyMoq.Initialize(); 28 | VerifyHttp.Initialize(); 29 | VerifyMicrosoftLogging.Initialize(); 30 | } 31 | } 32 | } 33 | --------------------------------------------------------------------------------