|
- using Campaign_Tracker.Server.Seed.Models;
-
- namespace Campaign_Tracker.Server.Seed;
-
- public sealed class SeedService : ISeedService
- {
- internal const string ElectionCycleJobEntityType = "ElectionCycleJob";
- internal const string ReadinessFeatureKey = "FR29.ReadinessStatus";
- internal const string OverdueMilestoneScenario = "OverdueMilestoneAlert";
-
- private static readonly string[] ExpectedReferenceSeedKeys =
- [
- "operational-status.not-started",
- "operational-status.in-progress",
- "operational-status.blocked",
- "operational-status.complete",
- "service-template.addressing",
- "service-template.sorting",
- "service-template.transportation",
- "service-template.office-copy",
- "extension-reference.election-cycle.primary",
- "extension-reference.election-cycle.general",
- "extension-reference.mail-class.first-class",
- "extension-reference.mail-class.standard",
- ];
-
- private static readonly string[] ExpectedRequiredFieldSeedKeys =
- [
- "required-field.election-cycle-job.municipality-profile-id",
- "required-field.election-cycle-job.legacy-jurisdiction-j-code",
- "required-field.election-cycle-job.election-date",
- "required-field.election-cycle-job.mail-date",
- "required-field.election-cycle-job.service-template",
- ];
-
- private static readonly string[] ExpectedEscalationSeedKeys =
- [
- "escalation.overdue-milestone.operations-lead",
- ];
-
- private readonly ISeedDataStore _store;
- private readonly TimeProvider _timeProvider;
-
- public SeedService(ISeedDataStore store, TimeProvider timeProvider)
- {
- _store = store;
- _timeProvider = timeProvider;
- }
-
- public Task SeedAsync(CancellationToken cancellationToken = default) =>
- _store.UpsertSeedDataAsync(CreateDefaults(_timeProvider.GetUtcNow()), cancellationToken);
-
- public async Task<bool> IsSeededAsync(CancellationToken cancellationToken = default)
- {
- var referenceValues = await _store.GetReferenceValuesAsync(cancellationToken);
- var requiredRules = await _store.GetRequiredFieldRulesAsync(cancellationToken);
- var escalationRules = await _store.GetEscalationRulesAsync(cancellationToken);
-
- return ContainsAll(referenceValues.Select(value => value.SeedKey), ExpectedReferenceSeedKeys)
- && ContainsAll(requiredRules.Select(rule => rule.SeedKey), ExpectedRequiredFieldSeedKeys)
- && ContainsAll(escalationRules.Select(rule => rule.SeedKey), ExpectedEscalationSeedKeys);
- }
-
- private static SeedDataSet CreateDefaults(DateTimeOffset now) => new()
- {
- ReferenceValues =
- [
- Reference("operational-status.not-started", "OperationalStatus", "Not Started", "not-started",
- "Election-cycle job work has not started.", now),
- Reference("operational-status.in-progress", "OperationalStatus", "In Progress", "in-progress",
- "Election-cycle job work is actively in progress.", now),
- Reference("operational-status.blocked", "OperationalStatus", "Blocked", "blocked",
- "Election-cycle job work is blocked and needs intervention.", now),
- Reference("operational-status.complete", "OperationalStatus", "Complete", "complete",
- "Election-cycle job work is complete.", now),
- Reference("service-template.addressing", "ServiceTemplate", "Addressing", "addressing",
- "Default service template for addressing work.", now),
- Reference("service-template.sorting", "ServiceTemplate", "Sorting", "sorting",
- "Default service template for sorting work.", now),
- Reference("service-template.transportation", "ServiceTemplate", "Transportation", "transportation",
- "Default service template for transportation work.", now),
- Reference("service-template.office-copy", "ServiceTemplate", "Office Copy", "office-copy",
- "Default service template for office-copy work.", now),
- Reference("extension-reference.election-cycle.primary", "ElectionCycleType", "Primary", "primary",
- "Extension-layer election-cycle reference value for primary elections.", now),
- Reference("extension-reference.election-cycle.general", "ElectionCycleType", "General", "general",
- "Extension-layer election-cycle reference value for general elections.", now),
- Reference("extension-reference.mail-class.first-class", "MailClass", "First Class", "first-class",
- "Extension-layer mail-class reference value.", now),
- Reference("extension-reference.mail-class.standard", "MailClass", "Standard", "standard",
- "Extension-layer mail-class reference value.", now),
- ],
- RequiredFieldRules =
- [
- RequiredField("required-field.election-cycle-job.municipality-profile-id",
- "Municipality Profile", "municipalityProfileId",
- "Election-cycle jobs must be linked to a municipality profile.", now),
- RequiredField("required-field.election-cycle-job.legacy-jurisdiction-j-code",
- "Legacy Jurisdiction Code", "legacyJurisdictionJCode",
- "Election-cycle jobs must keep the legacy jurisdiction bridge required by Story 1.8.", now),
- RequiredField("required-field.election-cycle-job.election-date",
- "Election Date", "electionDate",
- "Election-cycle jobs need an election date before readiness can pass.", now),
- RequiredField("required-field.election-cycle-job.mail-date",
- "Mail Date", "mailDate",
- "Election-cycle jobs need a planned mail date before readiness can pass.", now),
- RequiredField("required-field.election-cycle-job.service-template",
- "Service Template", "serviceTemplate",
- "Election-cycle jobs need a selected service template before readiness can pass.", now),
- ],
- EscalationRules =
- [
- new()
- {
- SeedKey = "escalation.overdue-milestone.operations-lead",
- Name = "Overdue Milestone Operations Lead Alert",
- Description = "Escalates election-cycle jobs whose active milestone is overdue.",
- Scenario = OverdueMilestoneScenario,
- TriggerCondition = "activeMilestone.dueDate < today && job.status != 'complete'",
- Action = "NotifyOperationsLead",
- MilestoneBasis = "activeMilestone.dueDate",
- AlertWindow = TimeSpan.Zero,
- Priority = 1,
- Source = SeedRecordSource.SystemSeed,
- IsActive = true,
- CreatedAt = now,
- UpdatedAt = now,
- },
- ],
- };
-
- private static ReferenceValue Reference(
- string seedKey,
- string category,
- string name,
- string value,
- string description,
- DateTimeOffset now) => new()
- {
- SeedKey = seedKey,
- Category = category,
- Name = name,
- Value = value,
- Description = description,
- Source = SeedRecordSource.SystemSeed,
- IsActive = true,
- CreatedAt = now,
- UpdatedAt = now,
- };
-
- private static RequiredFieldRule RequiredField(
- string seedKey,
- string name,
- string fieldPath,
- string description,
- DateTimeOffset now) => new()
- {
- SeedKey = seedKey,
- Name = name,
- Description = description,
- EntityType = ElectionCycleJobEntityType,
- FieldPath = fieldPath,
- ReadinessFeatureKey = ReadinessFeatureKey,
- IsRequired = true,
- Source = SeedRecordSource.SystemSeed,
- IsActive = true,
- CreatedAt = now,
- UpdatedAt = now,
- };
-
- private static bool ContainsAll(IEnumerable<string> actual, IEnumerable<string> expected)
- {
- var actualSet = actual.ToHashSet(StringComparer.OrdinalIgnoreCase);
- return expected.All(actualSet.Contains);
- }
- }
|