From 5ed034320e8082e03f8587d9c8ee1cfcdb0372bb Mon Sep 17 00:00:00 2001 From: Robin Dadswell <19610103+RobinDadswell@users.noreply.github.com> Date: Mon, 20 Dec 2021 23:36:17 +0000 Subject: [PATCH] New: Migrate Discord from Slack to Discord notifications --- .../201_migrate_discord_from_slackFixture.cs | 71 ++++++++++++ .../201_migrate_discord_from_slack.cs | 101 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 src/NzbDrone.Core.Test/Datastore/Migration/201_migrate_discord_from_slackFixture.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/201_migrate_discord_from_slack.cs diff --git a/src/NzbDrone.Core.Test/Datastore/Migration/201_migrate_discord_from_slackFixture.cs b/src/NzbDrone.Core.Test/Datastore/Migration/201_migrate_discord_from_slackFixture.cs new file mode 100644 index 000000000..370618416 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Migration/201_migrate_discord_from_slackFixture.cs @@ -0,0 +1,71 @@ +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Datastore.Migration; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Datastore.Migration +{ + [TestFixture] + public class migrate_discord_from_slackFixture : MigrationTest + { + private readonly JsonSerializerOptions _serializerSettings; + + public migrate_discord_from_slackFixture() + { + _serializerSettings = new JsonSerializerOptions + { + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + _serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); + } + + [Test] + public void should_replace_old_url() + { + var webhookUrl = "https://discord.com/api/webhooks/922499153416847361/f9CAcD5i_E_-0AoPfMVa8igVK8h271HpJDbd6euUrPh9KonWlMCziLOSMmD-2SQ4CHmX/slack"; + var db = WithMigrationTestDb(c => + { + c.Insert.IntoTable("Notifications").Row(new + { + Name = "SlackDiscord", + Implementation = "Slack", + Settings = new SlackNotificationSettings201 + { + Icon = "TestURL", + Username = "TestUsername", + WebHookUrl = webhookUrl + }.ToJson(), + ConfigContract = "SlackSettings", + OnGrab = true, + OnDownload = true, + OnUpgrade = true, + OnRename = true, + OnHealthIssue = true, + OnMovieDelete = true, + OnMovieFileDelete = true, + OnMovieFileDeleteForUpgrade = true, + IncludeHealthWarnings = true + }); + }); + + var items = db.Query("SELECT Id,ConfigContract,Implementation,Name,Settings FROM Notifications"); + + items.Should().HaveCount(1); + items.First().ConfigContract.Should().Be("DiscordSettings"); + var settings = JsonSerializer.Deserialize(items.First().Settings, _serializerSettings); + settings.Avatar.Should().Be("TestURL"); + settings.Username.Should().Be("TestUsername"); + settings.WebHookUrl.Should().Be(webhookUrl.Replace("/slack", "")); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/201_migrate_discord_from_slack.cs b/src/NzbDrone.Core/Datastore/Migration/201_migrate_discord_from_slack.cs new file mode 100644 index 000000000..f477d953c --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/201_migrate_discord_from_slack.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(201)] + public class migrate_discord_from_slack : NzbDroneMigrationBase + { + private readonly JsonSerializerOptions _serializerSettings; + + public migrate_discord_from_slack() + { + _serializerSettings = new JsonSerializerOptions + { + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + _serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); + } + + protected override void MainDbUpgrade() + { + Execute.WithConnection(MigrateDiscordFromSlack); + } + + private void MigrateDiscordFromSlack(IDbConnection conn, IDbTransaction tran) + { + var notificationRows = conn.Query($"SELECT Id,ConfigContract,Implementation,Name,Settings FROM Notifications WHERE Implementation = 'Slack'"); + + var discordSlackNotifications = notificationRows.Where(n => JsonSerializer.Deserialize(n.Settings, _serializerSettings).WebHookUrl.Contains("discord")); + + if (!discordSlackNotifications.Any()) + { + return; + } + + foreach (NotificationEntity201 notification in discordSlackNotifications) + { + SlackNotificationSettings201 settings = JsonSerializer.Deserialize(notification.Settings, _serializerSettings); + DiscordNotificationSettings201 discordSettings = new DiscordNotificationSettings201 + { + Avatar = settings.Icon, + GrabFields = new List { 0, 1, 2, 3, 5, 6, 7, 8, 9 }, + ImportFields = new List { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12 }, + Username = settings.Username, + WebHookUrl = settings.WebHookUrl.Replace("/slack", "") + }; + + notification.ConfigContract = "DiscordSettings"; + notification.Implementation = "Discord"; + notification.Name = $"{notification.Name}-Slack_Migrated"; + notification.Settings = JsonSerializer.Serialize(discordSettings, _serializerSettings); + } + + var updateSql = "UPDATE Notifications SET ConfigContract = @ConfigContract, " + + "Implementation = @Implementation, " + + "Name = @Name, " + + "Settings = @Settings " + + "WHERE Id = @Id"; + + conn.Execute(updateSql, discordSlackNotifications, transaction: tran); + } + } + + public class NotificationEntity201 + { + public int Id { get; set; } + public string ConfigContract { get; set; } + public string Implementation { get; set; } + public string Name { get; set; } + public string Settings { get; set; } + } + + public class SlackNotificationSettings201 + { + public string Channel { get; set; } + public string Icon { get; set; } + public string Username { get; set; } + public string WebHookUrl { get; set; } + } + + public class DiscordNotificationSettings201 + { + public string Avatar { get; set; } + public string Username { get; set; } + public string WebHookUrl { get; set; } + public IEnumerable GrabFields { get; set; } + public IEnumerable ImportFields { get; set; } + } +}