Merge pull request #1046 from tgstation/MoreAPIFixes2 [APIDeploy]

More api fixes2
This commit is contained in:
Jordan Brown
2020-06-11 18:19:19 -04:00
committed by GitHub
13 changed files with 53 additions and 26 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Tgstation.Server.Api.Models
/// <summary>
/// Indicates an API upgrade was required by the server.
/// </summary>
[Description("API Mismatch but no current API version provided!")]
[Description("API version mismatch!")]
ApiMismatch,
/// <summary>
@@ -233,12 +233,14 @@ namespace Tgstation.Server.Host.Controllers
/// <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="202">Update has been started successfully.</response>
/// <response code="410">The requested release version could not be found in the target GitHub repository.</response>
/// <response code="422">Upgrade operations are unavailable due to the launch configuration of TGS.</response>
/// <response code="424">A GitHub rate limit was encountered.</response>
/// <response code="429">A GitHub API error occurred.</response>
[HttpPost]
[TgsAuthorize(AdministrationRights.ChangeVersion)]
[ProducesResponseType(typeof(Administration), 202)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
[ProducesResponseType(typeof(ErrorMessage), 422)]
[ProducesResponseType(typeof(ErrorMessage), 424)]
[ProducesResponseType(typeof(ErrorMessage), 429)]
@@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@@ -87,7 +86,7 @@ namespace Tgstation.Server.Host.Controllers
/// Generic 404 response.
/// </summary>
/// <returns>An <see cref="ObjectResult"/> with <see cref="HttpStatusCode.NotFound"/>.</returns>
protected new ObjectResult NotFound() => StatusCode((int)HttpStatusCode.NotFound, new ErrorMessage(ErrorCode.ResourceNeverPresent));
protected new ObjectResult NotFound() => NotFound(new ErrorMessage(ErrorCode.ResourceNeverPresent));
/// <summary>
/// Generic 501 response.
@@ -190,14 +189,9 @@ namespace Tgstation.Server.Host.Controllers
{
if (ApiHeaders != null)
Logger.LogDebug(
"Starting API Request: Version: {1}. User-Agent: {2}",
AuthenticationContext?.User.Id.Value.ToString(CultureInfo.InvariantCulture),
"Starting API Request: Version: {0}. User-Agent: {1}",
ApiHeaders.ApiVersion.Semver(),
ApiHeaders.RawUserAgent,
Request.Method,
Request.Path,
Request.QueryString,
ApiHeaders.InstanceId);
ApiHeaders.RawUserAgent);
await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
}
}
@@ -188,9 +188,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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 <see cref="Api.Models.ChatBot"/> successfully.</response>
/// <response code="410">The <see cref="Api.Models.ChatBot"/> with the given ID does not exist in this instance.</response>
[HttpGet("{id}")]
[TgsAuthorize(ChatBotRights.Read)]
[ProducesResponseType(typeof(Api.Models.ChatBot), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
var query = DatabaseContext.ChatBots
@@ -216,11 +218,14 @@ namespace Tgstation.Server.Host.Controllers
/// <param name="model">The <see cref="Api.Models.ChatBot"/> update to apply.</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">Update applied successfully. <see cref="Api.Models.ChatBot"/> may or may not be returned based on user permissions.</response>
/// <response code="200">Update applied successfully.</response>
/// <response code="204">Update applied successfully. <see cref="Api.Models.ChatBot"/> not returned based on user permissions.</response>
/// <response code="410">The <see cref="Api.Models.ChatBot"/> with the given ID does not exist in this instance.</response>
[HttpPost]
[TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)]
[ProducesResponseType(200)]
[ProducesResponseType(typeof(Api.Models.ChatBot), 200)]
[ProducesResponseType(204)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
#pragma warning disable CA1502, CA1506 // TODO: Decomplexify
public async Task<IActionResult> Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
#pragma warning restore CA1502, CA1506
@@ -314,7 +319,7 @@ namespace Tgstation.Server.Host.Controllers
return Json(current.ToApi());
}
return Ok();
return NoContent();
}
/// <summary>
@@ -116,10 +116,13 @@ namespace Tgstation.Server.Host.Controllers
/// </summary>
/// <param name="filePath">The path of the file to get</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>
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation</returns>>
/// <response code="200">File read successfully.</response>>
/// <response code="410">File does not currently exist.</response>
[HttpGet(Routes.File + "/{*filePath}")]
[TgsAuthorize(ConfigurationRights.Read)]
[ProducesResponseType(typeof(ConfigurationFile), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
{
if (ForbidDueToModeConflicts(filePath, out var systemIdentity))
@@ -153,9 +156,12 @@ namespace Tgstation.Server.Host.Controllers
/// <param name="directoryPath">The path of the directory to get</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(ErrorMessage), 410)]
public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
{
if (ForbidDueToModeConflicts(directoryPath, out var systemIdentity))
@@ -84,9 +84,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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">Read <see cref="DreamDaemon"/> information successfully.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpGet]
[TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)]
[ProducesResponseType(typeof(DreamDaemon), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public Task<IActionResult> Read(CancellationToken cancellationToken) => ReadImpl(null, cancellationToken);
/// <summary>
@@ -171,9 +173,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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">Settings applied successfully.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpPost]
[TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start | DreamDaemonRights.SetStartupTimeout | DreamDaemonRights.SetHeartbeatInterval)]
[ProducesResponseType(typeof(DreamDaemon), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
#pragma warning disable CA1502 // TODO: Decomplexify
#pragma warning disable CA1506
public async Task<IActionResult> Update([FromBody] DreamDaemon model, CancellationToken cancellationToken)
@@ -76,9 +76,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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"><see cref="Api.Models.CompileJob"/> retrieved successfully.</response>
/// <response code="404">Specified <see cref="Api.Models.CompileJob"/> ID does not exist in this instance.</response>
[HttpGet("{id}")]
[TgsAuthorize(DreamMakerRights.CompileJobs)]
[ProducesResponseType(typeof(Api.Models.CompileJob), 200)]
[ProducesResponseType(typeof(ErrorMessage), 404)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
var compileJob = await DatabaseContext
@@ -156,10 +158,12 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
/// <response code="200">Changes applied successfully. The updated <see cref="DreamMaker"/> settings will be returned.</response>
/// <response code="204">Changes applied successfully. The updated <see cref="DreamMaker"/> settings will be not be returned due to permissions.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpPost]
[TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetApiValidationPort)]
[ProducesResponseType(typeof(DreamMaker), 200)]
[ProducesResponseType(204)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> Update([FromBody] DreamMaker model, CancellationToken cancellationToken)
{
if (model == null)
@@ -312,9 +312,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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="204">Instance detatched successfully.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpDelete("{id}")]
[TgsAuthorize(InstanceManagerRights.Delete)]
[ProducesResponseType(204)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
{
var originalModel = await DatabaseContext
@@ -355,10 +357,12 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
/// <response code="200">Instance updated successfully.</response>
/// <response code="202">Instance updated successfully and relocation job created.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpPost]
[TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline | InstanceManagerRights.SetChatBotLimit)]
[ProducesResponseType(typeof(Api.Models.Instance), 200)]
[ProducesResponseType(typeof(Api.Models.Instance), 202)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
#pragma warning disable CA1502 // TODO: Decomplexify
public async Task<IActionResult> Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
{
@@ -583,9 +587,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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"/> successfully.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpGet("{id}")]
[TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
[ProducesResponseType(typeof(Api.Models.Instance), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
@@ -89,9 +89,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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"><see cref="Api.Models.InstanceUser"/> updated successfully.</response>
/// <response code="410">The requested <see cref="Api.Models.InstanceUser"/> does not currently exist.</response>
[HttpPost]
[TgsAuthorize(InstanceUserRights.WriteUsers)]
[ProducesResponseType(typeof(Api.Models.InstanceUser), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
#pragma warning disable CA1506 // TODO: Decomplexify
public async Task<IActionResult> Update([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
{
@@ -163,9 +165,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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">Retrieve <see cref="Api.Models.InstanceUser"/> successfully.</response>
/// <response code="410">The requested <see cref="Api.Models.InstanceUser"/> does not currently exist.</response>
[HttpGet("{id}")]
[TgsAuthorize(InstanceUserRights.ReadUsers)]
[ProducesResponseType(typeof(Api.Models.InstanceUser), 200)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
// this functions as userId
@@ -93,9 +93,11 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the request.</returns>
/// <response code="202"><see cref="Api.Models.Job"/> cancellation requested successfully.</response>
/// <response code="404"><see cref="Api.Models.Job"/> does not exist in this instance.</response>
/// <response code="410"><see cref="Api.Models.Job"/> could not be found in the job manager. Has it already completed?</response>
[HttpDelete("{id}")]
[TgsAuthorize]
[ProducesResponseType(typeof(Api.Models.Job), 202)]
[ProducesResponseType(typeof(ErrorMessage), 404)]
public async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
{
// don't care if an instance post or not at this point
@@ -129,6 +131,7 @@ namespace Tgstation.Server.Host.Controllers
[HttpGet("{id}")]
[TgsAuthorize]
[ProducesResponseType(typeof(Api.Models.Job), 200)]
[ProducesResponseType(typeof(ErrorMessage), 404)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
var job = await DatabaseContext
@@ -140,9 +140,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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="201">The <see cref="Repository"/> was created successfully and the <see cref="Api.Models.Job"/> to clone it has begun.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpPut]
[TgsAuthorize(RepositoryRights.SetOrigin)]
[ProducesResponseType(typeof(Repository), 201)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> Create([FromBody] Repository model, CancellationToken cancellationToken)
{
if (model == null)
@@ -232,9 +234,11 @@ namespace Tgstation.Server.Host.Controllers
/// <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="202">Job to delete the repository created successfully.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpDelete]
[TgsAuthorize(RepositoryRights.Delete)]
[ProducesResponseType(typeof(Repository), 202)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> Delete(CancellationToken cancellationToken)
{
var currentModel = await DatabaseContext
@@ -273,10 +277,12 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation.</returns>
/// <response code="200">Retrieved the <see cref="Repository"/> settings successfully.</response>
/// <response code="201">Retrieved the <see cref="Repository"/> settings successfully, though they did not previously exist.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpGet]
[TgsAuthorize(RepositoryRights.Read)]
[ProducesResponseType(typeof(Repository), 200)]
[ProducesResponseType(typeof(Repository), 201)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
public async Task<IActionResult> Read(CancellationToken cancellationToken)
{
var currentModel = await DatabaseContext
@@ -317,10 +323,12 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> of the operation.</returns>
/// <response code="200">Updated the <see cref="Repository"/> settings successfully.</response>
/// <response code="202">Updated the <see cref="Repository"/> settings successfully and a <see cref="Api.Models.Job"/> was created to make the requested git changes.</response>
/// <response code="410">The database entity for the requested instance could not be retrieved. The instance was likely detached.</response>
[HttpPost]
[TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)]
[ProducesResponseType(typeof(Repository), 200)]
[ProducesResponseType(typeof(Repository), 202)]
[ProducesResponseType(typeof(ErrorMessage), 410)]
#pragma warning disable CA1502, CA1505 // TODO: Decomplexify
public async Task<IActionResult> Update([FromBody]Repository model, CancellationToken cancellationToken)
{
@@ -99,6 +99,7 @@ namespace Tgstation.Server.Host.Controllers
/// <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="201"><see cref="Api.Models.User"/> created successfully.</response>
/// <response code="410">The requested system identifier could not be found.</response>
[HttpPut]
[TgsAuthorize(AdministrationRights.WriteUsers)]
[ProducesResponseType(typeof(Api.Models.User), 201)]
@@ -173,6 +174,7 @@ namespace Tgstation.Server.Host.Controllers
[HttpPost]
[TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)]
[ProducesResponseType(typeof(Api.Models.User), 200)]
[ProducesResponseType(typeof(ErrorMessage), 404)]
#pragma warning disable CA1502 // TODO: Decomplexify
#pragma warning disable CA1506
public async Task<IActionResult> Update([FromBody] UserUpdate model, CancellationToken cancellationToken)
@@ -290,6 +292,7 @@ namespace Tgstation.Server.Host.Controllers
[HttpGet("{id}")]
[TgsAuthorize]
[ProducesResponseType(typeof(Api.Models.User), 200)]
[ProducesResponseType(typeof(ErrorMessage), 404)]
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
if (id == AuthenticationContext.User.Id)
@@ -84,24 +84,12 @@ namespace Tgstation.Server.Host.Core
Description = "User lacks sufficient permissions for the operation."
});
AddDefaultResponse(HttpStatusCode.NotFound, new OpenApiResponse
{
Description = ErrorCode.ResourceNeverPresent.Describe(),
Content = errorMessageContent
});
AddDefaultResponse(HttpStatusCode.Conflict, new OpenApiResponse
{
Description = "A data integrity check failed while performing the operation. See error message for details.",
Content = errorMessageContent
});
AddDefaultResponse(HttpStatusCode.Gone, new OpenApiResponse
{
Description = ErrorCode.ResourceNotPresent.Describe(),
Content = errorMessageContent
});
AddDefaultResponse(HttpStatusCode.InternalServerError, new OpenApiResponse
{
Description = ErrorCode.InternalServerError.Describe(),