From ac30657aea678c097a710c92a779aac173573f10 Mon Sep 17 00:00:00 2001 From: Anton Shepilov Date: Thu, 29 Feb 2024 15:58:20 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Do=20not=20display=20deleted=20i?= =?UTF-8?q?tems=20in=20the=20shared=20with=20me=20section=20(#394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/services/documents/services/index.ts | 10 ++-- .../backend/node/test/e2e/common/user-api.ts | 2 +- .../e2e/documents/documents-browser.spec.ts | 54 ++++++++++++++++++- .../body/drive/modals/confirm-trash/index.tsx | 13 ++++- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/tdrive/backend/node/src/services/documents/services/index.ts b/tdrive/backend/node/src/services/documents/services/index.ts index dba92c22..f8fc726d 100644 --- a/tdrive/backend/node/src/services/documents/services/index.ts +++ b/tdrive/backend/node/src/services/documents/services/index.ts @@ -678,6 +678,7 @@ export class DocumentsService { * restore a Drive Document and its children * * @param {string} id - the item id + * @param item item to restore * @param {DriveExecutionContext} context - the execution context * @returns {Promise} */ @@ -981,7 +982,7 @@ export class DocumentsService { ] : []), ], - $nin: [...(options.onlyDirectlyShared ? [["creator", [context.user.id]] as inType] : [])], + $nin: [...(options.onlyUploadedNotByMe ? [["creator", [context.user.id]] as inType] : [])], $lte: [ ...(options.last_modified_lt ? [["last_modified", options.last_modified_lt] as comparisonType] @@ -1011,7 +1012,10 @@ export class DocumentsService { const filteredResult = await this.filter(result.getEntities(), async item => { try { //skip all the fiels - return await checkAccess(item.id, null, "read", this.repository, context); + return ( + !item.is_in_trash && + (await checkAccess(item.id, null, "read", this.repository, context)) + ); } catch (error) { this.logger.warn("failed to check item access", error); return false; @@ -1023,7 +1027,7 @@ export class DocumentsService { if (options.onlyUploadedNotByMe) { const filteredResult = await this.filter(result.getEntities(), async item => { - return item.creator != context.user.id; + return item.creator != context.user.id && !item.is_in_trash; }); return new ListResult(result.type, filteredResult, result.nextPage); diff --git a/tdrive/backend/node/test/e2e/common/user-api.ts b/tdrive/backend/node/test/e2e/common/user-api.ts index f19376be..3ef7ad78 100644 --- a/tdrive/backend/node/test/e2e/common/user-api.ts +++ b/tdrive/backend/node/test/e2e/common/user-api.ts @@ -287,7 +287,7 @@ export default class UserApi { async browseDocuments( id: string, - payload: Record + payload: Record = {} ) { const response = await this.platform.app.inject({ method: "POST", diff --git a/tdrive/backend/node/test/e2e/documents/documents-browser.spec.ts b/tdrive/backend/node/test/e2e/documents/documents-browser.spec.ts index 8c174116..c132ac2e 100644 --- a/tdrive/backend/node/test/e2e/documents/documents-browser.spec.ts +++ b/tdrive/backend/node/test/e2e/documents/documents-browser.spec.ts @@ -150,7 +150,7 @@ describe("The Documents Browser Window and API", () => { expect((await anotherUser.browseDocuments("shared_with_me", {pagination: {limitStr: 100}})).children).toHaveLength(1); }); - it("User should be able to delete file that was shared with him with right permissions", async () => { + it("User should be able to delete file that was shared with him with 'manage' permissions", async () => { const oneUser = await UserApi.getInstance(platform, true, {companyRole: "admin"}); const anotherUser = await UserApi.getInstance(platform, true, {companyRole: "admin"}); @@ -170,6 +170,58 @@ describe("The Documents Browser Window and API", () => { expect(response.statusCode).toBe(200); }); + it("User should be able to delete folder with the files that was shared with him with 'manage' permissions", async () => { + const oneUser = await UserApi.getInstance(platform, true); + const anotherUser = await UserApi.getInstance(platform, true); + + const dir = await oneUser.createDirectory("user_" + oneUser.user.id); + const level2Dir = await oneUser.createDirectory(dir.id); + const level2Dir2 = await oneUser.createDirectory(dir.id); + await oneUser.uploadAllFilesOneByOne(level2Dir.id); + await oneUser.uploadAllFilesOneByOne(level2Dir2.id); + await oneUser.uploadAllFilesOneByOne(dir.id); + await new Promise(r => setTimeout(r, 5000)); + + dir.access_info.entities.push({ + type: "user", + id: anotherUser.user.id, + level: "manage", + grantor: null, + }); + await oneUser.updateDocument(dir.id, dir); + + const response = await anotherUser.delete(dir.id); + expect(response.statusCode).toBe(200); + }); + + it("Shouldn't return files that are in trash", async () => { + const oneUser = await UserApi.getInstance(platform, true); + const anotherUser = await UserApi.getInstance(platform, true); + + const dir = await oneUser.createDirectory("user_" + oneUser.user.id); + + dir.access_info.entities.push({ + type: "user", + id: anotherUser.user.id, + level: "manage", + grantor: null, + }); + await oneUser.updateDocument(dir.id, dir); + await new Promise(r => setTimeout(r, 3000)); + + //can brows files + let sharedDocs = await anotherUser.browseDocuments("shared_with_me"); + expect(sharedDocs.children.length).toBe(1); + + //when + const response = await anotherUser.delete(dir.id); + expect(response.statusCode).toBe(200); + await new Promise(r => setTimeout(r, 3000)); + + sharedDocs = await anotherUser.browseDocuments("shared_with_me"); + expect(sharedDocs.children.length).toBe(0); + }); + }); }); diff --git a/tdrive/frontend/src/app/views/client/body/drive/modals/confirm-trash/index.tsx b/tdrive/frontend/src/app/views/client/body/drive/modals/confirm-trash/index.tsx index 51827590..01fe8cf8 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/modals/confirm-trash/index.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/modals/confirm-trash/index.tsx @@ -8,6 +8,7 @@ import { DriveItem } from '@features/drive/types'; import { useEffect, useState } from 'react'; import { atom, useRecoilState } from 'recoil'; import Languages from '@features/global/services/languages-service'; +import RouterServices from "features/router/services/router-service"; export type ConfirmTrashModalType = { @@ -39,6 +40,7 @@ const ConfirmTrashModalContent = ({ items }: { items: DriveItem[] }) => { const [loading, setLoading] = useState(false); const [state, setState] = useRecoilState(ConfirmTrashModalAtom); const [, setSelected] = useRecoilState(DriveItemSelectedList); + const { viewId } = RouterServices.getStateFromRoute(); useEffect(() => { refresh(items[0].id); @@ -59,10 +61,17 @@ const ConfirmTrashModalContent = ({ items }: { items: DriveItem[] }) => { loading={loading} onClick={async () => { setLoading(true); - await Promise.all((items || []).map(async item => await remove(item.id, item.parent_id))); + await Promise.all((items || []).map(async item => { + let parent = item.parent_id; + if (viewId === "shared_with_me" && parent && parent.startsWith("user_")) { + console.log("Refresh shared_with_me"); + parent = "shared_with_me"; + } + await remove(item.id, parent) + })); setSelected({}); setLoading(false); - refresh('trash'); + await refresh("trash"); setState({ ...state, open: false }); }} >