Extract some Controller methods into extensions

This commit is contained in:
Dominion
2023-06-10 11:21:17 -04:00
parent 873b4cf8c8
commit 160bd3165d
17 changed files with 169 additions and 92 deletions
@@ -19,6 +19,7 @@ using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.System;
@@ -173,7 +174,7 @@ namespace Tgstation.Server.Host.Controllers
catch (ApiException e)
{
Logger.LogWarning(e, OctokitException);
return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
{
AdditionalData = e.Message,
});
@@ -224,9 +225,9 @@ namespace Tgstation.Server.Host.Controllers
{
NewVersion = model.NewVersion,
}),
ServerUpdateResult.ReleaseMissing => Gone(),
ServerUpdateResult.ReleaseMissing => this.Gone(),
ServerUpdateResult.UpdateInProgress => BadRequest(new ErrorMessageResponse(ErrorCode.ServerUpdateInProgress)),
ServerUpdateResult.SwarmIntegrityCheckFailed => StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.SwarmIntegrityCheckFailed)),
ServerUpdateResult.SwarmIntegrityCheckFailed => this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.SwarmIntegrityCheckFailed)),
_ => throw new InvalidOperationException($"Unexpected ServerUpdateResult: {updateResult}"),
};
}
@@ -237,7 +238,7 @@ namespace Tgstation.Server.Host.Controllers
catch (ApiException e)
{
Logger.LogWarning(e, OctokitException);
return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
{
AdditionalData = e.Message,
});
@@ -14,13 +14,16 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Octokit;
using Serilog.Context;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.Utils;
@@ -119,7 +122,7 @@ namespace Tgstation.Server.Host.Controllers
if (!ApiHeaders.Compatible())
{
await StatusCode(
await this.StatusCode(
HttpStatusCode.UpgradeRequired,
new ErrorMessageResponse(ErrorCode.ApiMismatch))
.ExecuteResultAsync(context)
@@ -206,12 +209,6 @@ namespace Tgstation.Server.Host.Controllers
}
#pragma warning restore CA1506
/// <summary>
/// Generic 410 response.
/// </summary>
/// <returns>An <see cref="ObjectResult"/> with <see cref="HttpStatusCode.Gone"/>.</returns>
protected ObjectResult Gone() => StatusCode(HttpStatusCode.Gone, new ErrorMessageResponse(ErrorCode.ResourceNotPresent));
/// <summary>
/// Generic 404 response.
/// </summary>
@@ -222,7 +219,7 @@ namespace Tgstation.Server.Host.Controllers
/// Generic 501 response.
/// </summary>
/// <returns>An <see cref="ObjectResult"/> with <see cref="HttpStatusCode.NotImplemented"/>.</returns>
protected ObjectResult RequiresPosixSystemIdentity() => StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity));
protected ObjectResult RequiresPosixSystemIdentity() => this.StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity));
/// <summary>
/// Strongly type calls to <see cref="ControllerBase.StatusCode(int)"/>.
@@ -231,14 +228,6 @@ namespace Tgstation.Server.Host.Controllers
/// <returns>A <see cref="StatusCodeResult"/> with the given <paramref name="statusCode"/>.</returns>
protected StatusCodeResult StatusCode(HttpStatusCode statusCode) => StatusCode((int)statusCode);
/// <summary>
/// Strongly type calls to <see cref="ControllerBase.StatusCode(int, object)"/>.
/// </summary>
/// <param name="statusCode">The <see cref="HttpStatusCode"/>.</param>
/// <param name="errorMessage">The accompanying <see cref="ErrorMessageResponse"/> payload.</param>
/// <returns>A <see cref="StatusCodeResult"/> with the given <paramref name="statusCode"/>.</returns>
protected ObjectResult StatusCode(HttpStatusCode statusCode, object errorMessage) => StatusCode((int)statusCode, errorMessage);
/// <summary>
/// Generic 201 response with a given <paramref name="payload"/>.
/// </summary>
@@ -260,7 +249,7 @@ namespace Tgstation.Server.Host.Controllers
var secondsString = Math.Ceiling(rateLimitException.GetRetryAfterTimeSpan().TotalSeconds).ToString(CultureInfo.InvariantCulture);
Response.Headers.Add(HeaderNames.RetryAfter, secondsString);
return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessageResponse(ErrorCode.GitHubApiRateLimit));
return this.StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessageResponse(ErrorCode.GitHubApiRateLimit));
}
/// <summary>
@@ -295,7 +284,7 @@ namespace Tgstation.Server.Host.Controllers
};
if (headersException.MissingOrMalformedHeaders.HasFlag(HeaderTypes.Accept))
return StatusCode(HttpStatusCode.NotAcceptable, errorMessage);
return this.StatusCode(HttpStatusCode.NotAcceptable, errorMessage);
return BadRequest(errorMessage);
}
@@ -14,6 +14,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -290,7 +291,7 @@ namespace Tgstation.Server.Host.Controllers
return Task.FromResult<IActionResult>(
versionNotInstalled
? Gone()
? this.Gone()
: null);
});
@@ -20,6 +20,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -165,7 +166,7 @@ namespace Tgstation.Server.Host.Controllers
return null;
})
?? StatusCode(HttpStatusCode.Created, dbModel.ToApi());
?? this.StatusCode(HttpStatusCode.Created, dbModel.ToApi());
}
/// <summary>
@@ -251,7 +252,7 @@ namespace Tgstation.Server.Host.Controllers
var results = await query.FirstOrDefaultAsync(cancellationToken);
if (results == default)
return Gone();
return this.Gone();
var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
@@ -295,7 +296,7 @@ namespace Tgstation.Server.Host.Controllers
var current = await query.FirstOrDefaultAsync(cancellationToken);
if (current == default)
return Gone();
return this.Gone();
if ((model.Channels?.Count ?? current.Channels.Count) > (model.ChannelLimit ?? current.ChannelLimit.Value))
{
@@ -14,6 +14,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -132,7 +133,7 @@ namespace Tgstation.Server.Host.Controllers
.Read(filePath, systemIdentity, cancellationToken)
;
if (result == null)
return Gone();
return this.Gone();
return Json(result);
})
@@ -186,7 +187,7 @@ namespace Tgstation.Server.Host.Controllers
.ListDirectory(directoryPath, systemIdentity, cancellationToken)
;
if (result == null)
return new PaginatableResult<ConfigurationFileResponse>(Gone());
return new PaginatableResult<ConfigurationFileResponse>(this.Gone());
return new PaginatableResult<ConfigurationFileResponse>(
result
@@ -17,6 +17,7 @@ using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Components.Session;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -173,7 +174,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken);
if (current == default)
return Gone();
return this.Gone();
if (model.Port.HasValue && model.Port.Value != current.Port.Value)
{
@@ -339,7 +340,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken)
;
if (settings == default)
return Gone();
return this.Gone();
}
var result = new DreamDaemonResponse();
@@ -14,6 +14,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -191,7 +192,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken)
;
if (hostModel == null)
return Gone();
return this.Gone();
if (model.ProjectName != null)
{
@@ -21,6 +21,7 @@ using Tgstation.Server.Host.Components.Interop;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.Security.OAuth;
@@ -178,7 +179,7 @@ namespace Tgstation.Server.Host.Controllers
// we only allow authorization header issues
var headers = new ApiHeaders(Request.GetTypedHeaders(), true);
if (!headers.Compatible())
return StatusCode(
return this.StatusCode(
HttpStatusCode.UpgradeRequired,
new ErrorMessageResponse(ErrorCode.ApiMismatch));
}
@@ -20,6 +20,7 @@ using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
@@ -288,7 +289,7 @@ namespace Tgstation.Server.Host.Controllers
.Where(x => x.Id == id && x.SwarmIdentifer == swarmConfiguration.Identifier)
.FirstOrDefaultAsync(cancellationToken);
if (originalModel == default)
return Gone();
return this.Gone();
if (originalModel.Online.Value)
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceDetachOnline));
@@ -359,7 +360,7 @@ namespace Tgstation.Server.Host.Controllers
.Include(x => x.DreamDaemonSettings) // need these for onlining
.FirstOrDefaultAsync(cancellationToken);
if (originalModel == default(Models.Instance))
return Gone();
return this.Gone();
if (ValidateInstanceOnlineStatus(originalModel))
await DatabaseContext.Save(cancellationToken);
@@ -604,7 +605,7 @@ namespace Tgstation.Server.Host.Controllers
var instance = await QueryForUser().FirstOrDefaultAsync(cancellationToken);
if (instance == null)
return Gone();
return this.Gone();
if (ValidateInstanceOnlineStatus(instance))
await DatabaseContext.Save(cancellationToken);
@@ -664,7 +665,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (!instanceExists)
return Gone();
return this.Gone();
var instanceAdminUser = InstanceAdminPermissionSet(null);
instanceAdminUser.InstanceId = id;
@@ -14,6 +14,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -77,7 +78,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (existingPermissionSet == default)
return Gone();
return this.Gone();
if (existingPermissionSet.UserId.HasValue)
{
@@ -140,7 +141,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken)
;
if (originalPermissionSet == null)
return Gone();
return this.Gone();
originalPermissionSet.ByondRights = RightsHelper.Clamp(model.ByondRights ?? originalPermissionSet.ByondRights.Value);
originalPermissionSet.RepositoryRights = RightsHelper.Clamp(model.RepositoryRights ?? originalPermissionSet.RepositoryRights.Value);
@@ -222,7 +223,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken)
;
if (permissionSet == default)
return Gone();
return this.Gone();
return Json(permissionSet.ToApi());
}
@@ -248,7 +249,7 @@ namespace Tgstation.Server.Host.Controllers
.Where(x => x.PermissionSetId == id)
.DeleteAsync(cancellationToken)
;
return numDeleted > 0 ? NoContent() : Gone();
return numDeleted > 0 ? NoContent() : this.Gone();
}
}
}
@@ -12,6 +12,7 @@ using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -139,7 +140,7 @@ namespace Tgstation.Server.Host.Controllers
return Forbid();
var updatedJob = await jobManager.CancelJob(job, AuthenticationContext.User, false, cancellationToken);
return updatedJob != null ? Accepted(updatedJob.ToApi()) : Gone();
return updatedJob != null ? Accepted(updatedJob.ToApi()) : this.Gone();
}
/// <summary>
@@ -18,6 +18,7 @@ using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -107,7 +108,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (currentModel == default)
return Gone();
return this.Gone();
currentModel.UpdateSubmodules = model.UpdateSubmodules ?? true;
currentModel.AccessToken = model.AccessToken;
@@ -214,7 +215,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (currentModel == default)
return Gone();
return this.Gone();
currentModel.AccessToken = null;
currentModel.AccessUser = null;
@@ -262,7 +263,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (currentModel == default)
return Gone();
return this.Gone();
var api = currentModel.ToApi();
@@ -351,7 +352,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (currentModel == default)
return Gone();
return this.Gone();
bool CheckModified<T>(Expression<Func<Api.Models.Internal.RepositorySettings, T>> expression, RepositoryRights requiredRight)
{
@@ -1,20 +1,16 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.Transfer;
@@ -65,41 +61,8 @@ namespace Tgstation.Server.Host.Controllers
[HttpGet]
[ProducesResponseType(200, Type = typeof(LimitedStreamResult))]
[ProducesResponseType(410, Type = typeof(ErrorMessageResponse))]
public async Task<IActionResult> Download([Required, FromQuery] string ticket, CancellationToken cancellationToken)
{
if (ticket == null)
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
var streamAccept = new MediaTypeHeaderValue(MediaTypeNames.Application.Octet);
if (!Request.GetTypedHeaders().Accept.Any(x => streamAccept.IsSubsetOf(x)))
return StatusCode(HttpStatusCode.NotAcceptable, new ErrorMessageResponse(ErrorCode.BadHeaders)
{
AdditionalData = $"File downloads must accept both {MediaTypeNames.Application.Octet} and {MediaTypeNames.Application.Json}!",
});
var fileTicketResult = new FileTicketResponse
{
FileTicket = ticket,
};
var tuple = await fileTransferService.RetrieveDownloadStream(fileTicketResult, cancellationToken);
var stream = tuple.Item1;
try
{
if (tuple.Item2 != null)
return Conflict(tuple.Item2);
if (stream == null)
return Gone();
return new LimitedStreamResult(stream);
}
catch
{
await stream.DisposeAsync();
throw;
}
}
public Task<IActionResult> Download([Required, FromQuery] string ticket, CancellationToken cancellationToken)
=> fileTransferService.GenerateDownloadResponse(this, ticket, cancellationToken);
/// <summary>
/// Uploads a file with a given <paramref name="ticket"/>.
@@ -127,7 +90,7 @@ namespace Tgstation.Server.Host.Controllers
var result = await fileTransferService.SetUploadStream(fileTicketResult, Request.Body, cancellationToken);
if (result != null)
return result.ErrorCode == ErrorCode.ResourceNotPresent
? Gone()
? this.Gone()
: Conflict(result);
return NoContent();
@@ -16,6 +16,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -116,14 +117,14 @@ namespace Tgstation.Server.Host.Controllers
var dbUser = await CreateNewUserFromModel(model, cancellationToken);
if (dbUser == null)
return Gone();
return this.Gone();
if (model.SystemIdentifier != null)
try
{
using var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken);
if (sysIdentity == null)
return Gone();
return this.Gone();
dbUser.Name = sysIdentity.Username;
dbUser.SystemIdentifier = sysIdentity.Uid;
}
@@ -269,7 +270,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (originalUser.Group == default)
return Gone();
return this.Gone();
DatabaseContext.Groups.Attach(originalUser.Group);
if (originalUser.PermissionSet != null)
@@ -15,6 +15,7 @@ using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -125,7 +126,7 @@ namespace Tgstation.Server.Host.Controllers
;
if (currentGroup == default)
return Gone();
return this.Gone();
if (model.PermissionSet != null)
{
@@ -170,7 +171,7 @@ namespace Tgstation.Server.Host.Controllers
.FirstOrDefaultAsync(cancellationToken)
;
if (group == default)
return Gone();
return this.Gone();
return Json(group.ToApi(true));
}
@@ -236,7 +237,7 @@ namespace Tgstation.Server.Host.Controllers
return groupExists
? Conflict(new ErrorMessageResponse(ErrorCode.UserGroupNotEmpty))
: Gone();
: this.Gone();
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
namespace Tgstation.Server.Host.Extensions
{
/// <summary>
/// Extension methods for <see cref="ControllerBase"/>.
/// </summary>
static class ControllerBaseExtensions
{
/// <summary>
/// Generic 410 response.
/// </summary>
/// <param name="controller">The <see cref="ControllerBase"/> the request is coming from.</param>
/// <returns>An <see cref="ObjectResult"/> with <see cref="HttpStatusCode.Gone"/>.</returns>
public static ObjectResult Gone(this ControllerBase controller)
=> controller?.StatusCode(HttpStatusCode.Gone, new ErrorMessageResponse(ErrorCode.ResourceNotPresent)) ?? throw new ArgumentNullException(nameof(controller));
/// <summary>
/// Strongly type calls to <see cref="ControllerBase.StatusCode(int, object)"/>.
/// </summary>
/// <param name="controller">The <see cref="ControllerBase"/> the request is coming from.</param>
/// <param name="statusCode">The <see cref="HttpStatusCode"/>.</param>
/// <param name="errorMessage">The accompanying <see cref="ErrorMessageResponse"/> payload.</param>
/// <returns>A <see cref="StatusCodeResult"/> with the given <paramref name="statusCode"/>.</returns>
public static ObjectResult StatusCode(this ControllerBase controller, HttpStatusCode statusCode, object errorMessage)
=> controller?.StatusCode((int)statusCode, errorMessage) ?? throw new ArgumentNullException(nameof(controller));
}
}
@@ -0,0 +1,78 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Controllers;
using Tgstation.Server.Host.Transfer;
namespace Tgstation.Server.Host.Extensions
{
/// <summary>
/// Extension methods for <see cref="IFileTransferStreamHandler"/>.
/// </summary>
static class FileTransferStreamHandlerExtensions
{
/// <summary>
/// Downloads a file with a given <paramref name="ticket"/>.
/// </summary>
/// <param name="fileTransferService">The <see cref="IFileTransferStreamHandler"/>.</param>
/// <param name="controller">The <see cref="ControllerBase"/> the request is coming from.</param>
/// <param name="ticket">The <see cref="FileTicketResponse.FileTicket"/> for the download.</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 method.</returns>
public static async Task<IActionResult> GenerateDownloadResponse(
this IFileTransferStreamHandler fileTransferService,
ControllerBase controller,
string ticket,
CancellationToken cancellationToken)
{
if (fileTransferService == null)
throw new ArgumentNullException(nameof(fileTransferService));
if (controller == null)
throw new ArgumentNullException(nameof(controller));
if (ticket == null)
return controller.BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
var streamAccept = new MediaTypeHeaderValue(MediaTypeNames.Application.Octet);
if (!controller.Request.GetTypedHeaders().Accept.Any(streamAccept.IsSubsetOf))
return controller.StatusCode((int)HttpStatusCode.NotAcceptable, new ErrorMessageResponse(ErrorCode.BadHeaders)
{
AdditionalData = $"File downloads must accept both {MediaTypeNames.Application.Octet} and {MediaTypeNames.Application.Json}!",
});
var fileTicketResult = new FileTicketResponse
{
FileTicket = ticket,
};
var tuple = await fileTransferService.RetrieveDownloadStream(fileTicketResult, cancellationToken);
var stream = tuple.Item1;
try
{
if (tuple.Item2 != null)
return controller.Conflict(tuple.Item2);
if (stream == null)
return controller.Gone();
return new LimitedStreamResult(stream);
}
catch
{
await stream.DisposeAsync();
throw;
}
}
}
}