From d75ca51d754c08b27618efe343b77061e0c72a88 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 18 Dec 2017 23:15:16 -0500 Subject: [PATCH] sql event repo "Get" implementations --- .../Repositories/SqlServer/EventRepository.cs | 89 ++++++++--- src/Sql/Sql.sqlproj | 4 + .../Event_ReadPageByCipherId.sql | 33 +++++ .../Event_ReadPageByOrganizationId.sql | 23 +++ ...t_ReadPageByOrganizationIdActingUserId.sql | 25 ++++ .../Event_ReadPageByUserId.sql | 24 +++ src/Sql/dbo/Tables/Event.sql | 2 +- util/Setup/DbScripts/2017-12-12_00_Events.sql | 139 +++++++++++++++++- 8 files changed, 320 insertions(+), 19 deletions(-) create mode 100644 src/Sql/dbo/Stored Procedures/Event_ReadPageByCipherId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationIdActingUserId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Event_ReadPageByUserId.sql diff --git a/src/Core/Repositories/SqlServer/EventRepository.cs b/src/Core/Repositories/SqlServer/EventRepository.cs index a3c9100c5..e345ee9c1 100644 --- a/src/Core/Repositories/SqlServer/EventRepository.cs +++ b/src/Core/Repositories/SqlServer/EventRepository.cs @@ -6,6 +6,7 @@ using Bit.Core.Models.Data; using System.Data.SqlClient; using System.Linq; using System.Data; +using Dapper; namespace Bit.Core.Repositories.SqlServer { @@ -19,32 +20,47 @@ namespace Bit.Core.Repositories.SqlServer : base(connectionString) { } - public Task> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate, + public async Task> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate, PageOptions pageOptions) { - // TODO - throw new NotImplementedException(); + return await GetManyAsync($"[{Schema}].[Event_ReadPageByUserId]", + new Dictionary + { + ["@UserId"] = userId + }, startDate, endDate, pageOptions); } - public Task> GetManyByOrganizationAsync(Guid organizationId, + public async Task> GetManyByOrganizationAsync(Guid organizationId, DateTime startDate, DateTime endDate, PageOptions pageOptions) { - // TODO - throw new NotImplementedException(); + return await GetManyAsync($"[{Schema}].[Event_ReadPageByOrganizationId]", + new Dictionary + { + ["@OrganizationId"] = organizationId + }, startDate, endDate, pageOptions); } - public Task> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId, + public async Task> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId, DateTime startDate, DateTime endDate, PageOptions pageOptions) { - // TODO - throw new NotImplementedException(); + return await GetManyAsync($"[{Schema}].[Event_ReadPageByOrganizationIdActingUserId]", + new Dictionary + { + ["@OrganizationId"] = organizationId, + ["@ActingUserId"] = actingUserId + }, startDate, endDate, pageOptions); } - public Task> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate, + public async Task> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate, PageOptions pageOptions) { - // TODO - throw new NotImplementedException(); + return await GetManyAsync($"[{Schema}].[Event_ReadPageByCipherId]", + new Dictionary + { + ["@OrganizationId"] = cipher.OrganizationId, + ["@UserId"] = cipher.UserId, + ["@CipherId"] = cipher.Id + }, startDate, endDate, pageOptions); } public async Task CreateAsync(IEvent e) @@ -82,6 +98,39 @@ namespace Bit.Core.Repositories.SqlServer } } + private async Task> GetManyAsync(string sprocName, + IDictionary sprocParams, DateTime startDate, DateTime endDate, PageOptions pageOptions) + { + DateTime? beforeDate = null; + if(!string.IsNullOrWhiteSpace(pageOptions.ContinuationToken) && + long.TryParse(pageOptions.ContinuationToken, out var binaryDate)) + { + beforeDate = DateTime.SpecifyKind(DateTime.FromBinary(binaryDate), DateTimeKind.Utc); + } + + var parameters = new DynamicParameters(sprocParams); + parameters.Add("@PageSize", pageOptions.PageSize, DbType.Int32); + // Explicitly use DbType.DateTime2 for proper precision. + // ref: https://github.com/StackExchange/Dapper/issues/229 + parameters.Add("@StartDate", startDate.ToUniversalTime(), DbType.DateTime2, null, 7); + parameters.Add("@EndDate", endDate.ToUniversalTime(), DbType.DateTime2, null, 7); + parameters.Add("@BeforeDate", beforeDate, DbType.DateTime2, null, 7); + + using(var connection = new SqlConnection(ConnectionString)) + { + var events = (await connection.QueryAsync(sprocName, parameters, + commandType: CommandType.StoredProcedure)).ToList(); + + var result = new PagedResult(); + if(events.Any() && events.Count >= pageOptions.PageSize) + { + result.ContinuationToken = events.Last().Date.ToBinary().ToString(); + } + result.Data.AddRange(events); + return result; + } + } + private DataTable BuildEventsTable(IEnumerable events) { var e = events.FirstOrDefault(); @@ -106,10 +155,14 @@ namespace Bit.Core.Repositories.SqlServer eventsTable.Columns.Add(collectionIdColumn); var groupIdColumn = new DataColumn(nameof(e.GroupId), typeof(Guid)); eventsTable.Columns.Add(groupIdColumn); - 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 actingUserIdColumn = new DataColumn(nameof(e.ActingUserId), typeof(Guid)); + eventsTable.Columns.Add(actingUserIdColumn); + var deviceTypeColumn = new DataColumn(nameof(e.DeviceType), typeof(int)); + eventsTable.Columns.Add(deviceTypeColumn); + var ipAddressColumn = new DataColumn(nameof(e.IpAddress), e.IpAddress.GetType()); + eventsTable.Columns.Add(ipAddressColumn); var dateColumn = new DataColumn(nameof(e.Date), e.Date.GetType()); eventsTable.Columns.Add(dateColumn); @@ -125,15 +178,17 @@ namespace Bit.Core.Repositories.SqlServer row[idColumn] = ev.Id; row[typeColumn] = (int)ev.Type; - row[dateColumn] = ev.Date; row[userIdColumn] = ev.UserId.HasValue ? (object)ev.UserId.Value : DBNull.Value; row[organizationIdColumn] = ev.OrganizationId.HasValue ? (object)ev.OrganizationId.Value : DBNull.Value; row[cipherIdColumn] = ev.CipherId.HasValue ? (object)ev.CipherId.Value : DBNull.Value; - row[groupIdColumn] = ev.GroupId.HasValue ? (object)ev.GroupId.Value : DBNull.Value; row[collectionIdColumn] = ev.CollectionId.HasValue ? (object)ev.CollectionId.Value : DBNull.Value; - row[actingUserIdColumn] = ev.ActingUserId.HasValue ? (object)ev.ActingUserId.Value : DBNull.Value; + row[groupIdColumn] = ev.GroupId.HasValue ? (object)ev.GroupId.Value : DBNull.Value; row[organizationUserIdColumn] = ev.OrganizationUserId.HasValue ? (object)ev.OrganizationUserId.Value : DBNull.Value; + row[actingUserIdColumn] = ev.ActingUserId.HasValue ? (object)ev.ActingUserId.Value : DBNull.Value; + row[deviceTypeColumn] = ev.DeviceType.HasValue ? (object)ev.DeviceType.Value : DBNull.Value; + row[ipAddressColumn] = ev.IpAddress != null ? (object)ev.IpAddress : DBNull.Value; + row[dateColumn] = ev.Date; eventsTable.Rows.Add(row); } diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 2ce8d2cd0..49596402a 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -219,5 +219,9 @@ + + + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Event_ReadPageByCipherId.sql b/src/Sql/dbo/Stored Procedures/Event_ReadPageByCipherId.sql new file mode 100644 index 000000000..e450a0762 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Event_ReadPageByCipherId.sql @@ -0,0 +1,33 @@ +CREATE PROCEDURE [dbo].[Event_ReadPageByCipherId] + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @CipherId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND ( + (@OrganizationId IS NULL AND [OrganizationId] IS NULL) + OR (@OrganizationId IS NOT NULL AND [OrganizationId] = @OrganizationId) + ) + AND ( + (@UserId IS NULL AND [UserId] IS NULL) + OR (@UserId IS NOT NULL AND [UserId] = @UserId) + ) + AND [CipherId] = @CipherId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationId.sql new file mode 100644 index 000000000..dd28848c7 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationId.sql @@ -0,0 +1,23 @@ +CREATE PROCEDURE [dbo].[Event_ReadPageByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationIdActingUserId.sql b/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationIdActingUserId.sql new file mode 100644 index 000000000..e8affd2ed --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Event_ReadPageByOrganizationIdActingUserId.sql @@ -0,0 +1,25 @@ +CREATE PROCEDURE [dbo].[Event_ReadPageByOrganizationIdActingUserId] + @OrganizationId UNIQUEIDENTIFIER, + @ActingUserId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + AND [ActingUserId] = @ActingUserId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Event_ReadPageByUserId.sql b/src/Sql/dbo/Stored Procedures/Event_ReadPageByUserId.sql new file mode 100644 index 000000000..560758145 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Event_ReadPageByUserId.sql @@ -0,0 +1,24 @@ +CREATE PROCEDURE [dbo].[Event_ReadPageByUserId] + @UserId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] IS NULL + AND [ActingUserId] = @UserId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END \ No newline at end of file diff --git a/src/Sql/dbo/Tables/Event.sql b/src/Sql/dbo/Tables/Event.sql index 250079387..debc0abea 100644 --- a/src/Sql/dbo/Tables/Event.sql +++ b/src/Sql/dbo/Tables/Event.sql @@ -17,5 +17,5 @@ GO CREATE NONCLUSTERED INDEX [IX_Event_DateOrganizationIdUserId] - ON [dbo].[Event]([Date] ASC, [OrganizationId] ASC, [UserId] ASC, [CipherId] ASC); + ON [dbo].[Event]([Date] DESC, [OrganizationId] ASC, [ActingUserId] ASC, [CipherId] ASC); diff --git a/util/Setup/DbScripts/2017-12-12_00_Events.sql b/util/Setup/DbScripts/2017-12-12_00_Events.sql index d97450fbb..88a9809f4 100644 --- a/util/Setup/DbScripts/2017-12-12_00_Events.sql +++ b/util/Setup/DbScripts/2017-12-12_00_Events.sql @@ -266,7 +266,7 @@ BEGIN ); CREATE NONCLUSTERED INDEX [IX_Event_DateOrganizationIdUserId] - ON [dbo].[Event]([Date] ASC, [OrganizationId] ASC, [UserId] ASC, [CipherId] ASC); + ON [dbo].[Event]([Date] DESC, [OrganizationId] ASC, [ActingUserId] ASC, [CipherId] ASC); END GO @@ -326,6 +326,143 @@ BEGIN END GO +IF OBJECT_ID('[dbo].[Event_ReadPageByCipherId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Event_ReadPageByCipherId] +END +GO + +CREATE PROCEDURE [dbo].[Event_ReadPageByCipherId] + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @CipherId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND ( + (@OrganizationId IS NULL AND [OrganizationId] IS NULL) + OR (@OrganizationId IS NOT NULL AND [OrganizationId] = @OrganizationId) + ) + AND ( + (@UserId IS NULL AND [UserId] IS NULL) + OR (@UserId IS NOT NULL AND [UserId] = @UserId) + ) + AND [CipherId] = @CipherId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END +GO + +IF OBJECT_ID('[dbo].[Event_ReadPageByOrganizationId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Event_ReadPageByOrganizationId] +END +GO + +CREATE PROCEDURE [dbo].[Event_ReadPageByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END +GO + +IF OBJECT_ID('[dbo].[Event_ReadPageByOrganizationIdActingUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Event_ReadPageByOrganizationIdActingUserId] +END +GO + +CREATE PROCEDURE [dbo].[Event_ReadPageByOrganizationIdActingUserId] + @OrganizationId UNIQUEIDENTIFIER, + @ActingUserId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + AND [ActingUserId] = @ActingUserId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END +GO + +IF OBJECT_ID('[dbo].[Event_ReadPageByUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Event_ReadPageByUserId] +END +GO + +CREATE PROCEDURE [dbo].[Event_ReadPageByUserId] + @UserId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] IS NULL + AND [ActingUserId] = @UserId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END +GO + IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'EventView') BEGIN DROP VIEW [dbo].[EventView]