diff --git a/Core/Models/TagDatabase/TagDatabase.Events.cs b/Core/Models/TagDatabase/TagDatabase.Events.cs index 1c8bf5d..46d993d 100644 --- a/Core/Models/TagDatabase/TagDatabase.Events.cs +++ b/Core/Models/TagDatabase/TagDatabase.Events.cs @@ -13,6 +13,10 @@ public partial class TagDatabase public event EventHandler<(int id, string name)> TagDeleted = delegate { }; + public event EventHandler> TagsAdded = delegate { }; + + public event EventHandler> TagsUpdated = delegate { }; + private void OnInitialisationComplete(EventArgs e) { this.Logger.Debug("OnInitialised invoked!"); diff --git a/Core/Models/TagDatabase/TagDatabase.WriteMethods.cs b/Core/Models/TagDatabase/TagDatabase.WriteMethods.cs index 2a3ce26..63588b5 100644 --- a/Core/Models/TagDatabase/TagDatabase.WriteMethods.cs +++ b/Core/Models/TagDatabase/TagDatabase.WriteMethods.cs @@ -61,6 +61,71 @@ public partial class TagDatabase if (isTransactionOwner) await transaction.DisposeAsync().ConfigureAwait(false); } } + + public async Task WriteTagsToDatabase(List tags, SqliteTransaction? transaction = null) + { + this.CheckInitialisation(); + bool isTransactionOwner = transaction == null; + transaction ??= (SqliteTransaction)await this.currentConnection.BeginTransactionAsync().ConfigureAwait(false); + + HashSet updatedTags = []; + HashSet newlyAddedTags = []; + + try + { + // technically, this supports bulk tag updates as well, but i ain't doing that rn. + foreach (var tag in tags) + { + bool alreadyOnDatabase = tag.Id != 0; + + SqliteCommand addCommand = this.currentConnection.CreateCommand(); + addCommand.Transaction = transaction; + QueryProcessorHandler.ProcessTagSaveCommand(addCommand, tag); + + if (await this.SelectTagFromDatabase(tag.Name) is not null && !alreadyOnDatabase) + throw new ArgumentException(ErrorMessages.TagAlreadyExists(tag.Name)); + + tag.Id = Convert.ToInt32(await addCommand.ExecuteScalarAsync().ConfigureAwait(false), + CultureInfo.InvariantCulture); + + await this.SaveTagAliases(transaction, tag.Id, tag.Aliases).ConfigureAwait(false); + await this.SaveTagParents(transaction, tag.Id, tag.Parents, tag).ConfigureAwait(false); + + int index = this.Tags.FindIndex(t => t.Id == tag.Id); + if (index != -1) + { + this.Tags[index] = tag; + updatedTags.Add(tag); + } + else + { + this.Tags.Add(tag); + newlyAddedTags.Add(tag); + } + } + + if (isTransactionOwner) await transaction.CommitAsync().ConfigureAwait(false); + + if (updatedTags.Count > 0) + { + this.TagsUpdated.Invoke(this, updatedTags); + } + + if (newlyAddedTags.Count > 0) + { + this.TagsAdded.Invoke(this, newlyAddedTags); + } + } + catch (SqliteException) + { + await transaction.RollbackAsync().ConfigureAwait(false); + throw; + } + finally + { + if (isTransactionOwner) await transaction.DisposeAsync().ConfigureAwait(false); + } + } private async Task SaveTagAliases(SqliteTransaction transaction, int id, IReadOnlyCollection aliases) { diff --git a/Tests/TagDatabaseSaveTests.cs b/Tests/TagDatabaseSaveTests.cs index cdd0126..6147f1a 100644 --- a/Tests/TagDatabaseSaveTests.cs +++ b/Tests/TagDatabaseSaveTests.cs @@ -186,4 +186,22 @@ public class TagDatabaseWriteTests : TestBase Assert.That(savedTag.Aliases, Is.EquivalentTo(inputTag.Aliases)); } } + + [Test] + public async Task TagDatabase_WriteTagsToDatabase_TagAdded() + { + // Arrange + HashSet eventExpectedTags = []; + List testTags = [TestSampleTags.Ambient, TestSampleTags.Electronic, TestSampleTags.SpaceAmbient]; + this.Database.TagsAdded += (_, tags) => eventExpectedTags.UnionWith(tags); + this.Database.ClearTags(); + + // Act + await this.Database.WriteTagsToDatabase(testTags); + + // Assert + var retrievedTags = await this.Database.GetAllTagsFromDatabase(); + Assert.That(retrievedTags.Count, Is.EqualTo(testTags.Count)); + Assert.That(eventExpectedTags.Count, Is.EqualTo(testTags.Count)); + } } \ No newline at end of file