mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
refactored cipherfolder and fav to JSON columns
This commit is contained in:
@ -10,6 +10,8 @@ namespace Bit.Core.Models.Table
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public Enums.CipherType Type { get; set; }
|
||||
public string Data { get; set; }
|
||||
public string Favorites { get; set; }
|
||||
public string Folders { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
public class Favorite
|
||||
{
|
||||
public Guid CipherId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
public class FolderCipher
|
||||
{
|
||||
public Guid CipherId { get; set; }
|
||||
public Guid FolderId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ namespace Bit.Core.Repositories
|
||||
Task ReplaceAsync(Cipher obj, IEnumerable<Guid> subvaultIds);
|
||||
Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite);
|
||||
Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers);
|
||||
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Favorite> favorites, IEnumerable<Folder> folders,
|
||||
IEnumerable<FolderCipher> folderCiphers);
|
||||
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
||||
}
|
||||
}
|
||||
|
@ -237,8 +237,7 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Favorite> favorites, IEnumerable<Folder> folders,
|
||||
IEnumerable<FolderCipher> folderCiphers)
|
||||
public Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders)
|
||||
{
|
||||
if(!ciphers.Any())
|
||||
{
|
||||
@ -272,28 +271,6 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
bulkCopy.WriteToServer(dataTable);
|
||||
}
|
||||
|
||||
if(folderCiphers.Any())
|
||||
{
|
||||
using(var bulkCopy = new SqlBulkCopy(connection,
|
||||
SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.FireTriggers, transaction))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "[dbo].[FolderCipher]";
|
||||
var dataTable = BuildFolderCiphersTable(folderCiphers);
|
||||
bulkCopy.WriteToServer(dataTable);
|
||||
}
|
||||
}
|
||||
|
||||
if(favorites.Any())
|
||||
{
|
||||
using(var bulkCopy = new SqlBulkCopy(connection,
|
||||
SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.FireTriggers, transaction))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "[dbo].[Favorite]";
|
||||
var dataTable = BuildFavoritesTable(favorites);
|
||||
bulkCopy.WriteToServer(dataTable);
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch
|
||||
@ -327,6 +304,10 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
ciphersTable.Columns.Add(typeColumn);
|
||||
var dataColumn = new DataColumn(nameof(c.Data), typeof(string));
|
||||
ciphersTable.Columns.Add(dataColumn);
|
||||
var favoritesColumn = new DataColumn(nameof(c.Favorites), typeof(string));
|
||||
ciphersTable.Columns.Add(favoritesColumn);
|
||||
var foldersColumn = new DataColumn(nameof(c.Folders), typeof(string));
|
||||
ciphersTable.Columns.Add(foldersColumn);
|
||||
var creationDateColumn = new DataColumn(nameof(c.CreationDate), c.CreationDate.GetType());
|
||||
ciphersTable.Columns.Add(creationDateColumn);
|
||||
var revisionDateColumn = new DataColumn(nameof(c.RevisionDate), c.RevisionDate.GetType());
|
||||
@ -345,6 +326,8 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
row[organizationId] = cipher.OrganizationId.HasValue ? (object)cipher.OrganizationId.Value : DBNull.Value;
|
||||
row[typeColumn] = (short)cipher.Type;
|
||||
row[dataColumn] = cipher.Data;
|
||||
row[favoritesColumn] = cipher.Favorites;
|
||||
row[foldersColumn] = cipher.Folders;
|
||||
row[creationDateColumn] = cipher.CreationDate;
|
||||
row[revisionDateColumn] = cipher.RevisionDate;
|
||||
|
||||
@ -354,76 +337,6 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
return ciphersTable;
|
||||
}
|
||||
|
||||
private DataTable BuildFavoritesTable(IEnumerable<Favorite> favorites)
|
||||
{
|
||||
var f = favorites.FirstOrDefault();
|
||||
if(f == null)
|
||||
{
|
||||
throw new ApplicationException("Must have some favorites to bulk import.");
|
||||
}
|
||||
|
||||
var favoritesTable = new DataTable("FavoriteDataTable");
|
||||
|
||||
var userIdColumn = new DataColumn(nameof(f.UserId), f.UserId.GetType());
|
||||
favoritesTable.Columns.Add(userIdColumn);
|
||||
var cipherIdColumn = new DataColumn(nameof(f.CipherId), f.CipherId.GetType());
|
||||
favoritesTable.Columns.Add(cipherIdColumn);
|
||||
|
||||
var keys = new DataColumn[2];
|
||||
keys[0] = userIdColumn;
|
||||
keys[1] = cipherIdColumn;
|
||||
favoritesTable.PrimaryKey = keys;
|
||||
|
||||
foreach(var favorite in favorites)
|
||||
{
|
||||
var row = favoritesTable.NewRow();
|
||||
|
||||
row[cipherIdColumn] = favorite.CipherId;
|
||||
row[userIdColumn] = favorite.UserId;
|
||||
|
||||
favoritesTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
return favoritesTable;
|
||||
}
|
||||
|
||||
private DataTable BuildFolderCiphersTable(IEnumerable<FolderCipher> folderCiphers)
|
||||
{
|
||||
var f = folderCiphers.FirstOrDefault();
|
||||
if(f == null)
|
||||
{
|
||||
throw new ApplicationException("Must have some folderCiphers to bulk import.");
|
||||
}
|
||||
|
||||
var folderCiphersTable = new DataTable("FolderCipherDataTable");
|
||||
|
||||
var folderIdColumn = new DataColumn(nameof(f.FolderId), f.FolderId.GetType());
|
||||
folderCiphersTable.Columns.Add(folderIdColumn);
|
||||
var cipherIdColumn = new DataColumn(nameof(f.CipherId), f.CipherId.GetType());
|
||||
folderCiphersTable.Columns.Add(cipherIdColumn);
|
||||
var userIdColumn = new DataColumn(nameof(f.UserId), f.UserId.GetType());
|
||||
folderCiphersTable.Columns.Add(userIdColumn);
|
||||
|
||||
var keys = new DataColumn[3];
|
||||
keys[0] = folderIdColumn;
|
||||
keys[1] = cipherIdColumn;
|
||||
keys[2] = userIdColumn;
|
||||
folderCiphersTable.PrimaryKey = keys;
|
||||
|
||||
foreach(var folderCipher in folderCiphers)
|
||||
{
|
||||
var row = folderCiphersTable.NewRow();
|
||||
|
||||
row[folderIdColumn] = folderCipher.FolderId;
|
||||
row[cipherIdColumn] = folderCipher.CipherId;
|
||||
row[userIdColumn] = folderCipher.UserId;
|
||||
|
||||
folderCiphersTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
return folderCiphersTable;
|
||||
}
|
||||
|
||||
private DataTable BuildFoldersTable(IEnumerable<Folder> folders)
|
||||
{
|
||||
var f = folders.FirstOrDefault();
|
||||
|
@ -186,19 +186,13 @@ namespace Bit.Core.Services
|
||||
List<CipherDetails> ciphers,
|
||||
IEnumerable<KeyValuePair<int, int>> folderRelationships)
|
||||
{
|
||||
// Init. ids and build out favorites.
|
||||
var favorites = new List<Favorite>();
|
||||
foreach(var cipher in ciphers)
|
||||
{
|
||||
cipher.SetNewId();
|
||||
|
||||
if(cipher.UserId.HasValue && cipher.Favorite)
|
||||
{
|
||||
favorites.Add(new Favorite
|
||||
{
|
||||
UserId = cipher.UserId.Value,
|
||||
CipherId = cipher.Id
|
||||
});
|
||||
cipher.Favorites = $"[{{\"u\":\"{cipher.UserId.ToString().ToUpperInvariant()}\"}}]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,7 +203,6 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
// Create the folder associations based on the newly created folder ids
|
||||
var folderCiphers = new List<FolderCipher>();
|
||||
foreach(var relationship in folderRelationships)
|
||||
{
|
||||
var cipher = ciphers.ElementAtOrDefault(relationship.Key);
|
||||
@ -220,16 +213,12 @@ namespace Bit.Core.Services
|
||||
continue;
|
||||
}
|
||||
|
||||
folderCiphers.Add(new FolderCipher
|
||||
{
|
||||
FolderId = folder.Id,
|
||||
CipherId = cipher.Id,
|
||||
UserId = folder.UserId
|
||||
});
|
||||
cipher.Folders = $"[{{\"u\":\"{cipher.UserId.ToString().ToUpperInvariant()}\"," +
|
||||
$"\"f\":\"{folder.Id.ToString().ToUpperInvariant()}\"}}]";
|
||||
}
|
||||
|
||||
// Create it all
|
||||
await _cipherRepository.CreateAsync(ciphers, favorites, folders, folderCiphers);
|
||||
await _cipherRepository.CreateAsync(ciphers, folders);
|
||||
|
||||
// push
|
||||
var userId = folders.FirstOrDefault()?.UserId ?? ciphers.FirstOrDefault()?.UserId;
|
||||
|
@ -70,9 +70,7 @@
|
||||
<Build Include="dbo\Tables\Device.sql" />
|
||||
<Build Include="dbo\Tables\Cipher.sql" />
|
||||
<Build Include="dbo\Tables\Organization.sql" />
|
||||
<Build Include="dbo\Tables\FolderCipher.sql" />
|
||||
<Build Include="dbo\Tables\Grant.sql" />
|
||||
<Build Include="dbo\Tables\Favorite.sql" />
|
||||
<Build Include="dbo\Tables\Group.sql" />
|
||||
<Build Include="dbo\Tables\User.sql" />
|
||||
<Build Include="dbo\Tables\GroupUser.sql" />
|
||||
@ -104,10 +102,8 @@
|
||||
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadCountByFreeOrganizationAdminUser.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Cipher_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Cipher_DeleteById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Favorite_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Cipher_ReadById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadCountByOrganizationId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Favorite_Delete.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Subvault_ReadCountByOrganizationId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Folder_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Folder_DeleteById.sql" />
|
||||
@ -115,9 +111,7 @@
|
||||
<Build Include="dbo\Stored Procedures\Cipher_Update.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Folder_Update.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Device_ClearPushTokenByIdentifier.sql" />
|
||||
<Build Include="dbo\Stored Procedures\FolderCipher_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Device_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\FolderCipher_Delete.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Device_DeleteById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Organization_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Device_ReadById.sql" />
|
||||
|
@ -2,14 +2,44 @@
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.*,
|
||||
CASE WHEN F.[CipherId] IS NULL THEN 0 ELSE 1 END [Favorite],
|
||||
FO.[Id] [FolderId]
|
||||
C.[Id],
|
||||
C.[UserId],
|
||||
C.[OrganizationId],
|
||||
C.[Type],
|
||||
C.[Data],
|
||||
C.[CreationDate],
|
||||
C.[RevisionDate],
|
||||
CASE WHEN
|
||||
C.[Favorites] IS NULL
|
||||
OR (
|
||||
SELECT TOP 1
|
||||
1
|
||||
FROM
|
||||
OPENJSON(C.[Favorites])
|
||||
WITH (
|
||||
[Favorites_UserId] UNIQUEIDENTIFIER '$.u'
|
||||
)
|
||||
WHERE
|
||||
[Favorites_UserId] = @UserId
|
||||
) IS NULL
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [Favorite],
|
||||
CASE WHEN
|
||||
C.[Folders] IS NULL
|
||||
THEN NULL
|
||||
ELSE (
|
||||
SELECT TOP 1
|
||||
[Folders_FolderId]
|
||||
FROM
|
||||
OPENJSON(C.[Folders])
|
||||
WITH (
|
||||
[Folders_UserId] UNIQUEIDENTIFIER '$.u',
|
||||
[Folders_FolderId] UNIQUEIDENTIFIER '$.f'
|
||||
)
|
||||
WHERE
|
||||
[Folders_UserId] = @UserId
|
||||
)
|
||||
END [FolderId]
|
||||
FROM
|
||||
[dbo].[Cipher] C
|
||||
LEFT JOIN
|
||||
[dbo].[Favorite] F ON F.[CipherId] = C.[Id] AND F.[UserId] = @UserId
|
||||
LEFT JOIN
|
||||
[dbo].[FolderCipher] FC ON FC.[UserId] = @UserId AND FC.[CipherId] = C.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Folder] FO ON FO.[Id] = FC.[FolderId]
|
@ -4,6 +4,8 @@
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@ -19,6 +21,8 @@ BEGIN
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Favorites],
|
||||
[Folders],
|
||||
[CreationDate],
|
||||
[RevisionDate]
|
||||
)
|
||||
@ -29,17 +33,9 @@ BEGIN
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
CASE WHEN @Favorite = 0 THEN NULL ELSE JSON_QUERY((SELECT @UserId u FOR JSON PATH)) END,
|
||||
CASE WHEN @FolderId IS NULL THEN NULL ELSE JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH)) END,
|
||||
@CreationDate,
|
||||
@RevisionDate
|
||||
)
|
||||
|
||||
IF @FolderId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[FolderCipher_Create] @FolderId, @Id, @UserId
|
||||
END
|
||||
|
||||
IF @Favorite = 1
|
||||
BEGIN
|
||||
EXEC [dbo].[Favorite_Create] @UserId, @Id
|
||||
END
|
||||
END
|
@ -4,6 +4,8 @@
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
|
@ -4,6 +4,8 @@
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
@ -17,6 +19,8 @@ BEGIN
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Favorites],
|
||||
[Folders],
|
||||
[CreationDate],
|
||||
[RevisionDate]
|
||||
)
|
||||
@ -27,6 +31,8 @@ BEGIN
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
@Favorites,
|
||||
@Folders,
|
||||
@CreationDate,
|
||||
@RevisionDate
|
||||
)
|
||||
|
@ -4,6 +4,8 @@
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
@ -17,6 +19,8 @@ BEGIN
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[Favorites] = @Favorites,
|
||||
[Folders] = @Folders,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE
|
||||
|
@ -2,39 +2,158 @@
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@Favorite TINYINT
|
||||
@Favorite BIT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @FavoritesJson VARCHAR(MAX) = NULL
|
||||
DECLARE @FoldersJson VARCHAR(MAX) = NULL
|
||||
|
||||
SELECT
|
||||
@FavoritesJson = [Favorites],
|
||||
@FoldersJson = [Folders]
|
||||
FROM
|
||||
[dbo].[Cipher]
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
DECLARE @ExistingFolderId UNIQUEIDENTIFIER = NULL
|
||||
|
||||
SELECT TOP 1
|
||||
@ExistingFolderId = F.Id
|
||||
FROM
|
||||
[dbo].[FolderCipher] FC
|
||||
INNER JOIN
|
||||
[dbo].[Folder] F ON F.[Id] = FC.[FolderId]
|
||||
WHERE
|
||||
F.[UserId] = @UserId
|
||||
AND FC.[CipherId] = @Id
|
||||
-- NOTE
|
||||
-- JSON_MODIFY operations involving @Existing__Index may be subject to race conditions involving that index/key.
|
||||
-- Look for a better approach, like removing objects by conditions.
|
||||
DECLARE @ExistingFolderIndex INT = NULL
|
||||
DECLARE @ExistingFavoriteIndex INT = NULL
|
||||
|
||||
IF @ExistingFolderId IS NOT NULL AND (@FolderId IS NULL OR @FolderId != @ExistingFolderId)
|
||||
IF @FoldersJson IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[FolderCipher_Delete] @ExistingFolderId, @Id
|
||||
END
|
||||
|
||||
IF @FolderId IS NOT NULL AND (@ExistingFolderId IS NULL OR @FolderId != @ExistingFolderId)
|
||||
BEGIN
|
||||
EXEC [dbo].[FolderCipher_Create] @FolderId, @Id, @UserId
|
||||
SELECT TOP 1
|
||||
@ExistingFolderId = JSON_VALUE([Value], '$.f'),
|
||||
@ExistingFolderIndex = [Key]
|
||||
FROM (
|
||||
SELECT
|
||||
[Key],
|
||||
[Value]
|
||||
FROM
|
||||
OPENJSON(@FoldersJson)
|
||||
) [Results]
|
||||
WHERE JSON_VALUE([Value], '$.u') = @UserId
|
||||
END
|
||||
|
||||
IF @Favorite = 0
|
||||
IF @FavoritesJson IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[Favorite_Delete] @UserId, @Id
|
||||
SELECT TOP 1
|
||||
@ExistingFavoriteIndex = [Key]
|
||||
FROM (
|
||||
SELECT
|
||||
[Key],
|
||||
[Value]
|
||||
FROM
|
||||
OPENJSON(@FavoritesJson)
|
||||
) [Results]
|
||||
WHERE JSON_VALUE([Value], '$.u') = @UserId
|
||||
END
|
||||
ELSE IF (SELECT COUNT(1) FROM [dbo].[Favorite] WHERE [UserId] = @UserId AND [CipherId] = @Id) = 0
|
||||
|
||||
-- ----------------------------
|
||||
-- Update [Folders]
|
||||
-- ----------------------------
|
||||
|
||||
IF @ExistingFolderId IS NOT NULL AND @FolderId IS NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[Favorite_Create] @UserId, @Id
|
||||
-- User had an existing folder, but now they have removed the folder assignment.
|
||||
-- Remove the index of the existing folder object from the [Folders] JSON array.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
-- TODO: How to remove index?
|
||||
[Folders] = JSON_MODIFY([Folders], CONCAT('$[', @ExistingFolderIndex, ']'), NULL)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
ELSE IF @FolderId IS NOT NULL
|
||||
BEGIN
|
||||
IF @FoldersJson IS NULL
|
||||
BEGIN
|
||||
-- [Folders] has no existing JSON data.
|
||||
-- Set @FolderId (with @UserId) as a new JSON object for index 0 of an array.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[Folders] = JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH))
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
ELSE IF @ExistingFolderId IS NULL
|
||||
BEGIN
|
||||
-- [Folders] has some existing JSON data, but the user had no existing folder.
|
||||
-- Append @FolderId (with @UserId) as a new JSON object.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[Folders] = JSON_MODIFY([Folders], 'append $',
|
||||
JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)))
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
ELSE IF @FolderId != @ExistingFolderId
|
||||
BEGIN
|
||||
-- User had an existing folder assignemnt, but have changed the assignment to another folder.
|
||||
-- Update the index of the existing folder object from the [Folders] JSON array to to include the new @FolderId
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[Folders] = JSON_MODIFY([Folders], CONCAT('$[', @ExistingFolderIndex, ']'),
|
||||
JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)))
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
END
|
||||
|
||||
-- ----------------------------
|
||||
-- Update [Favorites]
|
||||
-- ----------------------------
|
||||
|
||||
IF @Favorite = 0 AND @ExistingFavoriteIndex IS NOT NULL
|
||||
BEGIN
|
||||
-- User had the cipher marked as a favorite, but now it is not.
|
||||
-- Remove the index of the existing user object from the [Favorites] JSON array.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
-- TODO: How to remove index?
|
||||
[Favorites] = JSON_MODIFY([Favorites], CONCAT('$[', @ExistingFavoriteIndex, ']'), NULL)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
ELSE IF @Favorite = 1 AND @FavoritesJson IS NULL
|
||||
BEGIN
|
||||
-- User is marking the cipher as a favorite and there is no existing JSON data
|
||||
-- Set @UserId as a new JSON object for index 0 of an array.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[Favorites] = JSON_QUERY((SELECT @UserId u FOR JSON PATH))
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
ELSE IF @Favorite = 1 AND @ExistingFavoriteIndex IS NULL
|
||||
BEGIN
|
||||
-- User is marking the cipher as a favorite whenever it previously was not.
|
||||
-- Append @UserId as a new JSON object.
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[Favorites] = JSON_MODIFY([Favorites], 'append $',
|
||||
JSON_QUERY((SELECT @UserId u FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)))
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
END
|
@ -4,6 +4,8 @@
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@SubvaultIds AS [dbo].[GuidIdArray] READONLY
|
||||
@ -16,10 +18,9 @@ BEGIN
|
||||
SET
|
||||
[UserId] = NULL,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate
|
||||
-- No need to update CreationDate, Favorites, Folders, or Type since that data will not change
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
CREATE PROCEDURE [dbo].[Favorite_Create]
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@CipherId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[Favorite]
|
||||
(
|
||||
[UserId],
|
||||
[CipherId]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@UserId,
|
||||
@CipherId
|
||||
)
|
||||
END
|
@ -1,14 +0,0 @@
|
||||
CREATE PROCEDURE [dbo].[Favorite_Delete]
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@CipherId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DELETE
|
||||
FROM
|
||||
[dbo].[Favorite]
|
||||
WHERE
|
||||
[UserId] = @UserId
|
||||
AND [CipherId] = @CipherId
|
||||
END
|
@ -1,21 +0,0 @@
|
||||
CREATE PROCEDURE [dbo].[FolderCipher_Create]
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@CipherId UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[FolderCipher]
|
||||
(
|
||||
[FolderId],
|
||||
[CipherId],
|
||||
[UserId]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@FolderId,
|
||||
@CipherId,
|
||||
@UserId
|
||||
)
|
||||
END
|
@ -1,16 +0,0 @@
|
||||
CREATE PROCEDURE [dbo].[FolderCipher_Delete]
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@CipherId UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DELETE
|
||||
FROM
|
||||
[dbo].[FolderCipher]
|
||||
WHERE
|
||||
[FolderId] = @FolderId
|
||||
AND [CipherId] = @CipherId
|
||||
AND [UserId] = @UserId
|
||||
END
|
@ -4,6 +4,8 @@
|
||||
[OrganizationId] UNIQUEIDENTIFIER NULL,
|
||||
[Type] TINYINT NOT NULL,
|
||||
[Data] NVARCHAR (MAX) NOT NULL,
|
||||
[Favorites] NVARCHAR (MAX) NULL,
|
||||
[Folders] NVARCHAR (MAX) NULL,
|
||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||
[RevisionDate] DATETIME2 (7) NOT NULL,
|
||||
CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||
|
@ -1,8 +0,0 @@
|
||||
CREATE TABLE [dbo].[Favorite] (
|
||||
[UserId] UNIQUEIDENTIFIER NOT NULL,
|
||||
[CipherId] UNIQUEIDENTIFIER NOT NULL,
|
||||
CONSTRAINT [PK_Favorite] PRIMARY KEY CLUSTERED ([UserId] ASC, [CipherId] ASC),
|
||||
CONSTRAINT [FK_Favorite_Cipher] FOREIGN KEY ([CipherId]) REFERENCES [dbo].[Cipher] ([Id]) ON DELETE CASCADE,
|
||||
CONSTRAINT [FK_Favorite_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
|
||||
);
|
||||
|
@ -1,15 +0,0 @@
|
||||
CREATE TABLE [dbo].[FolderCipher] (
|
||||
[FolderId] UNIQUEIDENTIFIER NOT NULL,
|
||||
[CipherId] UNIQUEIDENTIFIER NOT NULL,
|
||||
[UserId] UNIQUEIDENTIFIER NOT NULL,
|
||||
CONSTRAINT [PK_FolderCipher] PRIMARY KEY CLUSTERED ([UserId] ASC, [FolderId] ASC, [CipherId] ASC),
|
||||
CONSTRAINT [FK_FolderCipher_Cipher] FOREIGN KEY ([CipherId]) REFERENCES [dbo].[Cipher] ([Id]) ON DELETE CASCADE,
|
||||
CONSTRAINT [FK_FolderCipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE,
|
||||
CONSTRAINT [FK_FolderCipher_Folder] FOREIGN KEY ([FolderId]) REFERENCES [dbo].[Folder] ([Id])
|
||||
);
|
||||
|
||||
|
||||
GO
|
||||
CREATE NONCLUSTERED INDEX [IX_FolderCipher_CipherId]
|
||||
ON [dbo].[FolderCipher]([CipherId] ASC);
|
||||
|
Reference in New Issue
Block a user