304 lines
11 KiB
C#
304 lines
11 KiB
C#
using AutoMapper;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.Deployment.Add;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.Deployment.Get;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.Deployment.Edit;
|
|
using Microsoft.SelfService.Portal.Core.API.Interfaces;
|
|
using Microsoft.SelfService.Portal.Core.API.Models;
|
|
using Microsoft.SelfService.Portal.Core.API.Context;
|
|
|
|
using Microsoft.SelfService.Portal.Core.API.Events.Interfaces;
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
using System.Dynamic;
|
|
using System.Text.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Microsoft.SelfService.Portal.Core.API.Controllers
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class DeploymentController : Controller
|
|
{
|
|
private readonly IDeploymentInterface _deploymentInterface;
|
|
private readonly IQueueJobService _queueJobService;
|
|
private readonly DataContext _context;
|
|
private readonly IMapper _mapper;
|
|
private readonly IEventHandlerInterface _eventHandlerInterface;
|
|
|
|
public DeploymentController(
|
|
IDeploymentInterface deploymentInterface,
|
|
IQueueJobService queueJobService,
|
|
DataContext context,
|
|
IMapper mapper,
|
|
IEventHandlerInterface eventHandlerInterface)
|
|
{
|
|
_deploymentInterface = deploymentInterface;
|
|
_queueJobService = queueJobService;
|
|
_context = context;
|
|
_mapper = mapper;
|
|
_eventHandlerInterface = eventHandlerInterface;
|
|
}
|
|
|
|
[HttpGet]
|
|
[ProducesResponseType(200, Type = typeof(IEnumerable<DeploymentModel>))]
|
|
public IActionResult GetDeployments()
|
|
{
|
|
var deployment = _mapper.Map<List<GetDeploymentDto>>(_deploymentInterface.GetDeployments());
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
return Ok(deployment);
|
|
}
|
|
|
|
[HttpGet("{Id}")]
|
|
[ProducesResponseType(200, Type = typeof(DeploymentModel))]
|
|
[ProducesResponseType(400)]
|
|
public IActionResult GetDeploymentById(Guid Id)
|
|
{
|
|
|
|
if (!_deploymentInterface.CheckDeploymentById(Id))
|
|
return NotFound();
|
|
|
|
var deployment = _mapper.Map<GetDeploymentDetailsDto>(_deploymentInterface.GetDeploymentById(Id));
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
return Ok(deployment);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
public IActionResult AddDeploymentById([FromBody] AddDeploymentDto deployment)
|
|
{
|
|
|
|
if (deployment == null)
|
|
return BadRequest(ModelState);
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deploymentMap = _mapper.Map<DeploymentModel>(deployment);
|
|
|
|
if (!_deploymentInterface.AddDeploymentById(deploymentMap))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong while saving");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
|
|
return Ok(deploymentMap.Id);
|
|
}
|
|
|
|
[HttpPost("Request")]
|
|
[ProducesResponseType(200)]
|
|
[ProducesResponseType(400)]
|
|
public IActionResult AddDeploymentRequest([FromBody] AddDeploymentRequestDto request)
|
|
{
|
|
if (request == null)
|
|
return BadRequest(ModelState);
|
|
|
|
if (request.VirtualMachineIds == null || request.VirtualMachineIds.Count == 0)
|
|
{
|
|
ModelState.AddModelError("", "At least one VirtualMachine must be selected.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
try
|
|
{
|
|
var queueJobId = _queueJobService.EnqueueDeploymentRequest(
|
|
request.DeploymentGroupId,
|
|
request.VirtualMachineIds,
|
|
request.JsonData);
|
|
|
|
return Ok(queueJobId);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
ModelState.AddModelError("", ex.Message);
|
|
return BadRequest(ModelState);
|
|
}
|
|
}
|
|
|
|
[HttpGet("QueueJobs")]
|
|
[ProducesResponseType(200, Type = typeof(IEnumerable<GetQueueJobDto>))]
|
|
public IActionResult GetQueueJobs()
|
|
{
|
|
var queueJobs = _context.QueueJobs
|
|
.AsNoTracking()
|
|
.Include(job => job.Targets)
|
|
.Include(job => job.Steps)
|
|
.OrderByDescending(job => job.Created)
|
|
.Select(job => new GetQueueJobDto
|
|
{
|
|
Id = job.Id,
|
|
Type = job.Type,
|
|
Status = job.Status,
|
|
Attempts = job.Attempts,
|
|
MaxAttempts = job.MaxAttempts,
|
|
Started = job.Started,
|
|
Finished = job.Finished,
|
|
ErrorMessage = job.ErrorMessage,
|
|
TargetCount = job.Targets.Count,
|
|
SucceededTargetCount = job.Targets.Count(target => target.Status == QueueJobStatus.Succeeded),
|
|
FailedTargetCount = job.Targets.Count(target => target.Status == QueueJobStatus.Failed)
|
|
})
|
|
.ToList();
|
|
|
|
return Ok(queueJobs);
|
|
}
|
|
|
|
[HttpGet("QueueJobs/{Id}")]
|
|
[ProducesResponseType(200, Type = typeof(GetQueueJobDetailsDto))]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult GetQueueJobById(Guid Id)
|
|
{
|
|
var queueJob = _context.QueueJobs
|
|
.AsNoTracking()
|
|
.Include(job => job.Targets)
|
|
.Include(job => job.Steps)
|
|
.FirstOrDefault(job => job.Id == Id);
|
|
|
|
if (queueJob == null)
|
|
return NotFound();
|
|
|
|
var details = new GetQueueJobDetailsDto
|
|
{
|
|
Id = queueJob.Id,
|
|
Type = queueJob.Type,
|
|
Status = queueJob.Status,
|
|
PayloadJson = queueJob.PayloadJson,
|
|
Attempts = queueJob.Attempts,
|
|
MaxAttempts = queueJob.MaxAttempts,
|
|
Started = queueJob.Started,
|
|
Finished = queueJob.Finished,
|
|
ErrorMessage = queueJob.ErrorMessage,
|
|
MetadataJson = queueJob.MetadataJson,
|
|
RuleSnapshotJson = queueJob.RuleSnapshotJson,
|
|
Created = queueJob.Created,
|
|
CreatedBy = queueJob.CreatedBy,
|
|
Modified = queueJob.Modified,
|
|
ModifiedBy = queueJob.ModifiedBy,
|
|
Targets = queueJob.Targets.Select(target => new GetQueueJobTargetDto
|
|
{
|
|
Id = target.Id,
|
|
VirtualMachineId = target.VirtualMachineId,
|
|
DeploymentGroupId = target.DeploymentGroupId,
|
|
TemplateId = target.TemplateId,
|
|
Status = target.Status,
|
|
Attempts = target.Attempts,
|
|
ErrorMessage = target.ErrorMessage
|
|
}).ToList(),
|
|
Steps = queueJob.Steps
|
|
.OrderBy(step => step.SortOrder)
|
|
.Select(step => new GetQueueJobStepDto
|
|
{
|
|
Id = step.Id,
|
|
DependsOnQueueJobStepId = step.DependsOnQueueJobStepId,
|
|
SortOrder = step.SortOrder,
|
|
Name = step.Name,
|
|
StepType = step.StepType,
|
|
Status = step.Status,
|
|
MetadataJson = step.MetadataJson,
|
|
ApprovedAt = step.ApprovedAt,
|
|
ApprovedBy = step.ApprovedBy,
|
|
ApprovalComment = step.ApprovalComment
|
|
}).ToList()
|
|
};
|
|
|
|
return Ok(details);
|
|
}
|
|
|
|
[HttpPost("QueueJobs/{Id}/Retry")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult RetryQueueJob(Guid Id)
|
|
{
|
|
if (!_queueJobService.RetryQueueJob(Id))
|
|
return NotFound();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost("QueueJobs/Steps/{stepId}/Approve")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult ApproveQueueJobStep(Guid stepId, [FromBody] QueueJobStepApprovalDto? payload)
|
|
{
|
|
var approvedBy = payload?.ApprovedBy ?? User?.Identity?.Name ?? "System";
|
|
if (!_queueJobService.ApproveQueueJobStep(stepId, approvedBy, payload?.Comment))
|
|
return NotFound();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost("QueueJobs/Steps/{stepId}/Reject")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult RejectQueueJobStep(Guid stepId, [FromBody] QueueJobStepApprovalDto? payload)
|
|
{
|
|
var approvedBy = payload?.ApprovedBy ?? User?.Identity?.Name ?? "System";
|
|
if (!_queueJobService.RejectQueueJobStep(stepId, approvedBy, payload?.Comment))
|
|
return NotFound();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpDelete("{Id}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult DeleteDeploymentById(Guid Id)
|
|
{
|
|
if (!_deploymentInterface.CheckDeploymentById(Id))
|
|
return NotFound();
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deployment = _deploymentInterface.GetDeploymentById(Id);
|
|
|
|
if (!_deploymentInterface.DeleteDeploymentById(deployment))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong while deleting");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPut("{Id}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult EditDeploymentById(Guid Id, [FromBody] EditDeploymentDto deployment)
|
|
{
|
|
if (deployment == null)
|
|
return BadRequest(ModelState);
|
|
|
|
if (!_deploymentInterface.CheckDeploymentById(Id))
|
|
return NotFound();
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deploymentMap = _mapper.Map<DeploymentModel>(deployment);
|
|
deploymentMap.Id = Id;
|
|
|
|
if (!_deploymentInterface.EditDeploymentById(deploymentMap))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
|
|
_eventHandlerInterface.FireEvent(this.GetType().Name, MethodBase.GetCurrentMethod().Name, Id);
|
|
|
|
return NoContent();
|
|
}
|
|
}
|
|
}
|