feat: add external ticket storage for Google Sheet items
This commit is contained in:
@@ -7,15 +7,12 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Disco.Plugins.ServiceTracker.Services
|
namespace Disco.Plugins.ServiceTracker.Services
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// JSON file-based data store for ServiceTracker metadata.
|
|
||||||
/// Stores ticket extensions and configuration in the plugin data directory.
|
|
||||||
/// </summary>
|
|
||||||
public class ServiceTrackerDataStore
|
public class ServiceTrackerDataStore
|
||||||
{
|
{
|
||||||
private readonly string _dataDirectory;
|
private readonly string _dataDirectory;
|
||||||
private readonly string _ticketsPath;
|
private readonly string _ticketsPath;
|
||||||
private readonly string _configPath;
|
private readonly string _configPath;
|
||||||
|
private readonly string _externalPath;
|
||||||
private static readonly object _lock = new object();
|
private static readonly object _lock = new object();
|
||||||
|
|
||||||
public ServiceTrackerDataStore(string pluginDataDirectory)
|
public ServiceTrackerDataStore(string pluginDataDirectory)
|
||||||
@@ -23,11 +20,12 @@ namespace Disco.Plugins.ServiceTracker.Services
|
|||||||
_dataDirectory = pluginDataDirectory;
|
_dataDirectory = pluginDataDirectory;
|
||||||
_ticketsPath = Path.Combine(_dataDirectory, "tickets.json");
|
_ticketsPath = Path.Combine(_dataDirectory, "tickets.json");
|
||||||
_configPath = Path.Combine(_dataDirectory, "config.json");
|
_configPath = Path.Combine(_dataDirectory, "config.json");
|
||||||
|
_externalPath = Path.Combine(_dataDirectory, "external_tickets.json");
|
||||||
if (!Directory.Exists(_dataDirectory))
|
if (!Directory.Exists(_dataDirectory)) Directory.CreateDirectory(_dataDirectory);
|
||||||
Directory.CreateDirectory(_dataDirectory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string DataDirectory { get { return _dataDirectory; } }
|
||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
|
|
||||||
public ServiceTrackerConfig LoadConfig()
|
public ServiceTrackerConfig LoadConfig()
|
||||||
@@ -36,12 +34,19 @@ namespace Disco.Plugins.ServiceTracker.Services
|
|||||||
{
|
{
|
||||||
if (!File.Exists(_configPath))
|
if (!File.Exists(_configPath))
|
||||||
{
|
{
|
||||||
var defaultConfig = ServiceTrackerConfig.CreateDefault();
|
var def = ServiceTrackerConfig.CreateDefault();
|
||||||
SaveConfig(defaultConfig);
|
SaveConfig(def);
|
||||||
return defaultConfig;
|
return def;
|
||||||
}
|
}
|
||||||
var json = File.ReadAllText(_configPath);
|
var json = File.ReadAllText(_configPath);
|
||||||
return JsonConvert.DeserializeObject<ServiceTrackerConfig>(json) ?? ServiceTrackerConfig.CreateDefault();
|
var config = JsonConvert.DeserializeObject<ServiceTrackerConfig>(json);
|
||||||
|
if (config == null) return ServiceTrackerConfig.CreateDefault();
|
||||||
|
// Ensure new fields have defaults
|
||||||
|
if (config.Technicians == null) config.Technicians = new List<TechEntry>();
|
||||||
|
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<ServiceTicket> LoadAllTickets()
|
public List<ServiceTicket> LoadAllTickets()
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (!File.Exists(_ticketsPath))
|
if (!File.Exists(_ticketsPath)) return new List<ServiceTicket>();
|
||||||
return new List<ServiceTicket>();
|
|
||||||
|
|
||||||
var json = File.ReadAllText(_ticketsPath);
|
var json = File.ReadAllText(_ticketsPath);
|
||||||
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
||||||
}
|
}
|
||||||
@@ -78,14 +81,9 @@ namespace Disco.Plugins.ServiceTracker.Services
|
|||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
var tickets = LoadAllTicketsUnsafe();
|
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;
|
ticket.LastModifiedDate = DateTime.Now;
|
||||||
|
if (idx >= 0) tickets[idx] = ticket; else tickets.Add(ticket);
|
||||||
if (existing >= 0)
|
|
||||||
tickets[existing] = ticket;
|
|
||||||
else
|
|
||||||
tickets.Add(ticket);
|
|
||||||
|
|
||||||
SaveAllTicketsUnsafe(tickets);
|
SaveAllTicketsUnsafe(tickets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,8 +106,7 @@ namespace Disco.Plugins.ServiceTracker.Services
|
|||||||
var ticket = tickets.FirstOrDefault(t => t.JobId == jobId);
|
var ticket = tickets.FirstOrDefault(t => t.JobId == jobId);
|
||||||
if (ticket != null)
|
if (ticket != null)
|
||||||
{
|
{
|
||||||
if (ticket.Notes == null)
|
if (ticket.Notes == null) ticket.Notes = new List<TicketNote>();
|
||||||
ticket.Notes = new List<TicketNote>();
|
|
||||||
ticket.Notes.Add(note);
|
ticket.Notes.Add(note);
|
||||||
ticket.LastModifiedDate = DateTime.Now;
|
ticket.LastModifiedDate = DateTime.Now;
|
||||||
SaveAllTicketsUnsafe(tickets);
|
SaveAllTicketsUnsafe(tickets);
|
||||||
@@ -117,19 +114,79 @@ namespace Disco.Plugins.ServiceTracker.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal methods (caller must hold lock)
|
// --- External Tickets (Google Sheet) ---
|
||||||
|
|
||||||
|
public List<ServiceTicket> LoadExternalTickets()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!File.Exists(_externalPath)) return new List<ServiceTicket>();
|
||||||
|
var json = File.ReadAllText(_externalPath);
|
||||||
|
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveExternalTickets(List<ServiceTicket> 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<TicketNote>();
|
||||||
|
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<ServiceTicket> LoadAllTicketsUnsafe()
|
private List<ServiceTicket> LoadAllTicketsUnsafe()
|
||||||
{
|
{
|
||||||
if (!File.Exists(_ticketsPath))
|
if (!File.Exists(_ticketsPath)) return new List<ServiceTicket>();
|
||||||
return new List<ServiceTicket>();
|
|
||||||
var json = File.ReadAllText(_ticketsPath);
|
var json = File.ReadAllText(_ticketsPath);
|
||||||
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveAllTicketsUnsafe(List<ServiceTicket> tickets)
|
private void SaveAllTicketsUnsafe(List<ServiceTicket> tickets)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(tickets, Formatting.Indented);
|
var json = JsonConvert.SerializeObject(tickets, Formatting.Indented);
|
||||||
File.WriteAllText(_ticketsPath, json);
|
File.WriteAllText(_ticketsPath, json);
|
||||||
}
|
}
|
||||||
|
private List<ServiceTicket> LoadExternalTicketsUnsafe()
|
||||||
|
{
|
||||||
|
if (!File.Exists(_externalPath)) return new List<ServiceTicket>();
|
||||||
|
var json = File.ReadAllText(_externalPath);
|
||||||
|
return JsonConvert.DeserializeObject<List<ServiceTicket>>(json) ?? new List<ServiceTicket>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user