#138 device flag services
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
internal class Cache
|
||||
{
|
||||
private ConcurrentDictionary<int, DeviceFlag> _Cache;
|
||||
|
||||
public Cache(DiscoDataContext Database)
|
||||
{
|
||||
Initialize(Database);
|
||||
}
|
||||
|
||||
public void ReInitialize(DiscoDataContext Database)
|
||||
{
|
||||
Initialize(Database);
|
||||
}
|
||||
|
||||
private void Initialize(DiscoDataContext Database)
|
||||
{
|
||||
// Queues from Database
|
||||
var flags = Database.DeviceFlags.ToList();
|
||||
|
||||
// Add Queues to In-Memory Cache
|
||||
_Cache = new ConcurrentDictionary<int, DeviceFlag>(flags.Select(f => new KeyValuePair<int, DeviceFlag>(f.Id, f)));
|
||||
}
|
||||
|
||||
public DeviceFlag GetDeviceFlag(int deviceFlagId)
|
||||
{
|
||||
if (_Cache.TryGetValue(deviceFlagId, out var item))
|
||||
return item;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
public List<DeviceFlag> GetDeviceFlags()
|
||||
{
|
||||
return _Cache.Values.ToList();
|
||||
}
|
||||
|
||||
public void AddOrUpdate(DeviceFlag flag)
|
||||
{
|
||||
_Cache.AddOrUpdate(flag.Id, flag, (key, existingItem) => flag);
|
||||
}
|
||||
|
||||
public DeviceFlag Remove(int deviceFlagId)
|
||||
{
|
||||
if (_Cache.TryRemove(deviceFlagId, out var item))
|
||||
return item;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
public DeviceFlag Remove(DeviceFlag deviceFlag)
|
||||
{
|
||||
return Remove(deviceFlag.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public class DeviceFlagDeviceAssignedUsersManagedGroup : ADManagedGroup
|
||||
{
|
||||
private const string KeyFormat = "DeviceFlag_{0}_DeviceAssignedUsers";
|
||||
private const string DescriptionFormat = "User associated with devices which have the {0} Flag will be added to this Active Directory group.";
|
||||
private const string CategoryDescriptionFormat = "Device Assigned Users Linked Group";
|
||||
private const string GroupDescriptionFormat = "{0} [Device Flag Device Assigned Users]";
|
||||
|
||||
private IDisposable repositorySubscription;
|
||||
private int deviceFlagId;
|
||||
private string deviceFlagName;
|
||||
|
||||
public override string Description { get { return string.Format(DescriptionFormat, deviceFlagName); } }
|
||||
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
|
||||
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, deviceFlagName); } }
|
||||
public override bool IncludeFilterBeginDate { get { return true; } }
|
||||
|
||||
private DeviceFlagDeviceAssignedUsersManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DeviceFlag deviceFlag)
|
||||
: base(Key, Configuration)
|
||||
{
|
||||
deviceFlagId = deviceFlag.Id;
|
||||
deviceFlagName = deviceFlag.Name;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Subscribe to changes
|
||||
repositorySubscription = DeviceFlagService.DeviceFlagAssignmentRepositoryEvents.Value
|
||||
.Where(e =>
|
||||
(((DeviceFlagAssignment)e.Entity).DeviceFlagId == deviceFlagId))
|
||||
.Subscribe(ProcessRepositoryEvent);
|
||||
}
|
||||
|
||||
public static string GetKey(DeviceFlag deviceFlag)
|
||||
{
|
||||
return string.Format(KeyFormat, deviceFlag.Id);
|
||||
}
|
||||
public static string GetDescription(DeviceFlag deviceFlag)
|
||||
{
|
||||
return string.Format(DescriptionFormat, deviceFlag.Name);
|
||||
}
|
||||
public static string GetCategoryDescription(DeviceFlag deviceFlag)
|
||||
{
|
||||
return CategoryDescriptionFormat;
|
||||
}
|
||||
|
||||
public static bool TryGetManagedGroup(DeviceFlag deviceFlag, out DeviceFlagDeviceAssignedUsersManagedGroup managedGroup)
|
||||
{
|
||||
return ActiveDirectory.Context.ManagedGroups.TryGetValue(GetKey(deviceFlag), out managedGroup);
|
||||
}
|
||||
|
||||
public static DeviceFlagDeviceAssignedUsersManagedGroup Initialize(DeviceFlag deviceFlag)
|
||||
{
|
||||
if (deviceFlag.Id > 0)
|
||||
{
|
||||
var key = GetKey(deviceFlag);
|
||||
|
||||
if (!string.IsNullOrEmpty(deviceFlag.DeviceUsersLinkedGroup))
|
||||
{
|
||||
var config = ConfigurationFromJson(deviceFlag.DeviceUsersLinkedGroup);
|
||||
|
||||
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
|
||||
{
|
||||
var group = new DeviceFlagDeviceAssignedUsersManagedGroup(
|
||||
key,
|
||||
config,
|
||||
deviceFlag);
|
||||
|
||||
// Add to AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
|
||||
{
|
||||
var query = (IQueryable<User>)Database.Users;
|
||||
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
{
|
||||
query = query
|
||||
.Where(u => u.DeviceUserAssignments.Any(a =>
|
||||
a.UnassignedDate == null &&
|
||||
a.Device.DeviceFlagAssignments.Any(fa =>
|
||||
fa.DeviceFlagId == deviceFlagId &&
|
||||
!fa.RemovedDate.HasValue &&
|
||||
fa.AddedDate >= Configuration.FilterBeginDate)));
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query
|
||||
.Where(u => u.DeviceUserAssignments.Any(a =>
|
||||
a.UnassignedDate == null &&
|
||||
a.Device.DeviceFlagAssignments.Any(fa =>
|
||||
fa.DeviceFlagId == deviceFlagId &&
|
||||
!fa.RemovedDate.HasValue)));
|
||||
}
|
||||
|
||||
return query.Select(u => u.UserId)
|
||||
.Distinct()
|
||||
.ToList()
|
||||
.Where(ActiveDirectory.IsValidDomainAccountId)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void ProcessRepositoryEvent(RepositoryMonitorEvent Event)
|
||||
{
|
||||
var assignment = (DeviceFlagAssignment)Event.Entity;
|
||||
|
||||
string userId = assignment.Device?.AssignedUserId;
|
||||
if (!ActiveDirectory.IsValidDomainAccountId(userId))
|
||||
return;
|
||||
|
||||
switch (Event.EventType)
|
||||
{
|
||||
case RepositoryMonitorEventType.Added:
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
{
|
||||
if (!assignment.RemovedDate.HasValue && assignment.AddedDate >= Configuration.FilterBeginDate)
|
||||
{
|
||||
AddMember(userId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!assignment.RemovedDate.HasValue)
|
||||
{
|
||||
AddMember(userId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RepositoryMonitorEventType.Modified:
|
||||
if (!Configuration.FilterBeginDate.HasValue || assignment.AddedDate >= Configuration.FilterBeginDate)
|
||||
{
|
||||
if (assignment.RemovedDate.HasValue)
|
||||
RemoveMember(userId, (database) =>
|
||||
{
|
||||
if (database.Users.Any(u => u.DeviceUserAssignments.Any(a =>
|
||||
a.UnassignedDate == null &&
|
||||
a.Device.DeviceFlagAssignments.Any(fa =>
|
||||
fa.DeviceFlagId == deviceFlagId &&
|
||||
!fa.RemovedDate.HasValue))))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new[] { userId };
|
||||
}
|
||||
}
|
||||
);
|
||||
else
|
||||
AddMember(userId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (repositorySubscription != null)
|
||||
repositorySubscription.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public class DeviceFlagDevicesManagedGroup : ADManagedGroup
|
||||
{
|
||||
private const string KeyFormat = "DeviceFlag_{0}_Devices";
|
||||
private const string DescriptionFormat = "Devices associated with the {0} Flag will be added to this Active Directory group.";
|
||||
private const string CategoryDescriptionFormat = "Assigned Devices Linked Group";
|
||||
private const string GroupDescriptionFormat = "{0} [Device Flag Devices]";
|
||||
|
||||
private IDisposable repositorySubscription;
|
||||
private int deviceFlagId;
|
||||
private string deviceFlagName;
|
||||
|
||||
public override string Description { get { return string.Format(DescriptionFormat, deviceFlagName); } }
|
||||
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
|
||||
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, deviceFlagName); } }
|
||||
public override bool IncludeFilterBeginDate { get { return true; } }
|
||||
|
||||
private DeviceFlagDevicesManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DeviceFlag DeviceFlag)
|
||||
: base(Key, Configuration)
|
||||
{
|
||||
deviceFlagId = DeviceFlag.Id;
|
||||
deviceFlagName = DeviceFlag.Name;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Subscribe to changes
|
||||
repositorySubscription = DeviceFlagService.DeviceFlagAssignmentRepositoryEvents.Value
|
||||
.Where(e =>
|
||||
((DeviceFlagAssignment)e.Entity).DeviceFlagId == deviceFlagId)
|
||||
.Subscribe(ProcessRepositoryEvent);
|
||||
}
|
||||
|
||||
public static string GetKey(DeviceFlag deviceFlag)
|
||||
{
|
||||
return string.Format(KeyFormat, deviceFlag.Id);
|
||||
}
|
||||
public static string GetDescription(DeviceFlag deviceFlag)
|
||||
{
|
||||
return string.Format(DescriptionFormat, deviceFlag.Name);
|
||||
}
|
||||
public static string GetCategoryDescription(DeviceFlag deviceFlag)
|
||||
{
|
||||
return CategoryDescriptionFormat;
|
||||
}
|
||||
|
||||
public static bool TryGetManagedGroup(DeviceFlag deviceFlag, out DeviceFlagDevicesManagedGroup managedGroup)
|
||||
{
|
||||
return ActiveDirectory.Context.ManagedGroups.TryGetValue(GetKey(deviceFlag), out managedGroup);
|
||||
}
|
||||
|
||||
public static DeviceFlagDevicesManagedGroup Initialize(DeviceFlag deviceFlag)
|
||||
{
|
||||
if (deviceFlag.Id > 0)
|
||||
{
|
||||
var key = GetKey(deviceFlag);
|
||||
|
||||
if (!string.IsNullOrEmpty(deviceFlag.DevicesLinkedGroup))
|
||||
{
|
||||
var config = ConfigurationFromJson(deviceFlag.DevicesLinkedGroup);
|
||||
|
||||
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
|
||||
{
|
||||
var group = new DeviceFlagDevicesManagedGroup(
|
||||
key,
|
||||
config,
|
||||
deviceFlag);
|
||||
|
||||
// Add to AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
|
||||
{
|
||||
var query = Database.DeviceFlagAssignments
|
||||
.Where(a => a.DeviceFlagId == deviceFlagId && !a.RemovedDate.HasValue && a.Device.DeviceDomainId != null);
|
||||
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
query = query.Where(a => a.AddedDate >= Configuration.FilterBeginDate);
|
||||
|
||||
return query
|
||||
.Select(a => a.Device.DeviceDomainId)
|
||||
.ToList()
|
||||
.Where(ActiveDirectory.IsValidDomainAccountId)
|
||||
.Select(id => id + "$");
|
||||
}
|
||||
|
||||
private void ProcessRepositoryEvent(RepositoryMonitorEvent Event)
|
||||
{
|
||||
var assignment = (DeviceFlagAssignment)Event.Entity;
|
||||
|
||||
var domainId = assignment.Device?.DeviceDomainId;
|
||||
|
||||
if (!ActiveDirectory.IsValidDomainAccountId(domainId))
|
||||
return;
|
||||
domainId += "$";
|
||||
|
||||
switch (Event.EventType)
|
||||
{
|
||||
case RepositoryMonitorEventType.Added:
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
{
|
||||
if (!assignment.RemovedDate.HasValue && assignment.AddedDate >= Configuration.FilterBeginDate)
|
||||
{
|
||||
AddMember(domainId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!assignment.RemovedDate.HasValue)
|
||||
{
|
||||
AddMember(domainId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RepositoryMonitorEventType.Modified:
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
{
|
||||
if (assignment.AddedDate >= Configuration.FilterBeginDate)
|
||||
{
|
||||
if (assignment.RemovedDate.HasValue)
|
||||
{
|
||||
RemoveMember(domainId);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMember(domainId);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assignment.RemovedDate.HasValue)
|
||||
{
|
||||
RemoveMember(domainId);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMember(domainId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RepositoryMonitorEventType.Deleted:
|
||||
// Remove the device if no other (non-removed) assignments exist.
|
||||
var serialNumber = assignment.DeviceSerialNumber;
|
||||
RemoveMember(domainId, (database) =>
|
||||
{
|
||||
if (Configuration.FilterBeginDate.HasValue)
|
||||
{
|
||||
if (database.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlagId && a.DeviceSerialNumber == serialNumber && !a.RemovedDate.HasValue && a.AddedDate >= Configuration.FilterBeginDate))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new string[] { domainId };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (database.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlagId && a.DeviceSerialNumber == serialNumber && !a.RemovedDate.HasValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new string[] { domainId };
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (repositorySubscription != null)
|
||||
repositorySubscription.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Exporting;
|
||||
using Disco.Models.Services.Devices.DeviceFlag;
|
||||
using Disco.Models.Services.Exporting;
|
||||
using Disco.Services.Plugins.Features.DetailsProvider;
|
||||
using Disco.Services.Tasks;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
using Metadata = ExportFieldMetadata<DeviceFlagExportRecord>;
|
||||
|
||||
public class DeviceFlagExport
|
||||
{
|
||||
private readonly DiscoDataContext database;
|
||||
private readonly DeviceFlagExportOptions options;
|
||||
|
||||
public DeviceFlagExport(DiscoDataContext database, DeviceFlagExportOptions options)
|
||||
{
|
||||
this.database = database;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public ExportResult Generate(IScheduledTaskStatus status)
|
||||
{
|
||||
var records = BuildRecords(status);
|
||||
|
||||
var metadata = BuildMetadata(records, status);
|
||||
|
||||
if (metadata.Count == 0)
|
||||
throw new ArgumentException("At least one export field must be specified", nameof(options));
|
||||
|
||||
status.UpdateStatus(90, $"Formatting {records.Count} records for export");
|
||||
return ExportHelpers.WriteExport(options, status, metadata, records);
|
||||
}
|
||||
|
||||
private List<DeviceFlagExportRecord> BuildRecords(IScheduledTaskStatus status)
|
||||
{
|
||||
var query = database.DeviceFlagAssignments
|
||||
.Include(a => a.DeviceFlag);
|
||||
|
||||
if (options.HasDeviceOptions())
|
||||
query = query.Include(a => a.Device);
|
||||
if (options.HasDeviceModelOptions())
|
||||
query = query.Include(a => a.Device.DeviceModel);
|
||||
if (options.HasDeviceBatchOptions())
|
||||
query = query.Include(a => a.Device.DeviceBatch);
|
||||
if (options.HasDeviceProfileOptions())
|
||||
query = query.Include(a => a.Device.DeviceProfile);
|
||||
if (options.HasAssignedUserOptions())
|
||||
query = query.Include(a => a.Device.AssignedUser);
|
||||
if (options.AssignedUserDetailCustom)
|
||||
query = query.Include(a => a.Device.AssignedUser.UserDetails);
|
||||
|
||||
query = query.Where(a => options.DeviceFlagIds.Contains(a.DeviceFlagId));
|
||||
|
||||
if (options.CurrentOnly)
|
||||
{
|
||||
query = query.Where(a => !a.RemovedDate.HasValue);
|
||||
}
|
||||
|
||||
// Update Users
|
||||
if (options.HasAssignedUserOptions())
|
||||
{
|
||||
status.UpdateStatus(5, "Refreshing user details from Active Directory");
|
||||
var userIds = query.Where(d => d.Device.AssignedUserId != null).Select(d => d.Device.AssignedUserId).Distinct().ToList();
|
||||
foreach (var userId in userIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
UserService.GetUser(userId, database);
|
||||
}
|
||||
catch (Exception) { } // Ignore Errors
|
||||
}
|
||||
}
|
||||
|
||||
status.UpdateStatus(15, "Extracting records from the database");
|
||||
|
||||
var records = query.Select(a => new DeviceFlagExportRecord()
|
||||
{
|
||||
Assignment = a
|
||||
}).ToList();
|
||||
|
||||
if (options.AssignedUserDetailCustom)
|
||||
{
|
||||
status.UpdateStatus(50, "Extracting custom user detail records");
|
||||
|
||||
var detailsService = new DetailsProviderService(database);
|
||||
var cache = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal);
|
||||
foreach (var record in records)
|
||||
{
|
||||
var userId = record.Assignment.Device.AssignedUserId;
|
||||
|
||||
if (userId == null)
|
||||
continue;
|
||||
|
||||
if (!cache.TryGetValue(userId, out var details))
|
||||
details = detailsService.GetDetails(record.Assignment.Device.AssignedUser);
|
||||
record.AssignedUserCustomDetails = details;
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
private List<Metadata> BuildMetadata(List<DeviceFlagExportRecord> records, IScheduledTaskStatus status)
|
||||
{
|
||||
status.UpdateStatus(80, "Building metadata");
|
||||
|
||||
IEnumerable<string> userDetailCustomKeys = null;
|
||||
if (options.AssignedUserDetailCustom)
|
||||
userDetailCustomKeys = records.Where(r => r.AssignedUserCustomDetails != null).SelectMany(r => r.AssignedUserCustomDetails.Keys).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
var accessors = BuildAccessors(userDetailCustomKeys);
|
||||
|
||||
return typeof(DeviceFlagExportOptions).GetProperties()
|
||||
.Where(p => p.PropertyType == typeof(bool))
|
||||
.Select(p => new
|
||||
{
|
||||
property = p,
|
||||
details = (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault()
|
||||
})
|
||||
.Where(p => p.details != null && p.property.Name != nameof(options.CurrentOnly) && (bool)p.property.GetValue(options))
|
||||
.SelectMany(p =>
|
||||
{
|
||||
var fieldMetadata = accessors[p.property.Name];
|
||||
fieldMetadata.ForEach(f =>
|
||||
{
|
||||
if (f.ColumnName == null)
|
||||
f.ColumnName = (p.details.ShortName == "Device Flag") ? p.details.Name : $"{p.details.ShortName} {p.details.Name}";
|
||||
});
|
||||
return fieldMetadata;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<Metadata>> BuildAccessors(IEnumerable<string> userDetailsCustomKeys)
|
||||
{
|
||||
const string DateFormat = "yyyy-MM-dd";
|
||||
const string DateTimeFormat = DateFormat + " HH:mm:ss";
|
||||
|
||||
Func<object, string> csvStringEncoded = (o) => o == null ? null : $"\"{((string)o).Replace("\"", "\"\"")}\"";
|
||||
Func<object, string> csvToStringEncoded = (o) => o == null ? null : o.ToString();
|
||||
Func<object, string> csvCurrencyEncoded = (o) => ((decimal?)o).HasValue ? ((decimal?)o).Value.ToString("C") : null;
|
||||
Func<object, string> csvDateEncoded = (o) => ((DateTime)o).ToString(DateFormat);
|
||||
Func<object, string> csvDateTimeEncoded = (o) => ((DateTime)o).ToString(DateTimeFormat);
|
||||
Func<object, string> csvNullableDateEncoded = (o) => ((DateTime?)o).HasValue ? csvDateEncoded(o) : null;
|
||||
Func<object, string> csvNullableDateTimeEncoded = (o) => ((DateTime?)o).HasValue ? csvDateTimeEncoded(o) : null;
|
||||
|
||||
var metadata = new Dictionary<string, List<Metadata>>();
|
||||
|
||||
// Device Flag
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.Id), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Id), typeof(string), r => r.Assignment.DeviceFlagId, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.Name), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Name), typeof(string), r => r.Assignment.DeviceFlag.Name, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.Description), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Description), typeof(string), r => r.Assignment.DeviceFlag.Description, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.Icon), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Icon), typeof(string), r => r.Assignment.DeviceFlag.Icon, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.IconColour), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.IconColour), typeof(string), r => r.Assignment.DeviceFlag.IconColour, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignmentId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignmentId), typeof(string), r => r.Assignment.Id, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AddedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AddedDate), typeof(string), r => r.Assignment.AddedDate, csvDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AddedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AddedUserId), typeof(string), r => r.Assignment.AddedUserId, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.RemovedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.RemovedUserId), typeof(string), r => r.Assignment.RemovedUserId, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.RemovedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.RemovedDate), typeof(string), r => r.Assignment.RemovedDate, csvNullableDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.Comments), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.Comments), typeof(string), r => r.Assignment.Comments, csvStringEncoded) });
|
||||
|
||||
|
||||
// Device
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceSerialNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceSerialNumber), typeof(string), r => r.Assignment.Device.SerialNumber, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceAssetNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceAssetNumber), typeof(string), r => r.Assignment.Device.AssetNumber, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLocation), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLocation), typeof(string), r => r.Assignment.Device.Location, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceComputerName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceComputerName), typeof(string), r => r.Assignment.Device.DeviceDomainId, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLastNetworkLogon), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLastNetworkLogon), typeof(DateTime), r => r.Assignment.Device.LastNetworkLogonDate, csvNullableDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceCreatedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceCreatedDate), typeof(DateTime), r => r.Assignment.Device.CreatedDate, csvDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceFirstEnrolledDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceFirstEnrolledDate), typeof(DateTime), r => r.Assignment.Device.EnrolledDate, csvNullableDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceLastEnrolledDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceLastEnrolledDate), typeof(DateTime), r => r.Assignment.Device.LastEnrolDate, csvNullableDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceAllowUnauthenticatedEnrol), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceAllowUnauthenticatedEnrol), typeof(bool), r => r.Assignment.Device.AllowUnauthenticatedEnrol, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceDecommissionedDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceDecommissionedDate), typeof(DateTime), r => r.Assignment.Device.DecommissionedDate, csvNullableDateTimeEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.DeviceDecommissionedReason), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.DeviceDecommissionedReason), typeof(string), r => r.Assignment.Device.DecommissionReason, csvToStringEncoded) });
|
||||
|
||||
// Model
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ModelId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelId), typeof(int), r => r.Assignment.Device.DeviceModel.Id, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ModelDescription), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelDescription), typeof(string), r => r.Assignment.Device.DeviceModel.Description, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ModelManufacturer), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelManufacturer), typeof(string), r => r.Assignment.Device.DeviceModel.Manufacturer, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ModelModel), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelModel), typeof(string), r => r.Assignment.Device.DeviceModel.Model, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ModelType), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ModelType), typeof(string), r => r.Assignment.Device.DeviceModel.ModelType, csvStringEncoded) });
|
||||
|
||||
// Batch
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchId), typeof(int), r => r.Assignment.Device.DeviceBatch?.Id, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchName), typeof(string), r => r.Assignment.Device.DeviceBatch?.Name, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchPurchaseDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchPurchaseDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.PurchaseDate, csvNullableDateEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchSupplier), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchSupplier), typeof(string), r => r.Assignment.Device.DeviceBatch?.Supplier, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchUnitCost), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchUnitCost), typeof(decimal), r => r.Assignment.Device.DeviceBatch?.UnitCost, csvCurrencyEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchWarrantyValidUntilDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchWarrantyValidUntilDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.WarrantyValidUntil, csvNullableDateEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuredDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuredDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.InsuredDate, csvNullableDateEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuranceSupplier), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuranceSupplier), typeof(string), r => r.Assignment.Device.DeviceBatch?.InsuranceSupplier, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.BatchInsuredUntilDate), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.BatchInsuredUntilDate), typeof(DateTime), r => r.Assignment.Device.DeviceBatch?.InsuredUntil, csvNullableDateEncoded) });
|
||||
|
||||
// Profile
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ProfileId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileId), typeof(int), r => r.Assignment.Device.DeviceProfile?.Id, csvToStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ProfileName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileName), typeof(string), r => r.Assignment.Device.DeviceProfile?.Name, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.ProfileShortName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.ProfileShortName), typeof(string), r => r.Assignment.Device.DeviceProfile?.ShortName, csvStringEncoded) });
|
||||
|
||||
|
||||
// User
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserId), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserId), typeof(string), r => r.Assignment.Device?.AssignedUser?.UserId, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserDisplayName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserDisplayName), typeof(string), r => r.Assignment.Device?.AssignedUser?.DisplayName, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserSurname), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserSurname), typeof(string), r => r.Assignment.Device?.AssignedUser?.Surname, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserGivenName), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserGivenName), typeof(string), r => r.Assignment.Device?.AssignedUser?.GivenName, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserPhoneNumber), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserPhoneNumber), typeof(string), r => r.Assignment.Device?.AssignedUser?.PhoneNumber, csvStringEncoded) });
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserEmailAddress), new List<Metadata>() { new Metadata(nameof(DeviceFlagExportOptions.AssignedUserEmailAddress), typeof(string), r => r.Assignment.Device?.AssignedUser?.EmailAddress, csvStringEncoded) });
|
||||
if (userDetailsCustomKeys != null)
|
||||
{
|
||||
var userDetailCustomFields = new List<Metadata>();
|
||||
foreach (var detailKey in userDetailsCustomKeys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var key = detailKey;
|
||||
userDetailCustomFields.Add(new Metadata(detailKey, detailKey, typeof(string), r => r.AssignedUserCustomDetails != null && r.AssignedUserCustomDetails.TryGetValue(key, out var value) ? value : null, csvStringEncoded));
|
||||
}
|
||||
metadata.Add(nameof(DeviceFlagExportOptions.AssignedUserDetailCustom), userDetailCustomFields);
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Services.Devices.DeviceFlag;
|
||||
using Disco.Services.Exporting;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public class DeviceFlagExportTask : ScheduledTask
|
||||
{
|
||||
private const string JobDataMapContext = "Context";
|
||||
|
||||
public override string TaskName { get; } = "Export Device Flags";
|
||||
public override bool SingleInstanceTask { get { return false; } }
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
|
||||
public static ExportTaskContext<DeviceFlagExportOptions> ScheduleNow(DeviceFlagExportOptions options)
|
||||
{
|
||||
// Build Context
|
||||
var context = new ExportTaskContext<DeviceFlagExportOptions>(options);
|
||||
|
||||
// Build Data Map
|
||||
var task = new DeviceFlagExportTask();
|
||||
JobDataMap taskData = new JobDataMap() { { JobDataMapContext, context } };
|
||||
|
||||
// Schedule Task
|
||||
context.TaskStatus = task.ScheduleTask(taskData);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
var context = (ExportTaskContext<DeviceFlagExportOptions>)ExecutionContext.JobDetail.JobDataMap[JobDataMapContext];
|
||||
|
||||
Status.UpdateStatus(10, "Exporting Device Flag Records", "Starting...");
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
{
|
||||
var export = new DeviceFlagExport(Database, context.Options);
|
||||
|
||||
context.Result = export.Generate(Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Expressions;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceFlagExtensions
|
||||
{
|
||||
|
||||
#region Edit Comments
|
||||
public static bool CanEditComments(this DeviceFlagAssignment fa)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.EditFlags);
|
||||
}
|
||||
public static void OnEditComments(this DeviceFlagAssignment fa, string Comments)
|
||||
{
|
||||
if (!fa.CanEditComments())
|
||||
throw new InvalidOperationException("Editing comments for device flags is denied");
|
||||
|
||||
fa.Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Remove
|
||||
public static bool CanRemove(this DeviceFlagAssignment fa)
|
||||
{
|
||||
if (fa.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveFlags);
|
||||
}
|
||||
public static void OnRemove(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
|
||||
{
|
||||
if (!fa.CanRemove())
|
||||
throw new InvalidOperationException("Removing device flags is denied");
|
||||
|
||||
fa.OnRemoveUnsafe(Database, RemovingUser);
|
||||
}
|
||||
|
||||
public static void OnRemoveUnsafe(this DeviceFlagAssignment fa, DiscoDataContext Database, User RemovingUser)
|
||||
{
|
||||
fa = Database.DeviceFlagAssignments
|
||||
.Include(a => a.DeviceFlag)
|
||||
.First(a => a.Id == fa.Id);
|
||||
RemovingUser = Database.Users.First(u => u.UserId == RemovingUser.UserId);
|
||||
|
||||
fa.RemovedDate = DateTime.Now;
|
||||
fa.RemovedUserId = RemovingUser.UserId;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fa.DeviceFlag.OnUnassignmentExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
Database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnUnassignmentExpression(Database, RemovingUser, fa.AddedDate);
|
||||
if (!string.IsNullOrWhiteSpace(expressionResult))
|
||||
{
|
||||
fa.OnUnassignmentExpressionResult = expressionResult;
|
||||
Database.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SystemLog.LogException("Device Flag Expression - OnUnassignmentExpression", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
public static bool CanAddDeviceFlags(this Device d)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.Device.Actions.AddFlags);
|
||||
}
|
||||
public static bool CanAddDeviceFlag(this Device d, DeviceFlag flag)
|
||||
{
|
||||
// Shortcut
|
||||
if (!d.CanAddDeviceFlags())
|
||||
return false;
|
||||
|
||||
// Already has Device Flag?
|
||||
if (d.DeviceFlagAssignments.Any(fa => !fa.RemovedDate.HasValue && fa.DeviceFlagId == flag.Id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
public static DeviceFlagAssignment OnAddDeviceFlag(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
|
||||
{
|
||||
if (!d.CanAddDeviceFlag(flag))
|
||||
throw new InvalidOperationException("Adding device flag is denied");
|
||||
|
||||
return d.OnAddDeviceFlagUnsafe(Database, flag, AddingUser, Comments);
|
||||
}
|
||||
|
||||
public static DeviceFlagAssignment OnAddDeviceFlagUnsafe(this Device d, DiscoDataContext Database, DeviceFlag flag, User AddingUser, string Comments)
|
||||
{
|
||||
flag = Database.DeviceFlags.First(f => f.Id == flag.Id);
|
||||
d = Database.Devices.First(de => de.SerialNumber == d.SerialNumber);
|
||||
AddingUser = Database.Users.First(user => user.UserId == AddingUser.UserId);
|
||||
|
||||
var fa = new DeviceFlagAssignment()
|
||||
{
|
||||
DeviceFlag = flag,
|
||||
Device = d,
|
||||
AddedDate = DateTime.Now,
|
||||
AddedUser = AddingUser,
|
||||
AddedUserId = AddingUser.UserId,
|
||||
Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim()
|
||||
};
|
||||
|
||||
Database.DeviceFlagAssignments.Add(fa);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(flag.OnAssignmentExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
Database.SaveChanges();
|
||||
var expressionResult = fa.EvaluateOnAssignmentExpression(Database, AddingUser, fa.AddedDate);
|
||||
if (!string.IsNullOrWhiteSpace(expressionResult))
|
||||
{
|
||||
fa.OnAssignmentExpressionResult = expressionResult;
|
||||
Database.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SystemLog.LogException("Device Flag Expression - OnAssignmentExpression", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return fa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Expressions
|
||||
|
||||
public static Expression OnAssignmentExpressionFromCache(this DeviceFlag uf)
|
||||
{
|
||||
return ExpressionCache.GetOrCreateSingleExpressions($"DeviceFlag_OnAssignmentExpression_{uf.Id}", () => Expression.TokenizeSingleDynamic(null, uf.OnAssignmentExpression, 0));
|
||||
}
|
||||
|
||||
public static void OnAssignmentExpressionInvalidateCache(this DeviceFlag uf)
|
||||
{
|
||||
ExpressionCache.InvalidateSingleCache($"DeviceFlag_OnAssignmentExpression_{uf.Id}");
|
||||
}
|
||||
|
||||
public static string EvaluateOnAssignmentExpression(this DeviceFlagAssignment dfa, DiscoDataContext Database, User AddingUser, DateTime TimeStamp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dfa.DeviceFlag.OnAssignmentExpression))
|
||||
{
|
||||
Expression compiledExpression = dfa.DeviceFlag.OnAssignmentExpressionFromCache();
|
||||
IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, AddingUser, TimeStamp, null, dfa.Device);
|
||||
object result = compiledExpression.EvaluateFirst<object>(dfa, evaluatorVariables);
|
||||
if (result == null)
|
||||
return null;
|
||||
else
|
||||
return result.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Expression OnUnassignmentExpressionFromCache(this DeviceFlag df)
|
||||
{
|
||||
return ExpressionCache.GetOrCreateSingleExpressions($"DeviceFlag_OnUnassignmentExpression_{df.Id}", () => Expression.TokenizeSingleDynamic(null, df.OnUnassignmentExpression, 0));
|
||||
}
|
||||
|
||||
public static void OnUnassignmentExpressionInvalidateCache(this DeviceFlag df)
|
||||
{
|
||||
ExpressionCache.InvalidateSingleCache($"DeviceFlag_OnUnassignmentExpression_{df.Id}");
|
||||
}
|
||||
|
||||
public static string EvaluateOnUnassignmentExpression(this DeviceFlagAssignment dfa, DiscoDataContext Database, User RemovingUser, DateTime TimeStamp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dfa.DeviceFlag.OnUnassignmentExpression))
|
||||
{
|
||||
Expression compiledExpression = dfa.DeviceFlag.OnUnassignmentExpressionFromCache();
|
||||
IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, RemovingUser, TimeStamp, null, dfa.Device);
|
||||
object result = compiledExpression.EvaluateFirst<object>(dfa, evaluatorVariables);
|
||||
if (result == null)
|
||||
return null;
|
||||
else
|
||||
return result.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Extensions;
|
||||
using Disco.Services.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public static class DeviceFlagService
|
||||
{
|
||||
private static Cache _cache;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceFlagAssignmentRepositoryEvents;
|
||||
|
||||
static DeviceFlagService()
|
||||
{
|
||||
// Statically defined (lazy) Assignment Repository Definition
|
||||
DeviceFlagAssignmentRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamAfterCommit.Where(e =>
|
||||
e.EntityType == typeof(DeviceFlagAssignment) &&
|
||||
(e.EventType != RepositoryMonitorEventType.Modified ||
|
||||
e.ModifiedProperties.Contains(nameof(DeviceFlagAssignment.RemovedDate)))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static void Initialize(DiscoDataContext database)
|
||||
{
|
||||
_cache = new Cache(database);
|
||||
|
||||
// Initialize Managed Groups (if configured)
|
||||
_cache.GetDeviceFlags().ForEach(uf =>
|
||||
{
|
||||
DeviceFlagDevicesManagedGroup.Initialize(uf);
|
||||
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(uf);
|
||||
});
|
||||
}
|
||||
|
||||
public static List<DeviceFlag> GetDeviceFlags() { return _cache.GetDeviceFlags(); }
|
||||
public static DeviceFlag GetDeviceFlag(int deviceFlagId) { return _cache.GetDeviceFlag(deviceFlagId); }
|
||||
|
||||
#region Device Flag Maintenance
|
||||
public static DeviceFlag CreateDeviceFlag(DiscoDataContext database, DeviceFlag deviceFlag)
|
||||
{
|
||||
// Verify
|
||||
if (string.IsNullOrWhiteSpace(deviceFlag.Name))
|
||||
throw new ArgumentException("The Device Flag Name is required", nameof(deviceFlag));
|
||||
|
||||
// Name Unique
|
||||
if (_cache.GetDeviceFlags().Any(f => f.Name == deviceFlag.Name))
|
||||
throw new ArgumentException("Another Device Flag already exists with that name", nameof(deviceFlag));
|
||||
|
||||
// Clone to break reference
|
||||
var flag = new DeviceFlag()
|
||||
{
|
||||
Name = deviceFlag.Name,
|
||||
Description = deviceFlag.Description,
|
||||
Icon = deviceFlag.Icon,
|
||||
IconColour = deviceFlag.IconColour,
|
||||
DevicesLinkedGroup = deviceFlag.DevicesLinkedGroup,
|
||||
DeviceUsersLinkedGroup = deviceFlag.DeviceUsersLinkedGroup,
|
||||
};
|
||||
|
||||
database.DeviceFlags.Add(flag);
|
||||
database.SaveChanges();
|
||||
|
||||
_cache.AddOrUpdate(flag);
|
||||
|
||||
return flag;
|
||||
}
|
||||
public static DeviceFlag Update(DiscoDataContext database, DeviceFlag deviceFlag)
|
||||
{
|
||||
// Verify
|
||||
if (string.IsNullOrWhiteSpace(deviceFlag.Name))
|
||||
throw new ArgumentException("The Device Flag Name is required", nameof(deviceFlag));
|
||||
|
||||
// Name Unique
|
||||
if (_cache.GetDeviceFlags().Any(f => f.Id != deviceFlag.Id && f.Name == deviceFlag.Name))
|
||||
throw new ArgumentException("Another Device Flag already exists with that name", nameof(deviceFlag));
|
||||
|
||||
database.SaveChanges();
|
||||
|
||||
_cache.AddOrUpdate(deviceFlag);
|
||||
DeviceFlagDevicesManagedGroup.Initialize(deviceFlag);
|
||||
DeviceFlagDeviceAssignedUsersManagedGroup.Initialize(deviceFlag);
|
||||
|
||||
return deviceFlag;
|
||||
}
|
||||
public static void DeleteDeviceFlag(DiscoDataContext database, int deviceFlagId, IScheduledTaskStatus status)
|
||||
{
|
||||
var flag = database.DeviceFlags.Find(deviceFlagId);
|
||||
|
||||
// Dispose of AD Managed Groups
|
||||
Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(DeviceFlagDeviceAssignedUsersManagedGroup.GetKey(flag));
|
||||
Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(DeviceFlagDevicesManagedGroup.GetKey(flag));
|
||||
|
||||
// Delete Assignments
|
||||
status.UpdateStatus(0, $"Removing '{flag.Name}' [{flag.Id}] Device Flag", "Starting");
|
||||
var flagAssignments = database.DeviceFlagAssignments.Where(fa => fa.DeviceFlagId == flag.Id).ToList();
|
||||
if (flagAssignments.Count > 0)
|
||||
{
|
||||
status.UpdateStatus(20, "Removing flag from devices");
|
||||
flagAssignments.ForEach(flagAssignment => database.DeviceFlagAssignments.Remove(flagAssignment));
|
||||
database.SaveChanges();
|
||||
}
|
||||
|
||||
// Delete Flag
|
||||
status.UpdateStatus(90, "Deleting Device Flag");
|
||||
database.DeviceFlags.Remove(flag);
|
||||
database.SaveChanges();
|
||||
|
||||
// Remove from Cache
|
||||
_cache.Remove(deviceFlagId);
|
||||
|
||||
status.Finished($"Successfully Deleted Device Flag: '{flag.Name}' [{flag.Id}]");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Bulk Assignment
|
||||
public static IEnumerable<DeviceFlagAssignment> BulkAssignAddDevices(DiscoDataContext database, DeviceFlag deviceFlag, User technician, string comments, List<Device> devices, IScheduledTaskStatus status)
|
||||
{
|
||||
if (devices.Count > 0)
|
||||
{
|
||||
double progressInterval;
|
||||
const int databaseChunkSize = 100;
|
||||
comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim();
|
||||
|
||||
var addDevices = devices.Where(d => !d.DeviceFlagAssignments.Any(a => a.DeviceFlagId == deviceFlag.Id && !a.RemovedDate.HasValue)).ToList();
|
||||
|
||||
progressInterval = (double)100 / addDevices.Count;
|
||||
|
||||
var addedDeviceAssignments = addDevices.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) =>
|
||||
{
|
||||
var chunkIndexOffset = databaseChunkSize * chunkIndex;
|
||||
|
||||
var chunkResults = chunk.Select((device, index) =>
|
||||
{
|
||||
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
|
||||
|
||||
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
|
||||
}).ToList();
|
||||
|
||||
// Save Chunk Items to Database
|
||||
database.SaveChanges();
|
||||
|
||||
return chunkResults;
|
||||
}).Where(fa => fa != null).ToList();
|
||||
|
||||
status.SetFinishedMessage($"{addDevices.Count} Devices/s Added; {(devices.Count - addDevices.Count)} Devices/s Skipped");
|
||||
|
||||
return addedDeviceAssignments;
|
||||
}
|
||||
else
|
||||
{
|
||||
status.SetFinishedMessage("No changes found");
|
||||
return Enumerable.Empty<DeviceFlagAssignment>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<DeviceFlagAssignment> BulkAssignOverrideDevices(DiscoDataContext database, DeviceFlag deviceFlag, User technician, string comments, List<Device> devices, IScheduledTaskStatus status)
|
||||
{
|
||||
double progressInterval;
|
||||
const int databaseChunkSize = 100;
|
||||
comments = string.IsNullOrWhiteSpace(comments) ? null : comments.Trim();
|
||||
|
||||
status.UpdateStatus(0, "Calculating assignment changes");
|
||||
|
||||
var currentAssignments = database.DeviceFlagAssignments.Include(fa => fa.Device).Where(a => a.DeviceFlagId == deviceFlag.Id && !a.RemovedDate.HasValue).ToList();
|
||||
var removeAssignments = currentAssignments.Where(ca => !devices.Any(d => d.SerialNumber.Equals(ca.DeviceSerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
|
||||
var addAssignments = devices.Where(d => !currentAssignments.Any(ca => ca.DeviceSerialNumber.Equals(d.SerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
|
||||
|
||||
if (removeAssignments.Count > 0 || addAssignments.Count > 0)
|
||||
{
|
||||
progressInterval = (double)100 / (removeAssignments.Count + addAssignments.Count);
|
||||
var removedDateTime = DateTime.Now;
|
||||
|
||||
// Remove Assignments
|
||||
removeAssignments.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) =>
|
||||
{
|
||||
var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count;
|
||||
|
||||
var chunkResults = chunk.Select((flagAssignment, index) =>
|
||||
{
|
||||
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Removing Flag: {flagAssignment.Device}");
|
||||
|
||||
flagAssignment.OnRemoveUnsafe(database, technician);
|
||||
|
||||
return flagAssignment;
|
||||
}).ToList();
|
||||
|
||||
// Save Chunk Items to Database
|
||||
database.SaveChanges();
|
||||
|
||||
return chunkResults;
|
||||
}).ToList();
|
||||
|
||||
// Add Assignments
|
||||
var addedAssignments = addAssignments.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) =>
|
||||
{
|
||||
var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count;
|
||||
|
||||
var chunkResults = chunk.Select((device, index) =>
|
||||
{
|
||||
status.UpdateStatus((chunkIndexOffset + index) * progressInterval, $"Assigning Flag: {device}");
|
||||
|
||||
return device.OnAddDeviceFlag(database, deviceFlag, technician, comments);
|
||||
}).ToList();
|
||||
|
||||
// Save Chunk Items to Database
|
||||
database.SaveChanges();
|
||||
|
||||
return chunkResults;
|
||||
}).ToList();
|
||||
|
||||
status.SetFinishedMessage($"{addAssignments.Count} Devices/s Added; {removeAssignments.Count} Devices/s Removed; {(devices.Count - addAssignments.Count)} Devices/s Skipped");
|
||||
|
||||
return addedAssignments;
|
||||
}
|
||||
else
|
||||
{
|
||||
status.SetFinishedMessage("No changes found");
|
||||
return Enumerable.Empty<DeviceFlagAssignment>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static string RandomUnusedIcon()
|
||||
{
|
||||
return UIHelpers.RandomIcon(_cache.GetDeviceFlags().Select(f => f.Icon));
|
||||
}
|
||||
public static string RandomUnusedThemeColour()
|
||||
{
|
||||
return UIHelpers.RandomThemeColour(_cache.GetDeviceFlags().Select(f => f.IconColour));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public class DeviceFlagBulkAssignTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Device Flags - Bulk Assign Devices"; } }
|
||||
|
||||
public override bool SingleInstanceTask { get { return false; } }
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
public override bool LogExceptionsOnly { get { return true; } }
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
int deviceFlagId = (int)ExecutionContext.JobDetail.JobDataMap["DeviceFlagId"];
|
||||
string technicianUserId = (string)ExecutionContext.JobDetail.JobDataMap["TechnicianUserId"];
|
||||
string comments = (string)ExecutionContext.JobDetail.JobDataMap["Comments"];
|
||||
List<string> deviceSerialNumbers = (List<string>)ExecutionContext.JobDetail.JobDataMap["DeviceSerialNumbers"];
|
||||
bool @override = (bool)ExecutionContext.JobDetail.JobDataMap["Override"];
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
{
|
||||
// Load Flag
|
||||
var flag = Database.DeviceFlags.FirstOrDefault(uf => uf.Id == deviceFlagId);
|
||||
if (flag == null)
|
||||
throw new Exception("Invalid Device Flag Id");
|
||||
|
||||
Status.UpdateStatus(0, string.Format("Bulk Assigning Devices to Device Flag: {0}", flag.Name), "Preparing to start");
|
||||
|
||||
// Load Technician
|
||||
var technician = Database.Users.FirstOrDefault(user => user.UserId == technicianUserId);
|
||||
if (technician == null)
|
||||
throw new Exception("Invalid Technician User Id");
|
||||
|
||||
// Parse Devices
|
||||
Status.UpdateStatus(10, "Loading devices from the database");
|
||||
var devices = Database.Devices
|
||||
.Include(d => d.DeviceFlagAssignments)
|
||||
.Where(d => deviceSerialNumbers.Contains(d.SerialNumber)).ToList();
|
||||
|
||||
var missingDevices = deviceSerialNumbers.Where(sn => !devices.Any(u => string.Equals(sn, u.SerialNumber, StringComparison.OrdinalIgnoreCase))).ToList();
|
||||
if (missingDevices.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Bulk assignment aborted, invalid Serial Numbers: {0}", string.Join(", ", missingDevices)));
|
||||
}
|
||||
devices = devices.OrderBy(d => d.SerialNumber).ToList();
|
||||
|
||||
Status.ProgressOffset = 50;
|
||||
Status.ProgressMultiplier = 0.5;
|
||||
|
||||
if (@override)
|
||||
{
|
||||
DeviceFlagService.BulkAssignOverrideDevices(Database, flag, technician, comments, devices, Status);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeviceFlagService.BulkAssignAddDevices(Database, flag, technician, comments, devices, Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus ScheduleBulkAssignDevices(DeviceFlag deviceFlag, User technician, string comments, List<string> deviceSerialNumbers, bool @override)
|
||||
{
|
||||
JobDataMap taskData = new JobDataMap() {
|
||||
{"DeviceFlagId", deviceFlag.Id },
|
||||
{"TechnicianUserId", technician.UserId },
|
||||
{"Comments", comments },
|
||||
{"DeviceSerialNumbers", deviceSerialNumbers },
|
||||
{"Override", @override }
|
||||
};
|
||||
|
||||
var instance = new DeviceFlagBulkAssignTask();
|
||||
|
||||
return instance.ScheduleTask(taskData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
|
||||
namespace Disco.Services.Devices.DeviceFlags
|
||||
{
|
||||
public class DeviceFlagDeleteTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Device Flags - Delete Flag"; } }
|
||||
|
||||
public override bool SingleInstanceTask { get { return false; }}
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
public override bool LogExceptionsOnly { get { return true; } }
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
int deviceFlagId = (int)ExecutionContext.JobDetail.JobDataMap["DeviceFlagId"];
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
{
|
||||
DeviceFlagService.DeleteDeviceFlag(Database, deviceFlagId, Status);
|
||||
}
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus ScheduleNow(int deviceFlagId)
|
||||
{
|
||||
var taskData = new JobDataMap() { { "DeviceFlagId", deviceFlagId } };
|
||||
|
||||
var instance = new DeviceFlagDeleteTask();
|
||||
|
||||
return instance.ScheduleTask(taskData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,10 +61,25 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(string Key, out ADManagedGroup ManagedGroup)
|
||||
{
|
||||
return managedGroups.TryGetValue(Key, out ManagedGroup);
|
||||
}
|
||||
public bool TryGetValue<T>(string key, out T managedGroup) where T : ADManagedGroup
|
||||
{
|
||||
if (managedGroups.TryGetValue(key, out var item) && item is T typedItem)
|
||||
{
|
||||
managedGroup = typedItem;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
managedGroup = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ADManagedGroup> Values
|
||||
{
|
||||
get
|
||||
|
||||
Reference in New Issue
Block a user