Skip to content

Commit 7a87c95

Browse files
Merge pull request #2462 from RocketSurgeonsGuild/feature/primitives
Feature/primitives
2 parents b00087d + 884a5de commit 7a87c95

File tree

91 files changed

+1101
-694
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1101
-694
lines changed

Directory.Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<PackageVersion Include="Serilog.Enrichers.AssemblyName" Version="2.0.0" />
3434
<PackageVersion Include="Serilog.Enrichers.Demystifier" Version="1.0.3" />
3535
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
36+
<PackageVersion Include="Serilog.Sinks.BrowserConsole" Version="8.0.0" />
3637
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.1.1" />
3738
<PackageVersion Include="Serilog.Sinks.Spectre" Version="0.5.0" />
3839
<PackageVersion Include="Serilog.Sinks.Trace" Version="4.0.0" />

LaunchPad.sln

+30
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Minimal", "sample\Sa
193193
EndProject
194194
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Minimal.Tests", "test\Sample.Minimal.Tests\Sample.Minimal.Tests.csproj", "{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}"
195195
EndProject
196+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.LaunchPad.Primitives", "src\Primitives\Rocket.Surgery.LaunchPad.Primitives.csproj", "{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}"
197+
EndProject
198+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.LaunchPad.WebAssembly.Hosting", "src\WebAssembly.Hosting\Rocket.Surgery.LaunchPad.WebAssembly.Hosting.csproj", "{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}"
199+
EndProject
196200
Global
197201
GlobalSection(SolutionConfigurationPlatforms) = preSolution
198202
Debug|Any CPU = Debug|Any CPU
@@ -845,6 +849,30 @@ Global
845849
{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x64.Build.0 = Release|Any CPU
846850
{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x86.ActiveCfg = Release|Any CPU
847851
{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x86.Build.0 = Release|Any CPU
852+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
853+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
854+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|x64.ActiveCfg = Debug|Any CPU
855+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|x64.Build.0 = Debug|Any CPU
856+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|x86.ActiveCfg = Debug|Any CPU
857+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Debug|x86.Build.0 = Debug|Any CPU
858+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
859+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|Any CPU.Build.0 = Release|Any CPU
860+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|x64.ActiveCfg = Release|Any CPU
861+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|x64.Build.0 = Release|Any CPU
862+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|x86.ActiveCfg = Release|Any CPU
863+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2}.Release|x86.Build.0 = Release|Any CPU
864+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
865+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
866+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|x64.ActiveCfg = Debug|Any CPU
867+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|x64.Build.0 = Debug|Any CPU
868+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|x86.ActiveCfg = Debug|Any CPU
869+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Debug|x86.Build.0 = Debug|Any CPU
870+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
871+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|Any CPU.Build.0 = Release|Any CPU
872+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|x64.ActiveCfg = Release|Any CPU
873+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|x64.Build.0 = Release|Any CPU
874+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|x86.ActiveCfg = Release|Any CPU
875+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3}.Release|x86.Build.0 = Release|Any CPU
848876
EndGlobalSection
849877
GlobalSection(SolutionProperties) = preSolution
850878
HideSolutionNode = FALSE
@@ -908,6 +936,8 @@ Global
908936
{F0DD1D80-6948-4F10-8789-228ACC1F412F} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1}
909937
{75C5361A-B1E8-4194-8BBC-CD68D4FC62CA} = {5D11C19B-E8E4-4CE3-9C8A-1D368578EBCB}
910938
{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1}
939+
{8E25E25B-622E-4A15-BE2F-13EA01AA8CA2} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
940+
{6EA9A9C8-8AFC-46F6-84C0-1B97F4B59FC3} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
911941
EndGlobalSection
912942
GlobalSection(ExtensibilityGlobals) = postSolution
913943
SolutionGuid = {439897C2-CCBD-44FE-B2DC-A3E4670ADA59}

sample/Sample.Restful.Client/Sample.Restful.Client.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
/>
1212
<PackageReference Include="Newtonsoft.Json" />
1313
<PackageReference Include="NSwag.ApiDescription.Client" />
14-
<PackageReference Include="System.ComponentModel.Annotations" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
1514
</ItemGroup>
1615
<ItemGroup>
1716
<OpenApiReference

src/AspNetCore/Conventions/AspNetCoreConventionInstrumentationConvention.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using Microsoft.Extensions.Configuration;
2+
23
using OpenTelemetry;
34
using OpenTelemetry.Metrics;
45
using OpenTelemetry.Trace;
6+
57
using Rocket.Surgery.Conventions;
68
using Rocket.Surgery.LaunchPad.Telemetry;
79

@@ -15,7 +17,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1517
/// <seealso cref="IOpenTelemetryConvention" />
1618
[PublicAPI]
1719
[ExportConvention]
18-
[AfterConvention(typeof(AspNetCoreConvention))]
20+
[AfterConvention<AspNetCoreConvention>]
1921
[ConventionCategory(ConventionCategory.Application)]
2022
public class AspNetCoreConventionInstrumentationConvention : IOpenTelemetryConvention
2123
{

src/AspNetCore/Conventions/FluentValidationConvention.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using FluentValidation.AspNetCore;
1+
using FluentValidation.AspNetCore;
2+
23
using Microsoft.AspNetCore.Mvc;
34
using Microsoft.Extensions.Configuration;
45
using Microsoft.Extensions.DependencyInjection;
6+
57
using Rocket.Surgery.Conventions;
68
using Rocket.Surgery.Conventions.DependencyInjection;
79
using Rocket.Surgery.LaunchPad.AspNetCore.Validation;
8-
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;
910
using HttpJsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions;
11+
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;
1012

1113
namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1214

@@ -18,7 +20,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1820
/// <seealso cref="IServiceConvention" />
1921
[PublicAPI]
2022
[ExportConvention]
21-
[AfterConvention(typeof(AspNetCoreConvention))]
23+
[AfterConvention<AspNetCoreConvention>]
2224
[ConventionCategory(ConventionCategory.Application)]
2325
public partial class FluentValidationConvention : IServiceConvention
2426
{
@@ -33,9 +35,9 @@ public void Register(IConventionContext context, IConfiguration configuration, I
3335
services.AddFluentValidationClientsideAdapters();
3436
services
3537
.Configure<MvcOptions>(mvcOptions => mvcOptions.Filters.Insert(0, new ValidationExceptionFilter()))
36-
.Configure<MvcJsonOptions>(options => options.JsonSerializerOptions.Converters.Add(new ValidationProblemDetailsConverter ()))
38+
.Configure<MvcJsonOptions>(options => options.JsonSerializerOptions.Converters.Add(new ValidationProblemDetailsConverter()))
3739
.Configure<HttpJsonOptions>(options => options.SerializerOptions.Converters.Add(new ValidationProblemDetailsConverter()));
3840

39-
// AddFluentValidationRules(services);
41+
// AddFluentValidationRules(services);
4042
}
4143
}

src/AspNetCore/Conventions/OpenApiConvention.cs

+12-16
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
using System.Text;
2-
using System.Text.Json;
3-
using FluentValidation;
4-
using Microsoft.AspNetCore.Mvc;
51
using Microsoft.AspNetCore.OpenApi;
62
using Microsoft.Extensions.Configuration;
73
using Microsoft.Extensions.DependencyInjection;
84
using Microsoft.Extensions.Logging;
9-
using Microsoft.Extensions.Options;
105
using Microsoft.OpenApi.Models;
6+
117
using Rocket.Surgery.Conventions;
128
using Rocket.Surgery.Conventions.DependencyInjection;
139
using Rocket.Surgery.LaunchPad.AspNetCore.Composition;
@@ -24,17 +20,10 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
2420
/// <seealso cref="IServiceConvention" />
2521
[PublicAPI]
2622
[ExportConvention]
27-
[AfterConvention(typeof(AspNetCoreConvention))]
23+
[AfterConvention<AspNetCoreConvention>]
2824
[ConventionCategory(ConventionCategory.Application)]
2925
public partial class OpenApiConvention : IServiceConvention
3026
{
31-
[LoggerMessage(
32-
EventId = 0,
33-
Level = LogLevel.Debug,
34-
Message = "Error adding XML comments from {XmlFile}"
35-
)]
36-
internal static partial void ErrorAddingXMLComments(ILogger logger, Exception exception, string xmlFile);
37-
3827
/// <summary>
3928
/// Registers the specified context.
4029
/// </summary>
@@ -56,17 +45,24 @@ public void Register(IConventionContext context, IConfiguration configuration, I
5645
options.AddOperationTransformer<StatusCode201Filter>();
5746
options.AddOperationTransformer<OperationMediaTypesFilter>();
5847
options.AddOperationTransformer<AuthorizeFilter>();
59-
60-
});
48+
}
49+
);
6150
services.AddFluentValidationOpenApi();
6251
}
52+
53+
[LoggerMessage(
54+
EventId = 0,
55+
Level = LogLevel.Debug,
56+
Message = "Error adding XML comments from {XmlFile}"
57+
)]
58+
internal static partial void ErrorAddingXMLComments(ILogger logger, Exception exception, string xmlFile);
6359
}
6460

6561
internal class NestedTypeSchemaFilter : IOpenApiSchemaTransformer
6662
{
6763
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
6864
{
69-
if (context is not { JsonTypeInfo.Type.DeclaringType: {} }) return Task.CompletedTask;
65+
if (context is not { JsonTypeInfo.Type.DeclaringType: { } }) return Task.CompletedTask;
7066
schema.Annotations["x-schema-id"] = $"{context.JsonTypeInfo.Type.DeclaringType.Name}{context.JsonTypeInfo.Type.Name}";
7167
return Task.CompletedTask;
7268
}

src/AspNetCore/Conventions/ProblemDetailsConvention.cs

+22-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using FluentValidation;
1+
using FluentValidation;
22
using FluentValidation.Results;
3+
34
using Microsoft.AspNetCore.Http;
45
using Microsoft.AspNetCore.Mvc;
56
using Microsoft.Extensions.Configuration;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.DependencyInjection.Extensions;
89
using Microsoft.Extensions.Options;
10+
911
using Rocket.Surgery.Conventions;
1012
using Rocket.Surgery.Conventions.DependencyInjection;
1113
using Rocket.Surgery.LaunchPad.AspNetCore.Validation;
@@ -21,7 +23,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
2123
/// <seealso cref="IServiceConvention" />
2224
[PublicAPI]
2325
[ExportConvention]
24-
[AfterConvention(typeof(AspNetCoreConvention))]
26+
[AfterConvention<AspNetCoreConvention>]
2527
[ConventionCategory(ConventionCategory.Application)]
2628
public class ProblemDetailsConvention : IServiceConvention
2729
{
@@ -33,14 +35,14 @@ public void Register(IConventionContext context, IConfiguration configuration, I
3335
options =>
3436
{
3537
var old = options.StatusCodeSelector;
36-
options.StatusCodeSelector = (exception) => exception switch
37-
{
38-
NotFoundException => StatusCodes.Status404NotFound,
39-
RequestFailedException => StatusCodes.Status400BadRequest,
40-
NotAuthorizedException => StatusCodes.Status403Forbidden,
41-
ValidationException => StatusCodes.Status422UnprocessableEntity,
42-
_ => old?.Invoke(exception) ?? StatusCodes.Status500InternalServerError,
43-
};
38+
options.StatusCodeSelector = exception => exception switch
39+
{
40+
NotFoundException => StatusCodes.Status404NotFound,
41+
RequestFailedException => StatusCodes.Status400BadRequest,
42+
NotAuthorizedException => StatusCodes.Status403Forbidden,
43+
ValidationException => StatusCodes.Status422UnprocessableEntity,
44+
_ => old?.Invoke(exception) ?? StatusCodes.Status500InternalServerError,
45+
};
4446
}
4547
);
4648
services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, OnBeforeWriteProblemDetailsWriter>());
@@ -53,28 +55,33 @@ public void Register(IConventionContext context, IConfiguration configuration, I
5355
}
5456
}
5557

56-
class OnBeforeWriteProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
58+
internal class OnBeforeWriteProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
5759
{
5860
public ValueTask WriteAsync(ProblemDetailsContext context) => throw new NotImplementedException();
5961

6062
public bool CanWrite(ProblemDetailsContext context)
6163
{
6264
if (!context.ProblemDetails.Status.HasValue
6365
|| !apiBehaviorOptions.Value.ClientErrorMapping.TryGetValue(context.ProblemDetails.Status.Value, out var clientErrorData))
66+
{
6467
return false;
68+
}
6569

6670
context.ProblemDetails.Title ??= clientErrorData.Title;
6771
context.ProblemDetails.Type ??= clientErrorData.Link;
6872
return false;
6973
}
7074
}
7175

72-
class FluentValidationProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
76+
internal class FluentValidationProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
7377
{
7478
public ValueTask WriteAsync(ProblemDetailsContext context)
7579
{
7680
if (context is not { Exception: IProblemDetailsData details }
77-
|| context.HttpContext.Items[typeof(ValidationResult)] is not ValidationResult validationResult) return ValueTask.CompletedTask;
81+
|| context.HttpContext.Items[typeof(ValidationResult)] is not ValidationResult validationResult)
82+
{
83+
return ValueTask.CompletedTask;
84+
}
7885

7986
context.ProblemDetails = new FluentValidationProblemDetails(validationResult.Errors)
8087
{
@@ -88,13 +95,10 @@ public ValueTask WriteAsync(ProblemDetailsContext context)
8895
return ValueTask.CompletedTask;
8996
}
9097

91-
public bool CanWrite(ProblemDetailsContext context)
92-
{
93-
return context.Exception is not IProblemDetailsData && context.HttpContext.Items[typeof(ValidationResult)] is ValidationResult;
94-
}
98+
public bool CanWrite(ProblemDetailsContext context) => context.Exception is not IProblemDetailsData && context.HttpContext.Items[typeof(ValidationResult)] is ValidationResult;
9599
}
96100

97-
class ValidationExceptionProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
101+
internal class ValidationExceptionProblemDetailsWriter(IOptions<ApiBehaviorOptions> apiBehaviorOptions) : IProblemDetailsWriter
98102
{
99103
public ValueTask WriteAsync(ProblemDetailsContext context)
100104
{

src/AspNetCore/Conventions/RestfulConvention.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
using Microsoft.AspNetCore.Mvc.ApplicationModels;
1+
using Microsoft.AspNetCore.Mvc.ApplicationModels;
22
using Microsoft.Extensions.Configuration;
33
using Microsoft.Extensions.DependencyInjection;
44
using Microsoft.Extensions.DependencyInjection.Extensions;
5+
56
using Rocket.Surgery.Conventions;
67
using Rocket.Surgery.Conventions.DependencyInjection;
78
using Rocket.Surgery.LaunchPad.AspNetCore.Composition;
@@ -16,7 +17,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1617
/// <seealso cref="IServiceConvention" />
1718
[PublicAPI]
1819
[ExportConvention]
19-
[AfterConvention(typeof(AspNetCoreConvention))]
20+
[AfterConvention<AspNetCoreConvention>]
2021
[ConventionCategory(ConventionCategory.Application)]
2122
public class RestfulConvention : IServiceConvention
2223
{
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using System.Text.Json;
21
using Microsoft.Extensions.Configuration;
32
using Microsoft.Extensions.DependencyInjection;
43
using Microsoft.Extensions.Options;
4+
55
using Rocket.Surgery.Conventions;
66
using Rocket.Surgery.Conventions.DependencyInjection;
77
using Rocket.Surgery.LaunchPad.Foundation;
8-
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;
98
using HttpJsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions;
9+
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;
1010

1111
namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1212

@@ -16,23 +16,16 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions;
1616
/// </summary>
1717
/// <seealso cref="IServiceConvention" />
1818
/// <seealso cref="IServiceConvention" />
19+
/// <remarks>
20+
/// Create a new SystemJsonTextConvention
21+
/// </remarks>
22+
/// <param name="options"></param>
1923
[PublicAPI]
2024
[ExportConvention]
21-
[AfterConvention(typeof(AspNetCoreConvention))]
25+
[AfterConvention<AspNetCoreConvention>]
2226
[ConventionCategory(ConventionCategory.Application)]
23-
public class SystemJsonTextConvention : IServiceConvention
27+
public class SystemJsonTextConvention(FoundationOptions? options = null) : IServiceConvention
2428
{
25-
private readonly FoundationOptions _options;
26-
27-
/// <summary>
28-
/// Create a new SystemJsonTextConvention
29-
/// </summary>
30-
/// <param name="options"></param>
31-
public SystemJsonTextConvention(FoundationOptions? options = null)
32-
{
33-
_options = options ?? new FoundationOptions();
34-
}
35-
3629
/// <summary>
3730
/// Registers the specified context.
3831
/// </summary>
@@ -50,4 +43,6 @@ public void Register(IConventionContext context, IConfiguration configuration, I
5043
.AddOptions<HttpJsonOptions>()
5144
.Configure<IServiceProvider>((options, provider) => ExistingValueOptions.Apply(provider, options.SerializerOptions, Options.DefaultName));
5245
}
46+
47+
private readonly FoundationOptions _options = options ?? new FoundationOptions();
5348
}

0 commit comments

Comments
 (0)