using Microsoft.EntityFrameworkCore; using Microsoft.SelfService.Portal.Core.API.Context; 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.Repository { public class TemplateRepository : ITemplateInterface { private readonly DataContext _context; public TemplateRepository(DataContext context) { _context = context; } public ICollection GetTemplates() { var templates = _context.Templates .AsNoTracking() .Include(template => template.TemplateVersions) .ToList(); foreach (var template in templates) { ApplyPublishedVersion(template); } return templates; } public TemplateModel GetTemplateById(Guid Id) { var template = _context.Templates .AsNoTracking() .Include(t => t.TemplateVersions) .Where(t => t.Id == Id) .FirstOrDefault(); if (template != null) { ApplyPublishedVersion(template); } return template; } public bool AddTemplateById(TemplateModel template) { if (template.Id == Guid.Empty) { template.Id = Guid.NewGuid(); } var document = ConfigurationDocumentValidator.NormalizeAndValidate(template.JSONData, ConfigurationDocumentKind.Template); template.JSONData = document.JsonData; var version = new TemplateVersionModel { Id = Guid.NewGuid(), TemplateId = template.Id, Version = string.IsNullOrWhiteSpace(template.Version) ? "1.0.0" : template.Version, SchemaVersion = document.SchemaVersion, IsPublished = true, PublishedAt = DateTime.UtcNow, PublishedBy = template.CreatedBy }; version.SetJsonData(document.JsonData); template.TemplateVersions.Add(version); _context.Add(template); return SaveChanges(); } public bool EditTemplateById(TemplateModel template) { var existingTemplate = _context.Templates .Include(existing => existing.TemplateVersions) .FirstOrDefault(existing => existing.Id == template.Id); if (existingTemplate == null) { return false; } var document = ConfigurationDocumentValidator.NormalizeAndValidate(template.JSONData, ConfigurationDocumentKind.Template); var jsonData = document.JsonData; var publishedVersion = existingTemplate.TemplateVersions.FirstOrDefault(version => version.IsPublished); var jsonChanged = publishedVersion == null || !string.Equals(publishedVersion.JsonData, jsonData, StringComparison.Ordinal); existingTemplate.Name = template.Name; existingTemplate.Version = template.Version; existingTemplate.Description = template.Description; existingTemplate.JSONData = jsonData; existingTemplate.TemplateCategoryId = template.TemplateCategoryId; existingTemplate.DeploymentRuleId = template.DeploymentRuleId; existingTemplate.Modified = DateTime.UtcNow; existingTemplate.ModifiedBy = template.ModifiedBy; if (jsonChanged) { var versionName = GetUniqueVersionName( existingTemplate.Id, string.IsNullOrWhiteSpace(template.Version) ? DateTime.UtcNow.ToString("yyyyMMddHHmmss") : template.Version); var version = new TemplateVersionModel { Id = Guid.NewGuid(), TemplateId = existingTemplate.Id, Version = versionName, SchemaVersion = document.SchemaVersion, IsPublished = true, PublishedAt = DateTime.UtcNow, PublishedBy = template.ModifiedBy }; version.SetJsonData(jsonData); foreach (var existingVersion in existingTemplate.TemplateVersions) { existingVersion.IsPublished = false; } existingTemplate.TemplateVersions.Add(version); } return SaveChanges(); } public bool DeleteTemplateById(TemplateModel template) { _context.Remove(template); return SaveChanges(); } public bool CheckTemplateById(Guid Id) { return _context.Templates .Any(t => t.Id == Id); } public TemplateModel GetTemplateByName(string Name) { return _context.Templates .AsNoTracking() .Where(t => t.Name == Name) .FirstOrDefault(); } public bool CheckTemplateByName(string Name) { return _context.Templates .Any(t => t.Name == Name); } public ICollection GetTemplateVersions(Guid templateId) { return _context.TemplateVersions .AsNoTracking() .Where(version => version.TemplateId == templateId) .OrderByDescending(version => version.Created) .ToList(); } public TemplateVersionModel? GetTemplateVersionById(Guid templateId, Guid versionId) { return _context.TemplateVersions .AsNoTracking() .FirstOrDefault(version => version.TemplateId == templateId && version.Id == versionId); } public TemplateVersionModel? GetPublishedTemplateVersion(Guid templateId) { return _context.TemplateVersions .AsNoTracking() .FirstOrDefault(version => version.TemplateId == templateId && version.IsPublished); } public bool AddTemplateVersion(Guid templateId, TemplateVersionModel version, bool publish) { if (!_context.Templates.Any(template => template.Id == templateId)) { return false; } version.Id = version.Id == Guid.Empty ? Guid.NewGuid() : version.Id; version.TemplateId = templateId; version.Version = string.IsNullOrWhiteSpace(version.Version) ? "1.0.0" : version.Version; var document = ConfigurationDocumentValidator.NormalizeAndValidate( version.JsonData, ConfigurationDocumentKind.Template, version.SchemaVersion); version.SchemaVersion = document.SchemaVersion; version.SetJsonData(document.JsonData); if (publish) { foreach (var existingVersion in _context.TemplateVersions.Where(existing => existing.TemplateId == templateId && existing.IsPublished)) { existingVersion.IsPublished = false; } version.IsPublished = true; version.PublishedAt = DateTime.UtcNow; } _context.TemplateVersions.Add(version); return SaveChanges(); } public bool PublishTemplateVersion(Guid templateId, Guid versionId, string? publishedBy) { var version = _context.TemplateVersions .FirstOrDefault(existing => existing.TemplateId == templateId && existing.Id == versionId); if (version == null) { return false; } foreach (var existingVersion in _context.TemplateVersions.Where(existing => existing.TemplateId == templateId && existing.IsPublished)) { existingVersion.IsPublished = false; } version.IsPublished = true; version.PublishedAt = DateTime.UtcNow; version.PublishedBy = string.IsNullOrWhiteSpace(publishedBy) ? "System" : publishedBy; var template = _context.Templates.FirstOrDefault(existing => existing.Id == templateId); if (template != null) { template.Version = version.Version; template.JSONData = version.JsonData; template.Modified = DateTime.UtcNow; template.ModifiedBy = version.PublishedBy; } return SaveChanges(); } public bool CheckTemplateVersionById(Guid templateId, Guid versionId) { return _context.TemplateVersions .Any(version => version.TemplateId == templateId && version.Id == versionId); } public bool CheckTemplateVersionByVersion(Guid templateId, string version) { return _context.TemplateVersions .Any(existing => existing.TemplateId == templateId && existing.Version == version); } public bool SaveChanges() { var saved = _context.SaveChanges(); return saved > 0 ? true : false; } private static void ApplyPublishedVersion(TemplateModel template) { var publishedVersion = template.TemplateVersions?.FirstOrDefault(version => version.IsPublished); if (publishedVersion == null) { return; } template.Version = publishedVersion.Version; template.JSONData = publishedVersion.JsonData; } private string GetUniqueVersionName(Guid templateId, string version) { if (!CheckTemplateVersionByVersion(templateId, version)) { return version; } var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); var candidate = $"{version}-{timestamp}"; var counter = 1; while (CheckTemplateVersionByVersion(templateId, candidate)) { counter++; candidate = $"{version}-{timestamp}-{counter}"; } return candidate; } } }