diff --git a/Services/ServiceTrackerDataStore.cs b/Services/ServiceTrackerDataStore.cs new file mode 100644 index 0000000..0fb2a95 --- /dev/null +++ b/Services/ServiceTrackerDataStore.cs @@ -0,0 +1,135 @@ +using Disco.Plugins.ServiceTracker.Models; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Disco.Plugins.ServiceTracker.Services +{ + /// + /// JSON file-based data store for ServiceTracker metadata. + /// Stores ticket extensions and configuration in the plugin data directory. + /// + public class ServiceTrackerDataStore + { + private readonly string _dataDirectory; + private readonly string _ticketsPath; + private readonly string _configPath; + private static readonly object _lock = new object(); + + public ServiceTrackerDataStore(string pluginDataDirectory) + { + _dataDirectory = pluginDataDirectory; + _ticketsPath = Path.Combine(_dataDirectory, "tickets.json"); + _configPath = Path.Combine(_dataDirectory, "config.json"); + + if (!Directory.Exists(_dataDirectory)) + Directory.CreateDirectory(_dataDirectory); + } + + // --- Configuration --- + + public ServiceTrackerConfig LoadConfig() + { + lock (_lock) + { + if (!File.Exists(_configPath)) + { + var defaultConfig = ServiceTrackerConfig.CreateDefault(); + SaveConfig(defaultConfig); + return defaultConfig; + } + var json = File.ReadAllText(_configPath); + return JsonConvert.DeserializeObject(json) ?? ServiceTrackerConfig.CreateDefault(); + } + } + + public void SaveConfig(ServiceTrackerConfig config) + { + lock (_lock) + { + var json = JsonConvert.SerializeObject(config, Formatting.Indented); + File.WriteAllText(_configPath, json); + } + } + + // --- Tickets --- + + public List LoadAllTickets() + { + lock (_lock) + { + if (!File.Exists(_ticketsPath)) + return new List(); + + var json = File.ReadAllText(_ticketsPath); + return JsonConvert.DeserializeObject>(json) ?? new List(); + } + } + + public ServiceTicket GetTicket(int jobId) + { + return LoadAllTickets().FirstOrDefault(t => t.JobId == jobId); + } + + public void SaveTicket(ServiceTicket ticket) + { + lock (_lock) + { + var tickets = LoadAllTicketsUnsafe(); + var existing = tickets.FindIndex(t => t.JobId == ticket.JobId); + ticket.LastModifiedDate = DateTime.Now; + + if (existing >= 0) + tickets[existing] = ticket; + else + tickets.Add(ticket); + + SaveAllTicketsUnsafe(tickets); + } + } + + public void DeleteTicket(int jobId) + { + lock (_lock) + { + var tickets = LoadAllTicketsUnsafe(); + tickets.RemoveAll(t => t.JobId == jobId); + SaveAllTicketsUnsafe(tickets); + } + } + + public void AddNote(int jobId, TicketNote note) + { + lock (_lock) + { + var tickets = LoadAllTicketsUnsafe(); + var ticket = tickets.FirstOrDefault(t => t.JobId == jobId); + if (ticket != null) + { + if (ticket.Notes == null) + ticket.Notes = new List(); + ticket.Notes.Add(note); + ticket.LastModifiedDate = DateTime.Now; + SaveAllTicketsUnsafe(tickets); + } + } + } + + // Internal methods (caller must hold lock) + private List LoadAllTicketsUnsafe() + { + if (!File.Exists(_ticketsPath)) + return new List(); + var json = File.ReadAllText(_ticketsPath); + return JsonConvert.DeserializeObject>(json) ?? new List(); + } + + private void SaveAllTicketsUnsafe(List tickets) + { + var json = JsonConvert.SerializeObject(tickets, Formatting.Indented); + File.WriteAllText(_ticketsPath, json); + } + } +}