From 3e717522c284a1d2ba87ee330c51954a7f0008c4 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 28 Jan 2025 19:12:14 +0000 Subject: [PATCH] Added checks for non-user foreign keys in stores. Load/Saving stores' order is now based on table dependencies. Added ability to use chat message when using redemption. --- Requests/CreateGroupPermission.cs | 2 +- Requests/CreateRedeemableAction.cs | 12 +-- Requests/DeleteGroup.cs | 6 +- Requests/DeleteGroupChatter.cs | 4 +- Requests/GetGroups.cs | 4 +- Requests/UpdateGroupChatter.cs | 2 +- Requests/UpdateGroupPermission.cs | 2 +- Requests/UpdateRedeemableAction.cs | 10 ++- Services/ChannelManager.cs | 140 ++++++++++++++++------------- Services/DatabaseService.cs | 14 ++- Startup.cs | 2 + Store/ActionStore.cs | 5 +- Store/GroupPermissionStore.cs | 13 ++- Store/IStore.cs | 2 +- Store/Internal/GroupedSaveStore.cs | 4 +- Store/PolicyStore.cs | 8 +- Store/RedemptionStore.cs | 10 ++- Store/VoiceStateStore.cs | 8 +- 18 files changed, 146 insertions(+), 102 deletions(-) diff --git a/Requests/CreateGroupPermission.cs b/Requests/CreateGroupPermission.cs index 5a5dcd2..a2622be 100644 --- a/Requests/CreateGroupPermission.cs +++ b/Requests/CreateGroupPermission.cs @@ -18,7 +18,7 @@ namespace HermesSocketServer.Requests public Task Grant(Channel channel, IDictionary data) { var id = Guid.NewGuid(); - string groupId = data["group"].ToString()!; + Guid groupId = new Guid(data["group"].ToString()!); string path = data["path"].ToString()!; bool? allow = bool.TryParse(data["allow"].ToString()!, out bool a) ? a : null; diff --git a/Requests/CreateRedeemableAction.cs b/Requests/CreateRedeemableAction.cs index 9e6aa9b..48f5ba1 100644 --- a/Requests/CreateRedeemableAction.cs +++ b/Requests/CreateRedeemableAction.cs @@ -8,7 +8,7 @@ namespace HermesSocketServer.Requests public class CreateRedeemableAction : IRequest { public string Name => "create_redeemable_action"; - public string[] RequiredKeys => ["name", "data", "type"]; + public string[] RequiredKeys => ["name", "has_message", "type", "data"]; private ILogger _logger; public CreateRedeemableAction(ILogger logger) @@ -19,8 +19,9 @@ namespace HermesSocketServer.Requests public Task Grant(Channel channel, IDictionary data) { string name = data["name"].ToString()!; - string d = data["data"].ToString()!; string type = data["type"].ToString()!; + bool hasMessage = data["has_message"].ToString()!.ToLower() == "true"; + string d = data["data"].ToString()!; IDictionary dict = new Dictionary(); try @@ -29,7 +30,7 @@ namespace HermesSocketServer.Requests } catch (Exception ex) { - _logger.Error(ex, $"Failed to parse data on redeemable action while creating action [name: {name}][type: {type}][data: {d}]"); + _logger.Error(ex, $"Failed to parse data on redeemable action while creating action [name: {name}][type: {type}][has message: {hasMessage}][data: {d}]"); return Task.FromResult(RequestResult.Failed("Could not parse the data on this action.")); } @@ -37,14 +38,15 @@ namespace HermesSocketServer.Requests { UserId = channel.Id, Name = name, - Data = dict, Type = type, + HasMessage = hasMessage, + Data = dict, }; bool result = channel.Actions.Set(name, action); if (result) { - _logger.Information($"Added redeemable action to channel [name: {name}][type: {type}][channel: {channel.Id}]"); + _logger.Information($"Added redeemable action to channel [name: {name}][type: {type}][has message: {hasMessage}][channel: {channel.Id}]"); return Task.FromResult(RequestResult.Successful(action)); } return Task.FromResult(RequestResult.Failed("Something went wrong when updating the cache.")); diff --git a/Requests/DeleteGroup.cs b/Requests/DeleteGroup.cs index 9b8194d..3d8606e 100644 --- a/Requests/DeleteGroup.cs +++ b/Requests/DeleteGroup.cs @@ -22,7 +22,7 @@ namespace HermesSocketServer.Requests if (result) { var permissions = channel.GroupPermissions.Get().Values - .Where(p => p.GroupId == groupId); + .Where(p => p.GroupId.ToString() == groupId); Task? chattersSave = null; if (channel.Groups.Chatters.TryGetValue(groupId, out var chatters)) @@ -32,7 +32,7 @@ namespace HermesSocketServer.Requests { foreach (var chatter in filteredChatters) { - var res = chatters.Remove(chatter.ChatterId.ToString()); + var res = chatters.Remove(chatter.ChatterId.ToString(), fromCascade: true); if (!res) _logger.Warning($"Failed to delete group chatter by id [group chatter id: {chatter.ChatterId}]"); } @@ -43,7 +43,7 @@ namespace HermesSocketServer.Requests foreach (var permission in permissions) { - var res = channel.GroupPermissions.Remove(permission.Id); + var res = channel.GroupPermissions.Remove(permission.Id, fromCascade: true); if (!res) _logger.Warning($"Failed to delete group permission by id [group chatter id: {permission.Id}]"); } diff --git a/Requests/DeleteGroupChatter.cs b/Requests/DeleteGroupChatter.cs index 32baf72..65821f7 100644 --- a/Requests/DeleteGroupChatter.cs +++ b/Requests/DeleteGroupChatter.cs @@ -6,7 +6,7 @@ namespace HermesSocketServer.Requests public class DeleteGroupChatter : IRequest { public string Name => "delete_group_chatter"; - public string[] RequiredKeys => ["id", "group"]; + public string[] RequiredKeys => ["chatter", "group"]; private ILogger _logger; public DeleteGroupChatter(ILogger logger) @@ -16,7 +16,7 @@ namespace HermesSocketServer.Requests public Task Grant(Channel channel, IDictionary data) { - var chatterId = data["id"].ToString()!; + var chatterId = data["chatter"].ToString()!; var groupId = data["group"].ToString()!; if (!channel.Groups.Chatters.TryGetValue(groupId, out var chatters)) diff --git a/Requests/GetGroups.cs b/Requests/GetGroups.cs index 63676ef..541e794 100644 --- a/Requests/GetGroups.cs +++ b/Requests/GetGroups.cs @@ -30,8 +30,8 @@ namespace HermesSocketServer.Requests private class GroupDetails { - public required Group Group; - public required IEnumerable Chatters; + public required Group Group { get; set; } + public required IEnumerable Chatters { get; set; } } } } \ No newline at end of file diff --git a/Requests/UpdateGroupChatter.cs b/Requests/UpdateGroupChatter.cs index 892cba3..e2831ea 100644 --- a/Requests/UpdateGroupChatter.cs +++ b/Requests/UpdateGroupChatter.cs @@ -20,7 +20,7 @@ namespace HermesSocketServer.Requests var id = Guid.NewGuid(); string groupId = data["group"].ToString()!; if (!int.TryParse(data["chatter"].ToString()!, out var chatterId)) - return Task.FromResult(RequestResult.Failed("Priority needs to be an integer.")); + return Task.FromResult(RequestResult.Failed("Chatter Id needs to be an integer.")); string chatterLabel = data["label"].ToString()!; var groupChatter = new GroupChatter() diff --git a/Requests/UpdateGroupPermission.cs b/Requests/UpdateGroupPermission.cs index 4c23bfc..36677fd 100644 --- a/Requests/UpdateGroupPermission.cs +++ b/Requests/UpdateGroupPermission.cs @@ -18,7 +18,7 @@ namespace HermesSocketServer.Requests public Task Grant(Channel channel, IDictionary data) { var id = data["id"].ToString()!; - string groupId = data["group"].ToString()!; + Guid groupId = new Guid(data["group"].ToString()!); string path = data["path"].ToString()!; bool? allow = bool.TryParse(data["allow"].ToString()!, out bool a) ? a : null; diff --git a/Requests/UpdateRedeemableAction.cs b/Requests/UpdateRedeemableAction.cs index 5487328..f35555b 100644 --- a/Requests/UpdateRedeemableAction.cs +++ b/Requests/UpdateRedeemableAction.cs @@ -19,8 +19,9 @@ namespace HermesSocketServer.Requests public Task Grant(Channel channel, IDictionary data) { string name = data["name"].ToString()!; - string d = data["data"].ToString()!; string type = data["type"].ToString()!; + bool hasMessage = data["has_message"].ToString()!.ToLower() == "true"; + string d = data["data"].ToString()!; IDictionary dict = new Dictionary(); try @@ -29,7 +30,7 @@ namespace HermesSocketServer.Requests } catch (Exception ex) { - _logger.Error(ex, $"Failed to parse data on redeemable action while updating action [name: {name}][type: {type}][data: {d}]"); + _logger.Error(ex, $"Failed to parse data on redeemable action while updating action [name: {name}][type: {type}][has message: {hasMessage}][data: {d}]"); return Task.FromResult(RequestResult.Failed("Could not parse the data on this action.")); } @@ -37,14 +38,15 @@ namespace HermesSocketServer.Requests { UserId = channel.Id, Name = name, - Data = dict, Type = type, + HasMessage = hasMessage, + Data = dict, }; bool result = channel.Actions.Modify(name, action); if (result) { - _logger.Information($"Updated redeemable action on channel [name: {name}][type: {type}][channel: {channel.Id}]"); + _logger.Information($"Updated redeemable action on channel [name: {name}][type: {type}][has message: {hasMessage}][channel: {channel.Id}]"); return Task.FromResult(RequestResult.Successful(action)); } if (channel.Actions.Get(name) == null) diff --git a/Services/ChannelManager.cs b/Services/ChannelManager.cs index 42b9a64..990aade 100644 --- a/Services/ChannelManager.cs +++ b/Services/ChannelManager.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using HermesSocketLibrary.db; +using HermesSocketLibrary.Requests.Messages; using HermesSocketServer.Models; using HermesSocketServer.Store; -using HermesSocketServer.Validators; namespace HermesSocketServer.Services { @@ -10,15 +10,17 @@ namespace HermesSocketServer.Services { private readonly IStore _users; private readonly Database _database; + private readonly IStore _voices; private readonly ServerConfiguration _configuration; private readonly Serilog.ILogger _logger; private readonly IDictionary _channels; private readonly object _lock; - public ChannelManager(IStore users, Database database, ServerConfiguration configuration, Serilog.ILogger logger) + public ChannelManager(IStore users, Database database, IStore voices, ServerConfiguration configuration, Serilog.ILogger logger) { _users = users; _database = database; + _voices = voices; _configuration = configuration; _logger = logger; _channels = new ConcurrentDictionary(); @@ -26,68 +28,74 @@ namespace HermesSocketServer.Services } - public Task Add(string userId) + public async Task Add(string userId) { var user = _users.Get(userId); if (user == null) - return Task.FromResult(null); + return null; - lock (_lock) + return await Task.Run(() => { - if (_channels.ContainsKey(userId)) - return Task.FromResult(null); - - var actionTable = _configuration.Database.Tables["Action"]; - var chatterTable = _configuration.Database.Tables["Chatter"]; - var connectionTable = _configuration.Database.Tables["Connection"]; - //var chatterGroupTable = _configuration.Database.Tables["ChatterGroup"]; - var groupTable = _configuration.Database.Tables["Group"]; - var groupPermissionTable = _configuration.Database.Tables["GroupPermission"]; - var policyTable = _configuration.Database.Tables["Policy"]; - var redemptionTable = _configuration.Database.Tables["Redemption"]; - var ttsFilterTable = _configuration.Database.Tables["TtsFilter"]; - var ttsVoiceStateTable = _configuration.Database.Tables["VoiceState"]; - - var chatters = new ChatterStore(userId, chatterTable, _database, _logger); - var connections = new ConnectionStore(userId, connectionTable, _database, _logger); - var groups = new GroupStore(userId, groupTable, _database, _configuration, _logger); - var groupPermissions = new GroupPermissionStore(userId, groupPermissionTable, _database, _logger); - var policies = new PolicyStore(userId, policyTable, _database, _logger); - var filters = new TTSFilterStore(userId, ttsFilterTable, _database, _logger); - var actions = new ActionStore(userId, actionTable, _database, _logger); - var redemptions = new RedemptionStore(userId, redemptionTable, _database, _logger); - var voiceStates = new VoiceStateStore(userId, new VoiceIdValidator(), ttsVoiceStateTable, _database, _logger); - - Task.WaitAll([ - chatters.Load(), - connections.Load(), - groups.Load(), - groupPermissions.Load(), - policies.Load(), - filters.Load(), - actions.Load(), - redemptions.Load(), - voiceStates.Save(), - ]); - - var channel = new Channel() + lock (_lock) { - Id = userId, - User = user, - Chatters = chatters, - Connections = connections, - Groups = groups, - GroupPermissions = groupPermissions, - Policies = policies, - Filters = filters, - Actions = actions, - Redemptions = redemptions, - VoiceStates = voiceStates, - }; + if (_channels.ContainsKey(userId)) + return Task.FromResult(null); - _channels.Add(userId, channel); - return Task.FromResult(channel); - } + var actionTable = _configuration.Database.Tables["Action"]; + var chatterTable = _configuration.Database.Tables["Chatter"]; + var connectionTable = _configuration.Database.Tables["Connection"]; + //var chatterGroupTable = _configuration.Database.Tables["ChatterGroup"]; + var groupTable = _configuration.Database.Tables["Group"]; + var groupPermissionTable = _configuration.Database.Tables["GroupPermission"]; + var policyTable = _configuration.Database.Tables["Policy"]; + var redemptionTable = _configuration.Database.Tables["Redemption"]; + var ttsFilterTable = _configuration.Database.Tables["TtsFilter"]; + var ttsVoiceStateTable = _configuration.Database.Tables["VoiceState"]; + + var chatters = new ChatterStore(userId, chatterTable, _database, _logger); + var connections = new ConnectionStore(userId, connectionTable, _database, _logger); + var groups = new GroupStore(userId, groupTable, _database, _configuration, _logger); + var groupPermissions = new GroupPermissionStore(userId, groupPermissionTable, groups, _database, _logger); + var policies = new PolicyStore(userId, policyTable, groups, _database, _logger); + var filters = new TTSFilterStore(userId, ttsFilterTable, _database, _logger); + var actions = new ActionStore(userId, actionTable, _database, _logger); + var redemptions = new RedemptionStore(userId, redemptionTable, actions, _database, _logger); + var voiceStates = new VoiceStateStore(userId, ttsVoiceStateTable, _voices, _database, _logger); + + var channel = new Channel() + { + Id = userId, + User = user, + Chatters = chatters, + Connections = connections, + Groups = groups, + GroupPermissions = groupPermissions, + Policies = policies, + Filters = filters, + Actions = actions, + Redemptions = redemptions, + VoiceStates = voiceStates, + }; + + Task.WaitAll([ + channel.Actions.Load(), + channel.Chatters.Load(), + channel.Connections.Load(), + channel.Groups.Load(), + channel.Filters.Load(), + channel.VoiceStates.Load(), + ]); + + Task.WaitAll([ + channel.GroupPermissions.Load(), + channel.Policies.Load(), + channel.Redemptions.Load(), + ]); + + _channels.Add(userId, channel); + return Task.FromResult(channel); + } + }); } public Channel? Get(string channelId) @@ -121,17 +129,25 @@ namespace HermesSocketServer.Services foreach (var channel in _channels.Values) { _logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]"); - await Task.WhenAll([ + var genericTablesTask = Task.WhenAll([ channel.Chatters.Save(), channel.Connections.Save(), + channel.Filters.Save(), + channel.VoiceStates.Save(), + ]).ConfigureAwait(false); + + await Task.WhenAll([ + channel.Actions.Save(), channel.Groups.Save(), + ]).ConfigureAwait(false); + + await Task.WhenAll([ channel.GroupPermissions.Save(), channel.Policies.Save(), - channel.Filters.Save(), - channel.Actions.Save(), channel.Redemptions.Save(), - channel.VoiceStates.Save(), - ]); + ]).ConfigureAwait(false); + + await genericTablesTask; } } } diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs index 1b6463d..530c25e 100644 --- a/Services/DatabaseService.cs +++ b/Services/DatabaseService.cs @@ -1,4 +1,3 @@ -using HermesSocketLibrary.db; using HermesSocketLibrary.Requests.Messages; using HermesSocketServer.Models; using HermesSocketServer.Store; @@ -24,10 +23,10 @@ namespace HermesSocketServer.Services protected override async Task ExecuteAsync(CancellationToken cancellationToken) { - _logger.Information("Loading TTS voices..."); - await _voices.Load(); _logger.Information("Loading users..."); await _users.Load(); + _logger.Information("Loading TTS voices..."); + await _voices.Load(); await Task.Run(async () => { @@ -35,11 +34,10 @@ namespace HermesSocketServer.Services while (true) { - await Task.WhenAll([ - _voices.Save(), - _users.Save(), - _channels.Save(), - ]); + await _users.Save(); + await _voices.Save(); + await _channels.Save(); + await Task.Delay(TimeSpan.FromSeconds(_configuration.Database.SaveDelayInSeconds)); } }); diff --git a/Startup.cs b/Startup.cs index 2494450..695eab2 100644 --- a/Startup.cs +++ b/Startup.cs @@ -118,6 +118,8 @@ s.AddSingleton(); s.AddSingleton(); s.AddSingleton(); s.AddSingleton(); +s.AddSingleton(); +s.AddSingleton(); s.AddSingleton(); s.AddSingleton(); s.AddSingleton(); diff --git a/Store/ActionStore.cs b/Store/ActionStore.cs index fa82b68..101409f 100644 --- a/Store/ActionStore.cs +++ b/Store/ActionStore.cs @@ -23,7 +23,7 @@ namespace HermesSocketServer.Store public override async Task Load() { var data = new Dictionary() { { "user", _userId } }; - string sql = $"SELECT name, type, data FROM \"Action\" WHERE \"userId\" = @user"; + string sql = $"SELECT name, type, has_message, data FROM \"Action\" WHERE \"userId\" = @user"; await _database.Execute(sql, data, (reader) => { var name = reader.GetString(0); @@ -32,7 +32,8 @@ namespace HermesSocketServer.Store UserId = _userId, Name = name, Type = reader.GetString(1), - Data = JsonSerializer.Deserialize>(reader.GetString(2))! + HasMessage = reader.GetBoolean(2), + Data = JsonSerializer.Deserialize>(reader.GetString(3))! }); }); _logger.Information($"Loaded {_store.Count} redeemable actions from database."); diff --git a/Store/GroupPermissionStore.cs b/Store/GroupPermissionStore.cs index 44db00b..560d434 100644 --- a/Store/GroupPermissionStore.cs +++ b/Store/GroupPermissionStore.cs @@ -7,14 +7,16 @@ namespace HermesSocketServer.Store public class GroupPermissionStore : AutoSavedStore { private readonly string _userId; + private readonly IStore _groups; private readonly Database _database; private readonly Serilog.ILogger _logger; - public GroupPermissionStore(string userId, DatabaseTable table, Database database, Serilog.ILogger logger) + public GroupPermissionStore(string userId, DatabaseTable table, IStore groups, Database database, Serilog.ILogger logger) : base(table, database, logger) { _userId = userId; + _groups = groups; _database = database; _logger = logger; } @@ -30,7 +32,7 @@ namespace HermesSocketServer.Store { Id = id, UserId = _userId, - GroupId = reader.GetGuid(1).ToString(), + GroupId = reader.GetGuid(1), Path = reader.GetString(2), Allow = await reader.IsDBNullAsync(3) ? null : reader.GetBoolean(3), }); @@ -43,15 +45,18 @@ namespace HermesSocketServer.Store ArgumentException.ThrowIfNullOrWhiteSpace(key, nameof(key)); ArgumentNullException.ThrowIfNull(value, nameof(value)); ArgumentException.ThrowIfNullOrWhiteSpace(value.UserId, nameof(value.UserId)); - ArgumentException.ThrowIfNullOrWhiteSpace(value.GroupId, nameof(value.GroupId)); + ArgumentNullException.ThrowIfNull(value.GroupId, nameof(value.GroupId)); ArgumentException.ThrowIfNullOrWhiteSpace(value.Path, nameof(value.Path)); + + if (_groups.Get(value.GroupId.ToString()) == null) + throw new ArgumentException("The group id does not exist."); } protected override void OnInitialModify(string key, GroupPermission oldValue, GroupPermission newValue) { ArgumentNullException.ThrowIfNull(newValue, nameof(newValue)); ArgumentException.ThrowIfNullOrWhiteSpace(newValue.UserId, nameof(newValue.UserId)); - ArgumentException.ThrowIfNullOrWhiteSpace(newValue.GroupId, nameof(newValue.GroupId)); + ArgumentNullException.ThrowIfNull(newValue.GroupId, nameof(newValue.GroupId)); ArgumentException.ThrowIfNullOrWhiteSpace(newValue.Path, nameof(newValue.Path)); ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.UserId, newValue.UserId, nameof(oldValue.UserId)); ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.GroupId, newValue.GroupId, nameof(oldValue.GroupId)); diff --git a/Store/IStore.cs b/Store/IStore.cs index ac75903..425a861 100644 --- a/Store/IStore.cs +++ b/Store/IStore.cs @@ -7,7 +7,7 @@ namespace HermesSocketServer.Store Task Load(); bool Modify(K? key, Action modify); bool Modify(K? key, V value); - bool Remove(K? key); + bool Remove(K? key, bool fromCascade = false); Task Save(); bool Set(K? key, V value); } diff --git a/Store/Internal/GroupedSaveStore.cs b/Store/Internal/GroupedSaveStore.cs index 50585b7..d383291 100644 --- a/Store/Internal/GroupedSaveStore.cs +++ b/Store/Internal/GroupedSaveStore.cs @@ -87,7 +87,7 @@ namespace HermesSocketServer.Store.Internal return false; } - public bool Remove(K? key) + public bool Remove(K? key, bool fromCascade = false) { if (key == null) return false; @@ -102,7 +102,7 @@ namespace HermesSocketServer.Store.Internal if (!_added.Remove(key)) { _modified.Remove(key); - if (!_deleted.Contains(key)) + if (!fromCascade && !_deleted.Contains(key)) { _deleted.Add(key); } diff --git a/Store/PolicyStore.cs b/Store/PolicyStore.cs index 7924e34..b71f3ac 100644 --- a/Store/PolicyStore.cs +++ b/Store/PolicyStore.cs @@ -1,4 +1,5 @@ using HermesSocketLibrary.db; +using HermesSocketLibrary.Requests.Messages; using HermesSocketServer.Messages; using HermesSocketServer.Store.Internal; @@ -7,14 +8,16 @@ namespace HermesSocketServer.Store public class PolicyStore : AutoSavedStore { private readonly string _userId; + private readonly IStore _groups; private readonly Database _database; private readonly Serilog.ILogger _logger; - public PolicyStore(string userId, DatabaseTable table, Database database, Serilog.ILogger logger) + public PolicyStore(string userId, DatabaseTable table, IStore groups, Database database, Serilog.ILogger logger) : base(table, database, logger) { _userId = userId; + _groups = groups; _database = database; _logger = logger; } @@ -50,6 +53,9 @@ namespace HermesSocketServer.Store ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value.Span, nameof(value.Span)); ArgumentOutOfRangeException.ThrowIfLessThan(value.Span, 1000, nameof(value.Span)); ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Span, 86400, nameof(value.Span)); + + if (_groups.Get(value.GroupId.ToString()) == null) + throw new ArgumentException("The group id does not exist."); } protected override void OnInitialModify(string key, Policy oldValue, Policy newValue) diff --git a/Store/RedemptionStore.cs b/Store/RedemptionStore.cs index 22e64c5..c645d28 100644 --- a/Store/RedemptionStore.cs +++ b/Store/RedemptionStore.cs @@ -7,14 +7,16 @@ namespace HermesSocketServer.Store public class RedemptionStore : AutoSavedStore { private readonly string _userId; + private readonly IStore _actions; private readonly Database _database; private readonly Serilog.ILogger _logger; - public RedemptionStore(string userId, DatabaseTable table, Database database, Serilog.ILogger logger) + public RedemptionStore(string userId, DatabaseTable table, IStore actions, Database database, Serilog.ILogger logger) : base(table, database, logger) { _userId = userId; + _actions = actions; _database = database; _logger = logger; } @@ -50,6 +52,9 @@ namespace HermesSocketServer.Store ArgumentNullException.ThrowIfNull(value.Order, nameof(value.Order)); ArgumentOutOfRangeException.ThrowIfNegative(value.Order, nameof(value.Order)); ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Order, 99, nameof(value.Order)); + + if (_actions.Get(value.ActionName) == null) + throw new ArgumentException("The action name does not exist."); } protected override void OnInitialModify(string key, Redemption oldValue, Redemption newValue) @@ -64,6 +69,9 @@ namespace HermesSocketServer.Store ArgumentOutOfRangeException.ThrowIfGreaterThan(newValue.Order, 99, nameof(newValue.Order)); ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.Id, newValue.Id, nameof(newValue.Id)); ArgumentOutOfRangeException.ThrowIfNotEqual(oldValue.UserId, newValue.UserId, nameof(newValue.UserId)); + + if (oldValue.ActionName != newValue.ActionName && _actions.Get(newValue.ActionName) == null) + throw new ArgumentException("The action name does not exist."); } protected override void OnPostRemove(string key, Redemption? value) diff --git a/Store/VoiceStateStore.cs b/Store/VoiceStateStore.cs index 394b974..d2de867 100644 --- a/Store/VoiceStateStore.cs +++ b/Store/VoiceStateStore.cs @@ -8,18 +8,21 @@ namespace HermesSocketServer.Store public class VoiceStateStore : ComplexAutoSavedStore { private readonly string _userId; + private readonly IStore _voices; private readonly VoiceIdValidator _idValidator; private readonly Database _database; private readonly Serilog.ILogger _logger; - public VoiceStateStore(string userId, VoiceIdValidator voiceIdValidator, DatabaseTable table, Database database, Serilog.ILogger logger) + public VoiceStateStore(string userId, DatabaseTable table, IStore voices, Database database, Serilog.ILogger logger) : base(table, database, logger) { _userId = userId; - _idValidator = voiceIdValidator; + _voices = voices; _database = database; _logger = logger; + + _idValidator = new VoiceIdValidator(); } public override async Task Load() @@ -47,6 +50,7 @@ namespace HermesSocketServer.Store ArgumentException.ThrowIfNullOrWhiteSpace(value.Id, nameof(value.Id)); ArgumentException.ThrowIfNullOrWhiteSpace(value.UserId, nameof(value.UserId)); ArgumentNullException.ThrowIfNull(value.Enabled, nameof(value.Enabled)); + ArgumentNullException.ThrowIfNull(_voices.Get(value.Id)); } protected override void OnInitialModify(string key, TTSVoiceState oldValue, TTSVoiceState newValue)