From ce1680a009c6f5eb2421227595c5357c6ae452dd Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 12 Dec 2017 14:22:22 -0500 Subject: [PATCH] event sql table and repo --- .../Models/Data/{Event.cs => EventMessage.cs} | 2 +- src/Core/Models/Data/IEvent.cs | 2 +- src/Core/Models/Table/Event.cs | 41 +++++++ src/Core/Repositories/IEventRepository.cs | 4 +- .../SqlServer/CipherRepository.cs | 6 +- .../Repositories/SqlServer/EventRepository.cs | 115 ++++++++++++++++++ .../Services/Implementations/EventService.cs | 16 +-- .../Utilities/ServiceCollectionExtensions.cs | 2 +- src/EventsProcessor/Functions.cs | 4 +- .../dbo/Stored Procedures/Event_Create.sql | 39 ++++++ src/Sql/dbo/Tables/Event.sql | 13 ++ 11 files changed, 225 insertions(+), 19 deletions(-) rename src/Core/Models/Data/{Event.cs => EventMessage.cs} (92%) create mode 100644 src/Core/Models/Table/Event.cs create mode 100644 src/Core/Repositories/SqlServer/EventRepository.cs create mode 100644 src/Sql/dbo/Stored Procedures/Event_Create.sql create mode 100644 src/Sql/dbo/Tables/Event.sql diff --git a/src/Core/Models/Data/Event.cs b/src/Core/Models/Data/EventMessage.cs similarity index 92% rename from src/Core/Models/Data/Event.cs rename to src/Core/Models/Data/EventMessage.cs index f4723a8652..0a1256a0dd 100644 --- a/src/Core/Models/Data/Event.cs +++ b/src/Core/Models/Data/EventMessage.cs @@ -3,7 +3,7 @@ using Bit.Core.Enums; namespace Bit.Core.Models.Data { - public class Event : IEvent + public class EventMessage : IEvent { public DateTime Date { get; set; } public EventType Type { get; set; } diff --git a/src/Core/Models/Data/IEvent.cs b/src/Core/Models/Data/IEvent.cs index 6dadc2fc83..5a975e0865 100644 --- a/src/Core/Models/Data/IEvent.cs +++ b/src/Core/Models/Data/IEvent.cs @@ -5,7 +5,6 @@ namespace Bit.Core.Models.Data { public interface IEvent { - DateTime Date { get; set; } EventType Type { get; set; } Guid? UserId { get; set; } Guid? OrganizationId { get; set; } @@ -14,5 +13,6 @@ namespace Bit.Core.Models.Data Guid? GroupId { get; set; } Guid? OrganizationUserId { get; set; } Guid? ActingUserId { get; set; } + DateTime Date { get; set; } } } diff --git a/src/Core/Models/Table/Event.cs b/src/Core/Models/Table/Event.cs new file mode 100644 index 0000000000..09584a66b0 --- /dev/null +++ b/src/Core/Models/Table/Event.cs @@ -0,0 +1,41 @@ +using System; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Utilities; + +namespace Bit.Core.Models.Table +{ + public class Event : ITableObject, IEvent + { + public Event() { } + + public Event(IEvent e) + { + Date = e.Date; + Type = e.Type; + UserId = e.UserId; + OrganizationId = e.OrganizationId; + CipherId = e.CipherId; + CollectionId = e.CollectionId; + GroupId = e.GroupId; + OrganizationUserId = e.OrganizationUserId; + ActingUserId = e.ActingUserId; + } + + public Guid Id { get; set; } + public DateTime Date { get; set; } + public EventType Type { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? CipherId { get; set; } + public Guid? CollectionId { get; set; } + public Guid? GroupId { get; set; } + public Guid? OrganizationUserId { get; set; } + public Guid? ActingUserId { get; set; } + + public void SetNewId() + { + Id = CoreHelpers.GenerateComb(); + } + } +} diff --git a/src/Core/Repositories/IEventRepository.cs b/src/Core/Repositories/IEventRepository.cs index dce8a6315f..4baf4eda83 100644 --- a/src/Core/Repositories/IEventRepository.cs +++ b/src/Core/Repositories/IEventRepository.cs @@ -8,7 +8,7 @@ namespace Bit.Core.Repositories public interface IEventRepository { Task> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate); - Task CreateAsync(IEvent entity); - Task CreateManyAsync(IList entities); + Task CreateAsync(IEvent e); + Task CreateManyAsync(IList e); } } diff --git a/src/Core/Repositories/SqlServer/CipherRepository.cs b/src/Core/Repositories/SqlServer/CipherRepository.cs index 0481689cba..c7afacfa52 100644 --- a/src/Core/Repositories/SqlServer/CipherRepository.cs +++ b/src/Core/Repositories/SqlServer/CipherRepository.cs @@ -392,8 +392,7 @@ namespace Bit.Core.Repositories.SqlServer { if(folders.Any()) { - using(var bulkCopy = new SqlBulkCopy(connection, - SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.FireTriggers, transaction)) + using(var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction)) { bulkCopy.DestinationTableName = "[dbo].[Folder]"; var dataTable = BuildFoldersTable(folders); @@ -401,8 +400,7 @@ namespace Bit.Core.Repositories.SqlServer } } - using(var bulkCopy = new SqlBulkCopy(connection, - SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.FireTriggers, transaction)) + using(var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction)) { bulkCopy.DestinationTableName = "[dbo].[Cipher]"; var dataTable = BuildCiphersTable(ciphers); diff --git a/src/Core/Repositories/SqlServer/EventRepository.cs b/src/Core/Repositories/SqlServer/EventRepository.cs new file mode 100644 index 0000000000..8b1ffc903b --- /dev/null +++ b/src/Core/Repositories/SqlServer/EventRepository.cs @@ -0,0 +1,115 @@ +using System; +using Bit.Core.Models.Table; +using System.Threading.Tasks; +using System.Collections.Generic; +using Bit.Core.Models.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Data; + +namespace Bit.Core.Repositories.SqlServer +{ + public class EventRepository : Repository, IEventRepository + { + public EventRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString) + { } + + public EventRepository(string connectionString) + : base(connectionString) + { } + + public Task> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate) + { + // TODO + throw new NotImplementedException(); + } + + public async Task CreateAsync(IEvent e) + { + if(!(e is Event ev)) + { + ev = new Event(e); + } + + await base.CreateAsync(ev); + } + + public async Task CreateManyAsync(IList entities) + { + if(!entities.Any()) + { + return; + } + + using(var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + using(var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, null)) + { + bulkCopy.DestinationTableName = "[dbo].[Event]"; + var dataTable = BuildEventsTable(entities.Select(e => e is Event ? e as Event : new Event(e))); + await bulkCopy.WriteToServerAsync(dataTable); + } + } + } + + private DataTable BuildEventsTable(IEnumerable events) + { + var e = events.FirstOrDefault(); + if(e == null) + { + throw new ApplicationException("Must have some events to bulk import."); + } + + var eventsTable = new DataTable("EventDataTable"); + + var idColumn = new DataColumn(nameof(e.Id), e.Id.GetType()); + eventsTable.Columns.Add(idColumn); + var typeColumn = new DataColumn(nameof(e.Type), typeof(int)); + eventsTable.Columns.Add(typeColumn); + var dateColumn = new DataColumn(nameof(e.Date), e.Date.GetType()); + eventsTable.Columns.Add(dateColumn); + var userIdColumn = new DataColumn(nameof(e.UserId), typeof(Guid)); + eventsTable.Columns.Add(userIdColumn); + var organizationIdColumn = new DataColumn(nameof(e.OrganizationId), typeof(Guid)); + eventsTable.Columns.Add(organizationIdColumn); + var cipherIdColumn = new DataColumn(nameof(e.CipherId), typeof(Guid)); + eventsTable.Columns.Add(cipherIdColumn); + var groupIdColumn = new DataColumn(nameof(e.GroupId), typeof(Guid)); + eventsTable.Columns.Add(groupIdColumn); + var collectionIdColumn = new DataColumn(nameof(e.CollectionId), typeof(Guid)); + eventsTable.Columns.Add(collectionIdColumn); + var actingUserIdColumn = new DataColumn(nameof(e.ActingUserId), typeof(Guid)); + eventsTable.Columns.Add(actingUserIdColumn); + var organizationUserIdColumn = new DataColumn(nameof(e.OrganizationUserId), typeof(Guid)); + eventsTable.Columns.Add(organizationUserIdColumn); + + var keys = new DataColumn[1]; + keys[0] = idColumn; + eventsTable.PrimaryKey = keys; + + foreach(var ev in events) + { + ev.SetNewId(); + + var row = eventsTable.NewRow(); + + row[idColumn] = ev.Id; + row[typeColumn] = ev.Type; + row[dateColumn] = ev.Date; + row[userIdColumn] = ev.UserId; + row[organizationIdColumn] = ev.OrganizationId; + row[cipherIdColumn] = ev.CipherId; + row[groupIdColumn] = ev.GroupId; + row[collectionIdColumn] = ev.CollectionId; + row[actingUserIdColumn] = ev.ActingUserId; + row[organizationUserIdColumn] = ev.OrganizationUserId; + + eventsTable.Rows.Add(row); + } + + return eventsTable; + } + } +} diff --git a/src/Core/Services/Implementations/EventService.cs b/src/Core/Services/Implementations/EventService.cs index 4e86f8d04e..19ad855757 100644 --- a/src/Core/Services/Implementations/EventService.cs +++ b/src/Core/Services/Implementations/EventService.cs @@ -33,7 +33,7 @@ namespace Bit.Core.Services var now = DateTime.UtcNow; var events = new List { - new Event + new EventMessage { UserId = userId, Type = type, @@ -44,7 +44,7 @@ namespace Bit.Core.Services IEnumerable orgEvents; if(_currentContext.UserId.HasValue) { - orgEvents = _currentContext.Organizations.Select(o => new Event + orgEvents = _currentContext.Organizations.Select(o => new EventMessage { OrganizationId = o.Id, UserId = userId, @@ -56,7 +56,7 @@ namespace Bit.Core.Services { var orgs = await _organizationUserRepository.GetManyByUserAsync(userId); orgEvents = orgs.Where(o => o.Status == OrganizationUserStatusType.Confirmed) - .Select(o => new Event + .Select(o => new EventMessage { OrganizationId = o.Id, UserId = userId, @@ -83,7 +83,7 @@ namespace Bit.Core.Services return; } - var e = new Event + var e = new EventMessage { OrganizationId = cipher.OrganizationId, UserId = cipher.OrganizationId.HasValue ? null : cipher.UserId, @@ -97,7 +97,7 @@ namespace Bit.Core.Services public async Task LogCollectionEventAsync(Collection collection, EventType type) { - var e = new Event + var e = new EventMessage { OrganizationId = collection.OrganizationId, CollectionId = collection.Id, @@ -110,7 +110,7 @@ namespace Bit.Core.Services public async Task LogGroupEventAsync(Group group, EventType type) { - var e = new Event + var e = new EventMessage { OrganizationId = group.OrganizationId, GroupId = group.Id, @@ -123,7 +123,7 @@ namespace Bit.Core.Services public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type) { - var e = new Event + var e = new EventMessage { OrganizationId = organizationUser.OrganizationId, UserId = organizationUser.UserId, @@ -137,7 +137,7 @@ namespace Bit.Core.Services public async Task LogOrganizationEventAsync(Organization organization, EventType type) { - var e = new Event + var e = new EventMessage { OrganizationId = organization.Id, Type = type, diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 5ac3a60877..48b89b2f6c 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -44,7 +44,7 @@ namespace Bit.Core.Utilities if(globalSettings.SelfHosted) { - // TODO: Sql server event repo + services.AddSingleton(); } else { diff --git a/src/EventsProcessor/Functions.cs b/src/EventsProcessor/Functions.cs index 6f03c8fd04..77b2af2c8d 100644 --- a/src/EventsProcessor/Functions.cs +++ b/src/EventsProcessor/Functions.cs @@ -42,13 +42,13 @@ namespace Bit.EventsProcessor var token = JToken.Parse(message); if(token is JArray) { - var events = token.ToObject>() + var events = token.ToObject>() .Select(e => new EventTableEntity(e) as IEvent).ToList(); await _eventWriteService.CreateManyAsync(events); } else if(token is JObject) { - var e = token.ToObject(); + var e = token.ToObject(); await _eventWriteService.CreateAsync(new EventTableEntity(e)); } } diff --git a/src/Sql/dbo/Stored Procedures/Event_Create.sql b/src/Sql/dbo/Stored Procedures/Event_Create.sql new file mode 100644 index 0000000000..e5ddeeb688 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Event_Create.sql @@ -0,0 +1,39 @@ +CREATE PROCEDURE [dbo].[Event_Create] + @Id UNIQUEIDENTIFIER, + @Type INT, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @CipherId UNIQUEIDENTIFIER, + @CollectionId UNIQUEIDENTIFIER, + @GroupId UNIQUEIDENTIFIER, + @OrganizationUserId UNIQUEIDENTIFIER, + @Date DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Event] + ( + [Id], + [Type], + [UserId], + [OrganizationId], + [CipherId], + [CollectionId], + [GroupId], + [OrganizationUserId], + [Date] + ) + VALUES + ( + @Id, + @Type, + @UserId, + @OrganizationId, + @CipherId, + @CollectionId, + @GroupId, + @OrganizationUserId, + @Date + ) +END diff --git a/src/Sql/dbo/Tables/Event.sql b/src/Sql/dbo/Tables/Event.sql new file mode 100644 index 0000000000..7a4a2aeb53 --- /dev/null +++ b/src/Sql/dbo/Tables/Event.sql @@ -0,0 +1,13 @@ +CREATE TABLE [dbo].[Event] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [Type] INT NOT NULL, + [UserId] UNIQUEIDENTIFIER NULL, + [OrganizationId] UNIQUEIDENTIFIER NULL, + [CipherId] UNIQUEIDENTIFIER NULL, + [CollectionId] UNIQUEIDENTIFIER NULL, + [GroupId] UNIQUEIDENTIFIER NULL, + [OrganizationUserId] UNIQUEIDENTIFIER NULL, + [Date] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC) +); +