public async Task TestCreateFolderHierarchy() { Assert.NotNull(context); // create a folder-hierarchy /A/B/C await repo.Create(new BookmarkEntity { Id = NewId, ChildCount = 0, Created = DateTime.UtcNow, DisplayName = "A", Path = "/", SortOrder = 0, Type = ItemType.Folder, Url = "http://url", UserName = Username }); await repo.Create(new BookmarkEntity { Id = NewId, ChildCount = 0, Created = DateTime.UtcNow, DisplayName = "B", Path = "/A", SortOrder = 0, Type = ItemType.Folder, Url = "http://url", UserName = Username }); await repo.Create(new BookmarkEntity { ChildCount = 0, Created = DateTime.UtcNow, DisplayName = "C", Path = "/A/B", SortOrder = 0, Type = ItemType.Folder, Url = "http://url", UserName = Username }); var bms = await repo.GetBookmarksByPathStart("/A", Username); Assert.NotNull(bms); Assert.Equal(2, bms.Count); bms = await repo.GetAllBookmarks(Username); Assert.NotNull(bms); Assert.Equal(3, bms.Count); Assert.Equal("A", bms[0].DisplayName); Assert.Equal("B", bms[1].DisplayName); Assert.Equal("C", bms[2].DisplayName); bms = await repo.GetBookmarksByName("B", Username); Assert.NotNull(bms); Assert.Single(bms); Assert.Equal("/A", bms[0].Path); bms = await repo.GetBookmarksByPath("/A/B", Username); Assert.NotNull(bms); Assert.Single(bms); Assert.Equal("C", bms[0].DisplayName); var bm = await repo.GetFolderByPath("/A/B", Username); Assert.NotNull(bm); Assert.Equal("B", bm.DisplayName); Assert.Equal("/A", bm.Path); // invalid folder bm = await repo.GetFolderByPath("|A|B", Username); Assert.Null(bm); // use the structure from above and get the child-count of nodes // this is just a folder-structure of /A/B/C var nodes = await repo.GetChildCountOfPath("/A", Username); Assert.NotNull(nodes); Assert.Single(nodes); Assert.Equal(1, nodes[0].Count); Assert.Equal("/A", nodes[0].Path); nodes = await repo.GetChildCountOfPath("", Username); Assert.NotNull(nodes); Assert.Equal(3, nodes.Count); Assert.Equal(1, nodes[0].Count); Assert.Equal("/", nodes[0].Path); // create a node to an existing path var nodeID = NewId; bm = await repo.Create(new BookmarkEntity { Id = nodeID, ChildCount = 0, Created = DateTime.UtcNow, DisplayName = "URL", Path = "/A/B", SortOrder = 0, Type = ItemType.Node, Url = "http://url", UserName = Username }); Assert.NotNull(bm); // get the folder bm = await repo.GetFolderByPath("/A/B", Username); Assert.NotNull(bm); Assert.Equal("B", bm.DisplayName); Assert.Equal("/A", bm.Path); Assert.Equal(2, bm.ChildCount); // sub-folder and the newly created node nodes = await repo.GetChildCountOfPath("/A/B", Username); Assert.NotNull(nodes); Assert.Single(nodes); Assert.Equal(2, nodes[0].Count); Assert.Equal("/A/B", nodes[0].Path); // unknown path nodes = await repo.GetChildCountOfPath("/A/B/C/D/E", Username); Assert.Empty(nodes); // remove a node Assert.True(await repo.Delete(new BookmarkEntity { Id = nodeID, UserName = Username })); bm = await repo.GetFolderByPath("/A/B", Username); Assert.NotNull(bm); Assert.Equal("B", bm.DisplayName); Assert.Equal("/A", bm.Path); Assert.Equal(1, bm.ChildCount); // sub-folder only Assert.False(await repo.Delete(new BookmarkEntity { Id = "-1", UserName = Username })); // we have the path /A/B/C // if we delete /A/B the only thing left will be /A Assert.True(await repo.DeletePath("/A/B", Username)); bms = await repo.GetAllBookmarks(Username); Assert.NotNull(bms); Assert.Single(bms); await Assert.ThrowsAsync <ArgumentException>(() => { return(repo.DeletePath("", Username)); }); await Assert.ThrowsAsync <ArgumentException>(() => { return(repo.DeletePath("/", Username)); }); Assert.False(await repo.DeletePath("/D/E", Username)); }
public async Task <ActionResult> Update([FromBody] BookmarkModel bookmark) { _logger.LogDebug($"Will try to update existing bookmark entry: {bookmark}"); if (string.IsNullOrEmpty(bookmark.Path) || string.IsNullOrEmpty(bookmark.DisplayName) || string.IsNullOrEmpty(bookmark.Id) ) { return(InvalidArguments($"Invalid request data supplied. Missing ID, Path or DisplayName!")); } try { var user = this.User.Get(); var outcome = await _repository.InUnitOfWorkAsync <ActionResult>(async() => { var existing = await _repository.GetBookmarkById(bookmark.Id, user.Username); if (existing == null) { _logger.LogWarning($"Could not find a bookmark with the given ID '{bookmark.Id}'"); return(true, ProblemDetailsResult( detail: $"No bookmark found by ID: {bookmark.Id}", statusCode: StatusCodes.Status404NotFound, title: Errors.NotFoundError, instance: HttpContext.Request.Path)); } var childCount = existing.ChildCount; if (existing.Type == Store.ItemType.Folder) { // on save of a folder, update the child-count! var parentPath = existing.Path; var path = EnsureFolderPath(parentPath, existing.DisplayName); var nodeCounts = await _repository.GetChildCountOfPath(path, user.Username); if (nodeCounts != null && nodeCounts.Count > 0) { var nodeCount = nodeCounts.Find(x => x.Path == path); if (nodeCount != null) { childCount = nodeCount.Count; } } } var existingDisplayName = existing.DisplayName; var existingPath = existing.Path; var item = await _repository.Update(new BookmarkEntity { Id = bookmark.Id, Created = existing.Created, DisplayName = bookmark.DisplayName, Path = bookmark.Path, SortOrder = bookmark.SortOrder, Type = existing.Type, // it does not make any sense to change the type of a bookmark! Url = bookmark.Url, UserName = user.Username, ChildCount = childCount, Favicon = bookmark.Favicon, AccessCount = bookmark.AccessCount }); if (existing.Type == Store.ItemType.Folder && existingDisplayName != bookmark.DisplayName) { // if we have a folder and change the displayname this also affects ALL sub-elements // therefore all paths of sub-elements where this folder-path is present, need to be updated var newPath = EnsureFolderPath(bookmark.Path, bookmark.DisplayName); var oldPath = EnsureFolderPath(existingPath, existingDisplayName); _logger.LogDebug($"will update all old paths '{oldPath}' to new path '{newPath}'."); var bookmarks = await _repository.GetBookmarksByPathStart(oldPath, user.Username); if (bookmarks == null) { bookmarks = new List <BookmarkEntity>(); } foreach (var bm in bookmarks) { var updatedPath = bm.Path.Replace(oldPath, newPath); await _repository.Update(new BookmarkEntity { Id = bm.Id, Created = existing.Created, DisplayName = bm.DisplayName, Path = updatedPath, SortOrder = bm.SortOrder, Type = bm.Type, Url = bm.Url, UserName = bm.UserName, ChildCount = bm.ChildCount }); } } _logger.LogInformation($"Updated Bookmark with ID {item.Id}"); var result = new OkObjectResult(new Result <string> { Success = true, Message = $"Bookmark with ID '{existing.Id}' was updated.", Value = existing.Id }); return(true, result); }); return(outcome.value); } catch (Exception EX) { _logger.LogError($"Could not update bookmark entry: {EX.Message}\nstack: {EX.StackTrace}"); return(ProblemDetailsResult( detail: $"Could not update bookmark because of error: {EX.Message}", statusCode: StatusCodes.Status500InternalServerError, title: Errors.UpdateBookmarksError, instance: HttpContext.Request.Path)); } }