diff --git a/Services/ServiceTrackerDataStore.cs b/Services/ServiceTrackerDataStore.cs index 0fb2a95..bb7a992 100644 --- a/Services/ServiceTrackerDataStore.cs +++ b/Services/ServiceTrackerDataStore.cs @@ -7,15 +7,12 @@ 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 readonly string _externalPath; private static readonly object _lock = new object(); public ServiceTrackerDataStore(string pluginDataDirectory) @@ -23,11 +20,12 @@ namespace Disco.Plugins.ServiceTracker.Services _dataDirectory = pluginDataDirectory; _ticketsPath = Path.Combine(_dataDirectory, "tickets.json"); _configPath = Path.Combine(_dataDirectory, "config.json"); - - if (!Directory.Exists(_dataDirectory)) - Directory.CreateDirectory(_dataDirectory); + _externalPath = Path.Combine(_dataDirectory, "external_tickets.json"); + if (!Directory.Exists(_dataDirectory)) Directory.CreateDirectory(_dataDirectory); } + public string DataDirectory { get { return _dataDirectory; } } + // --- Configuration --- public ServiceTrackerConfig LoadConfig() @@ -36,12 +34,19 @@ namespace Disco.Plugins.ServiceTracker.Services { if (!File.Exists(_configPath)) { - var defaultConfig = ServiceTrackerConfig.CreateDefault(); - SaveConfig(defaultConfig); - return defaultConfig; + var def = ServiceTrackerConfig.CreateDefault(); + SaveConfig(def); + return def; } var json = File.ReadAllText(_configPath); - return JsonConvert.DeserializeObject(json) ?? ServiceTrackerConfig.CreateDefault(); + var config = JsonConvert.DeserializeObject(json); + if (config == null) return ServiceTrackerConfig.CreateDefault(); + // Ensure new fields have defaults + if (config.Technicians == null) config.Technicians = new List(); + if (config.GoogleSheet == null) config.GoogleSheet = new GoogleSheetConfig(); + if (string.IsNullOrEmpty(config.DiscoBaseUrl)) config.DiscoBaseUrl = "http://disco:9292"; + if (config.DetailInactivitySeconds <= 0) config.DetailInactivitySeconds = 300; + return config; } } @@ -54,15 +59,13 @@ namespace Disco.Plugins.ServiceTracker.Services } } - // --- Tickets --- + // --- Disco Tickets --- public List LoadAllTickets() { lock (_lock) { - if (!File.Exists(_ticketsPath)) - return new List(); - + if (!File.Exists(_ticketsPath)) return new List(); var json = File.ReadAllText(_ticketsPath); return JsonConvert.DeserializeObject>(json) ?? new List(); } @@ -78,14 +81,9 @@ namespace Disco.Plugins.ServiceTracker.Services lock (_lock) { var tickets = LoadAllTicketsUnsafe(); - var existing = tickets.FindIndex(t => t.JobId == ticket.JobId); + var idx = tickets.FindIndex(t => t.JobId == ticket.JobId); ticket.LastModifiedDate = DateTime.Now; - - if (existing >= 0) - tickets[existing] = ticket; - else - tickets.Add(ticket); - + if (idx >= 0) tickets[idx] = ticket; else tickets.Add(ticket); SaveAllTicketsUnsafe(tickets); } } @@ -108,8 +106,7 @@ namespace Disco.Plugins.ServiceTracker.Services var ticket = tickets.FirstOrDefault(t => t.JobId == jobId); if (ticket != null) { - if (ticket.Notes == null) - ticket.Notes = new List(); + if (ticket.Notes == null) ticket.Notes = new List(); ticket.Notes.Add(note); ticket.LastModifiedDate = DateTime.Now; SaveAllTicketsUnsafe(tickets); @@ -117,19 +114,79 @@ namespace Disco.Plugins.ServiceTracker.Services } } - // Internal methods (caller must hold lock) + // --- External Tickets (Google Sheet) --- + + public List LoadExternalTickets() + { + lock (_lock) + { + if (!File.Exists(_externalPath)) return new List(); + var json = File.ReadAllText(_externalPath); + return JsonConvert.DeserializeObject>(json) ?? new List(); + } + } + + public void SaveExternalTickets(List tickets) + { + lock (_lock) + { + var json = JsonConvert.SerializeObject(tickets, Formatting.Indented); + File.WriteAllText(_externalPath, json); + } + } + + public ServiceTicket GetExternalTicket(int internalId) + { + return LoadExternalTickets().FirstOrDefault(t => t.JobId == internalId); + } + + public void SaveExternalTicket(ServiceTicket ticket) + { + lock (_lock) + { + var tickets = LoadExternalTicketsUnsafe(); + var idx = tickets.FindIndex(t => t.JobId == ticket.JobId); + ticket.LastModifiedDate = DateTime.Now; + if (idx >= 0) tickets[idx] = ticket; else tickets.Add(ticket); + var json = JsonConvert.SerializeObject(tickets, Formatting.Indented); + File.WriteAllText(_externalPath, json); + } + } + + public void AddExternalNote(int internalId, TicketNote note) + { + lock (_lock) + { + var tickets = LoadExternalTicketsUnsafe(); + var ticket = tickets.FirstOrDefault(t => t.JobId == internalId); + if (ticket != null) + { + if (ticket.Notes == null) ticket.Notes = new List(); + ticket.Notes.Add(note); + ticket.LastModifiedDate = DateTime.Now; + var json = JsonConvert.SerializeObject(tickets, Formatting.Indented); + File.WriteAllText(_externalPath, json); + } + } + } + + // Internal (caller must hold lock) private List LoadAllTicketsUnsafe() { - if (!File.Exists(_ticketsPath)) - return new List(); + 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); } + private List LoadExternalTicketsUnsafe() + { + if (!File.Exists(_externalPath)) return new List(); + var json = File.ReadAllText(_externalPath); + return JsonConvert.DeserializeObject>(json) ?? new List(); + } } }