1
0
mirror of https://github.com/akpaevj/onecmonitor.git synced 2025-03-03 14:42:18 +02:00
onecmonitor/onecmonitor-server/Controllers/TechLogSeancesController.cs
2023-01-27 20:58:44 +03:00

293 lines
13 KiB
C#

using Google.Protobuf.WellKnownTypes;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.CodeAnalysis.Host;
using Microsoft.EntityFrameworkCore;
using OnecMonitor.Server.Models;
using OnecMonitor.Server.Services;
using OnecMonitor.Server.ViewModels.Log;
using OnecMonitor.Server.ViewModels.TechLogSeances;
using System.Threading;
namespace OnecMonitor.Server.Controllers
{
public class TechLogSeancesController : Controller
{
private readonly AppDbContext _dbContext;
private readonly AgentsConnectionsManager _connectionsManager;
private readonly IClickHouseContext _clickHouseContext;
private readonly TechLogAnalyzer _analyzerService;
private readonly ILogger<TechLogSeancesController> _logger;
public TechLogSeancesController(
AppDbContext dbContext,
AgentsConnectionsManager connectionsManager,
IClickHouseContext clickHouseContext,
TechLogAnalyzer analyzerService,
ILogger<TechLogSeancesController> logger)
{
_dbContext = dbContext;
_connectionsManager = connectionsManager;
_clickHouseContext = clickHouseContext;
_analyzerService = analyzerService;
_logger = logger;
}
public async Task<IActionResult> Index(int pageNumber = 1, CancellationToken cancellationToken = default)
{
var viewModel = new TechLogSeancesIndexViewModel
{
CurrentPage = pageNumber,
};
var items = await _dbContext.TechLogSeances
.AsNoTracking()
.OrderBy(c => c.StartDateTime)
.Skip((viewModel.CurrentPage - 1) * viewModel.PageSize)
.Take(viewModel.PageSize)
.ToListAsync(cancellationToken);
viewModel.Seances = items.Select(c => new TechLogSeancesListItemViewModel()
{
Id = c.Id,
Description = c.Description,
StartMode = c.StartMode,
StartDateTime= c.StartDateTime,
Duration = c.Duration
}).ToList();
var eventsCount = await _dbContext.TechLogSeances.CountAsync(cancellationToken);
var pagesCount = eventsCount / viewModel.PageSize;
if (eventsCount % viewModel.PageSize != 0)
pagesCount++;
viewModel.PagesCount = pagesCount;
return View(viewModel);
}
public async Task<IActionResult> Edit(Guid id, CancellationToken cancellationToken)
=> View(await GetViewModel(id, cancellationToken));
[HttpPost]
public async Task<IActionResult> Edit(TechLogSeanceEditViewModel viewModel, Guid[] connectedAgents, Guid[] connectedTemplates, CancellationToken cancellationToken)
{
ModelState[nameof(TechLogSeanceEditViewModel.StartDateTime)]?.Errors.Clear();
viewModel.StartDateTime = viewModel.StartMode switch
{
TechLogSeanceStartMode.Monitor => DateTime.MinValue,
TechLogSeanceStartMode.Immediately => DateTime.UtcNow,
_ => viewModel.StartDateTime
};
viewModel.StartDateTime = DateTime.SpecifyKind(viewModel.StartDateTime, DateTimeKind.Utc);
viewModel.Duration = viewModel.StartMode switch
{
TechLogSeanceStartMode.Monitor => 0,
_ => viewModel.Duration
};
var newConnectedAgents = await _dbContext.Agents
.Where(c => connectedAgents
.Contains(c.Id))
.ToListAsync(cancellationToken);
var newConnectedTemplates = await _dbContext.LogTemplates
.Where(c => connectedTemplates
.Contains(c.Id))
.ToListAsync(cancellationToken);
if ((viewModel.StartMode == TechLogSeanceStartMode.Immediately || viewModel.StartMode == TechLogSeanceStartMode.Scheduled) && viewModel.Duration <= 0)
ModelState.AddModelError(nameof(viewModel.Duration), "Duration cannot be less than 1 minute");
if (viewModel.StartMode == TechLogSeanceStartMode.Scheduled && viewModel.StartDateTime < DateTime.UtcNow)
ModelState.AddModelError(nameof(viewModel.StartDateTime), "Scheduled seance must happend in the future");
if (viewModel.StartMode != TechLogSeanceStartMode.Monitor && newConnectedAgents.Count == 0)
ModelState.AddModelError(nameof(viewModel.ConnectedAgents), "It doesn't make sense to start seance without connected agents=)");
if (newConnectedTemplates.Count == 0)
ModelState.AddModelError(nameof(viewModel.ConnectedTemplates), "You must connect templates");
if (!ModelState.IsValid)
return View(await GetViewModel(viewModel, cancellationToken));
var affectedAgents = new List<Agent>();
if (viewModel.Id == Guid.Empty)
{
var item = new TechLogSeance()
{
Id = Guid.NewGuid(),
Description = viewModel.Description,
StartMode = viewModel.StartMode,
StartDateTime = viewModel.StartDateTime,
Duration = viewModel.Duration,
ConnectedTemplates = newConnectedTemplates,
ConnectedAgents = newConnectedAgents
};
affectedAgents.AddRange(item.ConnectedAgents);
await _dbContext.AddAsync(item, cancellationToken);
}
else
{
var item = await _dbContext.TechLogSeances
.Include(c => c.ConnectedTemplates)
.Include(c => c.ConnectedAgents)
.SingleAsync(c => c.Id == viewModel.Id, cancellationToken);
item.Description = viewModel.Description;
item.StartMode = viewModel.StartMode;
item.StartDateTime = viewModel.StartDateTime;
item.Duration = viewModel.Duration;
// add new items
newConnectedTemplates.Where(c => !item.ConnectedTemplates.Contains(c)).ToList().ForEach(item.ConnectedTemplates.Add);
// remove deleted items
item.ConnectedTemplates.Where(c => !newConnectedTemplates.Contains(c)).ToList().ForEach(c => item.ConnectedTemplates.Remove(c));
// add new items
newConnectedAgents.Where(c => !item.ConnectedAgents.Contains(c)).ToList().ForEach(item.ConnectedAgents.Add);
// fix affected agents to send notify
affectedAgents.AddRange(item.ConnectedAgents);
// remove deleted items
item.ConnectedAgents.Where(c => !newConnectedAgents.Contains(c)).ToList().ForEach(c => item.ConnectedAgents.Remove(c));
}
await _dbContext.SaveChangesAsync(cancellationToken);
await _connectionsManager.UpdateTechLogSeances(affectedAgents, cancellationToken);
return Redirect("/TechLogSeances");
}
public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
{
await _dbContext.Database.BeginTransactionAsync(cancellationToken);
try
{
var item = await _dbContext.TechLogSeances.Include(c => c.ConnectedAgents).AsNoTracking().FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
if (item == null)
throw new Exception($"Failed to find item with id {id}");
_dbContext.Entry(item).State = EntityState.Deleted;
await _clickHouseContext.DeleteTechLogSeanceData(id.ToString(), cancellationToken);
await _dbContext.Database.CommitTransactionAsync(cancellationToken);
await _dbContext.SaveChangesAsync(cancellationToken);
await _connectionsManager.UpdateTechLogSeances(item.ConnectedAgents, cancellationToken);
return Redirect("/TechLogSeances");
}
catch
{
await _dbContext.Database.RollbackTransactionAsync(cancellationToken);
throw;
}
}
public async Task<IActionResult> TechLog(Guid id, int pageNumber = 1, [FromQuery] string filter = "", CancellationToken cancellationToken = default)
{
var viewModel = new TechLogViewModel
{
SeanceId = id,
CurrentPage = pageNumber,
Filter = filter,
TechLogFilters = await _dbContext.TechLogFilters.ToListAsync(cancellationToken)
};
filter = filter.Trim();
if (string.IsNullOrEmpty(filter))
filter = $"_seance_id = toUUID('{id}')";
else
filter = $"_seance_id = toUUID('{id}') and {filter}";
var items = await _clickHouseContext.GetTjEvents(viewModel.PageSize, (viewModel.CurrentPage - 1) * viewModel.PageSize, filter, cancellationToken);
viewModel.TjEvents = items.Select(c => new TechLogListItemViewModel(c)).ToList();
var eventsCount = await _clickHouseContext.GetTjEventsCount(filter, cancellationToken);
var pagesCount = eventsCount / viewModel.PageSize;
if (eventsCount % viewModel.PageSize != 0)
pagesCount++;
viewModel.PagesCount = pagesCount;
return View(viewModel);
}
public async Task<IActionResult> CallTimeline(Guid id, CancellationToken cancellationToken)
{
var chain = await _analyzerService.GetCallEventsChain(id, cancellationToken);
return View(new CallChainViewModel { Chain = chain });
}
public async Task<IActionResult> LockWaitingTimeline(Guid id, CancellationToken cancellationToken)
{
var graph = await _analyzerService.GetLockWaitingGraph(id, cancellationToken);
return View(new LockWaitingTimelineViewModel { Graph = graph });
}
public async Task<IActionResult> LockWaitingGraph(Guid id, CancellationToken cancellationToken)
{
var graph = await _analyzerService.GetLockWaitingGraph(id, cancellationToken);
return View(new LockWaitingGraphViewModel { Graph = graph });
}
private async Task<TechLogSeanceEditViewModel> GetViewModel(Guid id, CancellationToken cancellationToken)
{
var viewModel = new TechLogSeanceEditViewModel();
if (id != Guid.Empty)
{
var item = await _dbContext.TechLogSeances
.AsNoTracking()
.Include(c => c.ConnectedTemplates)
.Include(c => c.ConnectedAgents)
.SingleAsync(c => c.Id == id, cancellationToken);
viewModel.Id = item!.Id;
viewModel.Description = item.Description;
viewModel.StartMode = item.StartMode;
viewModel.StartDateTime = item.StartDateTime;
viewModel.Duration = item.Duration;
viewModel.ConnectedTemplates = item.ConnectedTemplates.Select(c => (c.Id, c.Name)).ToList();
viewModel.ConnectedAgents = item.ConnectedAgents.Select(c => (c.Id, c.InstanceName)).ToList();
}
else
{
viewModel.StartMode = TechLogSeanceStartMode.Immediately;
viewModel.StartDateTime = DateTime.UtcNow;
viewModel.Duration = 6;
viewModel.ConnectedAgents = new List<(Guid Id, string Name)>();
viewModel.ConnectedTemplates = new List<(Guid Id, string Name)>();
}
return await GetViewModel(viewModel, cancellationToken);
}
private async Task<TechLogSeanceEditViewModel> GetViewModel(TechLogSeanceEditViewModel viewModel, CancellationToken cancellationToken)
{
viewModel.AllAgents = new SelectList(await _dbContext.Agents.OrderBy(c => c.InstanceName)
.ToListAsync(cancellationToken), nameof(Agent.Id), nameof(Agent.InstanceName));
viewModel.AllTemplates = new SelectList(await _dbContext.LogTemplates.OrderBy(c => c.Name)
.ToListAsync(cancellationToken), nameof(LogTemplate.Id), nameof(LogTemplate.Name));
return viewModel;
}
}
}