using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Migrations
{
///
/// SMS Notifications: makes a notification recipient's contact path optional and adds
/// the central SMS-provider (Twilio) configuration table.
///
/// 1. NotificationRecipients.EmailAddress becomes nullable — an SMS-only recipient
/// carries a PhoneNumber and no email. (ALTER COLUMN, naturally idempotent.)
/// 2. NotificationRecipients.PhoneNumber nvarchar(32) NULL — E.164 phone for SMS
/// recipients (guarded ADD).
/// 3. SmsConfigurations table — central-only provider config; AuthToken is
/// stored encrypted at rest (EncryptedStringConverter → nvarchar(max), mirroring
/// SmtpConfigurations.Credentials) (guarded CREATE TABLE).
///
/// Every DDL statement is guarded so the migration is safe to re-run against a
/// partially-migrated database (the repo's idempotent-migration convention; see
/// AddListAttributeElementType).
///
public partial class AddSmsNotifications : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
{
// ALTER COLUMN is naturally idempotent: re-running re-applies the same nullable shape.
migrationBuilder.Sql("ALTER TABLE [NotificationRecipients] ALTER COLUMN [EmailAddress] nvarchar(500) NULL;");
migrationBuilder.Sql(@"
IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE Name='PhoneNumber' AND Object_ID=Object_ID('NotificationRecipients'))
ALTER TABLE [NotificationRecipients] ADD [PhoneNumber] nvarchar(32) NULL;");
migrationBuilder.Sql(@"
IF OBJECT_ID(N'dbo.SmsConfigurations', N'U') IS NULL
CREATE TABLE [SmsConfigurations] (
[Id] int NOT NULL IDENTITY(1, 1),
[AccountSid] nvarchar(100) NOT NULL,
[AuthToken] nvarchar(max) NULL,
[FromNumber] nvarchar(32) NOT NULL,
[MessagingServiceSid] nvarchar(100) NULL,
[ApiBaseUrl] nvarchar(500) NULL,
[ConnectionTimeoutSeconds] int NOT NULL,
[MaxRetries] int NOT NULL,
[RetryDelay] time NOT NULL,
CONSTRAINT [PK_SmsConfigurations] PRIMARY KEY ([Id])
);");
}
///
protected override void Down(MigrationBuilder migrationBuilder)
{
// ConfigurationDatabase-NNN: refuse to roll back if SMS-only recipients exist.
// Such a row carries a PhoneNumber and a (legitimately) NULL EmailAddress. The
// backfill below sets every NULL EmailAddress to '' so the column can revert to
// NOT NULL — but for an SMS-only recipient that silently destroys its contact
// info (PhoneNumber is also dropped) and leaves an invalid empty-string email.
// This guard MUST run before DROP COLUMN [PhoneNumber], while the column is still
// queryable, so the operator gets a clear error instead of silent data loss.
migrationBuilder.Sql(@"
IF EXISTS (SELECT 1 FROM [NotificationRecipients] WHERE [EmailAddress] IS NULL AND [PhoneNumber] IS NOT NULL)
THROW 60000, 'Cannot roll back AddSmsNotifications: SMS-only recipients (PhoneNumber set, EmailAddress NULL) exist. Reassign or delete them before rolling back -- otherwise their PhoneNumber would be dropped and EmailAddress backfilled to an invalid empty string.', 1;");
migrationBuilder.Sql(@"
IF OBJECT_ID(N'dbo.SmsConfigurations', N'U') IS NOT NULL
DROP TABLE [SmsConfigurations];");
migrationBuilder.Sql(@"
IF EXISTS (SELECT 1 FROM sys.columns WHERE Name='PhoneNumber' AND Object_ID=Object_ID('NotificationRecipients'))
ALTER TABLE [NotificationRecipients] DROP COLUMN [PhoneNumber];");
// Reversing to NOT NULL requires backfilling existing NULLs first, otherwise the
// ALTER fails. Mirrors EF's generated default ('') for the previously-required column.
// The guard above guarantees any remaining NULL here belongs to a pre-existing email
// recipient that never had an address, not an SMS-only recipient.
migrationBuilder.Sql("UPDATE [NotificationRecipients] SET [EmailAddress] = '' WHERE [EmailAddress] IS NULL;");
migrationBuilder.Sql("ALTER TABLE [NotificationRecipients] ALTER COLUMN [EmailAddress] nvarchar(500) NOT NULL;");
}
}
}