Files
Microsoft.SelfService.Porta…/Repository/DeploymentBatchRepository.cs
Torsten Brendgen 01c2a5df2e Add queue hardening by modifying DeploymentJobTargets, DeploymentJobSteps, and DeploymentJobs tables
- Drop existing indexes on DeploymentJobTargets and DeploymentJobSteps
- Alter Status column to nvarchar(450) in DeploymentJobTargets, DeploymentJobSteps, and DeploymentJobs
- Add new columns: Finished, OutputMetadataJson, Started to DeploymentJobTargets; ErrorMessage, Finished, OutputMetadataJson, Started to DeploymentJobSteps; CorrelationId, HeartbeatAt, Priority, RowVersion, ScheduledAt, WorkerName to DeploymentJobs
- Create new indexes for improved query performance
- Add constraints to ensure OutputMetadataJson is valid JSON
- Record migration in __EFMigrationsHistory
2026-07-09 08:50:09 +02:00

243 lines
8.7 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.SelfService.Portal.Core.API.Context;
using Microsoft.SelfService.Portal.Core.API.Interfaces;
using Microsoft.SelfService.Portal.Core.API.Models;
using System.Text.Json;
namespace Microsoft.SelfService.Portal.Core.API.Repository
{
public class DeploymentBatchRepository : IDeploymentBatchInterface
{
private readonly DataContext _context;
public DeploymentBatchRepository(DataContext context)
{
_context = context;
}
public ICollection<DeploymentGroupModel> GetDeploymentBatches()
{
return _context.DeploymentGroups
.Include(batch => batch.TemplateSelections)
.ThenInclude(selection => selection.TemplateVersion)
.ThenInclude(version => version.Template)
.Include(batch => batch.TargetAssignments)
.ToList();
}
public DeploymentGroupModel? GetDeploymentBatchById(Guid id)
{
return _context.DeploymentGroups
.Include(t => t.Template)
.ThenInclude(tc => tc.TemplateCategory)
.ThenInclude(s => s.Service)
.Include(d => d.Deployments)
.ThenInclude(vm => vm.Target)
.Include(batch => batch.TemplateSelections)
.ThenInclude(selection => selection.TemplateVersion)
.Include(batch => batch.ParameterValues)
.Include(batch => batch.TargetAssignments)
.ThenInclude(target => target.Target)
.Where(dg => dg.Id == id).FirstOrDefault();
}
public bool AddDeploymentBatchById(DeploymentGroupModel deploymentBatch, ICollection<Guid>? targetIds)
{
if (deploymentBatch.Id == Guid.Empty)
{
deploymentBatch.Id = Guid.NewGuid();
}
var requestedTemplateVersionId = deploymentBatch.TemplateSelections
.OrderBy(selection => selection.SortOrder)
.Select(selection => (Guid?)selection.TemplateVersionId)
.FirstOrDefault();
var templateVersion = requestedTemplateVersionId.HasValue
? _context.TemplateVersions
.Include(version => version.Template)
.ThenInclude(template => template.TemplateCategory)
.ThenInclude(category => category.Service)
.FirstOrDefault(version => version.Id == requestedTemplateVersionId.Value)
: null;
var template = templateVersion?.Template
?? _context.Templates
.Include(existing => existing.TemplateCategory)
.ThenInclude(existing => existing.Service)
.Include(existing => existing.TemplateVersions)
.FirstOrDefault(existing => existing.Id == deploymentBatch.TemplateId);
if (template == null)
{
return false;
}
deploymentBatch.TemplateId = template.Id;
if (deploymentBatch.DeploymentRuleId.HasValue)
{
var ruleExists = _context.DeploymentRules.Any(existing => existing.Id == deploymentBatch.DeploymentRuleId.Value);
if (!ruleExists)
{
return false;
}
}
var isCloudService = template.TemplateCategory.Service.IsCloudService;
var selectedTargetIds = (targetIds ?? Array.Empty<Guid>())
.Concat(deploymentBatch.TargetAssignments.Select(assignment => assignment.TargetId))
.Distinct()
.ToList();
if (!isCloudService && selectedTargetIds.Count == 0)
{
return false;
}
if (selectedTargetIds.Count > 0)
{
var existingTargetIds = _context.Targets
.Where(existing => selectedTargetIds.Contains(existing.Id))
.Select(existing => existing.Id)
.ToHashSet();
if (existingTargetIds.Count != selectedTargetIds.Count)
{
return false;
}
}
_context.Add(deploymentBatch);
if (deploymentBatch.TemplateSelections.Count == 0)
{
templateVersion ??= template.TemplateVersions?
.OrderByDescending(version => version.IsPublished)
.ThenByDescending(version => version.PublishedAt)
.ThenByDescending(version => version.Created)
.FirstOrDefault();
if (templateVersion == null)
{
return false;
}
deploymentBatch.TemplateSelections.Add(new DeploymentTemplateSelectionModel
{
Id = Guid.NewGuid(),
DeploymentGroupId = deploymentBatch.Id,
TemplateVersionId = templateVersion.Id,
TemplateRole = "Service",
SortOrder = 10,
Alias = template.Name
});
}
if (deploymentBatch.TargetAssignments.Count == 0)
{
var sortOrder = 10;
foreach (var targetId in selectedTargetIds)
{
var targetName = _context.Targets
.Where(target => target.Id == targetId)
.Select(target => target.Name)
.FirstOrDefault();
deploymentBatch.TargetAssignments.Add(new DeploymentTargetAssignmentModel
{
Id = Guid.NewGuid(),
DeploymentGroupId = deploymentBatch.Id,
TargetId = targetId,
RoleKey = "Node",
SortOrder = sortOrder,
NodeDataJson = JsonSerializer.Serialize(new { nodeName = targetName ?? targetId.ToString() })
});
sortOrder += 10;
}
}
if (!SaveChanges())
{
return false;
}
if (!isCloudService)
{
foreach (var targetId in selectedTargetIds)
{
_context.Deployments.Add(new DeploymentModel
{
DeploymentGroupId = deploymentBatch.Id,
TargetId = targetId,
Status = QueueJobStatus.Pending,
JSONData = "{}"
});
}
return SaveChanges();
}
return true;
}
public bool DeleteDeploymentBatchById(DeploymentGroupModel deploymentBatch)
{
var templateSelections = _context.DeploymentTemplateSelections
.Where(existing => existing.DeploymentGroupId == deploymentBatch.Id)
.ToList();
var parameterValues = _context.DeploymentParameterValues
.Where(existing => existing.DeploymentGroupId == deploymentBatch.Id)
.ToList();
var targetAssignments = _context.DeploymentTargetAssignments
.Where(existing => existing.DeploymentGroupId == deploymentBatch.Id)
.ToList();
var deployments = _context.Deployments
.Where(existing => existing.DeploymentGroupId == deploymentBatch.Id)
.ToList();
if (parameterValues.Count > 0)
{
_context.DeploymentParameterValues.RemoveRange(parameterValues);
}
if (targetAssignments.Count > 0)
{
_context.DeploymentTargetAssignments.RemoveRange(targetAssignments);
}
if (templateSelections.Count > 0)
{
_context.DeploymentTemplateSelections.RemoveRange(templateSelections);
}
if (deployments.Count > 0)
{
_context.Deployments.RemoveRange(deployments);
}
_context.Remove(deploymentBatch);
return SaveChanges();
}
public bool EditDeploymentBatchById(DeploymentGroupModel deploymentBatch)
{
_context.Update(deploymentBatch);
return SaveChanges();
}
public bool CheckDeploymentBatchById(Guid id)
{
return _context.DeploymentGroups
.Any(d => d.Id == id);
}
public bool SaveChanges()
{
var saved = _context.SaveChanges();
return saved > 0 ? true : false;
}
}
}