Add swagger support

This commit is contained in:
jvyden 2022-02-01 00:18:14 -05:00
parent 219e02d6cb
commit 097b2b9445
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
13 changed files with 83 additions and 54 deletions

View file

@ -1,27 +0,0 @@
#nullable enable
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Api;
public class GetSlotEndpoint : ApiEndpoint
{
private readonly Database database;
public GetSlotEndpoint(Database database)
{
this.database = database;
}
[HttpGet("slot/{id:int}")]
public async Task<IActionResult> OnGet(int id)
{
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(u => u.SlotId == id);
if (slot == null) return this.NotFound();
return this.Ok(slot);
}
}

View file

@ -10,17 +10,17 @@ using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Controllers.Api;
public class GetSlotsEndpoint : ApiEndpoint public class SlotEndpoints : ApiEndpointController
{ {
private readonly Database database; private readonly Database database;
public GetSlotsEndpoint(Database database) public SlotEndpoints(Database database)
{ {
this.database = database; this.database = database;
} }
[HttpGet("slots")] [HttpGet("slots")]
public async Task<IActionResult> OnGet([FromQuery] int limit = 20, [FromQuery] int skip = 0) public async Task<IActionResult> GetSlots([FromQuery] int limit = 20, [FromQuery] int skip = 0)
{ {
limit = Math.Min(ServerStatics.PageSize, limit); limit = Math.Min(ServerStatics.PageSize, limit);
@ -29,4 +29,13 @@ public class GetSlotsEndpoint : ApiEndpoint
return this.Ok(minimalSlots); return this.Ok(minimalSlots);
} }
[HttpGet("slot/{id:int}")]
public async Task<IActionResult> GetSlot(int id)
{
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(u => u.SlotId == id);
if (slot == null) return this.NotFound();
return this.Ok(slot);
}
} }

View file

@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Controllers.Api;
public class StatisticsEndpoint : ApiEndpoint public class StatisticsEndpoints : ApiEndpointController
{ {
[HttpGet("statistics")] [HttpGet("statistics")]
public async Task<IActionResult> OnGet() public async Task<IActionResult> OnGet()

View file

@ -6,17 +6,17 @@ using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Controllers.Api;
public class GetUserEndpoint : ApiEndpoint public class UserEndpoints : ApiEndpointController
{ {
private readonly Database database; private readonly Database database;
public GetUserEndpoint(Database database) public UserEndpoints(Database database)
{ {
this.database = database; this.database = database;
} }
[HttpGet("user/{id:int}")] [HttpGet("user/{id:int}")]
public async Task<IActionResult> OnGet(int id) public async Task<IActionResult> GetUser(int id)
{ {
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); User? user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
if (user == null) return this.NotFound(); if (user == null) return this.NotFound();

View file

@ -38,7 +38,7 @@ public class PublishController : ControllerBase
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
Slot? slot = await this.GetSlotFromBody(); Slot? slot = await this.getSlotFromBody();
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest(); if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest();
@ -79,7 +79,7 @@ public class PublishController : ControllerBase
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
Slot? slot = await this.GetSlotFromBody(); Slot? slot = await this.getSlotFromBody();
if (slot?.Location == null) return this.BadRequest(); if (slot?.Location == null) return this.BadRequest();
// Republish logic // Republish logic
@ -186,7 +186,7 @@ public class PublishController : ControllerBase
return this.Ok(); return this.Ok();
} }
public async Task<Slot?> GetSlotFromBody() private async Task<Slot?> getSlotFromBody()
{ {
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();

View file

@ -91,7 +91,7 @@ public class ReviewController : ControllerBase
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
Review? newReview = await this.GetReviewFromBody(); Review? newReview = await this.getReviewFromBody();
if (newReview == null) return this.BadRequest(); if (newReview == null) return this.BadRequest();
if (review == null) if (review == null)
@ -317,7 +317,7 @@ public class ReviewController : ControllerBase
return this.Ok(); return this.Ok();
} }
public async Task<Review?> GetReviewFromBody() private async Task<Review?> getReviewFromBody()
{ {
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();

View file

@ -80,7 +80,7 @@ public class ScoreController : ControllerBase
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
string myRanking = this.GetScores(score.SlotId, score.Type, user); string myRanking = this.getScores(score.SlotId, score.Type, user);
return this.Ok(myRanking); return this.Ok(myRanking);
} }
@ -99,11 +99,11 @@ public class ScoreController : ControllerBase
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize)); return this.Ok(this.getScores(slotId, type, user, pageStart, pageSize));
} }
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5) private string getScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5)
{ {
// This is hella ugly but it technically assigns the proper rank to a score // This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT // var needed for Anonymous type returned from SELECT

View file

@ -235,7 +235,7 @@ public class SlotsController : ControllerBase
Random rand = new(); Random rand = new();
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType, token.GameVersion) IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable() .AsEnumerable()
.OrderByDescending(s => s.Thumbsup) .OrderByDescending(s => s.Thumbsup)
.ThenBy(_ => rand.Next()) .ThenBy(_ => rand.Next())
@ -279,14 +279,14 @@ public class SlotsController : ControllerBase
Random rand = new(); Random rand = new();
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType, token.GameVersion) IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable() .AsEnumerable()
.OrderByDescending .OrderByDescending
( (
// probably not the best way to do this? // probably not the best way to do this?
s => s =>
{ {
return this.GetGameFilter(gameFilterType, token.GameVersion) switch return this.getGameFilter(gameFilterType, token.GameVersion) switch
{ {
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
@ -337,7 +337,7 @@ public class SlotsController : ControllerBase
Random rand = new(); Random rand = new();
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType, token.GameVersion) IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable() .AsEnumerable()
.OrderByDescending(s => s.Hearts) .OrderByDescending(s => s.Hearts)
.ThenBy(_ => rand.Next()) .ThenBy(_ => rand.Next())
@ -365,7 +365,7 @@ public class SlotsController : ControllerBase
); );
} }
public GameVersion GetGameFilter(string? gameFilterType, GameVersion version) private GameVersion getGameFilter(string? gameFilterType, GameVersion version)
{ {
if (version == GameVersion.LittleBigPlanetVita) return GameVersion.LittleBigPlanetVita; if (version == GameVersion.LittleBigPlanetVita) return GameVersion.LittleBigPlanetVita;
if (version == GameVersion.LittleBigPlanetPSP) return GameVersion.LittleBigPlanetPSP; if (version == GameVersion.LittleBigPlanetPSP) return GameVersion.LittleBigPlanetPSP;
@ -381,7 +381,7 @@ public class SlotsController : ControllerBase
}; };
} }
public IQueryable<Slot> FilterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version) private IQueryable<Slot> filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version)
{ {
string _dateFilterType = dateFilterType ?? ""; string _dateFilterType = dateFilterType ?? "";
@ -392,7 +392,7 @@ public class SlotsController : ControllerBase
_ => 0, _ => 0,
}; };
GameVersion gameVersion = this.GetGameFilter(gameFilterType, version); GameVersion gameVersion = this.getGameFilter(gameFilterType, version);
IQueryable<Slot> whereSlots; IQueryable<Slot> whereSlots;

View file

@ -26,7 +26,7 @@ public class UserController : ControllerBase
this.database = database; this.database = database;
} }
public async Task<string?> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1) private async Task<string?> getSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{ {
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username); User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
return user?.Serialize(gameVersion); return user?.Serialize(gameVersion);
@ -38,7 +38,7 @@ public class UserController : ControllerBase
GameToken? token = await this.database.GameTokenFromRequest(this.Request); GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, ""); if (token == null) return this.StatusCode(403, "");
string? user = await this.GetSerializedUser(username, token.GameVersion); string? user = await this.getSerializedUser(username, token.GameVersion);
if (user == null) return this.NotFound(); if (user == null) return this.NotFound();
return this.Ok(user); return this.Ok(user);
@ -51,7 +51,7 @@ public class UserController : ControllerBase
if (token == null) return this.StatusCode(403, ""); if (token == null) return this.StatusCode(403, "");
List<string?> serializedUsers = new(); List<string?> serializedUsers = new();
foreach (string userId in u) serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion)); foreach (string userId in u) serializedUsers.Add(await this.getSerializedUser(userId, token.GameVersion));
string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user); string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user);

View file

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace LBPUnion.ProjectLighthouse.Helpers;
public class SwaggerFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
List<KeyValuePair<string, OpenApiPathItem>> nonApiRoutes = swaggerDoc.Paths.Where(x => !x.Key.ToLower().StartsWith("/api/v1")).ToList();
nonApiRoutes.ForEach(x => swaggerDoc.Paths.Remove(x.Key));
}
}

View file

@ -7,6 +7,11 @@
<RootNamespace>LBPUnion.ProjectLighthouse</RootNamespace> <RootNamespace>LBPUnion.ProjectLighthouse</RootNamespace>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/> <PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
<PackageReference Include="DDSReader" Version="1.0.8-pre"/> <PackageReference Include="DDSReader" Version="1.0.8-pre"/>
@ -23,6 +28,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0"/> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0"/>
<PackageReference Include="SharpZipLib" Version="1.3.3"/> <PackageReference Include="SharpZipLib" Version="1.3.3"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -13,8 +13,8 @@ using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
namespace LBPUnion.ProjectLighthouse.Startup; namespace LBPUnion.ProjectLighthouse.Startup;
@ -56,6 +56,23 @@ public class Startup
} }
); );
services.AddSwaggerGen
(
c =>
{
c.SwaggerDoc
(
"v1",
new OpenApiInfo
{
Title = "Project Lighthouse API",
Version = "v1",
}
);
c.DocumentFilter<SwaggerFilter>();
}
);
#if DEBUG #if DEBUG
services.AddSingleton<IHostLifetime, DebugWarmupLifetime>(); services.AddSingleton<IHostLifetime, DebugWarmupLifetime>();
#else #else
@ -85,6 +102,15 @@ public class Startup
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseSwagger();
app.UseSwaggerUI
(
c =>
{
c.SwaggerEndpoint("v1/swagger.json", "Project Lighthouse API");
}
);
// Logs every request and the response to it // Logs every request and the response to it
// Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news"
// Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr"

View file

@ -5,5 +5,5 @@ namespace LBPUnion.ProjectLighthouse.Types;
[ApiController] [ApiController]
[Route("/api/v1/")] [Route("/api/v1/")]
[Produces("application/json")] [Produces("application/json")]
public class ApiEndpoint : ControllerBase public class ApiEndpointController : ControllerBase
{} {}