Skip to content
Merged
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: 3 additions & 0 deletions Refresh.Database/GameDatabaseContext.Relations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@ public int GetTotalUniquePlaysForLevel(GameLevel level, bool includingAuthor = t

public int GetTotalCompletionsForLevel(GameLevel level) =>
this.GameScores.Count(s => s.Level == level);

public int GetTotalCompletionsForLevelByUser(GameLevel level, GameUser user) =>
this.GameScores.Count(s => s.LevelId == level.LevelId && s.PublisherId == user.UserId);

#endregion

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Refresh.Core.Types.Data;
using Refresh.Database.Models.Levels;

namespace Refresh.Interfaces.APIv3.Endpoints.DataTypes.Response.Levels;

[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class ApiGameLevelOwnRelationsResponse : IApiResponse
{
public required bool IsHearted { get; set; }
public required bool IsQueued { get; set; }
public required int LevelRating { get; set; }

/// <summary>
/// Returns the total amount of plays. Probably rename this in APIv4 for clarity.
/// </summary>
public required int MyPlaysCount { get; set; }
public required int CompletionCount { get; set; }
public required int PhotoCount { get; set; }

public static ApiGameLevelOwnRelationsResponse? FromOld(GameLevel level, DataContext dataContext)
{
if (dataContext.User == null)
return null;

return new()
{
// TODO: Probably cache these stats aswell
IsHearted = dataContext.Database.IsLevelFavouritedByUser(level, dataContext.User),
IsQueued = dataContext.Database.IsLevelQueuedByUser(level, dataContext.User),
LevelRating = (int?)dataContext.Database.GetRatingByUser(level, dataContext.User) ?? 0,
MyPlaysCount = dataContext.Database.GetTotalPlaysForLevelByUser(level, dataContext.User),
CompletionCount = dataContext.Database.GetTotalCompletionsForLevelByUser(level, dataContext.User),
PhotoCount = dataContext.Database.GetTotalPhotosInLevelByUser(level, dataContext.User)
};
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
public required bool IsCopyable { get; set; }
public required float Score { get; set; }
public required IEnumerable<Tag> Tags { get; set; }
public required ApiGameLevelOwnRelationsResponse? OwnRelations { get; set; }

public static ApiGameLevelResponse? FromOld(GameLevel? level, DataContext dataContext)
{
Expand Down Expand Up @@ -102,6 +103,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
Reviews = level.Statistics.ReviewCount,
Tags = dataContext.Database.GetTagsForLevel(level),
IsModded = level.IsModded,
OwnRelations = ApiGameLevelOwnRelationsResponse.FromOld(level, dataContext),
};
}

Expand Down
11 changes: 3 additions & 8 deletions Refresh.Interfaces.APIv3/Endpoints/LevelApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,13 @@ public ApiOkResponse SetLevelAsOverrideByHash(RequestContext context, GameDataba
[ApiV3Endpoint("levels/id/{id}/relations"), MinimumRole(GameUserRole.Restricted)]
[DocSummary("Gets your relations to a level by it's ID")]
[DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)]
public ApiResponse<ApiGameLevelRelationsResponse> GetLevelRelationsOfUser(RequestContext context, GameDatabaseContext database, GameUser user,
public ApiResponse<ApiGameLevelOwnRelationsResponse> GetLevelRelationsOfUser(RequestContext context, DataContext dataContext, GameUser user,
[DocSummary("The ID of the level")] int id)
{
GameLevel? level = database.GetLevelById(id);
GameLevel? level = dataContext.Database.GetLevelById(id);
if (level == null) return ApiNotFoundError.LevelMissingError;

return new ApiGameLevelRelationsResponse
{
IsHearted = database.IsLevelFavouritedByUser(level, user),
IsQueued = database.IsLevelQueuedByUser(level, user),
MyPlaysCount = database.GetTotalPlaysForLevelByUser(level, user)
};
return ApiGameLevelOwnRelationsResponse.FromOld(level, dataContext);
}

[ApiV3Endpoint("levels/id/{id}/heart", HttpMethods.Post)]
Expand Down
21 changes: 20 additions & 1 deletion RefreshTests.GameServer/Tests/ApiV3/LevelApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,23 @@ public async Task CantDeleteLevelIfLevelInvalid()
Assert.That(response.StatusCode, Is.EqualTo(NotFound));
Assert.That(context.Database.GetLevelById(id), Is.Not.Null);
}
}

[Test]
public void OnlyIncludesOwnLevelRelationsWhenSignedIn()
{
using TestContext context = this.GetServer();
GameUser me = context.CreateUser();
GameLevel level = context.CreateLevel(me);

// Try fetching the level without being signed in
ApiResponse<ApiGameLevelResponse>? response = context.Http.GetData<ApiGameLevelResponse>($"/api/v3/levels/id/{level.LevelId}");
Assert.That(response?.Data, Is.Not.Null);
Assert.That(response!.Data!.OwnRelations, Is.Null);

// Sign in and then get level again
using HttpClient client = context.GetAuthenticatedClient(TokenType.Api, me);
response = client.GetData<ApiGameLevelResponse>($"/api/v3/levels/id/{level.LevelId}");
Assert.That(response?.Data, Is.Not.Null);
Assert.That(response!.Data!.OwnRelations, Is.Not.Null);
}
}