diff --git a/Core/Resgrid.Config/ChatConfig.cs b/Core/Resgrid.Config/ChatConfig.cs index e5aa6cec..eefa92a4 100644 --- a/Core/Resgrid.Config/ChatConfig.cs +++ b/Core/Resgrid.Config/ChatConfig.cs @@ -10,10 +10,10 @@ public static class ChatConfig public static string NovuApplicationId = ""; public static string NovuSecretKey = ""; - public static string NovuUnitFcmProviderId = ""; - public static string NovuUnitApnsProviderId = ""; - public static string NovuResponderFcmProviderId = ""; - public static string NovuResponderApnsProviderId = ""; + public static string NovuUnitFcmProviderId = "firebase-cloud-messaging-7Z5wHFPpQ"; + public static string NovuUnitApnsProviderId = "unit-apns"; + public static string NovuResponderFcmProviderId = "respond-firebase-cloud-messaging"; + public static string NovuResponderApnsProviderId = "respond-apns"; public static string NovuDispatchUnitWorkflowId = "unit-dispatch"; public static string NovuDispatchUserWorkflowId = "user-dispatch"; public static string NovuMessageUserWorkflowId = "user-message"; diff --git a/Core/Resgrid.Config/InfoConfig.cs b/Core/Resgrid.Config/InfoConfig.cs index b5d2535a..70f51f12 100644 --- a/Core/Resgrid.Config/InfoConfig.cs +++ b/Core/Resgrid.Config/InfoConfig.cs @@ -1,4 +1,6 @@ -namespace Resgrid.Config +using System.Collections.Generic; + +namespace Resgrid.Config { public static class InfoConfig { @@ -21,5 +23,39 @@ public static class InfoConfig public static string RelayAppKey = "RelayAppKey"; public static string EmailProcessorKey = "EmailProcessorKey"; + + public static List Locations = new List() + { + new ResgridSystemLocation() + { + Name = "US-West", + DisplayName = "Resgrid North America (Global)", + LocationInfo = + "This is the Resgrid system hosted in the Western United States (private datacenter). This system services most Resgrid customers.", + IsDefault = true, + ApiUrl = "https://api.resgrid.com", + AllowsFreeAccounts = true + }, + new ResgridSystemLocation() + { + Name = "EU-Central", + DisplayName = "Resgrid Europe", + LocationInfo = + "This is the Resgrid system hosted in Central Europe (on OVH). This system services Resgrid customers in the European Union to help with data compliance requirements.", + IsDefault = false, + ApiUrl = "https://api.eu.resgrid.com", + AllowsFreeAccounts = false + } + }; + } + + public class ResgridSystemLocation + { + public string Name { get; set; } + public string DisplayName { get; set; } + public string LocationInfo { get; set; } + public bool IsDefault { get; set; } + public string ApiUrl { get; set; } + public bool AllowsFreeAccounts { get; set; } } } diff --git a/Core/Resgrid.Config/SystemBehaviorConfig.cs b/Core/Resgrid.Config/SystemBehaviorConfig.cs index 4617a35f..24e75cd1 100644 --- a/Core/Resgrid.Config/SystemBehaviorConfig.cs +++ b/Core/Resgrid.Config/SystemBehaviorConfig.cs @@ -185,6 +185,11 @@ public static class SystemBehaviorConfig /// public static string BlogUrl = "https://blog.resgrid.com"; + /// + /// Sets the name of the location this Resgrid system is running in + /// + public static string LocationName = "US-West"; + public static string GetEnvPrefix() { switch (Environment) diff --git a/Core/Resgrid.Model/Services/ICommunicationService.cs b/Core/Resgrid.Model/Services/ICommunicationService.cs index 801c60b7..a758e599 100644 --- a/Core/Resgrid.Model/Services/ICommunicationService.cs +++ b/Core/Resgrid.Model/Services/ICommunicationService.cs @@ -19,7 +19,7 @@ public interface ICommunicationService /// The profile. /// Task<System.Boolean>. Task SendMessageAsync(Message message, string sendersName, string departmentNumber, int departmentId, - UserProfile profile = null); + UserProfile profile = null, Department department = null); /// /// Sends the call asynchronous. @@ -110,6 +110,6 @@ Task SendTextMessageAsync(string userId, string title, string message, int /// The profile. /// Task<System.Boolean>. Task SendCalendarAsync(string userId, int departmentId, string message, string departmentNumber, - string title = "Notification", UserProfile profile = null); + string title = "Notification", UserProfile profile = null, Department department = null); } } diff --git a/Core/Resgrid.Services/CalendarService.cs b/Core/Resgrid.Services/CalendarService.cs index 568e0bfd..9f6016ae 100644 --- a/Core/Resgrid.Services/CalendarService.cs +++ b/Core/Resgrid.Services/CalendarService.cs @@ -469,7 +469,7 @@ public async Task NotifyNewCalendarItemAsync(CalendarItem calendarItem) // Notify the entire department foreach (var profile in profiles) { - await _communicationService.SendCalendarAsync(profile.Key, calendarItem.DepartmentId, message, departmentNumber, title, profile.Value); + await _communicationService.SendCalendarAsync(profile.Key, calendarItem.DepartmentId, message, departmentNumber, title, profile.Value, department); } } else @@ -487,9 +487,9 @@ public async Task NotifyNewCalendarItemAsync(CalendarItem calendarItem) foreach (var member in group.Members) { if (profiles.ContainsKey(member.UserId)) - await _communicationService.SendNotificationAsync(member.UserId, calendarItem.DepartmentId, message, departmentNumber, department, title, profiles[member.UserId]); + await _communicationService.SendCalendarAsync(member.UserId, calendarItem.DepartmentId, message, departmentNumber, title, profiles[member.UserId], department); else - await _communicationService.SendNotificationAsync(member.UserId, calendarItem.DepartmentId, message, departmentNumber, department, title, null); + await _communicationService.SendCalendarAsync(member.UserId, calendarItem.DepartmentId, message, departmentNumber, title, null, department); } } } diff --git a/Core/Resgrid.Services/CallEmailTemplates/OttawaKingstonTorontoTemplate.cs b/Core/Resgrid.Services/CallEmailTemplates/OttawaKingstonTorontoTemplate.cs index b19f78ae..0e03e435 100644 --- a/Core/Resgrid.Services/CallEmailTemplates/OttawaKingstonTorontoTemplate.cs +++ b/Core/Resgrid.Services/CallEmailTemplates/OttawaKingstonTorontoTemplate.cs @@ -49,48 +49,84 @@ public async Task GenerateCall(CallEmail email, string managingUser, List< if (String.IsNullOrEmpty(email.Subject)) return null; - string[] sections = email.TextBody.Split(new[] {" ALPHA 512 "}, StringSplitOptions.None); - string[] sectionOneParts = sections[0].Split(new[] {" "}, StringSplitOptions.None); - - Call c = new Call(); - c.Notes = email.TextBody; - c.Name = sections[1].Trim(); - c.LoggedOn = DateTime.UtcNow; - c.Priority = priority; - c.ReportingUserId = managingUser; - c.Dispatches = new Collection(); - c.CallSource = (int)CallSources.EmailImport; - c.SourceIdentifier = email.MessageId; - c.NatureOfCall = sections[1].Trim(); - c.IncidentNumber = sectionOneParts[0].Trim(); - c.ExternalIdentifier = sectionOneParts[0].Trim(); - - if (users != null && users.Any()) + try { - foreach (var u in users) + string[] sections = email.TextBody.Split(new[] { " ALPHA 512 " }, StringSplitOptions.None); + string[] sectionOneParts = sections[0].Split(new[] { " " }, StringSplitOptions.None); + + Call c = new Call(); + c.Notes = email.TextBody; + c.Name = sections[1].Trim(); + c.LoggedOn = DateTime.UtcNow; + c.Priority = priority; + c.ReportingUserId = managingUser; + c.Dispatches = new Collection(); + c.CallSource = (int)CallSources.EmailImport; + c.SourceIdentifier = email.MessageId; + c.NatureOfCall = sections[1].Trim(); + c.IncidentNumber = sectionOneParts[0].Trim(); + c.ExternalIdentifier = sectionOneParts[0].Trim(); + + if (users != null && users.Any()) { - CallDispatch cd = new CallDispatch(); - cd.UserId = u.UserId; + foreach (var u in users) + { + CallDispatch cd = new CallDispatch(); + cd.UserId = u.UserId; - c.Dispatches.Add(cd); + c.Dispatches.Add(cd); + } } - } - // Search for an active call - if (activeCalls != null && activeCalls.Any()) + // Search for an active call + if (activeCalls != null && activeCalls.Any()) + { + var activeCall = activeCalls.FirstOrDefault(x => x.IncidentNumber == c.IncidentNumber); + + if (activeCall != null) + { + activeCall.Notes = c.Notes; + activeCall.LastDispatchedOn = DateTime.UtcNow; + + return activeCall; + } + } + + return c; + } + catch (Exception ex) { - var activeCall = activeCalls.FirstOrDefault(x => x.IncidentNumber == c.IncidentNumber); + Call c = new Call(); + c.Name = email.Subject; + c.NatureOfCall = $"ERROR PROCESSING DISPATCH EMAIL, Unprocessed email body: {email.TextBody}"; - if (activeCall != null) + if (users != null && users.Any()) { - activeCall.Notes = c.Notes; - activeCall.LastDispatchedOn = DateTime.UtcNow; + foreach (var u in users) + { + CallDispatch cd = new CallDispatch(); + cd.UserId = u.UserId; - return activeCall; + c.Dispatches.Add(cd); + } } - } - return c; + // Search for an active call + if (activeCalls != null && activeCalls.Any()) + { + var activeCall = activeCalls.FirstOrDefault(x => x.IncidentNumber == c.IncidentNumber); + + if (activeCall != null) + { + activeCall.Notes = c.Notes; + activeCall.LastDispatchedOn = DateTime.UtcNow; + + return activeCall; + } + } + + return c; + } } } } diff --git a/Core/Resgrid.Services/CommunicationService.cs b/Core/Resgrid.Services/CommunicationService.cs index 68a0d3e6..bba50d9b 100644 --- a/Core/Resgrid.Services/CommunicationService.cs +++ b/Core/Resgrid.Services/CommunicationService.cs @@ -39,7 +39,7 @@ public CommunicationService(ISmsService smsService, IEmailService emailService, _userStateService = userStateService; } - public async Task SendMessageAsync(Message message, string sendersName, string departmentNumber, int departmentId, UserProfile profile = null) + public async Task SendMessageAsync(Message message, string sendersName, string departmentNumber, int departmentId, UserProfile profile = null, Department department = null) { if (Config.SystemBehaviorConfig.DoNotBroadcast && !Config.SystemBehaviorConfig.BypassDoNotBroadcastDepartments.Contains(departmentId)) return false; @@ -79,6 +79,7 @@ public async Task SendMessageAsync(Message message, string sendersName, st { var spm = new StandardPushMessage(); spm.MessageId = message.MessageId; + spm.DepartmentCode = department?.Code; if (message.SystemGenerated) spm.SubTitle = "Msg from System"; @@ -124,6 +125,7 @@ public async Task SendCallAsync(Call call, CallDispatch dispatch, string d spc.Priority = call.Priority; spc.ActiveCallCount = 1; spc.DepartmentId = departmentId; + spc.DepartmentCode = call.Department?.Code; if (call.CallPriority != null && !String.IsNullOrWhiteSpace(call.CallPriority.Color)) { @@ -335,7 +337,7 @@ public async Task SendNotificationAsync(string userId, int departmentId, s return true; } - public async Task SendCalendarAsync(string userId, int departmentId, string message, string departmentNumber, string title = "Notification", UserProfile profile = null) + public async Task SendCalendarAsync(string userId, int departmentId, string message, string departmentNumber, string title = "Notification", UserProfile profile = null, Department department = null) { if (Config.SystemBehaviorConfig.DoNotBroadcast && !Config.SystemBehaviorConfig.BypassDoNotBroadcastDepartments.Contains(departmentId)) return false; @@ -364,6 +366,7 @@ public async Task SendCalendarAsync(string userId, int departmentId, strin var spm = new StandardPushMessage(); spm.Title = "Calendar"; spm.SubTitle = $"{title} {message}"; + spm.DepartmentCode = null; try { @@ -467,6 +470,7 @@ public async Task SendTroubleAlertAsync(TroubleAlertEvent troubleAlertEven spc.Priority = (int)CallPriority.Emergency; spc.ActiveCallCount = 1; spc.DepartmentId = departmentId; + spc.DepartmentCode = call.Department?.Code; string subTitle = String.Empty; if (!String.IsNullOrWhiteSpace(unitAddress)) diff --git a/Core/Resgrid.Services/PushService.cs b/Core/Resgrid.Services/PushService.cs index e74684d0..244b84b9 100644 --- a/Core/Resgrid.Services/PushService.cs +++ b/Core/Resgrid.Services/PushService.cs @@ -112,7 +112,8 @@ public async Task PushMessage(StandardPushMessage message, string userId, try { - await _novuProvider.SendUserMessage(message.Title, message.SubTitle, userId, message.DepartmentCode, string.Format("M{0}", message.MessageId), null); + if (!string.IsNullOrWhiteSpace(message.DepartmentCode)) + await _novuProvider.SendUserMessage(message.Title, message.SubTitle, userId, message.DepartmentCode, string.Format("M{0}", message.MessageId), null); } catch (Exception ex) { @@ -143,7 +144,8 @@ public async Task PushNotification(StandardPushMessage message, string use } try { - await _novuProvider.SendUserMessage(message.Title, message.SubTitle, userId, message.DepartmentCode, string.Format("N{0}", message.MessageId), null); + if (!string.IsNullOrWhiteSpace(message.DepartmentCode)) + await _novuProvider.SendUserNotification(message.Title, message.SubTitle, userId, message.DepartmentCode, string.Format("N{0}", message.MessageId), null); } catch (Exception ex) { diff --git a/Providers/Resgrid.Providers.Messaging/NovuProvider.cs b/Providers/Resgrid.Providers.Messaging/NovuProvider.cs index 21854657..948b3857 100644 --- a/Providers/Resgrid.Providers.Messaging/NovuProvider.cs +++ b/Providers/Resgrid.Providers.Messaging/NovuProvider.cs @@ -231,6 +231,7 @@ private async Task SendNotification(string title, string body, string reci { subject = title, body = body, + id = eventCode }, overrides = new { diff --git a/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs b/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs index 9ace215d..dd281646 100644 --- a/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs @@ -24,6 +24,23 @@ public ConfigController() } #endregion Members and Constructors + /// + /// Gets the system config + /// + /// + [HttpGet("GetSystemConfig")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetSystemConfig() + { + var result = new GetSystemConfigResult(); + + result.PageSize = 1; + result.Status = ResponseHelper.Success; + ResponseHelper.PopulateV4ResponseData(result); + + return result; + } + /// /// Gets the config values for a key /// diff --git a/Web/Resgrid.Web.Services/Models/v4/Configs/GetSystemConfigResult.cs b/Web/Resgrid.Web.Services/Models/v4/Configs/GetSystemConfigResult.cs new file mode 100644 index 00000000..126edadb --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Configs/GetSystemConfigResult.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Resgrid.Config; + +namespace Resgrid.Web.Services.Models.v4.Configs +{ + /// + /// Gets Configuration Information for the Resgrid System + /// + public class GetSystemConfigResult : StandardApiResponseV4Base + { + /// + /// Response Data + /// + public GetSystemConfigResultData Data { get; set; } + + /// + /// Default constructor + /// + public GetSystemConfigResult() + { + Data = new GetSystemConfigResultData(); + } + } + + /// + /// Information about the Resgrid System + /// + public class GetSystemConfigResultData + { + /// + /// Resgrid Datacenter Locations + /// + public List Locations { get; set; } + + public GetSystemConfigResultData() + { + Locations = InfoConfig.Locations; + } + } +} diff --git a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml index 3ffa17a0..7b1d9f63 100644 --- a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml +++ b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml @@ -3349,6 +3349,12 @@ Generic configuration api endpoints + + + Gets the system config + + + Gets the config values for a key @@ -5540,6 +5546,31 @@ Analytics Host + + + Gets Configuration Information for the Resgrid System + + + + + Response Data + + + + + Default constructor + + + + + Information about the Resgrid System + + + + + Resgrid Datacenter Locations + + Gets the notes for a contact diff --git a/Workers/Resgrid.Workers.Framework/Logic/BroadcastMessageLogic.cs b/Workers/Resgrid.Workers.Framework/Logic/BroadcastMessageLogic.cs index db9c7f6f..fd19df32 100644 --- a/Workers/Resgrid.Workers.Framework/Logic/BroadcastMessageLogic.cs +++ b/Workers/Resgrid.Workers.Framework/Logic/BroadcastMessageLogic.cs @@ -60,10 +60,12 @@ public static async Task ProcessMessageQueueItem(MessageQueueItem mqi) if (mqi.Profiles != null) { var sendingToProfile = mqi.Profiles.FirstOrDefault(x => x.UserId == mqi.Message.ReceivingUserId); + var departmentService = Bootstrapper.GetKernel().Resolve(); + var department = await departmentService.GetDepartmentByIdAsync(mqi.DepartmentId); if (sendingToProfile != null) { - await _communicationService.SendMessageAsync(mqi.Message, name, mqi.DepartmentTextNumber, mqi.DepartmentId, sendingToProfile); + await _communicationService.SendMessageAsync(mqi.Message, name, mqi.DepartmentTextNumber, mqi.DepartmentId, sendingToProfile, department); } else { @@ -72,11 +74,16 @@ public static async Task ProcessMessageQueueItem(MessageQueueItem mqi) if (sender != null) name = sender.FullName.AsFirstNameLastName; + + //TODO: What to do here, I don't know why this path is empty. -SJ } } } else if (mqi.Message.MessageRecipients != null && mqi.Message.MessageRecipients.Any()) { + var departmentService = Bootstrapper.GetKernel().Resolve(); + var department = await departmentService.GetDepartmentByIdAsync(mqi.DepartmentId); + foreach (var recipient in mqi.Message.MessageRecipients) { var sendingToProfile = mqi.Profiles.FirstOrDefault(x => x.UserId == recipient.UserId); @@ -84,7 +91,7 @@ public static async Task ProcessMessageQueueItem(MessageQueueItem mqi) if (sendingToProfile != null) { - await _communicationService.SendMessageAsync(mqi.Message, name, mqi.DepartmentTextNumber, mqi.DepartmentId, sendingToProfile); + await _communicationService.SendMessageAsync(mqi.Message, name, mqi.DepartmentTextNumber, mqi.DepartmentId, sendingToProfile, department); } } }