Implement baby's first pagination on Users

This commit is contained in:
Jordan Dominion
2024-09-10 23:34:10 -04:00
parent ed2de22f79
commit 74fa26fe02
3 changed files with 52 additions and 19 deletions
@@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Authority
/// Gets the <see cref="User"/> with a given <paramref name="id"/>.
/// </summary>
/// <param name="id">The <see cref="EntityId.Id"/> of the <see cref="User"/>.</param>
/// <param name="includeJoins">If relevant entities should be loaded.</param>
/// <param name="includeJoins">If related entities should be loaded.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="User"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
[TgsAuthorize(AdministrationRights.ReadUsers)]
@@ -36,10 +36,9 @@ namespace Tgstation.Server.Host.Authority
/// <summary>
/// Gets all registered <see cref="User"/>s.
/// </summary>
/// <param name="includeJoins">If relevant entities should be loaded.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <param name="includeJoins">If related entities should be loaded.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="IQueryable{T}"/> <see cref="User"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
[TgsAuthorize(AdministrationRights.ReadUsers)]
public ValueTask<AuthorityResponse<IQueryable<User>>> List(bool includeJoins, CancellationToken cancellationToken);
public ValueTask<AuthorityResponse<IQueryable<User>>> List(bool includeJoins);
}
}
@@ -49,18 +49,7 @@ namespace Tgstation.Server.Host.Authority
/// <inheritdoc />
public async ValueTask<AuthorityResponse<User>> GetId(long id, bool includeJoins, CancellationToken cancellationToken)
{
var queryable = databaseContext
.Users
.AsQueryable();
if (includeJoins)
queryable = queryable
.Where(x => x.Id == id)
.Include(x => x.CreatedBy)
.Include(x => x.OAuthConnections)
.Include(x => x.Group!)
.ThenInclude(x => x.PermissionSet)
.Include(x => x.PermissionSet);
var queryable = ListCore(includeJoins);
var user = await queryable.FirstOrDefaultAsync(
dbModel => dbModel.Id == id,
@@ -75,9 +64,35 @@ namespace Tgstation.Server.Host.Authority
}
/// <inheritdoc />
public ValueTask<AuthorityResponse<IQueryable<User>>> List(bool includeJoins, CancellationToken cancellationToken)
public ValueTask<AuthorityResponse<IQueryable<User>>> List(bool includeJoins)
{
throw new NotImplementedException();
var systemUserCanonicalName = User.CanonicalizeName(User.TgsSystemUserName);
return ValueTask.FromResult(
new AuthorityResponse<IQueryable<User>>(
ListCore(includeJoins)
.Where(x => x.CanonicalName != systemUserCanonicalName)));
}
/// <summary>
/// Generates an <see cref="IQueryable{T}"/> for listing <see cref="User"/>s.
/// </summary>
/// <param name="includeJoins">If related entities should be loaded.</param>
/// <returns>A new <see cref="IQueryable{T}"/> of <see cref="User"/>s.</returns>
private IQueryable<User> ListCore(bool includeJoins)
{
var queryable = databaseContext
.Users
.AsQueryable();
if (includeJoins)
queryable = queryable
.Include(x => x.CreatedBy)
.Include(x => x.OAuthConnections)
.Include(x => x.Group!)
.ThenInclude(x => x.PermissionSet)
.Include(x => x.PermissionSet);
return queryable;
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -8,6 +9,7 @@ using HotChocolate.Types.Relay;
using Tgstation.Server.Host.Authority;
using Tgstation.Server.Host.Authority.Core;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
#pragma warning disable CA1724 // conflict with GitLabApiClient.Models.Users. They can fuck off
@@ -40,7 +42,7 @@ namespace Tgstation.Server.Host.GraphQL.Types
/// <param name="id">The <see cref="Entity.Id"/> of the <see cref="User"/>.</param>
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserAuthority"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="User"/>.</returns>
/// <returns>The <see cref="User"/> represented by <paramref name="id"/>, if any.</returns>
[Error(typeof(ErrorMessageException))]
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.GetId))]
public async ValueTask<User?> ById(
@@ -51,5 +53,22 @@ namespace Tgstation.Server.Host.GraphQL.Types
ArgumentNullException.ThrowIfNull(userAuthority);
return await userAuthority.InvokeTransformable<Models.User, User>(authority => authority.GetId(id, false, cancellationToken));
}
/// <summary>
/// Lists all registered <see cref="User"/>s.
/// </summary>
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserAuthority"/>.</param>
/// <returns>A list of all registered <see cref="User"/>s.</returns>
[UsePaging(IncludeTotalCount = true)]
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.List))]
public async ValueTask<IQueryable<User?>> List(
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority)
{
ArgumentNullException.ThrowIfNull(userAuthority);
var dtoQueryable = await userAuthority.Invoke<IQueryable<Models.User>, IQueryable<Models.User>>(authority => authority.List(false));
return dtoQueryable
.Cast<IApiTransformable<User>>()
.Select(dto => dto.ToApi());
}
}
}