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:
Gary Sharp
2014-06-01 23:27:07 +10:00
parent f6fae26bc7
commit 4cd57f4a90
116 changed files with 9874 additions and 6462 deletions
@@ -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++;