using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Jobs; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// /// for s /// [Route(Routes.Jobs)] public sealed class JobController : ApiController { /// /// The for the /// readonly IJobManager jobManager; /// /// Construct a /// /// The for the /// The for the /// The value of /// The for the public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true, true) { this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); } /// /// Get active s for the instance. /// /// The for the operation. /// A resulting in the of the request. /// Retrieved active s successfully. [HttpGet] [TgsAuthorize] [ProducesResponseType(typeof(IEnumerable), 200)] public async Task Read(CancellationToken cancellationToken) { var result = await DatabaseContext.Jobs.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())); } /// /// List all s for the instance in reverse creation order. /// /// The for the operation. /// A resulting in the of the request. /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize] [ProducesResponseType(typeof(List), 200)] public async Task List(CancellationToken cancellationToken) { // you KNOW this will need pagination eventually right? var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.EntityId { Id = x.Id }).ToListAsync(cancellationToken).ConfigureAwait(false); return Json(jobs); } /// /// Cancel a running . /// /// The of the to cancel. /// The for the operation. /// A resulting in the of the request. /// cancellation requested successfully. /// does not exist in this instance. /// already cancelled or completed. [HttpDelete("{id}")] [TgsAuthorize] [ProducesResponseType(typeof(Api.Models.Job), 202)] [ProducesResponseType(404)] [ProducesResponseType(410)] public async Task Delete(long id, CancellationToken cancellationToken) { // don't care if an instance post or not at this point var job = await DatabaseContext.Jobs.Where(x => x.Id == id && x.Instance.Id == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (job == default(Job)) return NotFound(); if (job.StoppedAt != null) return StatusCode((int)HttpStatusCode.Gone); if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0) return Forbid(); var updatedJob = await jobManager.CancelJob(job, AuthenticationContext.User, false, cancellationToken).ConfigureAwait(false); return updatedJob != null ? (IActionResult)Accepted(updatedJob.ToApi()) : StatusCode((int)HttpStatusCode.Gone); } /// /// Get a specific . /// /// The of the to retrieve. /// The for the operation. /// A resulting in the of the request. /// Retrieved successfully. /// does not exist in this instance. [HttpGet("{id}")] [TgsAuthorize] [ProducesResponseType(typeof(Api.Models.Job), 200)] [ProducesResponseType(404)] public async Task GetId(long id, CancellationToken cancellationToken) { var job = await DatabaseContext .Jobs .Where(x => x.Id == id && x.Instance.Id == Instance.Id) .Include(x => x.StartedBy) .FirstOrDefaultAsync(cancellationToken) .ConfigureAwait(false); if (job == default) return NotFound(); var api = job.ToApi(); api.Progress = jobManager.JobProgress(job); return Json(api); } } }