395 lines
16 KiB
C#
395 lines
16 KiB
C#
using AutoMapper;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.SelfService.Portal.Core.API.Context;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.DeploymentBatch.Add;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.DeploymentBatch.Edit;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.DeploymentBatch.Get;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.DeploymentComposition.Add;
|
|
using Microsoft.SelfService.Portal.Core.API.Dto.DeploymentComposition.Get;
|
|
using Microsoft.SelfService.Portal.Core.API.Interfaces;
|
|
using Microsoft.SelfService.Portal.Core.API.JsonDocuments;
|
|
using Microsoft.SelfService.Portal.Core.API.Models;
|
|
|
|
namespace Microsoft.SelfService.Portal.Core.API.Controllers
|
|
{
|
|
[Route("api/deployment-batches")]
|
|
[ApiController]
|
|
public class DeploymentBatchController : Controller
|
|
{
|
|
private readonly IDeploymentBatchInterface _deploymentBatchInterface;
|
|
private readonly DataContext _context;
|
|
private readonly IMapper _mapper;
|
|
|
|
public DeploymentBatchController(IDeploymentBatchInterface deploymentBatchInterface, DataContext context, IMapper mapper)
|
|
{
|
|
_deploymentBatchInterface = deploymentBatchInterface;
|
|
_context = context;
|
|
_mapper = mapper;
|
|
}
|
|
|
|
[HttpGet]
|
|
[ProducesResponseType(200, Type = typeof(IEnumerable<GetDeploymentBatchDto>))]
|
|
public IActionResult GetDeploymentBatches()
|
|
{
|
|
var deploymentBatches = _deploymentBatchInterface
|
|
.GetDeploymentBatches()
|
|
.Select(batch => new GetDeploymentBatchDto
|
|
{
|
|
Id = batch.Id,
|
|
TemplateId = batch.TemplateId,
|
|
DeploymentRuleId = batch.DeploymentRuleId,
|
|
Status = batch.Status,
|
|
Created = batch.Created,
|
|
CreatedBy = batch.CreatedBy,
|
|
Modified = batch.Modified,
|
|
ModifiedBy = batch.ModifiedBy
|
|
})
|
|
.ToList();
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
return Ok(deploymentBatches);
|
|
}
|
|
|
|
[HttpGet("{id}")]
|
|
[ProducesResponseType(200, Type = typeof(DeploymentGroupModel))]
|
|
[ProducesResponseType(400)]
|
|
public IActionResult GetDeploymentBatchById(Guid id)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
var deploymentBatch = _mapper.Map<GetDeploymentBatchDetailsDto>(_deploymentBatchInterface.GetDeploymentBatchById(id));
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
return Ok(deploymentBatch);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
public IActionResult AddDeploymentBatch([FromBody] AddDeploymentBatchDto deploymentBatch)
|
|
{
|
|
if (deploymentBatch == null)
|
|
return BadRequest(ModelState);
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deploymentBatchMap = _mapper.Map<DeploymentGroupModel>(deploymentBatch);
|
|
|
|
if (!_deploymentBatchInterface.AddDeploymentBatchById(deploymentBatchMap, deploymentBatch.TargetIds))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong while saving");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
|
|
return Ok(deploymentBatchMap.Id);
|
|
}
|
|
|
|
[HttpDelete("{id}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult DeleteDeploymentBatchById(Guid id)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deploymentBatch = _deploymentBatchInterface.GetDeploymentBatchById(id);
|
|
|
|
if (deploymentBatch == null)
|
|
return NotFound();
|
|
|
|
if (!_deploymentBatchInterface.DeleteDeploymentBatchById(deploymentBatch))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong while deleting");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPut("{id}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult EditDeploymentBatchById(Guid id, [FromBody] EditDeploymentBatchDto deploymentBatch)
|
|
{
|
|
if (deploymentBatch == null)
|
|
return BadRequest(ModelState);
|
|
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(ModelState);
|
|
|
|
var deploymentBatchMap = _mapper.Map<DeploymentGroupModel>(deploymentBatch);
|
|
deploymentBatchMap.Id = id;
|
|
|
|
if (!_deploymentBatchInterface.EditDeploymentBatchById(deploymentBatchMap))
|
|
{
|
|
ModelState.AddModelError("", "Something went wrong");
|
|
return StatusCode(500, ModelState);
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpGet("{id}/composition")]
|
|
[ProducesResponseType(200, Type = typeof(GetDeploymentCompositionDto))]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult GetDeploymentComposition(Guid id)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
return Ok(LoadComposition(id));
|
|
}
|
|
|
|
[HttpPost("{id}/template-selections")]
|
|
[ProducesResponseType(200, Type = typeof(Guid))]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult AddTemplateSelection(Guid id, [FromBody] AddDeploymentTemplateSelectionDto templateSelection)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
if (!_context.TemplateVersions.Any(version => version.Id == templateSelection.TemplateVersionId))
|
|
{
|
|
ModelState.AddModelError(nameof(templateSelection.TemplateVersionId), "Template version was not found.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
var sortOrder = templateSelection.SortOrder > 0
|
|
? templateSelection.SortOrder
|
|
: GetNextTemplateSelectionSortOrder(id);
|
|
|
|
if (_context.DeploymentTemplateSelections.Any(selection => selection.DeploymentGroupId == id && selection.SortOrder == sortOrder))
|
|
{
|
|
ModelState.AddModelError(nameof(templateSelection.SortOrder), "SortOrder already exists in this deployment batch.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
var model = _mapper.Map<DeploymentTemplateSelectionModel>(templateSelection);
|
|
model.Id = Guid.NewGuid();
|
|
model.DeploymentGroupId = id;
|
|
model.SortOrder = sortOrder;
|
|
|
|
_context.DeploymentTemplateSelections.Add(model);
|
|
_context.SaveChanges();
|
|
|
|
return Ok(model.Id);
|
|
}
|
|
|
|
[HttpDelete("{id}/template-selections/{selectionId}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult DeleteTemplateSelection(Guid id, Guid selectionId)
|
|
{
|
|
var selection = _context.DeploymentTemplateSelections
|
|
.FirstOrDefault(existing => existing.DeploymentGroupId == id && existing.Id == selectionId);
|
|
|
|
if (selection == null)
|
|
return NotFound();
|
|
|
|
_context.DeploymentTemplateSelections.Remove(selection);
|
|
_context.SaveChanges();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost("{id}/parameter-values")]
|
|
[ProducesResponseType(200, Type = typeof(Guid))]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult AddParameterValue(Guid id, [FromBody] AddDeploymentParameterValueDto parameterValue)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
if (parameterValue.DeploymentTemplateSelectionId.HasValue
|
|
&& !_context.DeploymentTemplateSelections.Any(selection =>
|
|
selection.DeploymentGroupId == id
|
|
&& selection.Id == parameterValue.DeploymentTemplateSelectionId.Value))
|
|
{
|
|
ModelState.AddModelError(nameof(parameterValue.DeploymentTemplateSelectionId), "Template selection was not found in this deployment batch.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
if (_context.DeploymentParameterValues.Any(existing =>
|
|
existing.DeploymentGroupId == id
|
|
&& existing.DeploymentTemplateSelectionId == parameterValue.DeploymentTemplateSelectionId
|
|
&& existing.Name == parameterValue.Name))
|
|
{
|
|
ModelState.AddModelError(nameof(parameterValue.Name), "Parameter value already exists in this scope.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
try
|
|
{
|
|
parameterValue.ValueJson = ConfigurationDocumentValidator.NormalizeAndValidate(
|
|
parameterValue.ValueJson,
|
|
ConfigurationDocumentKind.Metadata).JsonData;
|
|
}
|
|
catch (ConfigurationDocumentValidationException ex)
|
|
{
|
|
ModelState.AddModelError(nameof(parameterValue.ValueJson), ex.Message);
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
var model = _mapper.Map<DeploymentParameterValueModel>(parameterValue);
|
|
model.Id = Guid.NewGuid();
|
|
model.DeploymentGroupId = id;
|
|
|
|
_context.DeploymentParameterValues.Add(model);
|
|
_context.SaveChanges();
|
|
|
|
return Ok(model.Id);
|
|
}
|
|
|
|
[HttpDelete("{id}/parameter-values/{parameterValueId}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult DeleteParameterValue(Guid id, Guid parameterValueId)
|
|
{
|
|
var parameterValue = _context.DeploymentParameterValues
|
|
.FirstOrDefault(existing => existing.DeploymentGroupId == id && existing.Id == parameterValueId);
|
|
|
|
if (parameterValue == null)
|
|
return NotFound();
|
|
|
|
_context.DeploymentParameterValues.Remove(parameterValue);
|
|
_context.SaveChanges();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost("{id}/target-assignments")]
|
|
[ProducesResponseType(200, Type = typeof(Guid))]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult AddTargetAssignment(Guid id, [FromBody] AddDeploymentTargetAssignmentDto targetAssignment)
|
|
{
|
|
if (!_deploymentBatchInterface.CheckDeploymentBatchById(id))
|
|
return NotFound();
|
|
|
|
if (!_context.Targets.Any(target => target.Id == targetAssignment.TargetId))
|
|
{
|
|
ModelState.AddModelError(nameof(targetAssignment.TargetId), "Target target was not found.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
if (_context.DeploymentTargetAssignments.Any(existing =>
|
|
existing.DeploymentGroupId == id
|
|
&& existing.TargetId == targetAssignment.TargetId
|
|
&& existing.RoleKey == targetAssignment.RoleKey))
|
|
{
|
|
ModelState.AddModelError(nameof(targetAssignment.TargetId), "Target assignment already exists for this role.");
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
try
|
|
{
|
|
targetAssignment.NodeDataJson = ConfigurationDocumentValidator.NormalizeAndValidate(
|
|
targetAssignment.NodeDataJson,
|
|
ConfigurationDocumentKind.Metadata).JsonData;
|
|
}
|
|
catch (ConfigurationDocumentValidationException ex)
|
|
{
|
|
ModelState.AddModelError(nameof(targetAssignment.NodeDataJson), ex.Message);
|
|
return BadRequest(ModelState);
|
|
}
|
|
|
|
var sortOrder = targetAssignment.SortOrder > 0
|
|
? targetAssignment.SortOrder
|
|
: GetNextTargetAssignmentSortOrder(id);
|
|
|
|
var model = _mapper.Map<DeploymentTargetAssignmentModel>(targetAssignment);
|
|
model.Id = Guid.NewGuid();
|
|
model.DeploymentGroupId = id;
|
|
model.SortOrder = sortOrder;
|
|
|
|
_context.DeploymentTargetAssignments.Add(model);
|
|
_context.SaveChanges();
|
|
|
|
return Ok(model.Id);
|
|
}
|
|
|
|
[HttpDelete("{id}/target-assignments/{targetAssignmentId}")]
|
|
[ProducesResponseType(204)]
|
|
[ProducesResponseType(404)]
|
|
public IActionResult DeleteTargetAssignment(Guid id, Guid targetAssignmentId)
|
|
{
|
|
var targetAssignment = _context.DeploymentTargetAssignments
|
|
.FirstOrDefault(existing => existing.DeploymentGroupId == id && existing.Id == targetAssignmentId);
|
|
|
|
if (targetAssignment == null)
|
|
return NotFound();
|
|
|
|
_context.DeploymentTargetAssignments.Remove(targetAssignment);
|
|
_context.SaveChanges();
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
private GetDeploymentCompositionDto LoadComposition(Guid deploymentBatchId)
|
|
{
|
|
return new GetDeploymentCompositionDto
|
|
{
|
|
DeploymentBatchId = deploymentBatchId,
|
|
TemplateSelections = _mapper.Map<List<GetDeploymentTemplateSelectionDto>>(
|
|
_context.DeploymentTemplateSelections
|
|
.AsNoTracking()
|
|
.Include(selection => selection.TemplateVersion)
|
|
.Where(selection => selection.DeploymentGroupId == deploymentBatchId)
|
|
.OrderBy(selection => selection.SortOrder)
|
|
.ToList()),
|
|
ParameterValues = _mapper.Map<List<GetDeploymentParameterValueDto>>(
|
|
_context.DeploymentParameterValues
|
|
.AsNoTracking()
|
|
.Where(parameterValue => parameterValue.DeploymentGroupId == deploymentBatchId)
|
|
.OrderBy(parameterValue => parameterValue.Name)
|
|
.ToList()),
|
|
TargetAssignments = _mapper.Map<List<GetDeploymentTargetAssignmentDto>>(
|
|
_context.DeploymentTargetAssignments
|
|
.AsNoTracking()
|
|
.Include(target => target.Target)
|
|
.Where(target => target.DeploymentGroupId == deploymentBatchId)
|
|
.OrderBy(target => target.SortOrder)
|
|
.ToList())
|
|
};
|
|
}
|
|
|
|
private int GetNextTemplateSelectionSortOrder(Guid deploymentBatchId)
|
|
{
|
|
var maxSortOrder = _context.DeploymentTemplateSelections
|
|
.Where(selection => selection.DeploymentGroupId == deploymentBatchId)
|
|
.Select(selection => (int?)selection.SortOrder)
|
|
.Max() ?? 0;
|
|
|
|
return maxSortOrder + 10;
|
|
}
|
|
|
|
private int GetNextTargetAssignmentSortOrder(Guid deploymentBatchId)
|
|
{
|
|
var maxSortOrder = _context.DeploymentTargetAssignments
|
|
.Where(target => target.DeploymentGroupId == deploymentBatchId)
|
|
.Select(target => (int?)target.SortOrder)
|
|
.Max() ?? 0;
|
|
|
|
return maxSortOrder + 10;
|
|
}
|
|
}
|
|
}
|
|
|