mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-28 23:52:36 +01:00
Adds pagination
- Add generic Paginated<TModel> for pagination responses. - Added pagination helper to ApiController. - Implemented pagination for all previously IEnumerable<T> response endpoints. - Added IApiTransformable<TApiModel> for host models finally. - Added PaginationSettings to client List endpoints. - Use two deprecated ErrorCodes for page parameter validation. - Re-enabled to OpenAPI root array response lint. - Add some integration tests with users.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"no_operation_id": "error",
|
||||
"operation_id_case_convention": "off",
|
||||
"no_summary": "error",
|
||||
"no_array_responses": "off",
|
||||
"no_array_responses": "error",
|
||||
"parameter_order": "error",
|
||||
"undefined_tag": "off",
|
||||
"unused_tag": "error",
|
||||
|
||||
@@ -250,18 +250,16 @@ namespace Tgstation.Server.Api.Models
|
||||
RepoWhitespaceCommitterEmail,
|
||||
|
||||
/// <summary>
|
||||
/// Deprecated.
|
||||
/// A paginated request asked for too large a page.
|
||||
/// </summary>
|
||||
[Description("Deprecated error code.")]
|
||||
[Obsolete("With API v7 ", true)]
|
||||
DreamDaemonDuplicatePorts,
|
||||
[Description("Requested pageSize is too large!")]
|
||||
ApiPageTooLarge,
|
||||
|
||||
/// <summary>
|
||||
/// Deprecated.
|
||||
/// A paginated request asked for page 0.
|
||||
/// </summary>
|
||||
[Description("Deprecated error code.")]
|
||||
[Obsolete("With DMAPI-5.0.0, ultrasafe security is now supported.", true)]
|
||||
InvalidSecurityLevel,
|
||||
[Description("Cannot request page or pageSize <= 0.")]
|
||||
ApiInvalidPageOrPageSize,
|
||||
|
||||
/// <summary>
|
||||
/// A requested <see cref="ChatChannel"/>'s data does not match with its <see cref="Internal.ChatBot.Provider"/>.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a paginated set of models.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="System.Type"/> of the returned model.</typeparam>
|
||||
public sealed class Paginated<TModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ICollection{T}"/> of the returned <typeparamref name="TModel"/>s.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public ICollection<TModel>? Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The total number of pages in the query.
|
||||
/// </summary>
|
||||
public int TotalPages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current size of pages in the query.
|
||||
/// </summary>
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,45 +10,40 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class AdministrationClient : IAdministrationClient
|
||||
sealed class AdministrationClient : PaginatedClient, IAdministrationClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="apiClient"/> for the <see cref="AdministrationClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// Construct an <see cref="AdministrationClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
public AdministrationClient(IApiClient apiClient)
|
||||
{
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
: base(apiClient)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Administration> Read(CancellationToken cancellationToken) => apiClient.Read<Administration>(Routes.Administration, cancellationToken);
|
||||
public Task<Administration> Read(CancellationToken cancellationToken) => ApiClient.Read<Administration>(Routes.Administration, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Update(Administration administration, CancellationToken cancellationToken) => apiClient.Update(Routes.Administration, administration ?? throw new ArgumentNullException(nameof(administration)), cancellationToken);
|
||||
public Task Update(Administration administration, CancellationToken cancellationToken) => ApiClient.Update(Routes.Administration, administration ?? throw new ArgumentNullException(nameof(administration)), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Restart(CancellationToken cancellationToken) => apiClient.Delete(Routes.Administration, cancellationToken);
|
||||
public Task Restart(CancellationToken cancellationToken) => ApiClient.Delete(Routes.Administration, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<LogFile>> ListLogs(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<LogFile>>(Routes.Logs, cancellationToken);
|
||||
public Task<IReadOnlyList<LogFile>> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<LogFile>(paginationSettings, Routes.Logs, null, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Tuple<LogFile, Stream>> GetLog(LogFile logFile, CancellationToken cancellationToken)
|
||||
{
|
||||
var resultFile = await apiClient.Read<LogFile>(
|
||||
var resultFile = await ApiClient.Read<LogFile>(
|
||||
Routes.Logs + Routes.SanitizeGetPath(
|
||||
HttpUtility.UrlEncode(
|
||||
logFile?.Name ?? throw new ArgumentNullException(nameof(logFile)))),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var stream = await apiClient.Download(resultFile, cancellationToken).ConfigureAwait(false);
|
||||
var stream = await ApiClient.Download(resultFile, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return Tuple.Create(resultFile, stream);
|
||||
|
||||
@@ -9,13 +9,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class ByondClient : IByondClient
|
||||
sealed class ByondClient : PaginatedClient, IByondClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="ByondClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="ByondClient"/>
|
||||
/// </summary>
|
||||
@@ -24,24 +19,25 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="ByondClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="Instance"/></param>
|
||||
public ByondClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Byond> ActiveVersion(CancellationToken cancellationToken) => apiClient.Read<Byond>(Routes.Byond, instance.Id, cancellationToken);
|
||||
public Task<Byond> ActiveVersion(CancellationToken cancellationToken) => ApiClient.Read<Byond>(Routes.Byond, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Byond>>(Routes.ListRoute(Routes.Byond), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<Byond>> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<Byond>(paginationSettings, Routes.ListRoute(Routes.Byond), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Byond> SetActiveVersion(Byond byond, Stream zipFileStream, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await apiClient.Update<Byond, Byond>(
|
||||
var result = await ApiClient.Update<Byond, Byond>(
|
||||
Routes.Byond,
|
||||
byond ?? throw new ArgumentNullException(nameof(byond)),
|
||||
instance.Id,
|
||||
@@ -49,7 +45,7 @@ namespace Tgstation.Server.Client.Components
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (byond.UploadCustomZip == true)
|
||||
await apiClient.Upload(result, zipFileStream, cancellationToken).ConfigureAwait(false);
|
||||
await ApiClient.Upload(result, zipFileStream, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,13 +8,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class ChatBotsClient : IChatBotsClient
|
||||
sealed class ChatBotsClient : PaginatedClient, IChatBotsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="ChatBotsClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="ChatBotsClient"/>
|
||||
/// </summary>
|
||||
@@ -23,27 +18,28 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="ChatBotsClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="instance"/></param>
|
||||
public ChatBotsClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken) => apiClient.Create<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
|
||||
public Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Create<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Delete(ChatBot settings, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Chat, settings?.Id ?? throw new ArgumentNullException(nameof(settings))), instance.Id, cancellationToken);
|
||||
public Task Delete(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Chat, settings?.Id ?? throw new ArgumentNullException(nameof(settings))), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ChatBot>>(Routes.ListRoute(Routes.Chat), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<ChatBot>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<ChatBot>(paginationSettings, Routes.ListRoute(Routes.Chat), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken) => apiClient.Update<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
|
||||
public Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Update<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken) => apiClient.Read<ChatBot>(Routes.SetID(Routes.Chat, (settings ?? throw new ArgumentNullException(nameof(settings))).Id), instance.Id, cancellationToken);
|
||||
public Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Read<ChatBot>(Routes.SetID(Routes.Chat, (settings ?? throw new ArgumentNullException(nameof(settings))).Id), instance.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class ConfigurationClient : IConfigurationClient
|
||||
sealed class ConfigurationClient : PaginatedClient, IConfigurationClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="ConfigurationClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="ConfigurationClient"/>
|
||||
/// </summary>
|
||||
@@ -24,34 +19,42 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="ConfigurationClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="instance"/></param>
|
||||
public ConfigurationClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => apiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken);
|
||||
public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ConfigurationFile> CreateDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => apiClient.Create<ConfigurationFile, ConfigurationFile>(Routes.Configuration, directory, instance.Id, cancellationToken);
|
||||
public Task<ConfigurationFile> CreateDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Create<ConfigurationFile, ConfigurationFile>(Routes.Configuration, directory, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ConfigurationFile>>(Routes.ListRoute(Routes.Configuration) + Routes.SanitizeGetPath(directory), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<ConfigurationFile>> List(
|
||||
PaginationSettings? paginationSettings,
|
||||
string directory,
|
||||
CancellationToken cancellationToken)
|
||||
=> ReadPaged<ConfigurationFile>(
|
||||
paginationSettings,
|
||||
Routes.ListRoute(Routes.Configuration) + Routes.SanitizeGetPath(directory),
|
||||
instance.Id,
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Tuple<ConfigurationFile, Stream>> Read(ConfigurationFile file, CancellationToken cancellationToken)
|
||||
{
|
||||
if (file == null)
|
||||
throw new ArgumentNullException(nameof(file));
|
||||
var configFile = await apiClient.Read<ConfigurationFile>(
|
||||
var configFile = await ApiClient.Read<ConfigurationFile>(
|
||||
Routes.ConfigurationFile + Routes.SanitizeGetPath(file.Path ?? throw new ArgumentException("file.Path should not be null!", nameof(file))),
|
||||
instance.Id,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var downloadStream = await apiClient.Download(configFile, cancellationToken).ConfigureAwait(false);
|
||||
var downloadStream = await ApiClient.Download(configFile, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return Tuple.Create(configFile, downloadStream);
|
||||
@@ -75,7 +78,7 @@ namespace Tgstation.Server.Client.Components
|
||||
|
||||
using (memoryStream)
|
||||
{
|
||||
var configFileTask = apiClient.Update<ConfigurationFile, ConfigurationFile>(
|
||||
var configFileTask = ApiClient.Update<ConfigurationFile, ConfigurationFile>(
|
||||
Routes.Configuration,
|
||||
file ?? throw new ArgumentNullException(nameof(file)),
|
||||
instance.Id,
|
||||
@@ -88,7 +91,7 @@ namespace Tgstation.Server.Client.Components
|
||||
|
||||
var streamUsed = memoryStream ?? uploadStream;
|
||||
streamUsed?.Seek(initialStreamPosition, SeekOrigin.Begin);
|
||||
await apiClient.Upload(configFile, streamUsed, cancellationToken).ConfigureAwait(false);
|
||||
await ApiClient.Upload(configFile, streamUsed, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return configFile;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,13 +8,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class DreamMakerClient : IDreamMakerClient
|
||||
sealed class DreamMakerClient : PaginatedClient, IDreamMakerClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="DreamMakerClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="DreamMakerClient"/>
|
||||
/// </summary>
|
||||
@@ -23,27 +18,28 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="DreamMakerClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="Instance"/></param>
|
||||
public DreamMakerClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Job> Compile(CancellationToken cancellationToken) => apiClient.Create<Job>(Routes.DreamMaker, instance.Id, cancellationToken);
|
||||
public Task<Job> Compile(CancellationToken cancellationToken) => ApiClient.Create<Job>(Routes.DreamMaker, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<CompileJob> GetCompileJob(EntityId compileJob, CancellationToken cancellationToken) => apiClient.Read<CompileJob>(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id, cancellationToken);
|
||||
public Task<CompileJob> GetCompileJob(EntityId compileJob, CancellationToken cancellationToken) => ApiClient.Read<CompileJob>(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<EntityId>> GetJobIds(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<EntityId>>(Routes.ListRoute(Routes.DreamMaker), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<EntityId>> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<EntityId>(paginationSettings, Routes.ListRoute(Routes.DreamMaker), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DreamMaker> Read(CancellationToken cancellationToken) => apiClient.Read<DreamMaker>(Routes.DreamMaker, instance.Id, cancellationToken);
|
||||
public Task<DreamMaker> Read(CancellationToken cancellationToken) => ApiClient.Read<DreamMaker>(Routes.DreamMaker, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken) => apiClient.Update<DreamMaker, DreamMaker>(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id, cancellationToken);
|
||||
public Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken) => ApiClient.Update<DreamMaker, DreamMaker>(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,10 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Get all installed <see cref="Byond"/> <see cref="System.Version"/>s
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in an <see cref="IReadOnlyList{T}"/> of installed <see cref="Byond"/> <see cref="System.Version"/>s</returns>
|
||||
Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<Byond>> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Byond"/> information
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -13,9 +13,10 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// List the <see cref="ChatBot"/>s
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of the <see cref="ChatBot"/> of the server</returns>
|
||||
Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<ChatBot>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="ChatBot"/>
|
||||
|
||||
@@ -15,10 +15,14 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// List configuration files
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="directory">The path to the directory to list files in</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="IReadOnlyList{T}"/> of <see cref="ConfigurationFile"/>s in the <paramref name="directory"/></returns>
|
||||
Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<ConfigurationFile>> List(
|
||||
PaginationSettings? paginationSettings,
|
||||
string directory,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Read a <see cref="ConfigurationFile"/> file
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -33,11 +33,12 @@ namespace Tgstation.Server.Client.Components
|
||||
Task<Job> Compile(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="EntityId"/>s of all <see cref="CompileJob"/>s for the instance
|
||||
/// Gets the <see cref="CompileJob"/>s for the instance
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of <see cref="CompileJob"/> <see cref="EntityId"/>s.</returns>
|
||||
Task<IReadOnlyList<EntityId>> GetJobIds(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<EntityId>> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a <paramref name="compileJob"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -28,9 +28,10 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Get the <see cref="InstanceUser"/>s in the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of <see cref="InstanceUser"/>s in the instance</returns>
|
||||
Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<InstanceUser>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Update a <paramref name="instanceUser"/>
|
||||
@@ -56,4 +57,4 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -11,18 +11,20 @@ namespace Tgstation.Server.Client.Components
|
||||
public interface IJobsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// List the <see cref="Job"/> <see cref="EntityId"/>s in the <see cref="Instance"/>
|
||||
/// List the <see cref="Job"/>s in the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of the <see cref="Job"/> <see cref="EntityId"/>s in the <see cref="Instance"/></returns>
|
||||
Task<IReadOnlyList<EntityId>> List(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<Job>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// List the active <see cref="Job"/>s in the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of the active <see cref="Job"/>s in the <see cref="Instance"/></returns>
|
||||
Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<Job>> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a <paramref name="job"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,13 +8,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class InstanceUserClient : IInstanceUserClient
|
||||
sealed class InstanceUserClient : PaginatedClient, IInstanceUserClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="InstanceUserClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="InstanceUserClient"/>
|
||||
/// </summary>
|
||||
@@ -23,19 +18,19 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct an <see cref="InstanceUserClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="instance"/></param>
|
||||
public InstanceUserClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Create<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
|
||||
public Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken) => ApiClient.Create<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Delete(
|
||||
public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => ApiClient.Delete(
|
||||
Routes.SetID(
|
||||
Routes.InstanceUser,
|
||||
instanceUser.UserId),
|
||||
@@ -43,15 +38,16 @@ namespace Tgstation.Server.Client.Components
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<InstanceUser> Read(CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.InstanceUser, instance.Id, cancellationToken);
|
||||
public Task<InstanceUser> Read(CancellationToken cancellationToken) => ApiClient.Read<InstanceUser>(Routes.InstanceUser, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Update<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
|
||||
public Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken) => ApiClient.Update<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<InstanceUser>>(Routes.ListRoute(Routes.InstanceUser), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<InstanceUser>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<InstanceUser>(paginationSettings, Routes.ListRoute(Routes.InstanceUser), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.SetID(Routes.InstanceUser, instanceUser?.UserId ?? throw new ArgumentNullException(nameof(instanceUser))), instance.Id, cancellationToken);
|
||||
public Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken) => ApiClient.Read<InstanceUser>(Routes.SetID(Routes.InstanceUser, instanceUser?.UserId ?? throw new ArgumentNullException(nameof(instanceUser))), instance.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,13 +8,8 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client.Components
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class JobsClient : IJobsClient
|
||||
sealed class JobsClient : PaginatedClient, IJobsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="JobsClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> for the <see cref="JobsClient"/>
|
||||
/// </summary>
|
||||
@@ -23,24 +18,26 @@ namespace Tgstation.Server.Client.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="JobsClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="Instance"/></param>
|
||||
public JobsClient(IApiClient apiClient, Instance instance)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Cancel(Job job, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
|
||||
public Task Cancel(Job job, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<EntityId>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<EntityId>>(Routes.ListRoute(Routes.Jobs), instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<Job>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<Job>(paginationSettings, Routes.ListRoute(Routes.Jobs), instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Job>>(Routes.Jobs, instance.Id, cancellationToken);
|
||||
public Task<IReadOnlyList<Job>> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<Job>(paginationSettings, Routes.Jobs, instance.Id, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Job> GetId(EntityId job, CancellationToken cancellationToken) => apiClient.Read<Job>(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
|
||||
public Task<Job> GetId(EntityId job, CancellationToken cancellationToken) => ApiClient.Read<Job>(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,10 @@ namespace Tgstation.Server.Client
|
||||
/// <summary>
|
||||
/// Lists the log files available for download.
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in an <see cref="IReadOnlyList{T}"/> of <see cref="LogFile"/> metadata.</returns>
|
||||
Task<IReadOnlyList<LogFile>> ListLogs(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<LogFile>> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Download a given <paramref name="logFile"/>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -14,9 +14,10 @@ namespace Tgstation.Server.Client
|
||||
/// <summary>
|
||||
/// Get all <see cref="IInstanceClient"/>s for <see cref="Instance"/>s the user can view
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of all <see cref="Instance"/>s the user can view</returns>
|
||||
Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<Instance>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create or attach an <paramref name="instance"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -28,9 +28,10 @@ namespace Tgstation.Server.Client
|
||||
/// <summary>
|
||||
/// List all <see cref="User"/>s
|
||||
/// </summary>
|
||||
/// <param name="paginationSettings">The optional <see cref="PaginationSettings"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyList{T}"/> of all <see cref="User"/>s</returns>
|
||||
Task<IReadOnlyList<User>> List(CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<User>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <paramref name="user"/>
|
||||
@@ -48,4 +49,4 @@ namespace Tgstation.Server.Client
|
||||
/// <returns>The updated <see cref="User"/></returns>
|
||||
Task<User> Update(UserUpdate user, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -9,41 +9,36 @@ using Tgstation.Server.Client.Components;
|
||||
namespace Tgstation.Server.Client
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class InstanceManagerClient : IInstanceManagerClient
|
||||
sealed class InstanceManagerClient : PaginatedClient, IInstanceManagerClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="InstanceManagerClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// Construct an <see cref="InstanceManagerClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
public InstanceManagerClient(IApiClient apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
}
|
||||
: base(apiClient)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken) => apiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
|
||||
public Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken) => ApiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Detach(Instance instance, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
public Task Detach(Instance instance, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Instance>>(Routes.ListRoute(Routes.InstanceManager), cancellationToken);
|
||||
public Task<IReadOnlyList<Instance>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<Instance>(paginationSettings, Routes.ListRoute(Routes.InstanceManager), null, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => apiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
|
||||
public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => ApiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => apiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => ApiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task GrantPermissions(Instance instance, CancellationToken cancellationToken) => apiClient.Patch(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
public Task GrantPermissions(Instance instance, CancellationToken cancellationToken) => ApiClient.Patch(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IInstanceClient CreateClient(Instance instance) => new InstanceClient(apiClient, instance);
|
||||
public IInstanceClient CreateClient(Instance instance) => new InstanceClient(ApiClient, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Client that deals with getting paginated results.
|
||||
/// </summary>
|
||||
abstract class PaginatedClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>
|
||||
/// </summary>
|
||||
protected IApiClient ApiClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PaginatedClient"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="ApiClient"/>.</param>
|
||||
public PaginatedClient(IApiClient apiClient)
|
||||
{
|
||||
ApiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a given <paramref name="route"/> with paged results.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="Type"/> of result model.</typeparam>
|
||||
/// <param name="paginationSettings">The <see cref="PaginationSettings"/> if any.</param>
|
||||
/// <param name="route">The route.</param>
|
||||
/// <param name="instanceId">The optional <see cref="Instance"/> <see cref="EntityId.Id"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in an <see cref="IReadOnlyList{T}"/> of the paginated <typeparamref name="TModel"/>s.</returns>
|
||||
protected async Task<IReadOnlyList<TModel>> ReadPaged<TModel>(
|
||||
PaginationSettings? paginationSettings,
|
||||
string route,
|
||||
long? instanceId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (route == null)
|
||||
throw new ArgumentNullException(nameof(route));
|
||||
|
||||
var routeFormatter = $"{route}?page={{0}}";
|
||||
var currentPage = 1;
|
||||
if (paginationSettings != null)
|
||||
{
|
||||
if (paginationSettings.RetrieveCount == 0)
|
||||
return new List<TModel>(); // that was easy
|
||||
else if (paginationSettings.RetrieveCount < 0)
|
||||
throw new ArgumentOutOfRangeException("RetrieveCount cannot be less than 0!", nameof(paginationSettings));
|
||||
|
||||
int? pageSize = null;
|
||||
if (paginationSettings.PageSize.HasValue)
|
||||
{
|
||||
// pagesize validates itself on first request
|
||||
pageSize = paginationSettings.PageSize.Value;
|
||||
routeFormatter += $"&pageSize={pageSize}";
|
||||
}
|
||||
|
||||
if (paginationSettings.Offset.HasValue)
|
||||
{
|
||||
if(paginationSettings.Offset.Value < 0)
|
||||
throw new ArgumentOutOfRangeException("Offset cannot be less than 0!", nameof(paginationSettings));
|
||||
|
||||
pageSize ??= paginationSettings.Offset.Value;
|
||||
currentPage = (paginationSettings.Offset.Value / pageSize.Value) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Task<Paginated<TModel>> GetPage() => instanceId.HasValue
|
||||
? ApiClient.Read<Paginated<TModel>>(
|
||||
String.Format(routeFormatter, currentPage),
|
||||
instanceId.Value,
|
||||
cancellationToken)
|
||||
: ApiClient.Read<Paginated<TModel>>(
|
||||
String.Format(routeFormatter, currentPage),
|
||||
cancellationToken);
|
||||
|
||||
var firstPage = await GetPage().ConfigureAwait(false);
|
||||
|
||||
var totalAvailable = firstPage.TotalPages * firstPage.PageSize;
|
||||
var maximumItems = paginationSettings?.RetrieveCount.HasValue == true
|
||||
? Math.Min(paginationSettings.RetrieveCount!.Value, totalAvailable)
|
||||
: totalAvailable;
|
||||
|
||||
var results = new List<TModel>(maximumItems);
|
||||
var currentResults = firstPage;
|
||||
do
|
||||
{
|
||||
// check if first page
|
||||
if(currentPage > 1)
|
||||
currentResults = await GetPage().ConfigureAwait(false);
|
||||
|
||||
if (currentResults.Content == null)
|
||||
throw new ApiConflictException("Paginated results missing content!");
|
||||
|
||||
IEnumerable<TModel> rangeToAdd = currentResults.Content;
|
||||
if (paginationSettings?.Offset.HasValue == true)
|
||||
{
|
||||
rangeToAdd = rangeToAdd
|
||||
.Skip(paginationSettings.Offset!.Value % currentResults.PageSize);
|
||||
}
|
||||
|
||||
var itemsAvailableInPage = rangeToAdd.Count();
|
||||
var itemsStillRequired = maximumItems - results.Count;
|
||||
if (itemsAvailableInPage > itemsStillRequired)
|
||||
rangeToAdd = rangeToAdd
|
||||
.Take(itemsStillRequired);
|
||||
|
||||
results.AddRange(rangeToAdd);
|
||||
++currentPage;
|
||||
}
|
||||
while (results.Count < maximumItems && currentPage <= currentResults.TotalPages);
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Tgstation.Server.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for a paginated request.
|
||||
/// </summary>
|
||||
public sealed class PaginationSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of a page. Defaults to server settings.
|
||||
/// </summary>
|
||||
public int? PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The offset to take from. Default 0.
|
||||
/// </summary>
|
||||
public int? Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of items to retrieve. Default everything.
|
||||
/// </summary>
|
||||
/// <remarks>Results will be truncated if overflow occurs due to <see cref="PageSize"/>.</remarks>
|
||||
public int? RetrieveCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,35 +8,31 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Client
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class UsersClient : IUsersClient
|
||||
sealed class UsersClient : PaginatedClient, IUsersClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="apiClient"/> for the <see cref="UsersClient"/>
|
||||
/// </summary>
|
||||
readonly IApiClient apiClient;
|
||||
|
||||
/// <summary>
|
||||
/// Construct an <see cref="UsersClient"/>
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The value of <see cref="apiClient"/></param>
|
||||
/// <param name="apiClient">The <see cref="IApiClient"/> for the <see cref="PaginatedClient"/>.</param>
|
||||
public UsersClient(IApiClient apiClient)
|
||||
: base(apiClient)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<User> Create(UserUpdate user, CancellationToken cancellationToken) => apiClient.Create<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
|
||||
public Task<User> Create(UserUpdate user, CancellationToken cancellationToken) => ApiClient.Create<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<User> GetId(Api.Models.Internal.User user, CancellationToken cancellationToken) => apiClient.Read<User>(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken);
|
||||
public Task<User> GetId(Api.Models.Internal.User user, CancellationToken cancellationToken) => ApiClient.Read<User>(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<User>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<User>>(Routes.ListRoute(Routes.User), cancellationToken);
|
||||
public Task<IReadOnlyList<User>> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken)
|
||||
=> ReadPaged<User>(paginationSettings, Routes.ListRoute(Routes.User), null, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<User> Read(CancellationToken cancellationToken) => apiClient.Read<User>(Routes.User, cancellationToken);
|
||||
public Task<User> Read(CancellationToken cancellationToken) => ApiClient.Read<User>(Routes.User, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<User> Update(UserUpdate user, CancellationToken cancellationToken) => apiClient.Update<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
|
||||
public Task<User> Update(UserUpdate user, CancellationToken cancellationToken) => ApiClient.Update<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,48 +313,59 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// List <see cref="LogFile"/>s present.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Listed logs successfully.</response>
|
||||
/// <response code="409">An IO error occurred while listing.</response>
|
||||
[HttpGet(Routes.Logs)]
|
||||
[TgsAuthorize(AdministrationRights.DownloadLogs)]
|
||||
[ProducesResponseType(typeof(List<LogFile>), 200)]
|
||||
[ProducesResponseType(typeof(Paginated<LogFile>), 200)]
|
||||
[ProducesResponseType(typeof(ErrorMessage), 409)]
|
||||
public async Task<IActionResult> ListLogs(CancellationToken cancellationToken)
|
||||
{
|
||||
var path = fileLoggingConfiguration.GetFullLogDirectory(ioManager, assemblyInformationProvider, platformIdentifier);
|
||||
try
|
||||
{
|
||||
var files = await ioManager.GetFiles(path, cancellationToken).ConfigureAwait(false);
|
||||
var tasks = files.Select(
|
||||
async file => new LogFile
|
||||
{
|
||||
Name = ioManager.GetFileName(file),
|
||||
LastModified = await ioManager.GetLastModified(
|
||||
ioManager.ConcatPath(path, file),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
var result = tasks
|
||||
.Select(x => x.Result)
|
||||
.OrderByDescending(x => x.Name)
|
||||
.ToList();
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
return Conflict(new ErrorMessage(ErrorCode.IOError)
|
||||
public Task<IActionResult> ListLogs([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated(
|
||||
async () =>
|
||||
{
|
||||
AdditionalData = ex.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
var path = fileLoggingConfiguration.GetFullLogDirectory(ioManager, assemblyInformationProvider, platformIdentifier);
|
||||
try
|
||||
{
|
||||
var files = await ioManager.GetFiles(path, cancellationToken).ConfigureAwait(false);
|
||||
var tasks = files.Select(
|
||||
async file => new LogFile
|
||||
{
|
||||
Name = ioManager.GetFileName(file),
|
||||
LastModified = await ioManager
|
||||
.GetLastModified(
|
||||
ioManager.ConcatPath(path, file),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
var result = tasks
|
||||
.Select(x => x.Result)
|
||||
.OrderByDescending(x => x.Name)
|
||||
.ToList();
|
||||
|
||||
return new PaginatableResult<LogFile>(
|
||||
result.AsQueryable());
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
return new PaginatableResult<LogFile>(
|
||||
Conflict(new ErrorMessage(ErrorCode.IOError)
|
||||
{
|
||||
AdditionalData = ex.ToString()
|
||||
}));
|
||||
}
|
||||
},
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Download a <see cref="LogFile"/>.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Octokit;
|
||||
using Serilog.Context;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -15,6 +18,7 @@ using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Security;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
@@ -26,6 +30,16 @@ namespace Tgstation.Server.Host.Controllers
|
||||
[ApiController]
|
||||
public abstract class ApiController : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Default size of <see cref="Paginated{TModel}"/> results.
|
||||
/// </summary>
|
||||
private const ushort DefaultPageSize = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum size of <see cref="Paginated{TModel}"/> results.
|
||||
/// </summary>
|
||||
private const ushort MaximumPageSize = 100;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ApiHeaders"/> for the operation
|
||||
/// </summary>
|
||||
@@ -275,6 +289,132 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1506
|
||||
|
||||
/// <summary>
|
||||
/// Generates a paginated response.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="Type"/> of model being generated and returned.</typeparam>
|
||||
/// <param name="queryGenerator">A <see cref="Func{TResult}"/> resulting in a <see cref="Task{TResult}"/> resulting in the generated <see cref="PaginatableResult{TModel}"/>.</param>
|
||||
/// <param name="resultTransformer">An <see cref="Action{T1}"/> to transform the <typeparamref name="TModel"/>s after being queried.</param>
|
||||
/// <param name="pageQuery">The requested page from the query.</param>
|
||||
/// <param name="pageSizeQuery">The requested page size from the query.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
protected Task<IActionResult> Paginated<TModel>(
|
||||
Func<Task<PaginatableResult<TModel>>> queryGenerator,
|
||||
Action<TModel> resultTransformer,
|
||||
int? pageQuery,
|
||||
int? pageSizeQuery,
|
||||
CancellationToken cancellationToken) => PaginatedImpl<TModel, TModel>(
|
||||
queryGenerator,
|
||||
resultTransformer,
|
||||
pageQuery,
|
||||
pageSizeQuery,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a paginated response.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="Type"/> of model being generated.</typeparam>
|
||||
/// <typeparam name="TApiModel">The <see cref="Type"/> of model being returned.</typeparam>
|
||||
/// <param name="queryGenerator">A <see cref="Func{TResult}"/> resulting in a <see cref="Task{TResult}"/> resulting in the generated <see cref="PaginatableResult{TModel}"/>.</param>
|
||||
/// <param name="resultTransformer">An <see cref="Action{T1}"/> to transform the <typeparamref name="TModel"/>s after being queried.</param>
|
||||
/// <param name="pageQuery">The requested page from the query.</param>
|
||||
/// <param name="pageSizeQuery">The requested page size from the query.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
protected Task<IActionResult> Paginated<TModel, TApiModel>(
|
||||
Func<Task<PaginatableResult<TModel>>> queryGenerator,
|
||||
Action<TModel> resultTransformer,
|
||||
int? pageQuery,
|
||||
int? pageSizeQuery,
|
||||
CancellationToken cancellationToken)
|
||||
where TModel : IApiTransformable<TApiModel>
|
||||
=> PaginatedImpl<TModel, TApiModel>(
|
||||
queryGenerator,
|
||||
resultTransformer,
|
||||
pageQuery,
|
||||
pageSizeQuery,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a paginated response.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="Type"/> of model being generated. If different from <typeparamref name="TResultModel"/>, must implement <see cref="IApiTransformable{TApiModel}"/> for <typeparamref name="TResultModel"/>.</typeparam>
|
||||
/// <typeparam name="TResultModel">The <see cref="Type"/> of model being returned.</typeparam>
|
||||
/// <param name="queryGenerator">A <see cref="Func{TResult}"/> resulting in a <see cref="Task{TResult}"/> resulting in the generated <see cref="PaginatableResult{TModel}"/>.</param>
|
||||
/// <param name="resultTransformer">An <see cref="Action{T1}"/> to transform the <typeparamref name="TModel"/>s after being queried.</param>
|
||||
/// <param name="pageQuery">The requested page from the query.</param>
|
||||
/// <param name="pageSizeQuery">The requested page size from the query.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
async Task<IActionResult> PaginatedImpl<TModel, TResultModel>(
|
||||
Func<Task<PaginatableResult<TModel>>> queryGenerator,
|
||||
Action<TModel> resultTransformer,
|
||||
int? pageQuery,
|
||||
int? pageSizeQuery,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (queryGenerator == null)
|
||||
throw new ArgumentNullException(nameof(queryGenerator));
|
||||
|
||||
if (pageQuery <= 0 || pageSizeQuery <= 0)
|
||||
return BadRequest(new ErrorMessage(ErrorCode.ApiInvalidPageOrPageSize));
|
||||
|
||||
var pageSize = pageSizeQuery ?? DefaultPageSize;
|
||||
if (pageSize > MaximumPageSize)
|
||||
return BadRequest(new ErrorMessage(ErrorCode.ApiPageTooLarge)
|
||||
{
|
||||
AdditionalData = $"Maximum page size: {MaximumPageSize}"
|
||||
});
|
||||
|
||||
var page = pageQuery ?? 1;
|
||||
|
||||
var paginationResult = await queryGenerator().ConfigureAwait(false);
|
||||
if (paginationResult.EarlyOut != null)
|
||||
return paginationResult.EarlyOut;
|
||||
|
||||
var queriedResults = paginationResult
|
||||
.Results
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
|
||||
int totalResults;
|
||||
List<TModel> pagedResults;
|
||||
if (queriedResults.Provider is IAsyncQueryProvider)
|
||||
{
|
||||
totalResults = await paginationResult.Results.CountAsync(cancellationToken).ConfigureAwait(false);
|
||||
pagedResults = await queriedResults
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalResults = paginationResult.Results.Count();
|
||||
pagedResults = queriedResults.ToList();
|
||||
}
|
||||
|
||||
if (resultTransformer != null)
|
||||
foreach (var I in pagedResults)
|
||||
resultTransformer(I);
|
||||
|
||||
ICollection<TResultModel> finalResults;
|
||||
if (typeof(TModel) == typeof(TResultModel))
|
||||
finalResults = (List<TResultModel>)(object)pagedResults; // clearly a safe cast
|
||||
else
|
||||
finalResults = pagedResults
|
||||
.OfType<IApiTransformable<TResultModel>>()
|
||||
.Select(x => x.ToApi())
|
||||
.ToList();
|
||||
|
||||
return Json(
|
||||
new Paginated<TModel>
|
||||
{
|
||||
Content = pagedResults,
|
||||
PageSize = pageSize,
|
||||
TotalPages = (ushort)((totalResults % pageSize) + 1)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -79,21 +78,31 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Lists installed <see cref="Api.Models.Byond"/> versions.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
/// <response code="200">Retrieved version information successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(ByondRights.ListInstalled)]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.Byond>), 200)]
|
||||
public Task<IActionResult> List()
|
||||
=> WithComponentInstance(instance =>
|
||||
Task.FromResult<IActionResult>(
|
||||
Json(instance
|
||||
.ByondManager
|
||||
.InstalledVersions
|
||||
.Select(x => new Api.Models.Byond
|
||||
{
|
||||
Version = x
|
||||
}))));
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.Byond>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> WithComponentInstance(
|
||||
instance => Paginated(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Api.Models.Byond>(
|
||||
instance
|
||||
.ByondManager
|
||||
.InstalledVersions
|
||||
.Select(x => new Api.Models.Byond
|
||||
{
|
||||
Version = x
|
||||
})
|
||||
.AsQueryable())),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Changes the active BYOND version to the one specified in a given <paramref name="model"/>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@@ -168,29 +168,33 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// List <see cref="Api.Models.ChatBot"/>s.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
/// <response code="200">Listed chat bots successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(ChatBotRights.Read)]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.ChatBot>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.ChatBot>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = DatabaseContext
|
||||
.ChatBots
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.Include(x => x.Channels);
|
||||
|
||||
var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
|
||||
|
||||
if (!connectionStrings)
|
||||
foreach (var I in results)
|
||||
I.ConnectionString = null;
|
||||
|
||||
return Json(results.Select(x => x.ToApi()));
|
||||
return Paginated<Models.ChatBot, Api.Models.ChatBot>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.ChatBot>(
|
||||
DatabaseContext
|
||||
.ChatBots
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.Include(x => x.Channels))),
|
||||
chatBot =>
|
||||
{
|
||||
if (connectionStrings)
|
||||
chatBot.ConnectionString = null;
|
||||
},
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -172,54 +171,75 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// Get the contents of a directory at a <paramref name="directoryPath"/>
|
||||
/// </summary>
|
||||
/// <param name="directoryPath">The path of the directory to get</param>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation</returns>
|
||||
/// <response code="200">Directory listed successfully.</response>>
|
||||
/// <response code="410">Directory does not currently exist.</response>
|
||||
[HttpGet(Routes.List + "/{*directoryPath}")]
|
||||
[TgsAuthorize(ConfigurationRights.List)]
|
||||
[ProducesResponseType(typeof(IReadOnlyList<ConfigurationFile>), 200)]
|
||||
[ProducesResponseType(typeof(Paginated<ConfigurationFile>), 200)]
|
||||
[ProducesResponseType(typeof(ErrorMessage), 410)]
|
||||
public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
|
||||
{
|
||||
if (ForbidDueToModeConflicts(directoryPath, out var systemIdentity))
|
||||
return Forbid();
|
||||
public Task<IActionResult> Directory(
|
||||
string directoryPath,
|
||||
[FromQuery] int? page,
|
||||
[FromQuery] int? pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
=> Paginated(
|
||||
async () =>
|
||||
{
|
||||
if (ForbidDueToModeConflicts(directoryPath, out var systemIdentity))
|
||||
return new PaginatableResult<ConfigurationFile>(
|
||||
Forbid());
|
||||
|
||||
try
|
||||
{
|
||||
return await WithComponentInstance(
|
||||
async instance =>
|
||||
try
|
||||
{
|
||||
var result = await instance
|
||||
.Configuration
|
||||
.ListDirectory(directoryPath, systemIdentity, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (result == null)
|
||||
return Gone();
|
||||
return new PaginatableResult<ConfigurationFile>(
|
||||
await WithComponentInstance(
|
||||
async instance =>
|
||||
{
|
||||
var result = await instance
|
||||
.Configuration
|
||||
.ListDirectory(directoryPath, systemIdentity, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (result == null)
|
||||
return Gone();
|
||||
|
||||
return Json(result);
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
return Json(result);
|
||||
})
|
||||
.ConfigureAwait(false));
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
return new PaginatableResult<ConfigurationFile>(
|
||||
RequiresPosixSystemIdentity());
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return new PaginatableResult<ConfigurationFile>(
|
||||
Forbid());
|
||||
}
|
||||
},
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the contents of the root configuration directory.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(ConfigurationRights.List)]
|
||||
[ProducesResponseType(typeof(IReadOnlyList<ConfigurationFile>), 200)]
|
||||
public Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
|
||||
[ProducesResponseType(typeof(Paginated<ConfigurationFile>), 200)]
|
||||
public Task<IActionResult> List(
|
||||
[FromQuery] int? page,
|
||||
[FromQuery] int? pageSize,
|
||||
CancellationToken cancellationToken) => Directory(null, page, pageSize, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a configuration directory.
|
||||
|
||||
@@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -95,43 +94,53 @@ namespace Tgstation.Server.Host.Controllers
|
||||
[ProducesResponseType(typeof(ErrorMessage), 404)]
|
||||
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
|
||||
{
|
||||
var compileJob = await DatabaseContext
|
||||
.CompileJobs
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == id && x.Job.Instance.Id == Instance.Id)
|
||||
.Include(x => x.Job).ThenInclude(x => x.StartedBy)
|
||||
.Include(x => x.RevisionInformation).ThenInclude(x => x.PrimaryTestMerge).ThenInclude(x => x.MergedBy)
|
||||
.Include(x => x.RevisionInformation).ThenInclude(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).ThenInclude(x => x.MergedBy)
|
||||
var compileJob = await BaseCompileJobsQuery()
|
||||
.Where(x => x.Id == id)
|
||||
.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (compileJob == default)
|
||||
return NotFound();
|
||||
return Json(compileJob.ToApi());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base query for pulling in all required <see cref="CompileJob"/> fields.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IQueryable{T}"/> of <see cref="CompileJob"/> with all the inclusions.</returns>
|
||||
IQueryable<Models.CompileJob> BaseCompileJobsQuery() => DatabaseContext
|
||||
.CompileJobs
|
||||
.AsQueryable()
|
||||
.Include(x => x.Job)
|
||||
.ThenInclude(x => x.StartedBy)
|
||||
.Include(x => x.RevisionInformation)
|
||||
.ThenInclude(x => x.PrimaryTestMerge)
|
||||
.ThenInclude(x => x.MergedBy)
|
||||
.Include(x => x.RevisionInformation)
|
||||
.ThenInclude(x => x.ActiveTestMerges)
|
||||
.ThenInclude(x => x.TestMerge)
|
||||
.ThenInclude(x => x.MergedBy)
|
||||
.Where(x => x.Job.Instance.Id == Instance.Id);
|
||||
|
||||
/// <summary>
|
||||
/// List all <see cref="Api.Models.CompileJob"/> <see cref="EntityId"/>s for the instance.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Retrieved <see cref="EntityId"/>s successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(DreamMakerRights.CompileJobs)]
|
||||
[ProducesResponseType(typeof(List<EntityId>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
{
|
||||
var compileJobs = await DatabaseContext
|
||||
.CompileJobs
|
||||
.AsQueryable()
|
||||
.Where(x => x.Job.Instance.Id == Instance.Id)
|
||||
.OrderByDescending(x => x.Job.StoppedAt)
|
||||
.Select(x => new EntityId
|
||||
{
|
||||
Id = x.Id
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Json(compileJobs);
|
||||
}
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.CompileJob>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated<Models.CompileJob, Api.Models.CompileJob>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.CompileJob>(
|
||||
BaseCompileJobsQuery()
|
||||
.OrderByDescending(x => x.Job.StoppedAt))),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Begin deploying repository code.
|
||||
|
||||
@@ -586,13 +586,18 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// List <see cref="Api.Models.Instance"/>s.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Retrieved <see cref="Api.Models.Instance"/>s successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.Instance>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.Instance>), 200)]
|
||||
public async Task<IActionResult> List(
|
||||
[FromQuery] int? page,
|
||||
[FromQuery] int? pageSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
IQueryable<Models.Instance> GetBaseQuery()
|
||||
{
|
||||
@@ -622,21 +627,25 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var instances = await GetBaseQuery()
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var needsUpdate = false;
|
||||
foreach (var instance in instances)
|
||||
needsUpdate |= InstanceRequiredController.ValidateInstanceOnlineStatus(instanceManager, Logger, instance);
|
||||
var result = await Paginated<Models.Instance, Api.Models.Instance>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.Instance>(
|
||||
GetBaseQuery())),
|
||||
instance =>
|
||||
{
|
||||
needsUpdate |= InstanceRequiredController.ValidateInstanceOnlineStatus(instanceManager, Logger, instance);
|
||||
instance.MoveJob = moveJobs.FirstOrDefault(x => x.Instance.Id == instance.Id).ToApi();
|
||||
},
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (needsUpdate)
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var apis = instances.Select(x => x.ToApi());
|
||||
foreach(var I in moveJobs)
|
||||
apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); // if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible
|
||||
return Json(apis);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -147,23 +146,27 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Lists <see cref="Api.Models.InstanceUser"/>s for the instance.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Retrieved <see cref="Api.Models.InstanceUser"/>s successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(InstanceUserRights.ReadUsers)]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.InstanceUser>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
{
|
||||
var users = await DatabaseContext
|
||||
.Instances
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.SelectMany(x => x.InstanceUsers)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Json(users.Select(x => x.ToApi()));
|
||||
}
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.InstanceUser>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated<Models.InstanceUser, Api.Models.InstanceUser>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.InstanceUser>(
|
||||
DatabaseContext
|
||||
.Instances
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.SelectMany(x => x.InstanceUsers))),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific <see cref="Api.Models.InstanceUser"/>.
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -53,50 +52,54 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Get active <see cref="Api.Models.Job"/>s for the instance.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Retrieved active <see cref="Api.Models.Job"/>s successfully.</response>
|
||||
[HttpGet]
|
||||
[TgsAuthorize]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.Job>), 200)]
|
||||
public async Task<IActionResult> Read(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await DatabaseContext
|
||||
.Jobs
|
||||
.AsQueryable()
|
||||
.Include(x => x.StartedBy)
|
||||
.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue)
|
||||
.OrderByDescending(x => x.StartedAt)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Json(result.Select(x => x.ToApi()));
|
||||
}
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.Job>), 200)]
|
||||
public Task<IActionResult> Read([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated<Models.Job, Api.Models.Job>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.Job>(
|
||||
DatabaseContext
|
||||
.Jobs
|
||||
.AsQueryable()
|
||||
.Include(x => x.StartedBy)
|
||||
.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue)
|
||||
.OrderByDescending(x => x.StartedAt))),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// List all <see cref="Api.Models.Job"/> <see cref="EntityId"/>s for the instance in reverse creation order.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
|
||||
/// <response code="200">Retrieved <see cref="Api.Models.Job"/> <see cref="EntityId"/>s successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize]
|
||||
[ProducesResponseType(typeof(List<EntityId>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
{
|
||||
// you KNOW this will need pagination eventually right?
|
||||
var jobs = await DatabaseContext
|
||||
.Jobs
|
||||
.AsQueryable()
|
||||
.Where(x => x.Instance.Id == Instance.Id)
|
||||
.OrderByDescending(x => x.StartedAt)
|
||||
.Select(x => new EntityId
|
||||
{
|
||||
Id = x.Id
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Json(jobs);
|
||||
}
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.Job>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated<Models.Job, Api.Models.Job>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.Job>(
|
||||
DatabaseContext
|
||||
.Jobs
|
||||
.AsQueryable()
|
||||
.Include(x => x.StartedBy)
|
||||
.Where(x => x.Instance.Id == Instance.Id)
|
||||
.OrderByDescending(x => x.StartedAt))),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Cancel a running <see cref="Api.Models.Job"/>.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper for returning paginated models.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The <see cref="Type"/> of model intended to be returned.</typeparam>
|
||||
public sealed class PaginatableResult<TModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IQueryable{T}"/> <typeparamref name="TModel"/> results.
|
||||
/// </summary>
|
||||
public IQueryable<TModel> Results { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IActionResult"/> to return immediately.
|
||||
/// </summary>
|
||||
public IActionResult EarlyOut { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PaginatableResult{TModel}"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="results">The value of <see cref="Results"/>.</param>
|
||||
public PaginatableResult(IQueryable<TModel> results)
|
||||
{
|
||||
Results = results ?? throw new ArgumentNullException(nameof(results));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PaginatableResult{TModel}"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="earlyOut">The value of <see cref="EarlyOut"/>.</param>
|
||||
public PaginatableResult(IActionResult earlyOut)
|
||||
{
|
||||
EarlyOut = earlyOut ?? throw new ArgumentNullException(nameof(earlyOut));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,23 +298,28 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// List all <see cref="Api.Models.User"/>s in the server.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation.</returns>
|
||||
/// <response code="200">Retrieved <see cref="Api.Models.User"/>s successfully.</response>
|
||||
[HttpGet(Routes.List)]
|
||||
[TgsAuthorize(AdministrationRights.ReadUsers)]
|
||||
[ProducesResponseType(typeof(IEnumerable<Api.Models.User>), 200)]
|
||||
public async Task<IActionResult> List(CancellationToken cancellationToken)
|
||||
{
|
||||
var users = await DatabaseContext
|
||||
.Users
|
||||
.AsQueryable()
|
||||
.Where(x => x.CanonicalName != Models.User.CanonicalizeName(Models.User.TgsSystemUserName))
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.OAuthConnections)
|
||||
.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
return Json(users.Select(x => x.ToApi(true)));
|
||||
}
|
||||
[ProducesResponseType(typeof(Paginated<Api.Models.User>), 200)]
|
||||
public Task<IActionResult> List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
|
||||
=> Paginated<Models.User, Api.Models.User>(
|
||||
() => Task.FromResult(
|
||||
new PaginatableResult<Models.User>(
|
||||
DatabaseContext
|
||||
.Users
|
||||
.AsQueryable()
|
||||
.Where(x => x.CanonicalName != Models.User.CanonicalizeName(Models.User.TgsSystemUserName))
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.OAuthConnections))),
|
||||
null,
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific <see cref="Api.Models.User"/>.
|
||||
|
||||
@@ -164,6 +164,9 @@ namespace Tgstation.Server.Host.Core
|
||||
if (type == typeof(Api.Models.Internal.User))
|
||||
return "ShallowUser";
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Paginated<>))
|
||||
return $"Paginated{type.GenericTypeArguments.First().Name}";
|
||||
|
||||
return type.Name;
|
||||
});
|
||||
|
||||
@@ -241,7 +244,7 @@ namespace Tgstation.Server.Host.Core
|
||||
};
|
||||
|
||||
if (typeof(InstanceRequiredController).IsAssignableFrom(context.MethodInfo.DeclaringType))
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
operation.Parameters.Insert(0, new OpenApiParameter
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class ChatBot : Api.Models.Internal.ChatBot
|
||||
public sealed class ChatBot : Api.Models.Internal.ChatBot, IApiTransformable<Api.Models.ChatBot>
|
||||
{
|
||||
/// <summary>
|
||||
/// Default for <see cref="Api.Models.Internal.ChatBot.ChannelLimit"/>.
|
||||
@@ -28,10 +28,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public ICollection<ChatChannel> Channels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="ChatBot"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.ChatBot"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.ChatBot ToApi() => new Api.Models.ChatBot
|
||||
{
|
||||
Channels = Channels.Select(x => x.ToApi()).ToList(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Tgstation.Server.Host.Models
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class ChatChannel : Api.Models.ChatChannel
|
||||
public sealed class ChatChannel : Api.Models.ChatChannel, IApiTransformable<Api.Models.ChatChannel>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id
|
||||
@@ -18,10 +18,7 @@
|
||||
/// </summary>
|
||||
public ChatBot ChatSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="ChatChannel"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.ChatChannel"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel
|
||||
{
|
||||
DiscordChannelId = DiscordChannelId,
|
||||
|
||||
@@ -5,7 +5,7 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class CompileJob : Api.Models.Internal.CompileJob
|
||||
public sealed class CompileJob : Api.Models.Internal.CompileJob, IApiTransformable<Api.Models.CompileJob>
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="Api.Models.CompileJob.Job"/>
|
||||
@@ -78,10 +78,7 @@ namespace Tgstation.Server.Host.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="CompileJob"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.CompileJob"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.CompileJob ToApi() => new Api.Models.CompileJob
|
||||
{
|
||||
DirectoryName = DirectoryName,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class DreamMakerSettings : Api.Models.DreamMaker
|
||||
public sealed class DreamMakerSettings : Api.Models.DreamMaker, IApiTransformable<Api.Models.DreamMaker>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id
|
||||
@@ -21,10 +21,7 @@ namespace Tgstation.Server.Host.Models
|
||||
[Required]
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="DreamDaemonSettings"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.DreamMaker"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.DreamMaker ToApi() => new Api.Models.DreamMaker
|
||||
{
|
||||
ProjectName = ProjectName,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a host-side model that may be transformed into a <typeparamref name="TApiModel"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TApiModel">The API form of the model.</typeparam>
|
||||
public interface IApiTransformable<TApiModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert the <see cref="IApiTransformable{TApiModel}"/> to it's <typeparamref name="TApiModel"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new <typeparamref name="TApiModel"/> based on the <see cref="IApiTransformable{TApiModel}"/>.</returns>
|
||||
TApiModel ToApi();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an <see cref="Api.Models.Instance"/> in the database
|
||||
/// </summary>
|
||||
public sealed class Instance : Api.Models.Instance
|
||||
public sealed class Instance : Api.Models.Instance, IApiTransformable<Api.Models.Instance>
|
||||
{
|
||||
/// <summary>
|
||||
/// Default for <see cref="Api.Models.Instance.ChatBotLimit"/>.
|
||||
@@ -47,10 +47,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public ICollection<Job> Jobs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="Instance"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.Instance"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.Instance ToApi() => new Api.Models.Instance
|
||||
{
|
||||
AutoUpdateInterval = AutoUpdateInterval,
|
||||
@@ -59,7 +56,8 @@ namespace Tgstation.Server.Host.Models
|
||||
Name = Name,
|
||||
Path = Path,
|
||||
Online = Online,
|
||||
ChatBotLimit = ChatBotLimit
|
||||
ChatBotLimit = ChatBotLimit,
|
||||
MoveJob = MoveJob,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class InstanceUser : Api.Models.InstanceUser
|
||||
public sealed class InstanceUser : Api.Models.InstanceUser, IApiTransformable<Api.Models.InstanceUser>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id
|
||||
@@ -21,10 +21,7 @@ namespace Tgstation.Server.Host.Models
|
||||
[Required]
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="InstanceUser"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.InstanceUser"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.InstanceUser ToApi() => new Api.Models.InstanceUser
|
||||
{
|
||||
ByondRights = ByondRights,
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CA1724 // naming conflict with gitlab package
|
||||
public sealed class Job : Api.Models.Internal.Job
|
||||
public sealed class Job : Api.Models.Internal.Job, IApiTransformable<Api.Models.Job>
|
||||
#pragma warning restore CA1724
|
||||
{
|
||||
/// <summary>
|
||||
@@ -24,10 +24,7 @@ namespace Tgstation.Server.Host.Models
|
||||
[Required]
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="Job"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.Job"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.Job ToApi() => new Api.Models.Job
|
||||
{
|
||||
Id = Id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class OAuthConnection : Api.Models.OAuthConnection
|
||||
public sealed class OAuthConnection : Api.Models.OAuthConnection, IApiTransformable<Api.Models.OAuthConnection>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id.
|
||||
@@ -13,10 +13,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="OAuthConnection"/> to it's API form.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.OAuthConnection"/>.</returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.OAuthConnection ToApi() => new Api.Models.OAuthConnection
|
||||
{
|
||||
Provider = Provider,
|
||||
|
||||
@@ -4,7 +4,7 @@ using Tgstation.Server.Api.Models;
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings
|
||||
public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings, IApiTransformable<Repository>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id
|
||||
@@ -22,10 +22,7 @@ namespace Tgstation.Server.Host.Models
|
||||
[Required]
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="Repository"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Repository"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Repository ToApi() => new Repository
|
||||
{
|
||||
// AccessToken = AccessToken, // never show this
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation
|
||||
public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation, IApiTransformable<Api.Models.RevisionInformation>
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id
|
||||
@@ -38,10 +38,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public ICollection<CompileJob> CompileJobs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="RevisionInformation"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.RevisionInformation"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation
|
||||
{
|
||||
CommitSha = CommitSha,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class TestMerge : Api.Models.Internal.TestMerge
|
||||
public sealed class TestMerge : Api.Models.Internal.TestMerge, IApiTransformable<Api.Models.TestMerge>
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="Api.Models.TestMerge.MergedBy"/>
|
||||
@@ -28,10 +28,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public ICollection<RevInfoTestMerge> RevisonInformations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="TestMerge"/> to it's API form
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.TestMerge"/></returns>
|
||||
/// <inheritdoc />
|
||||
public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge
|
||||
{
|
||||
Author = Author,
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class User : Api.Models.Internal.User
|
||||
public sealed class User : Api.Models.Internal.User, IApiTransformable<Api.Models.User>
|
||||
{
|
||||
/// <summary>
|
||||
/// Username used when creating jobs automatically.
|
||||
@@ -88,5 +88,8 @@ namespace Tgstation.Server.Host.Models
|
||||
/// <param name="showDetails">If rights and system identifier should be shown</param>
|
||||
/// <returns>A new <see cref="Api.Models.User"/></returns>
|
||||
public Api.Models.User ToApi(bool showDetails) => ToApi(true, showDetails);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Api.Models.User ToApi() => ToApi(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Tgstation.Server.Tests
|
||||
|
||||
async Task TestLogs(CancellationToken cancellationToken)
|
||||
{
|
||||
var logs = await client.ListLogs(cancellationToken);
|
||||
var logs = await client.ListLogs(null, cancellationToken);
|
||||
Assert.AreNotEqual(0, logs.Count);
|
||||
var logFile = logs.First();
|
||||
Assert.IsNotNull(logFile);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
|
||||
async Task TestNoVersion(CancellationToken cancellationToken)
|
||||
{
|
||||
var allVersionsTask = byondClient.InstalledVersions(cancellationToken);
|
||||
var allVersionsTask = byondClient.InstalledVersions(null, cancellationToken);
|
||||
var currentShit = await byondClient.ActiveVersion(cancellationToken).ConfigureAwait(false);
|
||||
Assert.IsNotNull(currentShit);
|
||||
Assert.IsNull(currentShit.InstallJob);
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Assert.AreEqual(csb.ToString(), firstBot.ConnectionString);
|
||||
Assert.AreNotEqual(0, firstBot.Id);
|
||||
|
||||
var bots = await chatClient.List(cancellationToken);
|
||||
var bots = await chatClient.List(null, cancellationToken);
|
||||
Assert.AreEqual(firstBot.Id, bots.First(x => x.Provider.Value == ChatProvider.Irc).Id);
|
||||
|
||||
var retrievedBot = await chatClient.GetId(firstBot, cancellationToken);
|
||||
@@ -115,7 +115,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Assert.AreEqual(csb.ToString(), firstBot.ConnectionString);
|
||||
Assert.AreNotEqual(0, firstBot.Id);
|
||||
|
||||
var bots = await chatClient.List(cancellationToken);
|
||||
var bots = await chatClient.List(null, cancellationToken);
|
||||
Assert.AreEqual(firstBot.Id, bots.First(x => x.Provider.Value == ChatProvider.Discord).Id);
|
||||
|
||||
var retrievedBot = await chatClient.GetId(firstBot, cancellationToken);
|
||||
@@ -154,13 +154,13 @@ namespace Tgstation.Server.Tests.Instance
|
||||
|
||||
public async Task RunPostTest(CancellationToken cancellationToken)
|
||||
{
|
||||
var activeBots = await chatClient.List(cancellationToken);
|
||||
var activeBots = await chatClient.List(null, cancellationToken);
|
||||
|
||||
Assert.AreEqual(2, activeBots.Count);
|
||||
|
||||
await Task.WhenAll(activeBots.Select(bot => chatClient.Delete(bot, cancellationToken)));
|
||||
|
||||
var nowBots = await chatClient.List(cancellationToken);
|
||||
var nowBots = await chatClient.List(null, cancellationToken);
|
||||
Assert.AreEqual(0, nowBots.Count);
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Provider = ChatProvider.Irc
|
||||
}, cancellationToken), ErrorCode.ChatBotMax);
|
||||
|
||||
var bots = await chatClient.List(cancellationToken);
|
||||
var bots = await chatClient.List(null, cancellationToken);
|
||||
var discordBot = bots.First(bot => bot.Provider.Value == ChatProvider.Discord);
|
||||
|
||||
// We limited chat bots and channels to 1 and 2 respectively, try violating them
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -137,7 +137,7 @@ namespace Tgstation.Server.Tests
|
||||
|
||||
public async Task RunPostTest(CancellationToken cancellationToken)
|
||||
{
|
||||
var instances = await instanceManagerClient.List(cancellationToken);
|
||||
var instances = await instanceManagerClient.List(null, cancellationToken);
|
||||
var firstTest = instances.Single(x => x.Name == TestInstanceName);
|
||||
var instanceClient = instanceManagerClient.CreateClient(firstTest);
|
||||
|
||||
|
||||
@@ -320,10 +320,10 @@ namespace Tgstation.Server.Tests
|
||||
{
|
||||
var instanceClient = adminClient.Instances.CreateClient(instance);
|
||||
|
||||
var jobs = await instanceClient.Jobs.ListActive(cancellationToken);
|
||||
var jobs = await instanceClient.Jobs.ListActive(null, cancellationToken);
|
||||
if (!jobs.Any())
|
||||
{
|
||||
var entities = await instanceClient.Jobs.List(cancellationToken);
|
||||
var entities = await instanceClient.Jobs.List(null, cancellationToken);
|
||||
var getTasks = entities
|
||||
.Select(e => instanceClient.Jobs.GetId(e, cancellationToken))
|
||||
.ToList();
|
||||
@@ -363,10 +363,10 @@ namespace Tgstation.Server.Tests
|
||||
{
|
||||
var instanceClient = adminClient.Instances.CreateClient(instance);
|
||||
|
||||
var jobs = await instanceClient.Jobs.ListActive(cancellationToken);
|
||||
var jobs = await instanceClient.Jobs.ListActive(null, cancellationToken);
|
||||
if (!jobs.Any())
|
||||
{
|
||||
var entities = await instanceClient.Jobs.List(cancellationToken);
|
||||
var entities = await instanceClient.Jobs.List(null, cancellationToken);
|
||||
var getTasks = entities
|
||||
.Select(e => instanceClient.Jobs.GetId(e, cancellationToken))
|
||||
.ToList();
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Tgstation.Server.Tests
|
||||
BasicTests(cancellationToken),
|
||||
TestCreateSysUser(cancellationToken),
|
||||
TestSpamCreation(cancellationToken)).ConfigureAwait(false);
|
||||
|
||||
await TestPagination(cancellationToken);
|
||||
}
|
||||
|
||||
async Task BasicTests(CancellationToken cancellationToken)
|
||||
@@ -41,7 +43,7 @@ namespace Tgstation.Server.Tests
|
||||
Assert.AreEqual("TGS", systemUser.Name);
|
||||
Assert.AreEqual(false, systemUser.Enabled);
|
||||
|
||||
var users = await client.List(cancellationToken);
|
||||
var users = await client.List(null, cancellationToken);
|
||||
Assert.IsTrue(users.Count > 0);
|
||||
Assert.IsFalse(users.Any(x => x.Id == systemUser.Id));
|
||||
|
||||
@@ -133,5 +135,55 @@ namespace Tgstation.Server.Tests
|
||||
|
||||
Assert.AreEqual(RepeatCount, tasks.Select(task => task.Result.Id).Distinct().Count(), "Did not receive expected number of unique user IDs!");
|
||||
}
|
||||
|
||||
async Task TestPagination(CancellationToken cancellationToken)
|
||||
{
|
||||
// we test pagination here b/c it's the only spot we have a decent amount of entities
|
||||
var nullSettings = await client.List(null, cancellationToken);
|
||||
var emptySettings = await client.List(
|
||||
new PaginationSettings
|
||||
{
|
||||
}, cancellationToken);
|
||||
|
||||
Assert.AreEqual(nullSettings.Count, emptySettings.Count);
|
||||
Assert.IsTrue(nullSettings.All(x => emptySettings.SingleOrDefault(y => x.Id == y.Id) != null));
|
||||
|
||||
await ApiAssert.ThrowsException<ApiConflictException>(() => client.List(
|
||||
new PaginationSettings
|
||||
{
|
||||
PageSize = -2143
|
||||
}, cancellationToken), ErrorCode.ApiInvalidPageOrPageSize);
|
||||
await ApiAssert.ThrowsException<ApiConflictException>(() => client.List(
|
||||
new PaginationSettings
|
||||
{
|
||||
PageSize = Int32.MaxValue
|
||||
}, cancellationToken), ErrorCode.ApiPageTooLarge);
|
||||
|
||||
await client.List(
|
||||
new PaginationSettings
|
||||
{
|
||||
PageSize = 50
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
var skipped = await client.List(new PaginationSettings
|
||||
{
|
||||
Offset = 50,
|
||||
RetrieveCount = 5
|
||||
}, cancellationToken);
|
||||
Assert.AreEqual(5, skipped.Count);
|
||||
|
||||
var allAfterSkipped = await client.List(new PaginationSettings
|
||||
{
|
||||
Offset = 50,
|
||||
}, cancellationToken);
|
||||
Assert.IsTrue(5 < allAfterSkipped.Count);
|
||||
|
||||
var limited = await client.List(new PaginationSettings
|
||||
{
|
||||
RetrieveCount = 12,
|
||||
}, cancellationToken);
|
||||
Assert.AreEqual(12, limited.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user