feat(templates): add localized push templates and actions
Share template keys across email and push with independent editors and revisions. Render localized actions and preserve version-one delivery. Ref: IT-1627
This commit is contained in:
@@ -48,7 +48,7 @@ internal sealed class NotificationServiceRepository(NotificationDbContext db, Te
|
||||
|
||||
private async Task EnsureServiceIsEmptyAsync(Guid id, CancellationToken ct)
|
||||
{
|
||||
if (await db.TemplateDefinitions.AnyAsync(d => d.ServiceId == id, ct))
|
||||
if (await db.TemplateCatalog.AnyAsync(d => d.ServiceId == id, ct))
|
||||
throw new EmailTemplateWriteException("Remove every template definition before deleting this service, including definitions without translations.");
|
||||
if (await db.Providers.AnyAsync(c => c.ServiceId == id, ct) || await db.PushChannels.AnyAsync(c => c.ServiceId == id, ct))
|
||||
throw new EmailTemplateWriteException("Reassign or remove every notification channel before deleting this service.");
|
||||
@@ -76,9 +76,11 @@ internal sealed class NotificationServiceRepository(NotificationDbContext db, Te
|
||||
var language = service.Languages.SingleOrDefault(l => l.Id == languageId)
|
||||
?? throw new EmailTemplateWriteException("Language no longer exists. Reload the service.");
|
||||
var versions = await db.Templates.Where(t => t.ServiceLanguageId == languageId).ToListAsync(ct);
|
||||
if (service.Revision != expectedRevision || versions.Count != expectedVersionCount)
|
||||
var pushVersions = await db.PushTemplates.Where(t => t.ServiceLanguageId == languageId).ToListAsync(ct);
|
||||
if (service.Revision != expectedRevision || versions.Count + pushVersions.Count != expectedVersionCount)
|
||||
throw new EmailTemplateWriteException("The service or affected version count changed. Review the current count and confirm again.");
|
||||
db.Templates.RemoveRange(versions);
|
||||
db.PushTemplates.RemoveRange(pushVersions);
|
||||
// Save dependent removals first because language deletion is restricted by its FK.
|
||||
await db.SaveChangesAsync(ct);
|
||||
db.ServiceLanguages.Remove(language);
|
||||
@@ -112,7 +114,8 @@ internal sealed class NotificationServiceRepository(NotificationDbContext db, Te
|
||||
var removed = service.Languages.Where(l => !retained.Contains(l.Id)).ToList();
|
||||
foreach (var language in removed)
|
||||
{
|
||||
var count = await db.Templates.CountAsync(t => t.ServiceLanguageId == language.Id, ct);
|
||||
var count = await db.Templates.CountAsync(t => t.ServiceLanguageId == language.Id, ct)
|
||||
+ await db.PushTemplates.CountAsync(t => t.ServiceLanguageId == language.Id, ct);
|
||||
if (count > 0)
|
||||
throw new EmailTemplateWriteException($"Cannot remove language '{language.Code}': {count} version(s) use it. Use the separate delete-with-versions confirmation.");
|
||||
}
|
||||
@@ -168,10 +171,17 @@ internal sealed class NotificationServiceRepository(NotificationDbContext db, Te
|
||||
definition.Revision = Guid.NewGuid();
|
||||
definition.Updated = DateTimeOffset.UtcNow;
|
||||
}
|
||||
var pushDefinitions = await db.PushTemplateDefinitions.Where(d => d.ServiceId == serviceId).ToListAsync(ct);
|
||||
foreach (var definition in pushDefinitions)
|
||||
{
|
||||
definition.Revision = Guid.NewGuid();
|
||||
definition.Updated = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
private Task<Dictionary<Guid, int>> VersionCountsAsync(CancellationToken ct) => db.Templates.AsNoTracking()
|
||||
.GroupBy(t => t.ServiceLanguageId).Select(g => new { Id = g.Key, Count = g.Count() })
|
||||
.Select(t => t.ServiceLanguageId).Concat(db.PushTemplates.Select(t => t.ServiceLanguageId))
|
||||
.GroupBy(id => id).Select(g => new { Id = g.Key, Count = g.Count() })
|
||||
.ToDictionaryAsync(g => g.Id, g => g.Count, ct);
|
||||
|
||||
private static void Validate(NotificationServiceInfo service)
|
||||
|
||||
Reference in New Issue
Block a user