Skip to content

Replace swagger #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using OpenShock.Common.Services.LCGNodeProvisioner;
using OpenShock.Common.Services.Ota;
using OpenShock.Common.Services.Turnstile;
using OpenShock.Common.Swagger;
using Serilog;

var builder = OpenShockApplication.CreateDefaultBuilder<Program>(args);
Expand Down Expand Up @@ -47,8 +46,6 @@
builder.Services.AddScoped<IDeviceUpdateService, DeviceUpdateService>();
builder.Services.AddScoped<IAccountService, AccountService>();

builder.Services.AddSwaggerExt<Program>();

builder.Services.AddSingleton<ILCGNodeProvisioner, LCGNodeProvisioner>();

builder.AddCloudflareTurnstileService();
Expand Down
2 changes: 1 addition & 1 deletion Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
Expand All @@ -31,7 +32,6 @@
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.4" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="8.1.1" />
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="9.103.8" />
</ItemGroup>

Expand Down
16 changes: 1 addition & 15 deletions Common/DataAnnotations/EmailAddressAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using OpenShock.Common.Constants;
using OpenShock.Common.DataAnnotations.Interfaces;

namespace OpenShock.Common.DataAnnotations;

Expand All @@ -14,7 +11,7 @@ namespace OpenShock.Common.DataAnnotations;
/// Inherits from <see cref="ValidationAttribute"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class EmailAddressAttribute : ValidationAttribute, IParameterAttribute
public sealed class EmailAddressAttribute : ValidationAttribute
{
/// <summary>
/// Example value used to generate OpenApi documentation.
Expand Down Expand Up @@ -55,15 +52,4 @@ public sealed class EmailAddressAttribute : ValidationAttribute, IParameterAttri

return ValidationResult.Success;
}

/// <inheritdoc/>
public void Apply(OpenApiSchema schema)
{
//if (ShouldValidate) schema.Pattern = ???;

schema.Example = new OpenApiString(ExampleValue);
}

/// <inheritdoc/>
public void Apply(OpenApiParameter parameter) => Apply(parameter.Schema);
}
15 changes: 0 additions & 15 deletions Common/DataAnnotations/Interfaces/IOperationAttribute.cs

This file was deleted.

21 changes: 0 additions & 21 deletions Common/DataAnnotations/Interfaces/IParameterAttribute.cs

This file was deleted.

31 changes: 0 additions & 31 deletions Common/DataAnnotations/OpenApiSchemas.cs

This file was deleted.

17 changes: 1 addition & 16 deletions Common/DataAnnotations/PasswordAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using OpenShock.Common.Constants;
using OpenShock.Common.DataAnnotations.Interfaces;

namespace OpenShock.Common.DataAnnotations;

Expand All @@ -14,7 +10,7 @@ namespace OpenShock.Common.DataAnnotations;
/// Inherits from <see cref="ValidationAttribute"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class PasswordAttribute : ValidationAttribute, IParameterAttribute
public sealed class PasswordAttribute : ValidationAttribute
{
/// <summary>
/// Example value used to generate OpenApi documentation.
Expand Down Expand Up @@ -55,15 +51,4 @@ public sealed class PasswordAttribute : ValidationAttribute, IParameterAttribute

return ValidationResult.Success;
}

/// <inheritdoc/>
public void Apply(OpenApiSchema schema)
{
//if (ShouldValidate) schema.Pattern = ???;

schema.Example = new OpenApiString(ExampleValue);
}

/// <inheritdoc/>
public void Apply(OpenApiParameter parameter) => Apply(parameter.Schema);
}
16 changes: 1 addition & 15 deletions Common/DataAnnotations/UsernameAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using OpenShock.Common.DataAnnotations.Interfaces;
using OpenShock.Common.Validation;

namespace OpenShock.Common.DataAnnotations;
Expand All @@ -13,7 +10,7 @@ namespace OpenShock.Common.DataAnnotations;
/// Inherits from <see cref="ValidationAttribute"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class UsernameAttribute : ValidationAttribute, IParameterAttribute
public sealed class UsernameAttribute : ValidationAttribute
{
/// <summary>
/// Example value used to generate OpenApi documentation.
Expand Down Expand Up @@ -50,15 +47,4 @@ public sealed class UsernameAttribute : ValidationAttribute, IParameterAttribute
error => new ValidationResult($"{error.Type} - {error.Message}")
);
}

/// <inheritdoc/>
public void Apply(OpenApiSchema schema)
{
//if (ShouldValidate) schema.Pattern = ???;

schema.Example = new OpenApiString(ExampleValue);
}

/// <inheritdoc/>
public void Apply(OpenApiParameter parameter) => Apply(parameter.Schema);
}
2 changes: 1 addition & 1 deletion Common/Models/LegacyDataResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ public LegacyDataResponse(T data, string message = "")

public required string Message { get; set; }
public required T Data { get; set; }
}
}
2 changes: 1 addition & 1 deletion Common/Models/LegacyEmptyResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ public LegacyEmptyResponse(string message, object? data = null)

public required string Message { get; set; }
public object? Data { get; set; }
}
}
40 changes: 40 additions & 0 deletions Common/OpenApi/OpenApiDocumentTransformer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
using OpenShock.Common.Authentication;
using OpenShock.Common.Constants;

namespace OpenShock.Common.OpenApi;

public sealed class OpenApiDocumentTransformer : IOpenApiDocumentTransformer
{
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
document.Info = new OpenApiInfo
{
Title = "OpenShock API",
Description = "Test description of API",
Version = "v" + context.DocumentName,
TermsOfService = new Uri("https://github.com/OpenShock/"),
Contact = new OpenApiContact
{
Name = "Support",
Url = new Uri("mailto:support@openshock.app"),
Email = "support@openshock.app"
},
License = new OpenApiLicense
{
Name = "GNU affero General Public License v3.0",
Url = new Uri("https://github.com/OpenShock/API/blob/develop/LICENSE")
}
};
document.Servers = [
#if DEBUG
new OpenApiServer { Url = "https://localhost" },
#endif
new OpenApiServer { Url = "https://api.openshock.app" },
new OpenApiServer { Url = "https://api.openshock.dev" }
];

return Task.CompletedTask;
}
}
20 changes: 20 additions & 0 deletions Common/OpenApi/OpenApiOperationTransformer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

namespace OpenShock.Common.OpenApi;

public sealed class OpenApiOperationTransformer : IOpenApiOperationTransformer
{
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
{
if (context.Description.ActionDescriptor is not ControllerActionDescriptor actionDescriptor)
{
throw new NotImplementedException();
}

operation.OperationId = actionDescriptor.ControllerName + actionDescriptor.ActionName;

return Task.CompletedTask;
}
}
89 changes: 89 additions & 0 deletions Common/OpenApi/OpenApiSchemaUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.AspNetCore.OpenApi;
using OpenShock.Common.Models;
using System.Text.Json.Serialization.Metadata;

namespace OpenShock.Common.OpenApi;

public static class OpenApiSchemaUtils
{
private static readonly HashSet<Type> CollectionTypes =
[
typeof(List<>),
typeof(IList<>),
typeof(IReadOnlyList<>),
typeof(IEnumerable<>),
typeof(IAsyncEnumerable<>),
typeof(IReadOnlyCollection<>)
];

private static bool IsCollection(Type genericDef) => CollectionTypes.Contains(genericDef);

private static bool IsSystemType(Type type)
{
if (Type.GetTypeCode(type) is not (TypeCode.Empty or TypeCode.Object or TypeCode.DBNull))
return true;

return type == typeof(Guid) || type == typeof(DateTimeOffset) || type == typeof(TimeSpan) || type == typeof(Uri);
}

private static string? GetFriendlyGenericTypeName(Type type)
{
string suffix = "";

while (type.IsGenericType || type.IsArray)
{
if (type.IsArray)
{
suffix = "Array" + suffix;
type = type.GetElementType()!;
continue;
}

var genericTypeDefinition = type.GetGenericTypeDefinition();

if (genericTypeDefinition == typeof(LegacyDataResponse<>) || genericTypeDefinition == typeof(Nullable<>))
{
type = type.GetGenericArguments()[0];
if (IsSystemType(type)) return null;
continue;
}

if (IsCollection(genericTypeDefinition))
{
suffix = "Array" + suffix;
type = type.GetGenericArguments()[0];
continue;
}

if (genericTypeDefinition == typeof(Paginated<>))
{
suffix = "Page" + suffix;
type = type.GetGenericArguments()[0];
continue;
}

throw new NotImplementedException();
}

return type.Name + suffix;
}

private static string? GetFriendlyName(Type type)
{
if (IsSystemType(type)) return null;


if (type.IsGenericType || type.IsArray) return GetFriendlyGenericTypeName(type);

return type.Name;
}


public static void ConfigureOptions(OpenApiOptions options)
{
options.CreateSchemaReferenceId = (jsonTypeInfo) => GetFriendlyName(jsonTypeInfo.Type);

options.AddDocumentTransformer<OpenApiDocumentTransformer>();
options.AddOperationTransformer<OpenApiOperationTransformer>();
}
}
12 changes: 6 additions & 6 deletions Common/OpenShockMiddlewareHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ public static async Task<IApplicationBuilder> UseCommonOpenShockMiddleware(this
return remoteIp != null && metricsAllowedIpNetworks.Any(x => x.Contains(remoteIp));
});

app.UseSwagger();
app.MapOpenApi()
.CacheOutput();

Action<ScalarOptions> scalarOptions = options =>
app.MapScalarApiReference("/openapi/scalar", options =>
options
.WithOpenApiRoutePattern("/swagger/{documentName}/swagger.json")
.WithOpenApiRoutePattern("/openapi/{documentName}.json")
.AddDocument("1", "Version 1")
.AddDocument("2", "Version 2");

app.MapScalarApiReference("/scalar/viewer", scalarOptions);
.AddDocument("2", "Version 2")
);

app.MapControllers();

Expand Down
Loading