Update: SignalR 2.0.3 Migration; Noticeboards
Migrate all SignalR 1.x Persistent Connections to SignalR 2.x Hubs. Abstracts ScheduledTaskStatus with core interface and adds a Mock for optional status reporting. Noticeboards rewritten (with new theme) to be more resilient and accurate.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Web.Signalling;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.AspNet.SignalR.Hubs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reactive.Linq;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Data.Repository;
|
||||
|
||||
namespace Disco.Services.Devices
|
||||
{
|
||||
[HubName("deviceUpdates"), DiscoHubAuthorizeAll(Claims.Device.Show, Claims.Device.ShowAttachments)]
|
||||
public class DeviceUpdatesHub : Hub
|
||||
{
|
||||
private const string UserPrefix = "Device_";
|
||||
public static IHubContext HubContext { get; private set; }
|
||||
|
||||
private static IDisposable RepositoryBeforeSubscription;
|
||||
private static IDisposable RepositoryAfterSubscription;
|
||||
|
||||
static DeviceUpdatesHub()
|
||||
{
|
||||
HubContext = GlobalHost.ConnectionManager.GetHubContext<DeviceUpdatesHub>();
|
||||
|
||||
// Subscribe to Repository Monitor for Changes
|
||||
RepositoryBeforeSubscription = RepositoryMonitor.StreamBeforeCommit
|
||||
.Where(e => e.EntityType == typeof(DeviceAttachment) && e.EventType == RepositoryMonitorEventType.Deleted)
|
||||
.Subscribe(RepositoryEventBefore);
|
||||
RepositoryAfterSubscription = RepositoryMonitor.StreamAfterCommit
|
||||
.Where(e => e.EntityType == typeof(DeviceAttachment) && e.EventType == RepositoryMonitorEventType.Added)
|
||||
.Subscribe(RepositoryAfterEvent);
|
||||
}
|
||||
|
||||
private static string GroupName(string DeviceSerialNumber)
|
||||
{
|
||||
return UserPrefix + DeviceSerialNumber;
|
||||
}
|
||||
|
||||
public override Task OnConnected()
|
||||
{
|
||||
var deviceSerialNumber = Context.QueryString["DeviceSerialNumber"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(deviceSerialNumber))
|
||||
throw new ArgumentNullException("DeviceSerialNumber");
|
||||
|
||||
Groups.Add(Context.ConnectionId, GroupName(deviceSerialNumber));
|
||||
|
||||
return base.OnConnected();
|
||||
}
|
||||
|
||||
private static void RepositoryEventBefore(RepositoryMonitorEvent e)
|
||||
{
|
||||
if (e.EventType == RepositoryMonitorEventType.Deleted)
|
||||
{
|
||||
if (e.EntityType == typeof(DeviceAttachment))
|
||||
{
|
||||
var repositoryAttachment = (DeviceAttachment)e.Entity;
|
||||
string attachmentDeviceSerialNumber;
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
attachmentDeviceSerialNumber = Database.DeviceAttachments.Where(a => a.Id == repositoryAttachment.Id).Select(a => a.DeviceSerialNumber).First();
|
||||
|
||||
HubContext.Clients.Group(GroupName(attachmentDeviceSerialNumber)).removeAttachment(repositoryAttachment.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RepositoryAfterEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
if (e.EventType == RepositoryMonitorEventType.Added)
|
||||
{
|
||||
if (e.EntityType == typeof(DeviceAttachment))
|
||||
{
|
||||
var a = (DeviceAttachment)e.Entity;
|
||||
|
||||
HubContext.Clients.Group(GroupName(a.DeviceSerialNumber)).addAttachment(a.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Disco.Services.Devices.Exporting
|
||||
public static class DeviceExport
|
||||
{
|
||||
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable<Device> Devices, DeviceExportOptions Options, IScheduledTaskBasicStatus TaskStatus)
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable<Device> Devices, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus)
|
||||
{
|
||||
TaskStatus.UpdateStatus(15, "Building metadata and database query");
|
||||
var metadata = Options.BuildMetadata();
|
||||
@@ -32,15 +32,17 @@ namespace Disco.Services.Devices.Exporting
|
||||
Options.AssignedUserEmailAddress)
|
||||
{
|
||||
TaskStatus.UpdateStatus(20, "Updating Assigned User details");
|
||||
var users = Devices.Where(d => d.AssignedUserId != null).Select(d => d.AssignedUserId).Distinct().ToList();
|
||||
|
||||
Devices.Where(d => d.AssignedUserId != null).Select(d => d.AssignedUserId).Distinct().ToList().ForEach(userId =>
|
||||
users.Select((userId, index) =>
|
||||
{
|
||||
TaskStatus.UpdateStatus(20 + (((double)20 / users.Count) * index), string.Format("Updating Assigned User details: {0}", userId));
|
||||
try
|
||||
{
|
||||
UserService.GetUser(userId, Database);
|
||||
return UserService.GetUser(userId, Database);
|
||||
}
|
||||
catch (Exception) { } // Ignore Errors
|
||||
});
|
||||
catch (Exception) { return null; } // Ignore Errors
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// Update Last Network Logon Date
|
||||
@@ -49,8 +51,16 @@ namespace Disco.Services.Devices.Exporting
|
||||
TaskStatus.UpdateStatus(40, "Updating device last network logon dates");
|
||||
try
|
||||
{
|
||||
Interop.ActiveDirectory.ADTaskUpdateNetworkLogonDates.UpdateLastNetworkLogonDates(Database, ScheduledTaskMockStatus.Create());
|
||||
TaskStatus.IgnoreCurrentProcessChanges = true;
|
||||
TaskStatus.ProgressMultiplier = 20 / 100;
|
||||
TaskStatus.ProgressOffset = 40;
|
||||
|
||||
Interop.ActiveDirectory.ADTaskUpdateNetworkLogonDates.UpdateLastNetworkLogonDates(Database, TaskStatus);
|
||||
Database.SaveChanges();
|
||||
|
||||
TaskStatus.IgnoreCurrentProcessChanges = false;
|
||||
TaskStatus.ProgressMultiplier = 1;
|
||||
TaskStatus.ProgressOffset = 0;
|
||||
}
|
||||
catch (Exception) { } // Ignore Errors
|
||||
}
|
||||
@@ -103,7 +113,7 @@ namespace Disco.Services.Devices.Exporting
|
||||
return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create());
|
||||
}
|
||||
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskBasicStatus TaskStatus)
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus)
|
||||
{
|
||||
switch (Options.ExportType)
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Disco.Services.Devices.Importing
|
||||
Context.Header = Context.Header.Zip(HeaderTypes, (h, ht) => Tuple.Create(h.Item1, ht)).ToList();
|
||||
}
|
||||
|
||||
public static void ParseRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskBasicStatus Status)
|
||||
public static void ParseRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskStatus Status)
|
||||
{
|
||||
if (Context.Header == null)
|
||||
throw new InvalidOperationException("The Import Context has not been initialized");
|
||||
@@ -129,18 +129,13 @@ namespace Disco.Services.Devices.Importing
|
||||
.Select(h => new Tuple<string, DeviceImportFieldTypes, Func<string[], string>, Type>(h.Item1, h.Item2, (f) => f[h.Item3], DeviceImport.FieldHandlers.Value[h.Item2]))
|
||||
.ToList();
|
||||
|
||||
DateTime nextProgress = DateTime.Now;
|
||||
Status.UpdateStatus(0, "Parsing Import Records", "Starting...");
|
||||
|
||||
Context.Records = Context.RawData.Select((d, recordIndex) =>
|
||||
{
|
||||
string deviceSerialNumber = Fields.DeviceSerialNumberImportField.ParseRawDeviceSerialNumber(d[Context.HeaderDeviceSerialNumberIndex]);
|
||||
|
||||
if (nextProgress <= DateTime.Now)
|
||||
{
|
||||
Status.UpdateStatus(((double)recordIndex / Context.RawData.Count) * 100, string.Format("Parsing: {0}", deviceSerialNumber));
|
||||
nextProgress = DateTime.Now.AddSeconds(.5);
|
||||
}
|
||||
Status.UpdateStatus(((double)recordIndex / Context.RawData.Count) * 100, string.Format("Parsing: {0}", deviceSerialNumber));
|
||||
|
||||
Device existingDevice = null;
|
||||
if (Fields.DeviceSerialNumberImportField.IsDeviceSerialNumberValid(deviceSerialNumber))
|
||||
@@ -170,7 +165,7 @@ namespace Disco.Services.Devices.Importing
|
||||
}).Cast<IDeviceImportRecord>().ToList();
|
||||
}
|
||||
|
||||
public static int ApplyRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskBasicStatus Status)
|
||||
public static int ApplyRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskStatus Status)
|
||||
{
|
||||
if (Context.Records == null)
|
||||
throw new InvalidOperationException("Import Records have not been parsed");
|
||||
@@ -178,18 +173,13 @@ namespace Disco.Services.Devices.Importing
|
||||
if (Context.Records.Count == 0)
|
||||
throw new InvalidOperationException("There are no records to import");
|
||||
|
||||
DateTime nextProgress = DateTime.Now;
|
||||
Status.UpdateStatus(0, "Applying Import Records to Database", "Starting...");
|
||||
|
||||
int affectedRecords = 0;
|
||||
|
||||
foreach (var record in Context.Records.Cast<DeviceImportRecord>().Select((r, i) => Tuple.Create(r, i)))
|
||||
{
|
||||
if (nextProgress <= DateTime.Now)
|
||||
{
|
||||
Status.UpdateStatus(((double)record.Item2 / Context.Records.Count) * 100, string.Format("Applying: {0}", record.Item1.DeviceSerialNumber));
|
||||
nextProgress = DateTime.Now.AddSeconds(.5);
|
||||
}
|
||||
Status.UpdateStatus(((double)record.Item2 / Context.Records.Count) * 100, string.Format("Applying: {0}", record.Item1.DeviceSerialNumber));
|
||||
|
||||
if (record.Item1.Apply(Database))
|
||||
affectedRecords++;
|
||||
|
||||
Reference in New Issue
Block a user