update to .net 9 and add new OpenAPI packages
This commit is contained in:
		
							parent
							
								
									80b7f192f1
								
							
						
					
					
						commit
						7e6698c3fb
					
				
					 17 changed files with 451 additions and 1001 deletions
				
			
		|  | @ -26,6 +26,7 @@ using Microsoft.EntityFrameworkCore; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class AuthController( | ||||
|     Config config, | ||||
|     DatabaseContext db, | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ using NodaTime; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth/discord")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class DiscordAuthController( | ||||
|     [UsedImplicitly] Config config, | ||||
|     ILogger logger, | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ using NodaTime; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth/email")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class EmailAuthController( | ||||
|     [UsedImplicitly] Config config, | ||||
|     DatabaseContext db, | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ using NodaTime; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth/fediverse")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class FediverseAuthController( | ||||
|     ILogger logger, | ||||
|     DatabaseContext db, | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ using NodaTime; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth/google")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class GoogleAuthController( | ||||
|     [UsedImplicitly] Config config, | ||||
|     ILogger logger, | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ using NodaTime; | |||
| namespace Foxnouns.Backend.Controllers.Authentication; | ||||
| 
 | ||||
| [Route("/api/internal/auth/tumblr")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class TumblrAuthController( | ||||
|     [UsedImplicitly] Config config, | ||||
|     ILogger logger, | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ namespace Foxnouns.Backend.Controllers; | |||
| 
 | ||||
| [Route("/api/internal/data-exports")] | ||||
| [Authorize("identify")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class ExportsController( | ||||
|     ILogger logger, | ||||
|     Config config, | ||||
|  |  | |||
|  | @ -80,6 +80,7 @@ public class FlagsController( | |||
| 
 | ||||
|     [HttpPatch("{id}")] | ||||
|     [Authorize("user.update")] | ||||
|     [ProducesResponseType<PrideFlagResponse>(statusCode: StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> UpdateFlagAsync(Snowflake id, [FromBody] UpdateFlagRequest req) | ||||
|     { | ||||
|         ValidationUtils.Validate(ValidateFlag(req.Name, req.Description, null)); | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ namespace Foxnouns.Backend.Controllers; | |||
| 
 | ||||
| [ApiController] | ||||
| [Route("/api/internal")] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public partial class InternalController(DatabaseContext db) : ControllerBase | ||||
| { | ||||
|     [GeneratedRegex(@"(\{\w+\})")] | ||||
|  |  | |||
|  | @ -144,6 +144,7 @@ public class MembersController( | |||
|     } | ||||
| 
 | ||||
|     [HttpPatch("/api/v2/users/@me/members/{memberRef}")] | ||||
|     [ProducesResponseType<MemberResponse>(statusCode: StatusCodes.Status200OK)] | ||||
|     [Authorize("member.update")] | ||||
|     public async Task<IActionResult> UpdateMemberAsync( | ||||
|         string memberRef, | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ namespace Foxnouns.Backend.Controllers; | |||
|     "CA1862:Use the \'StringComparison\' method overloads to perform case-insensitive string comparisons", | ||||
|     Justification = "Not usable with EFCore" | ||||
| )] | ||||
| [ApiExplorerSettings(IgnoreApi = true)] | ||||
| public class SidController(Config config, DatabaseContext db) : ApiControllerBase | ||||
| { | ||||
|     [HttpGet("{**id}")] | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| using System.ComponentModel; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Globalization; | ||||
| using System.Text.Json; | ||||
| using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||||
| using Newtonsoft.Json; | ||||
| using NodaTime; | ||||
|  | @ -23,6 +24,7 @@ using JsonSerializer = Newtonsoft.Json.JsonSerializer; | |||
| namespace Foxnouns.Backend.Database; | ||||
| 
 | ||||
| [JsonConverter(typeof(JsonConverter))] | ||||
| [System.Text.Json.Serialization.JsonConverter(typeof(SystemJsonConverter))] | ||||
| [TypeConverter(typeof(TypeConverter))] | ||||
| public readonly struct Snowflake(ulong value) : IEquatable<Snowflake> | ||||
| { | ||||
|  | @ -96,6 +98,21 @@ public readonly struct Snowflake(ulong value) : IEquatable<Snowflake> | |||
|     // ReSharper disable once ClassNeverInstantiated.Global | ||||
|     public class ValueConverter() : ValueConverter<Snowflake, long>(x => x, x => x); | ||||
| 
 | ||||
|     private class SystemJsonConverter : System.Text.Json.Serialization.JsonConverter<Snowflake> | ||||
|     { | ||||
|         public override Snowflake Read( | ||||
|             ref Utf8JsonReader reader, | ||||
|             Type typeToConvert, | ||||
|             JsonSerializerOptions options | ||||
|         ) => ulong.Parse(reader.GetString()!); | ||||
| 
 | ||||
|         public override void Write( | ||||
|             Utf8JsonWriter writer, | ||||
|             Snowflake value, | ||||
|             JsonSerializerOptions options | ||||
|         ) => writer.WriteStringValue(value.Value.ToString()); | ||||
|     } | ||||
| 
 | ||||
|     private class JsonConverter : JsonConverter<Snowflake> | ||||
|     { | ||||
|         public override void WriteJson( | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||||
|     <PropertyGroup> | ||||
|         <TargetFramework>net8.0</TargetFramework> | ||||
|         <TargetFramework>net9.0</TargetFramework> | ||||
|         <Nullable>enable</Nullable> | ||||
|         <ImplicitUsings>enable</ImplicitUsings> | ||||
|         <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> | ||||
|  | @ -8,39 +8,39 @@ | |||
|     </PropertyGroup> | ||||
| 
 | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="Coravel" Version="5.0.4"/> | ||||
|         <PackageReference Include="Coravel.Mailer" Version="5.0.1"/> | ||||
|         <PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/> | ||||
|         <PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.1.2"/> | ||||
|         <PackageReference Include="Coravel" Version="6.0.0"/> | ||||
|         <PackageReference Include="Coravel.Mailer" Version="7.0.0"/> | ||||
|         <PackageReference Include="EFCore.NamingConventions" Version="9.0.0"/> | ||||
|         <PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.1.3"/> | ||||
|         <PackageReference Include="Humanizer.Core" Version="2.14.1"/> | ||||
|         <PackageReference Include="JetBrains.Annotations" Version="2024.2.0"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.7"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.7"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7"> | ||||
|         <PackageReference Include="JetBrains.Annotations" Version="2024.3.0"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0"/> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0"> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|         </PackageReference> | ||||
|         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0"/> | ||||
|         <PackageReference Include="Minio" Version="6.0.3"/> | ||||
|         <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> | ||||
|         <PackageReference Include="NodaTime" Version="3.1.11"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="8.0.4"/> | ||||
|         <PackageReference Include="Npgsql.Json.NET" Version="8.0.3"/> | ||||
|         <PackageReference Include="NodaTime" Version="3.2.0"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.2"/> | ||||
|         <PackageReference Include="Npgsql.Json.NET" Version="9.0.2"/> | ||||
|         <PackageReference Include="prometheus-net" Version="8.2.1"/> | ||||
|         <PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1"/> | ||||
|         <PackageReference Include="Roslynator.Analyzers" Version="4.12.9"> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|         </PackageReference> | ||||
|         <PackageReference Include="Sentry.AspNetCore" Version="4.9.0"/> | ||||
|         <PackageReference Include="Serilog" Version="4.0.1"/> | ||||
|         <PackageReference Include="Serilog.AspNetCore" Version="8.0.1"/> | ||||
|         <PackageReference Include="Scalar.AspNetCore" Version="1.2.51"/> | ||||
|         <PackageReference Include="Sentry.AspNetCore" Version="4.13.0"/> | ||||
|         <PackageReference Include="Serilog" Version="4.2.0"/> | ||||
|         <PackageReference Include="Serilog.AspNetCore" Version="9.0.0"/> | ||||
|         <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/> | ||||
|         <PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0"/> | ||||
|         <PackageReference Include="SixLabors.ImageSharp" Version="3.1.5"/> | ||||
|         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/> | ||||
|         <PackageReference Include="SixLabors.ImageSharp" Version="3.1.6"/> | ||||
|         <PackageReference Include="System.Text.Json" Version="9.0.0"/> | ||||
|         <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1"/> | ||||
|     </ItemGroup> | ||||
|  |  | |||
|  | @ -12,14 +12,18 @@ | |||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using Foxnouns.Backend; | ||||
| using Foxnouns.Backend.Extensions; | ||||
| using Foxnouns.Backend.Services; | ||||
| using Foxnouns.Backend.Utils; | ||||
| using Foxnouns.Backend.Utils.OpenApi; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Newtonsoft.Json; | ||||
| using Newtonsoft.Json.Serialization; | ||||
| using Prometheus; | ||||
| using Scalar.AspNetCore; | ||||
| using Sentry.Extensibility; | ||||
| using Serilog; | ||||
| 
 | ||||
|  | @ -46,6 +50,13 @@ builder | |||
| 
 | ||||
| builder | ||||
|     .Services.AddControllers() | ||||
|     .AddJsonOptions(options => | ||||
|     { | ||||
|         options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower; | ||||
|         options.JsonSerializerOptions.Converters.Add( | ||||
|             new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper) | ||||
|         ); | ||||
|     }) | ||||
|     .AddNewtonsoftJson(options => | ||||
|     { | ||||
|         options.SerializerSettings.ContractResolver = new PatchRequestContractResolver | ||||
|  | @ -60,6 +71,16 @@ builder | |||
|         ); | ||||
|     }); | ||||
| 
 | ||||
| builder.Services.AddOpenApi( | ||||
|     "v2", | ||||
|     options => | ||||
|     { | ||||
|         options.AddSchemaTransformer<PropertyKeySchemaTransformer>(); | ||||
|         options.AddSchemaTransformer<ExampleFixingSchemaTransformer>(); | ||||
|         options.AddDocumentTransformer(new DocumentTransformer(config)); | ||||
|     } | ||||
| ); | ||||
| 
 | ||||
| // Set the default converter to snake case as we use it in a couple places. | ||||
| JsonConvert.DefaultSettings = () => | ||||
|     new JsonSerializerSettings | ||||
|  | @ -70,7 +91,7 @@ JsonConvert.DefaultSettings = () => | |||
|         }, | ||||
|     }; | ||||
| 
 | ||||
| builder.AddServices(config).AddCustomMiddleware().AddEndpointsApiExplorer().AddSwaggerGen(); | ||||
| builder.AddServices(config).AddCustomMiddleware(); | ||||
| 
 | ||||
| WebApplication app = builder.Build(); | ||||
| 
 | ||||
|  | @ -83,11 +104,16 @@ app.UseRouting(); | |||
| // so it's locked behind a config option. | ||||
| if (config.Logging.SentryTracing) | ||||
|     app.UseSentryTracing(); | ||||
| app.UseSwagger(); | ||||
| app.UseSwaggerUI(); | ||||
| app.UseCors(); | ||||
| app.UseCustomMiddleware(); | ||||
| app.MapControllers(); | ||||
| app.MapOpenApi("/api-docs/openapi/{documentName}.json"); | ||||
| app.MapScalarApiReference(options => | ||||
| { | ||||
|     options.Title = "pronouns.cc API"; | ||||
|     options.OpenApiRoutePattern = "/api-docs/openapi/{documentName}.json"; | ||||
|     options.EndpointPathPrefix = "/api-docs/{documentName}"; | ||||
| }); | ||||
| 
 | ||||
| app.Urls.Clear(); | ||||
| app.Urls.Add(config.Address); | ||||
|  |  | |||
|  | @ -0,0 +1,69 @@ | |||
| using Foxnouns.Backend.Database; | ||||
| using Microsoft.AspNetCore.OpenApi; | ||||
| using Microsoft.OpenApi.Any; | ||||
| using Microsoft.OpenApi.Models; | ||||
| using Newtonsoft.Json.Serialization; | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Utils.OpenApi; | ||||
| 
 | ||||
| public class PropertyKeySchemaTransformer : IOpenApiSchemaTransformer | ||||
| { | ||||
|     private static readonly DefaultContractResolver SnakeCaseConverter = | ||||
|         new() { NamingStrategy = new SnakeCaseNamingStrategy() }; | ||||
| 
 | ||||
|     public Task TransformAsync( | ||||
|         OpenApiSchema schema, | ||||
|         OpenApiSchemaTransformerContext context, | ||||
|         CancellationToken cancellationToken | ||||
|     ) | ||||
|     { | ||||
|         Dictionary<string, OpenApiSchema> newProperties = new(); | ||||
|         foreach (KeyValuePair<string, OpenApiSchema> property in schema.Properties) | ||||
|         { | ||||
|             newProperties[SnakeCaseConverter.GetResolvedPropertyName(property.Key)] = | ||||
|                 property.Value; | ||||
|         } | ||||
| 
 | ||||
|         schema.Properties = newProperties; | ||||
|         schema.Required = schema | ||||
|             .Required.Select(SnakeCaseConverter.GetResolvedPropertyName) | ||||
|             .ToHashSet(); | ||||
| 
 | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public class ExampleFixingSchemaTransformer : IOpenApiSchemaTransformer | ||||
| { | ||||
|     public Task TransformAsync( | ||||
|         OpenApiSchema schema, | ||||
|         OpenApiSchemaTransformerContext context, | ||||
|         CancellationToken cancellationToken | ||||
|     ) | ||||
|     { | ||||
|         if (context.JsonTypeInfo.Type == typeof(Snowflake)) | ||||
|         { | ||||
|             schema.Type = "string"; | ||||
|             schema.Example = new OpenApiString("999999999999999999"); | ||||
|         } | ||||
| 
 | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public class DocumentTransformer(Config config) : IOpenApiDocumentTransformer | ||||
| { | ||||
|     public Task TransformAsync( | ||||
|         OpenApiDocument document, | ||||
|         OpenApiDocumentTransformerContext context, | ||||
|         CancellationToken cancellationToken | ||||
|     ) | ||||
|     { | ||||
|         document.Info.Title = "pronouns.cc API"; | ||||
|         document.Info.Version = "2.0.0"; | ||||
| 
 | ||||
|         document.Servers.Clear(); | ||||
|         document.Servers.Add(new OpenApiServer { Url = config.BaseUrl }); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <OutputType>Exe</OutputType> | ||||
|         <TargetFramework>net8.0</TargetFramework> | ||||
|         <TargetFramework>net9.0</TargetFramework> | ||||
|         <ImplicitUsings>enable</ImplicitUsings> | ||||
|         <Nullable>enable</Nullable> | ||||
|     </PropertyGroup> | ||||
|  | @ -14,13 +14,13 @@ | |||
|     <ItemGroup> | ||||
|         <PackageReference Include="Humanizer.Core" Version="2.14.1"/> | ||||
|         <PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.1.0"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7"> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0"/> | ||||
|         <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0"> | ||||
|             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
|             <PrivateAssets>all</PrivateAssets> | ||||
|         </PackageReference> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="8.0.4"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2"/> | ||||
|         <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.2"/> | ||||
|     </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue