From 2c77bd29add833933c994dc412fe1ca58d820963 Mon Sep 17 00:00:00 2001 From: Chen Gong Date: Thu, 21 Nov 2024 13:07:00 -0600 Subject: [PATCH] add backend services for dashboard conversation --- .../Repositories/IBotSharpRepository.cs | 2 + .../Users/IUserService.cs | 2 + .../Users/Models/Dashboard.cs | 7 +-- .../BotSharp.Abstraction/Users/Models/User.cs | 1 - .../FileRepository/FileRepository.User.cs | 42 +++++++++++++++++ .../Users/Services/UserService.cs | 21 +++++++++ .../Controllers/ConversationController.cs | 12 ++++- .../Controllers/DashboardController.cs | 26 ++++++++++ .../Collections/UserDocument.cs | 2 + .../Repository/MongoRepository.User.cs | 47 +++++++++++++++++++ 10 files changed, 154 insertions(+), 8 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 963b61be..efb6bccc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -31,6 +31,8 @@ public interface IBotSharpRepository void UpdateExistUser(string userId, User user) => throw new NotImplementedException(); void UpdateUserVerified(string userId) => throw new NotImplementedException(); void AddDashboardConversation(string userId, string conversationId) => throw new NotImplementedException(); + void RemoveDashboardConversation(string userId, string conversationId) => throw new NotImplementedException(); + void UpdateDashboardConversation(string userId, DashboardConversation dashConv) => throw new NotImplementedException(); void UpdateUserVerificationCode(string userId, string verficationCode) => throw new NotImplementedException(); void UpdateUserPassword(string userId, string password) => throw new NotImplementedException(); void UpdateUserEmail(string userId, string email) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 164017ea..a326f727 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -23,5 +23,7 @@ public interface IUserService Task GetUserTokenExpires(); Task UpdateUsersIsDisable(List userIds, bool isDisable); Task AddDashboardConversation(string userId, string conversationId); + Task RemoveDashboardConversation(string userId, string conversationId); + Task UpdateDashboardConversation(string userId, DashboardConversation dashConv); Task GetDashboard(string userId); } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs index 51d5f1a7..753cb390 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Dashboard.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BotSharp.Abstraction.Users.Models; @@ -20,6 +15,6 @@ public class DashboardComponent public class DashboardConversation : DashboardComponent { public string? ConversationId { get; set; } - public string? Instruction { get; set; } = "Default instruction: Ask bot to do something"; + public string? Instruction { get; set; } = ""; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index 2d62fe66..cb48701f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -23,7 +23,6 @@ public class User public bool Verified { get; set; } public string? AffiliateId { get; set; } public bool IsDisabled { get; set; } - public List DashboardConversations { get; set; } = []; public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index 0c731da1..4657d182 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -82,6 +82,8 @@ public partial class FileRepository // one user only has one dashboard currently var dash = Dashboards.FirstOrDefault(); dash ??= new(); + var existingConv = dash.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (existingConv != null) return; var dashconv = new DashboardConversation { @@ -95,4 +97,44 @@ public partial class FileRepository var path = Path.Combine(dir, DASHBOARD_FILE); File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); } + + public void RemoveDashboardConversation(string userId, string conversationId) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + if (dash == null) return; + + var dashconv = dash.ConversationList.FirstOrDefault( + c => string.Equals(c.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (dashconv == null) return; + + dash.ConversationList.Remove(dashconv); + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } + + public void UpdateDashboardConversation(string userId, DashboardConversation dashConv) + { + var user = GetUserById(userId); + if (user == null) return; + + // one user only has one dashboard currently + var dash = Dashboards.FirstOrDefault(); + if (dash == null) return; + + var curIdx = dash.ConversationList.ToList().FindIndex( + x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase)); + if (curIdx < 0) return; + + dash.ConversationList[curIdx] = dashConv; + + var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId); + var path = Path.Combine(dir, DASHBOARD_FILE); + File.WriteAllText(path, JsonSerializer.Serialize(dash, _options)); + } } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 65de880c..3060288e 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -649,6 +649,27 @@ public class UserService : IUserService await Task.CompletedTask; return true; } + + public async Task RemoveDashboardConversation(string userId, string conversationId) + { + var db = _services.GetRequiredService(); + db.RemoveDashboardConversation(userId, conversationId); + + await Task.CompletedTask; + return true; + } + + public async Task UpdateDashboardConversation(string userId, DashboardConversation newDashConv) + { + var db = _services.GetRequiredService(); + var dashConv = db.GetDashboard(userId)?.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, newDashConv.ConversationId)); + if (dashConv == null) return; + dashConv.Name = newDashConv.Name ?? dashConv.Name; + dashConv.Instruction = newDashConv.Instruction ?? dashConv.Instruction; + db.UpdateDashboardConversation(userId, dashConv); + await Task.CompletedTask; + return; + } public async Task GetDashboard(string userId) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 00fe8ce3..b42eebdf 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -491,7 +491,7 @@ public class ConversationController : ControllerBase #endregion #region miscellaneous - [HttpPut("/agent/{agentId}/conversation/{conversationId}/PinToDashboard")] + [HttpPut("/agent/{agentId}/conversation/{conversationId}/dashboard")] public async Task PinConversationToDashboard([FromRoute] string agentId, [FromRoute] string conversationId) { var userService = _services.GetRequiredService(); @@ -500,6 +500,16 @@ public class ConversationController : ControllerBase var pinned = await userService.AddDashboardConversation(user.Id, conversationId); return pinned; } + + [HttpDelete("/agent/{agentId}/conversation/{conversationId}/dashboard")] + public async Task UnpinConversationFromDashboard([FromRoute] string agentId, [FromRoute] string conversationId) + { + var userService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var unpinned = await userService.RemoveDashboardConversation(user.Id, conversationId); + return unpinned; + } #endregion #region Private methods diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs index 70b04e47..f75467fe 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs @@ -43,5 +43,31 @@ public class DashboardController : ControllerBase }; return result; } + + [HttpPost("/dashboard/component/conversation")] + public async Task UpdateDashboardConversationInstruction(string userId, UserDashboardConversationModel dashConv) + { + if (string.IsNullOrEmpty(dashConv.Name) && string.IsNullOrEmpty(dashConv.Instruction)) + { + return; + } + var newDashConv = new DashboardConversation + { + Id = Guid.Empty.ToString(), + ConversationId = dashConv.ConversationId + }; + if (!string.IsNullOrEmpty(dashConv.Name)) + { + newDashConv.Name = dashConv.Name; + } + if (!string.IsNullOrEmpty(dashConv.Instruction)) + { + newDashConv.Instruction = dashConv.Instruction; + } + + var userService = _services.GetRequiredService(); + await userService.UpdateDashboardConversation(userId, newDashConv); + return; + } #endregion } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index ffcd71a2..4195b641 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -23,6 +23,8 @@ public class UserDocument : MongoBase public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } + public Dashboard? Dashboard { get; set; } + public User ToUser() { return new User diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 55c454bd..ac339a01 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -146,4 +146,51 @@ public partial class MongoRepository UpdateUserIsDisable(userId, isDisable); } } + + public void AddDashboardConversation(string userId, string conversationId) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null) return; + var curDash = user.Dashboard ?? new Dashboard(); + curDash.ConversationList.Add(new DashboardConversation + { + Id = Guid.NewGuid().ToString(), + ConversationId = conversationId + }); + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard, curDash) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } + + public void RemoveDashboardConversation(string userId, string conversationId) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return; + var curDash = user.Dashboard; + var unpinConv = user.Dashboard.ConversationList.FirstOrDefault( + x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase)); + if (unpinConv == null) return; + curDash.ConversationList.Remove(unpinConv); + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard, curDash) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } + + public void UpdateDashboardConversation(string userId, DashboardConversation dashConv) + { + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId)); + if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return; + var curIdx = user.Dashboard.ConversationList.ToList().FindIndex( + x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase)); + if (curIdx < 0) return; + + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update.Set(x => x.Dashboard.ConversationList[curIdx], dashConv) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + } }