diff --git a/Library/App/App.csproj b/Library/App/App.csproj index cdc89b3..9c51dbc 100644 --- a/Library/App/App.csproj +++ b/Library/App/App.csproj @@ -37,9 +37,9 @@ + - diff --git a/Library/App/Forms/MainForm/MainForm.cs b/Library/App/Forms/MainForm/MainForm.cs index 0f11e43..b764037 100644 --- a/Library/App/Forms/MainForm/MainForm.cs +++ b/Library/App/Forms/MainForm/MainForm.cs @@ -3,6 +3,7 @@ using App.Utils; using Contracts.Scripts.Base; using Data; +using Data.Service.Interfaces; using JobManager.Service; using MaterialSkin.Controls; @@ -13,17 +14,19 @@ public partial class MainForm : BaseForm private readonly TrayContextMenu _trayContextMenu; private readonly IWindowsRegistryService _windowsRegistryService; - public MainForm(IRunScriptService runScriptService, IWindowsRegistryService windowsRegistryService) + public MainForm(IRunScriptService runScriptService, IWindowsRegistryService windowsRegistryService, IScriptsService scriptsService) { InitializeComponent(); _windowsRegistryService = windowsRegistryService; - _trayContextMenu = new TrayContextMenu(); - ConfigureTrayContextMenu(); + var scripts = runTabControl.SetScriptsService(scriptsService); runTabControl.SetRunScriptService(runScriptService); runTabControl.ScriptEdited += RunTabControl_ScriptEdited; runTabControl.ScriptAdded += RunTabControl_ScriptAdded; runTabControl.ScriptRemoved += RunTabControl_ScriptRemoved; + + _trayContextMenu = new TrayContextMenu(); + ConfigureTrayContextMenu(scripts); } protected override void WndProc(ref Message m) @@ -98,12 +101,12 @@ private void ShowForm() TopMost = true; TopMost = currentTop; } - private void ConfigureTrayContextMenu() + private void ConfigureTrayContextMenu(List scripts) { _trayContextMenu.CloseClicked += ContextMenu_Close_Click; _trayContextMenu.OpenClicked += ContextMenu_Open_Click; _trayContextMenu.ScriptStatusClicked += ContextMenu_StatusClick; - _trayContextMenu.LoadScriptList(SettingsManager.Scripts); + _trayContextMenu.LoadScriptList(scripts); this.appNotifyIcon.ContextMenuStrip = _trayContextMenu; } private void ChangeScriptStatus(ScriptAbs script) diff --git a/Library/App/Forms/MainForm/Tabs/Run/RunTabControl.cs b/Library/App/Forms/MainForm/Tabs/Run/RunTabControl.cs index 34ab8f8..a9a471a 100644 --- a/Library/App/Forms/MainForm/Tabs/Run/RunTabControl.cs +++ b/Library/App/Forms/MainForm/Tabs/Run/RunTabControl.cs @@ -3,7 +3,7 @@ using Contracts.Key; using Contracts.Scripts; using Contracts.Scripts.Base; -using Data; +using Data.Service.Interfaces; using JobManager.Service; using MaterialSkin.Controls; using Serilog; @@ -20,10 +20,14 @@ public partial class RunTabControl : UserControl private readonly ScriptListAdapter _scriptListAdapter; private readonly ListenKeysService _listenKeysService; private IRunScriptService? _runScriptService; + private IScriptsService? _scriptsService; + private List _scripts; public RunTabControl() { InitializeComponent(); + _scripts = new(); + _scriptListAdapter = new ScriptListAdapter(pnlScripts); _scriptListAdapter.ShowMenu += ShowContextMenuScript; _scriptListAdapter.StatusClicked += ChangeScriptStatusClicked; @@ -31,21 +35,35 @@ public RunTabControl() _listenKeysService = ListenKeysService.GetInstance(); _listenKeysService.KeyUpClicked += ListenKeysService_KeyUpClicked; } + + #region Public methods public void SetRunScriptService(IRunScriptService runScriptService) { _runScriptService = runScriptService; _runScriptService.OneOffScriptExecuted += OneOffJobWasExecuted; } + public List SetScriptsService(IScriptsService scriptsService) + { + _scriptsService = scriptsService; + _scripts = _scriptsService.GetScripts(); + return _scripts; + } + public async void RefreshScriptStatusAsync(string scriptId) + { + var script = _scripts.FirstOrDefault(s => s.Id == scriptId)?.Clone(); + await ChangeScriptStatusAsync(script); + } + #endregion #region Events private async void OnLoad(object sender, EventArgs e) { if (this.DesignMode) return; - _scriptListAdapter.CreateWithList(SettingsManager.Scripts); - await CheckIfScriptsNeedToRunOnLoad(); + _scriptListAdapter.CreateWithList(_scripts); + await CheckIfScriptsNeedToRunOnLoadAsync(); } private void OneOffJobWasExecuted(string scriptId) { - var script = SettingsManager.FindScriptById(scriptId); + var script = _scripts.FirstOrDefault(s => s.Id == scriptId)?.Clone(); if (script != null) ChangeScriptStatusThreadSafe(script); } @@ -74,7 +92,7 @@ private async void ContextMenuItemClicked(object sender, ToolStripItemClickedEve } else if (e.ClickedItem.Text == "Remove") { - await ShowRemoveScriptDialogAsync(script); + ShowRemoveScriptDialog(script); } } private void ListenKeysService_KeyUpClicked(KeyPressed keyPressed) @@ -93,7 +111,7 @@ private void ListenKeysService_KeyUpClicked(KeyPressed keyPressed) #endregion #region Private Methods - private async Task CheckIfScriptsNeedToRunOnLoad() + private async Task CheckIfScriptsNeedToRunOnLoadAsync() { foreach (var script in GetRunningScriptsByType()) await _runScriptService!.RunScriptAsync(script); @@ -101,10 +119,10 @@ private async Task CheckIfScriptsNeedToRunOnLoad() if (IsListenKeyServiceNecessary()) _listenKeysService.Run(); } - private static bool IsListenKeyServiceNecessary(string? executingScriptId = null) + private bool IsListenKeyServiceNecessary(string? executingScriptId = null) => GetRunningScriptsByType()?.Any(s => s.Id != executingScriptId) ?? false; - private static IEnumerable GetRunningScriptsByType() - where T : ScriptAbs => SettingsManager.Scripts.OfType().Where(s => s.ScriptStatus == ScriptStatus.Running); + private IEnumerable GetRunningScriptsByType() + where T : ScriptAbs => _scripts.OfType().Where(s => s.ScriptStatus == ScriptStatus.Running); private void ChangeScriptStatusThreadSafe(ScriptAbs script) => this.Invoke((MethodInvoker) async delegate () { await ChangeScriptStatusAsync(script); }); private async Task ShowEditScriptFormAsync(ScriptAbs script) @@ -114,7 +132,7 @@ await ShowScriptForm(script, async (ScriptAbs? editedScript) => { return; Log.Debug("Editing Script {@ScriptName} ({@ScriptType})", editedScript.ScriptName, editedScript.ScriptType); bool rescheduleJob = ScriptAbs.HasScriptTypeChanged(script.ScriptType, editedScript.ScriptType) || ScriptAbs.HasScheduledTimeChanged(script, editedScript); - await SettingsManager.EditScriptAsync(editedScript); + UpdateScript(editedScript); if (editedScript.ScriptStatus == ScriptStatus.Running && rescheduleJob) { await _runScriptService!.StopScriptAsync(editedScript); @@ -129,23 +147,24 @@ await ShowScriptForm(script, async (ScriptAbs? editedScript) => { } private async Task ShowAddScriptForm() { - await ShowScriptForm(null, async (ScriptAbs? addedScript) => { + await ShowScriptForm(null, (ScriptAbs? addedScript) => { if (addedScript == null) - return; + return Task.CompletedTask; Log.Debug("Adding Script {@ScriptName} ({@ScriptType})", addedScript.ScriptName, addedScript.ScriptType); addedScript.Id = Guid.NewGuid().ToString(); - await SettingsManager.AddScriptAsync(addedScript); + AddScript(addedScript); _scriptListAdapter.AddItem(addedScript); ScriptAdded?.Invoke(addedScript); + return Task.CompletedTask; }); } - private async Task ShowRemoveScriptDialogAsync(ScriptAbs script) + private void ShowRemoveScriptDialog(ScriptAbs script) { var dialog = new MaterialDialog(this.ParentForm, "Remove", "Do you want to remove the script?", "Yes", true, "No"); if (dialog.ShowDialog(this) == DialogResult.OK) { Log.Debug("Removing ScriptId {@ScriptId}", script.Id); - await SettingsManager.RemoveScriptAsync(script.Id); + DeleteScript(script.Id); _scriptListAdapter.RemoveItem(script.Id); ScriptRemoved?.Invoke(script); } @@ -182,7 +201,7 @@ private async Task ChangeScriptStatusAsync(ScriptAbs? script) await _runScriptService!.StopScriptAsync(script); } - await SettingsManager.EditScriptAsync(script); + UpdateScript(script); _scriptListAdapter.RefreshScriptStatus(script.Id, script.ScriptStatus); ScriptEdited?.Invoke(script); @@ -204,13 +223,26 @@ private static ScriptStatus GetNewScriptStatus(ScriptStatus oldStatus) } return newStatus; } - #endregion - - #region Public methods - public async void RefreshScriptStatusAsync(string scriptId) + private void AddScript(ScriptAbs addedScript) { - var script = SettingsManager.FindScriptById(scriptId); - await ChangeScriptStatusAsync(script); + _scriptsService!.AddScript(addedScript); + _scripts.Add(addedScript); + } + private void UpdateScript(ScriptAbs editedScript) + { + _scriptsService!.UpdateScript(editedScript); + int index = _scripts.FindIndex(s => s.Id == editedScript.Id); + if (index == -1) + return; + _scripts[index] = editedScript; + } + private void DeleteScript(string scriptId) + { + _scriptsService!.DeleteScript(scriptId); + var scriptToDelete = _scripts.FirstOrDefault(s => s.Id == scriptId); + if (scriptToDelete == null) + return; + _scripts.Remove(scriptToDelete); } #endregion diff --git a/Library/App/Program.cs b/Library/App/Program.cs index e900362..8246b6b 100644 --- a/Library/App/Program.cs +++ b/Library/App/Program.cs @@ -6,6 +6,7 @@ using App.Utils; using Data; using Data.Extensions; +using Data.Service.Interfaces; using JobManager.Service; using Logging; using MaterialSkin; @@ -66,7 +67,7 @@ private static void InitForm(string[] args) private static void InitServices() { ServiceProvider = ServiceCollection!.BuildServiceProvider(); - Data.Service.ISettingsService settingsService = ServiceProvider.GetRequiredService(); + ISettingsService settingsService = ServiceProvider.GetRequiredService(); Task.Run(async () => await SettingsManager.InitInstanceAsync(settingsService)) .Wait(); LogManager.InstanceLogger(SettingsManager.Settings); diff --git a/Library/CommonScripts.sln b/Library/CommonScripts.sln index ca7ad7e..d59e2c2 100644 --- a/Library/CommonScripts.sln +++ b/Library/CommonScripts.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32901.215 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Settings\Data.csproj", "{D36F28B5-E435-4D65-AC85-B360466D44FE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Data\Data.csproj", "{D36F28B5-E435-4D65-AC85-B360466D44FE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Contracts", "Contracts\Contracts.csproj", "{C2BC20B0-A72C-43DA-8C46-351ADBA4D1A2}" EndProject diff --git a/Library/Settings/Data.csproj b/Library/Data/Data.csproj similarity index 89% rename from Library/Settings/Data.csproj rename to Library/Data/Data.csproj index 9f181d7..7f3fa7e 100644 --- a/Library/Settings/Data.csproj +++ b/Library/Data/Data.csproj @@ -7,6 +7,7 @@ + diff --git a/Library/Data/DbAccessor.cs b/Library/Data/DbAccessor.cs new file mode 100644 index 0000000..44bff69 --- /dev/null +++ b/Library/Data/DbAccessor.cs @@ -0,0 +1,87 @@ +using Common; +using LiteDB; + +namespace Data +{ + public class DbAccessor : IDisposable + { + private const string DB_NAME = "CommonScripts.db"; + + private LiteDatabase? db; + private int _activeConnections = 0; + + private static DbAccessor? _instance; + private readonly static object _instanceLockObj = new(); + private readonly static object _accessorLockObj = new(); + + public static DbAccessor GetInstance() + { + if (_instance != null) + return _instance; + lock (_instanceLockObj) + { + if (_instance != null) + return _instance; + _instance = new DbAccessor(); + } + return _instance; + } + + public void RunAction(Action action) + { + try + { + InitDatabase(); + action(db!); + } + catch (Exception) + { + + throw; + } + finally + { + DecrementConnections(); + } + } + + private void InitDatabase() + { + Interlocked.Increment(ref _activeConnections); + if (db != null) + return; + lock (_accessorLockObj) + { + if (db != null) + return; + db = new LiteDatabase(GetDatabasePath()); + } + } + + private void DecrementConnections() + { + Interlocked.Decrement(ref _activeConnections); + if (_activeConnections == 0) + { + db?.Dispose(); + db = null; + } + } + + public void Dispose() + { + db?.Dispose(); + GC.SuppressFinalize(this); + } + + private static string GetDatabasePath() + { +#if DEBUG + string path = FileUtils.GetConfigDirectory(); +#else + string path = SettingsManager.Settings.Core.InstallationPath; +#endif + return Path.Combine(path, DB_NAME); + } + } +} diff --git a/Library/Settings/Extensions/SettingsManagerExtensions.cs b/Library/Data/Extensions/SettingsManagerExtensions.cs similarity index 60% rename from Library/Settings/Extensions/SettingsManagerExtensions.cs rename to Library/Data/Extensions/SettingsManagerExtensions.cs index e7d4074..52fb246 100644 --- a/Library/Settings/Extensions/SettingsManagerExtensions.cs +++ b/Library/Data/Extensions/SettingsManagerExtensions.cs @@ -1,5 +1,7 @@ using Data.Repository; +using Data.Repository.Interfaces; using Data.Service; +using Data.Service.Interfaces; using Microsoft.Extensions.DependencyInjection; namespace Data.Extensions @@ -10,7 +12,9 @@ public static IServiceCollection AddSettingServices(this IServiceCollection serv { return serviceCollection .AddScoped() - .AddScoped(); + .AddScoped() + .AddScoped() + .AddScoped(); } } } diff --git a/Library/Data/Repository/Interfaces/IScriptsRepository.cs b/Library/Data/Repository/Interfaces/IScriptsRepository.cs new file mode 100644 index 0000000..93fb7fe --- /dev/null +++ b/Library/Data/Repository/Interfaces/IScriptsRepository.cs @@ -0,0 +1,12 @@ +using Contracts.Scripts.Base; + +namespace Data.Repository.Interfaces +{ + public interface IScriptsRepository + { + List GetScripts(); + void DeleteScript(string scriptId); + void UpdateScript(ScriptAbs script); + void AddScript(ScriptAbs script); + } +} diff --git a/Library/Data/Repository/Interfaces/ISettingsRepository.cs b/Library/Data/Repository/Interfaces/ISettingsRepository.cs new file mode 100644 index 0000000..9e4d90d --- /dev/null +++ b/Library/Data/Repository/Interfaces/ISettingsRepository.cs @@ -0,0 +1,10 @@ +using Contracts.Config; + +namespace Data.Repository.Interfaces +{ + public interface ISettingsRepository + { + Task ReadSettingsAsync(); + Task UpdateSettingsAsync(object file); + } +} diff --git a/Library/Data/Repository/ScriptsRepository.cs b/Library/Data/Repository/ScriptsRepository.cs new file mode 100644 index 0000000..756558e --- /dev/null +++ b/Library/Data/Repository/ScriptsRepository.cs @@ -0,0 +1,48 @@ +using Contracts.Scripts.Base; +using Data.Repository.Interfaces; +using LiteDB; + +namespace Data.Repository +{ + public class ScriptsRepository : IScriptsRepository + { + public void DeleteScript(string scriptId) + { + RunAction(scriptsCollection => + { + scriptsCollection.Delete(new BsonValue(scriptId)); + }); + } + + public List GetScripts() + { + List scripts = new(); + + RunAction(scriptsCollection => + { + scripts = scriptsCollection.FindAll().ToList(); + }); + + return scripts; + } + + public void UpdateScript(ScriptAbs script) + { + RunAction(scriptsCollection => + { + scriptsCollection.Update(script); + }); + } + + public void AddScript(ScriptAbs script) + { + RunAction(scriptsCollection => + { + scriptsCollection.Insert(script); + }); + } + + private static void RunAction(Action> action) + => DbAccessor.GetInstance().RunAction(db => action(db.GetCollection("scripts"))); + } +} diff --git a/Library/Data/Repository/SettingsRepository.cs b/Library/Data/Repository/SettingsRepository.cs new file mode 100644 index 0000000..f5f9abb --- /dev/null +++ b/Library/Data/Repository/SettingsRepository.cs @@ -0,0 +1,55 @@ +using Common; +using Contracts.Config; +using Data.Repository.Interfaces; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Data.Repository +{ + public class SettingsRepository : ISettingsRepository + { + private const string SettingsFileName = "settings.json"; + private readonly JsonSerializerOptions _options; + + public SettingsRepository() + { + _options = new JsonSerializerOptions + { + Converters = { new JsonStringEnumConverter() }, + WriteIndented = true + }; + } + + public async Task ReadSettingsAsync() + { + Settings fileContent; + string settingsPath = GetSettingsPath(); + + if (!File.Exists(settingsPath)) + { + fileContent = new(); + CreateDirectoryIfNotExists(settingsPath); + await UpdateSettingsAsync(fileContent); + } + else + fileContent = JsonSerializer.Deserialize(await File.ReadAllTextAsync(settingsPath), _options)!; + + return fileContent; + } + + public async Task UpdateSettingsAsync(object file) + => await File.WriteAllTextAsync(GetSettingsPath(), JsonSerializer.Serialize(file, _options)); + + private static string GetSettingsPath() + => Path.Combine(FileUtils.GetConfigDirectory(), SettingsFileName); + + private static void CreateDirectoryIfNotExists(string filePath) + { + string? directoryPath = Path.GetDirectoryName(filePath); + if (directoryPath == null) + throw new ArgumentNullException(directoryPath); + if (!Directory.Exists(directoryPath)) + Directory.CreateDirectory(directoryPath); + } + } +} diff --git a/Library/Data/Service/Interfaces/IScriptsService.cs b/Library/Data/Service/Interfaces/IScriptsService.cs new file mode 100644 index 0000000..8f8ba5a --- /dev/null +++ b/Library/Data/Service/Interfaces/IScriptsService.cs @@ -0,0 +1,12 @@ +using Contracts.Scripts.Base; + +namespace Data.Service.Interfaces +{ + public interface IScriptsService + { + List GetScripts(); + void DeleteScript(string scriptId); + void UpdateScript(ScriptAbs script); + void AddScript(ScriptAbs script); + } +} diff --git a/Library/Settings/Service/ISettingsService.cs b/Library/Data/Service/Interfaces/ISettingsService.cs similarity index 52% rename from Library/Settings/Service/ISettingsService.cs rename to Library/Data/Service/Interfaces/ISettingsService.cs index d7dcebc..59f1a50 100644 --- a/Library/Settings/Service/ISettingsService.cs +++ b/Library/Data/Service/Interfaces/ISettingsService.cs @@ -1,13 +1,10 @@ using Contracts.Config; -using Contracts.Scripts.Base; -namespace Data.Service +namespace Data.Service.Interfaces { public interface ISettingsService { Task ReadSettingsAsync(); Task UpdateSettingsAsync(Settings settings); - Task> ReadScriptsAsync(); - Task UpdateScriptsAsync(List scripts); } } diff --git a/Library/Data/Service/ScriptsService.cs b/Library/Data/Service/ScriptsService.cs new file mode 100644 index 0000000..607572a --- /dev/null +++ b/Library/Data/Service/ScriptsService.cs @@ -0,0 +1,28 @@ +using Contracts.Scripts.Base; +using Data.Repository.Interfaces; +using Data.Service.Interfaces; + +namespace Data.Service +{ + public class ScriptsService : IScriptsService + { + private readonly IScriptsRepository _scriptsRepository; + + public ScriptsService(IScriptsRepository scriptsRepository) + { + _scriptsRepository = scriptsRepository; + } + + public void AddScript(ScriptAbs script) + => _scriptsRepository.AddScript(script); + + public void DeleteScript(string scriptId) + => _scriptsRepository.DeleteScript(scriptId); + + public List GetScripts() + => _scriptsRepository.GetScripts(); + + public void UpdateScript(ScriptAbs script) + => _scriptsRepository.UpdateScript(script); + } +} diff --git a/Library/Data/Service/SettingsService.cs b/Library/Data/Service/SettingsService.cs new file mode 100644 index 0000000..6689a8e --- /dev/null +++ b/Library/Data/Service/SettingsService.cs @@ -0,0 +1,22 @@ +using Contracts.Config; +using Data.Repository.Interfaces; +using Data.Service.Interfaces; + +namespace Data.Service +{ + public class SettingsService : ISettingsService + { + private readonly ISettingsRepository _fileRepository; + + public SettingsService(ISettingsRepository fileRepository) + { + _fileRepository = fileRepository; + } + + public async Task ReadSettingsAsync() + => await _fileRepository.ReadSettingsAsync(); + + public async Task UpdateSettingsAsync(Settings settings) + => await _fileRepository.UpdateSettingsAsync(settings); + } +} diff --git a/Library/Data/SettingsManager.cs b/Library/Data/SettingsManager.cs new file mode 100644 index 0000000..abea609 --- /dev/null +++ b/Library/Data/SettingsManager.cs @@ -0,0 +1,59 @@ +using Contracts.Config; +using Data.Service.Interfaces; + +namespace Data +{ + public class SettingsManager + { + private static SettingsManager? _instance; + + private readonly ISettingsService _settingsService; + private Settings? _settings; + + private SettingsManager(ISettingsService settingsService) + { + _settingsService = settingsService; + } + + public static Settings Settings + => GetInstance().GetSettings(); + + public static Settings CloneSettings + => (Settings)GetInstance().GetSettings().Clone(); + + public static async Task InitInstanceAsync(ISettingsService settingsService) + { + _instance = new(settingsService); + await _instance.InitConfigFilesAsync(); + } + + public static async Task UpdateSettingsAsync(Settings newSettings) + => await GetInstance().UpdateSettingsFileAsync(newSettings); + + public static async Task UpdateSettingsAsync(Action action) + { + Settings settings = CloneSettings; + action(settings); + await UpdateSettingsAsync(settings); + } + + private static SettingsManager GetInstance() + => _instance ?? throw new ArgumentNullException("Instance is not initiated. Call InitInstance() method first"); + + private async Task InitConfigFilesAsync() + { + _settings = await _settingsService.ReadSettingsAsync(); + } + + private Settings GetSettings() + => _settings!; + + private async Task UpdateSettingsFileAsync(Settings newSettings) + { + if (newSettings.Equals(_settings)) + return; + await _settingsService.UpdateSettingsAsync(newSettings); + _settings = newSettings; + } + } +} diff --git a/Library/JobManager/Job/RunScriptJob.cs b/Library/JobManager/Job/RunScriptJob.cs index e98611a..7fa87ca 100644 --- a/Library/JobManager/Job/RunScriptJob.cs +++ b/Library/JobManager/Job/RunScriptJob.cs @@ -28,13 +28,19 @@ private Task RunScript() if (File.Exists(realPath)) { Log.Information("Executing {@ScriptName}", _script.ScriptName); - string psScript = $"Set-Location \"{realPath}\"{Environment.NewLine}"; + string psScript = $"Set-Location \"{Path.GetDirectoryName(realPath)}\"{Environment.NewLine}"; psScript += File.ReadAllText(realPath); Log.Verbose("Script content: {ScriptContent}", psScript); using var powerShell = PowerShell.Create(); - powerShell + string output = ""; + var scriptOutput = powerShell .AddScript(psScript) .Invoke(); + foreach (var line in scriptOutput) + output += line + Environment.NewLine; + if (string.IsNullOrWhiteSpace(output)) + output = "(no output)"; + Log.Debug("Script output: {Output}", output.TrimEnd()); } else { diff --git a/Library/Settings/Converter/ScriptJsonConverter.cs b/Library/Settings/Converter/ScriptJsonConverter.cs deleted file mode 100644 index 7905692..0000000 --- a/Library/Settings/Converter/ScriptJsonConverter.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Contracts.Scripts; -using Contracts.Scripts.Base; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Data.Converter -{ - public class ScriptJsonConverter : JsonConverter - { - private enum TypeDiscriminator - { - BaseClass = 0, - Scheduled = 1, - OneOff = 2, - ListenKey = 3 - } - - private readonly JsonSerializerOptions _converterOptions = new() { Converters = { new JsonStringEnumConverter() } }; - - public override bool CanConvert(Type type) - { - return typeof(ScriptAbs).IsAssignableFrom(type); - } - - public override ScriptAbs? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException(); - } - - TypeDiscriminator typeDiscriminator = GetTypeDiscriminator(ref reader); - ScriptAbs baseClass = typeDiscriminator switch - { - TypeDiscriminator.Scheduled => GetScript(ref reader), - TypeDiscriminator.ListenKey => GetScript(ref reader), - TypeDiscriminator.OneOff => GetScript(ref reader), - _ => throw new NotSupportedException(), - }; - if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject) - { - throw new JsonException(); - } - - return baseClass; - } - - public override void Write(Utf8JsonWriter writer, ScriptAbs value, JsonSerializerOptions options) - { - writer.WriteStartObject(); - - if (value is ScriptOneOff derivedA) - { - writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.OneOff); - writer.WritePropertyName("TypeValue"); - JsonSerializer.Serialize(writer, derivedA, _converterOptions); - } - else if (value is ScriptListenKey derivedB) - { - writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.ListenKey); - writer.WritePropertyName("TypeValue"); - JsonSerializer.Serialize(writer, derivedB, _converterOptions); - } - else if (value is ScriptScheduled derivedC) - { - writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.Scheduled); - writer.WritePropertyName("TypeValue"); - JsonSerializer.Serialize(writer, derivedC, _converterOptions); - } - else - { - throw new NotSupportedException(); - } - - writer.WriteEndObject(); - } - - private static TypeDiscriminator GetTypeDiscriminator(ref Utf8JsonReader reader) - { - if (!reader.Read() - || reader.TokenType != JsonTokenType.PropertyName - || reader.GetString() != "TypeDiscriminator") - { - throw new JsonException(); - } - - if (!reader.Read() || reader.TokenType != JsonTokenType.Number) - { - throw new JsonException(); - } - - return (TypeDiscriminator)reader.GetInt32(); - } - - private T GetScript(ref Utf8JsonReader reader) - { - if (!reader.Read() || reader.GetString() != "TypeValue") - { - throw new JsonException(); - } - if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException(); - } - - return (T)JsonSerializer.Deserialize(ref reader, typeof(T), _converterOptions)!; - } - } -} diff --git a/Library/Settings/Repository/FileRepository.cs b/Library/Settings/Repository/FileRepository.cs deleted file mode 100644 index c26c88a..0000000 --- a/Library/Settings/Repository/FileRepository.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Data.Converter; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Data.Repository -{ - public class FileRepository : IFileRepository - { - private JsonSerializerOptions _options; - public FileRepository() - { - _options = new JsonSerializerOptions - { - Converters = { new ScriptJsonConverter(), new JsonStringEnumConverter() }, - WriteIndented = true - }; - } - public bool FileExists(string path) - => File.Exists(path); - - public async Task GetFileAsync(string path) - => JsonSerializer.Deserialize(await File.ReadAllTextAsync(path), _options)!; - - public async Task UpdateFileAsync(object file, string path) - => await File.WriteAllTextAsync(path, JsonSerializer.Serialize(file, _options)); - } -} diff --git a/Library/Settings/Repository/IFileRepository.cs b/Library/Settings/Repository/IFileRepository.cs deleted file mode 100644 index e514a9b..0000000 --- a/Library/Settings/Repository/IFileRepository.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Data.Repository -{ - public interface IFileRepository - { - bool FileExists(string path); - Task GetFileAsync(string path); - Task UpdateFileAsync(object file, string path); - } -} diff --git a/Library/Settings/Service/SettingsService.cs b/Library/Settings/Service/SettingsService.cs deleted file mode 100644 index 123a6f0..0000000 --- a/Library/Settings/Service/SettingsService.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Common; -using Contracts.Config; -using Contracts.Scripts.Base; -using Data.Repository; - -namespace Data.Service -{ - public class SettingsService : ISettingsService - { - private readonly IFileRepository _fileRepository; - private const string SettingsFileName = "settings.json"; - private const string ScriptsFileName = "scripts.json"; - - public SettingsService(IFileRepository fileRepository) - { - _fileRepository = fileRepository; - } - - public async Task> ReadScriptsAsync() - => await ReadFileAsync< List>(GetScriptsPath()); - - public async Task ReadSettingsAsync() - => await ReadFileAsync(GetConfigPath()); - - public async Task UpdateScriptsAsync(List scripts) - => await _fileRepository.UpdateFileAsync(scripts, GetScriptsPath()); - - public async Task UpdateSettingsAsync(Settings settings) - => await _fileRepository.UpdateFileAsync(settings, GetConfigPath()); - - private async Task ReadFileAsync(string filePath) - where T : new() - { - T fileContent; - - if (!_fileRepository.FileExists(filePath)) - { - fileContent = new(); - CreateDirectoryIfNotExists(filePath); - await _fileRepository.UpdateFileAsync(fileContent, filePath); - } - else - fileContent = await _fileRepository.GetFileAsync(filePath); - - return fileContent; - } - - private static void CreateDirectoryIfNotExists(string filePath) - { - string? directoryPath = Path.GetDirectoryName(filePath); - if (directoryPath == null) - throw new ArgumentNullException(directoryPath); - if (!Directory.Exists(directoryPath)) - Directory.CreateDirectory(directoryPath); - } - - private static string GetFilePath(string configFilename) - => Path.Combine(FileUtils.GetConfigDirectory(), configFilename); - - private static string GetConfigPath() - => GetFilePath(SettingsFileName); - - private static string GetScriptsPath() - => GetFilePath(ScriptsFileName); - } -} diff --git a/Library/Settings/SettingsManager.cs b/Library/Settings/SettingsManager.cs deleted file mode 100644 index f65a8d9..0000000 --- a/Library/Settings/SettingsManager.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Contracts.Config; -using Contracts.Scripts.Base; -using Data.Service; - -namespace Data -{ - public class SettingsManager - { - private static SettingsManager? _instance; - - private readonly ISettingsService _settingsService; - private Settings? _settings; - private List? _scripts; - - private SettingsManager(ISettingsService settingsService) - { - _settingsService = settingsService; - } - - public static Settings Settings - => GetInstance().GetSettings(); - - public static Settings CloneSettings - => (Settings)GetInstance().GetSettings().Clone(); - - public static List Scripts - => GetInstance().GetScripts(); - - public static ScriptAbs? FindScriptById(string scriptId) - => GetInstance().GetScripts().FirstOrDefault(s => s.Id == scriptId)?.Clone(); - - public static async Task InitInstanceAsync(ISettingsService settingsService) - { - _instance = new(settingsService); - await _instance.InitConfigFilesAsync(); - } - - public static async Task UpdateSettingsAsync(Settings newSettings) - => await GetInstance().UpdateSettingsFileAsync(newSettings); - - public static async Task UpdateSettingsAsync(Action action) - { - Settings settings = CloneSettings; - action(settings); - await UpdateSettingsAsync(settings); - } - - public static async Task UpdateScriptsAsync(List newScripts) - => await GetInstance().UpdateScriptsFileAsync(newScripts); - - public static async Task AddScriptAsync(ScriptAbs newScript) - => await UpdateScriptsAsync(scripts => scripts.Add(newScript)); - - public static async Task EditScriptAsync(ScriptAbs scriptToEdit) - { - int index = Scripts.FindIndex(s => s.Id == scriptToEdit.Id); - if (index == -1) - return; - await UpdateScriptsAsync(scripts => scripts[index] = scriptToEdit); - } - - public static async Task RemoveScriptAsync(string scriptId) - { - var scriptToDelete = Scripts.FirstOrDefault(s => s.Id == scriptId); - if (scriptToDelete == null) - return; - await UpdateScriptsAsync(scripts => scripts.Remove(scriptToDelete)); - } - - public static async Task UpdateScriptsAsync(Action> action) - { - List scripts = GetInstance().GetScripts(); - action(scripts); - await UpdateScriptsAsync(scripts); - } - - private static SettingsManager GetInstance() - => _instance ?? throw new ArgumentNullException("Instance is not initiated. Call InitInstance() method first"); - - private async Task InitConfigFilesAsync() - { - _settings = await _settingsService.ReadSettingsAsync(); - _scripts = await _settingsService.ReadScriptsAsync(); - } - - private Settings GetSettings() - => _settings!; - - private List GetScripts() - => _scripts!; - - private async Task UpdateSettingsFileAsync(Settings newSettings) - { - if (newSettings.Equals(_settings)) - return; - await _settingsService.UpdateSettingsAsync(newSettings); - _settings = newSettings; - } - private async Task UpdateScriptsFileAsync(List newScripts) - { - await _settingsService.UpdateScriptsAsync(newScripts); - _scripts = newScripts; - } - } -}