From 34372daf8bd9c42cbc44f270c03be0d93c813d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20No=C3=ABl?= Date: Mon, 2 May 2022 16:31:27 +0200 Subject: [PATCH] add bulk move route --- .../server/controllers/admin-folder-file.js | 74 ++- .../upload/server/controllers/admin-folder.js | 2 +- .../validation/admin/folder-file.js | 85 +++- .../controllers/validation/admin/folder.js | 63 +-- .../controllers/validation/admin/utils.js | 18 + packages/core/upload/server/routes/admin.js | 16 + .../tests/admin/folder-file.test.e2e.js | 433 ++++++++++++++++++ .../upload/tests/admin/folder.test.e2e.js | 110 +---- 8 files changed, 672 insertions(+), 129 deletions(-) create mode 100644 packages/core/upload/server/controllers/validation/admin/utils.js create mode 100644 packages/core/upload/tests/admin/folder-file.test.e2e.js diff --git a/packages/core/upload/server/controllers/admin-folder-file.js b/packages/core/upload/server/controllers/admin-folder-file.js index bd89650d3e..4735eff757 100644 --- a/packages/core/upload/server/controllers/admin-folder-file.js +++ b/packages/core/upload/server/controllers/admin-folder-file.js @@ -1,11 +1,32 @@ 'use strict'; const { getService } = require('../utils'); -const { validateDeleteManyFoldersFiles } = require('./validation/admin/folder-file'); +const { ACTIONS } = require('../constants'); +const { + validateDeleteManyFoldersFiles, + validateMoveManyFoldersFiles, +} = require('./validation/admin/folder-file'); + +const folderModel = 'plugin::upload.folder'; +const fileModel = 'plugin::upload.file'; module.exports = { async deleteMany(ctx) { const { body } = ctx.request; + const { + state: { userAbility }, + } = ctx; + + const pmFolder = strapi.admin.services.permission.createPermissionsManager({ + ability: ctx.state.userAbility, + model: folderModel, + }); + + const pmFile = strapi.admin.services.permission.createPermissionsManager({ + ability: userAbility, + action: ACTIONS.read, + model: fileModel, + }); await validateDeleteManyFoldersFiles(body); @@ -17,8 +38,55 @@ module.exports = { ctx.body = { data: { - files: deletedFiles, - folders: deletedFolders, + files: await pmFile.sanitizeOutput(deletedFiles), + folders: await pmFolder.sanitizeOutput(deletedFolders), + }, + }; + }, + async moveMany(ctx) { + const { body } = ctx.request; + const { + state: { userAbility, user }, + } = ctx; + + const pmFolder = strapi.admin.services.permission.createPermissionsManager({ + ability: ctx.state.userAbility, + model: folderModel, + }); + + const pmFile = strapi.admin.services.permission.createPermissionsManager({ + ability: userAbility, + action: ACTIONS.read, + model: fileModel, + }); + + await validateMoveManyFoldersFiles(body); + const { folderIds = [], fileIds = [], destinationFolderId } = body; + + const uploadService = getService('upload'); + const folderService = getService('folder'); + + const updatedFolders = []; + // updates are done in order (not in parallele) to avoid mixing queries (path) + for (let folderId of folderIds) { + const updatedFolder = await folderService.update( + folderId, + { parent: destinationFolderId }, + { user } + ); + updatedFolders.push(updatedFolder); + } + + const updatedFiles = await Promise.all( + fileIds.map(fileId => + uploadService.updateFileInfo(fileId, { folder: destinationFolderId }, { user }) + ) + ); + + ctx.body = { + data: { + files: await pmFile.sanitizeOutput(updatedFiles), + folders: await pmFolder.sanitizeOutput(updatedFolders), }, }; }, diff --git a/packages/core/upload/server/controllers/admin-folder.js b/packages/core/upload/server/controllers/admin-folder.js index 1503cfd65c..89085dffac 100644 --- a/packages/core/upload/server/controllers/admin-folder.js +++ b/packages/core/upload/server/controllers/admin-folder.js @@ -72,7 +72,7 @@ module.exports = { model: folderModel, }); - await validateUpdateFolder(body); + await validateUpdateFolder(id)(body); const { update } = getService('folder'); diff --git a/packages/core/upload/server/controllers/validation/admin/folder-file.js b/packages/core/upload/server/controllers/validation/admin/folder-file.js index 74c09a96c7..4b271ccb92 100644 --- a/packages/core/upload/server/controllers/validation/admin/folder-file.js +++ b/packages/core/upload/server/controllers/validation/admin/folder-file.js @@ -1,22 +1,91 @@ 'use strict'; +const { intersection, map, isEmpty } = require('lodash/fp'); const { yup, validateYupSchema } = require('@strapi/utils'); +const { folderExists } = require('./utils'); + +const folderModel = 'plugin::upload.folder'; const validateDeleteManyFoldersFilesSchema = yup .object() .shape({ - fileIds: yup - .array() - .min(1) - .of(yup.strapiID().required()), - folderIds: yup - .array() - .min(1) - .of(yup.strapiID().required()), + fileIds: yup.array().of(yup.strapiID().required()), + folderIds: yup.array().of(yup.strapiID().required()), }) .noUnknown() .required(); +const validateStructureMoveManyFoldersFilesSchema = yup + .object() + .shape({ + destinationFolderId: yup + .strapiID() + .nullable() + .test('folder-exists', 'destination folder does not exist', folderExists), + fileIds: yup.array().of(yup.strapiID().required()), + folderIds: yup.array().of(yup.strapiID().required()), + }) + .noUnknown() + .required(); + +const validateDuplicatesMoveManyFoldersFilesSchema = yup + .object() + .test('are-folders-unique', 'some folders already exist', async function(value) { + const { folderIds, destinationFolderId } = value; + if (isEmpty(folderIds)) return true; + + const folders = await strapi.entityService.findMany(folderModel, { + fields: ['name'], + filters: { id: { $in: folderIds } }, + }); + // TODO: handle when parent is null + const existingFolders = await strapi.entityService.findMany(folderModel, { + fields: ['name'], + filters: { parent: { id: destinationFolderId } }, + }); + const duplicatedNames = intersection(map('name', folders), map('name', existingFolders)); + if (duplicatedNames.length > 0) { + return this.createError({ + message: `some folders already exists: ${duplicatedNames.join(', ')}`, + }); + } + + return true; + }); + +const validateMoveFoldersNotInsideThemselvesSchema = yup + .object() + .test('dont-move-inside-self', 'folders cannot be moved inside themselves', async function( + value + ) { + const { folderIds, destinationFolderId } = value; + if (destinationFolderId === null || isEmpty(folderIds)) return true; + + const destinationFolder = await strapi.entityService.findOne(folderModel, destinationFolderId, { + fields: ['path'], + }); + const folders = await strapi.entityService.findMany(folderModel, { + fields: ['name', 'path'], + filters: { id: { $in: folderIds } }, + }); + + const unmovableFoldersNames = folders + .filter(folder => destinationFolder.path.startsWith(folder.path)) + .map(f => f.name); + if (unmovableFoldersNames.length > 0) { + return this.createError({ + message: `folders cannot be moved inside themselves: ${unmovableFoldersNames.join(', ')}`, + }); + } + + return true; + }); + module.exports = { validateDeleteManyFoldersFiles: validateYupSchema(validateDeleteManyFoldersFilesSchema), + async validateMoveManyFoldersFiles(body) { + await validateYupSchema(validateStructureMoveManyFoldersFilesSchema)(body); + await validateYupSchema(validateDuplicatesMoveManyFoldersFilesSchema)(body); + await validateYupSchema(validateMoveFoldersNotInsideThemselvesSchema)(body); + }, }; diff --git a/packages/core/upload/server/controllers/validation/admin/folder.js b/packages/core/upload/server/controllers/validation/admin/folder.js index cae82ca6f5..e20e41e7cc 100644 --- a/packages/core/upload/server/controllers/validation/admin/folder.js +++ b/packages/core/upload/server/controllers/validation/admin/folder.js @@ -1,25 +1,27 @@ 'use strict'; +const { isUndefined, get } = require('lodash/fp'); const { yup, validateYupSchema } = require('@strapi/utils'); -const { isNil } = require('lodash/fp'); const { getService } = require('../../../utils'); +const { folderExists } = require('./utils'); +const folderModel = 'plugin::upload.folder'; const NO_SLASH_REGEX = /^[^/]+$/; const NO_SPACES_AROUND = /^(?! ).+(? { - if (isNil(folderId)) { - return true; +const isNameUniqueInFolder = (id) => async function(name) { + const { exists } = getService('folder'); + const filters = { name, parent: this.parent.parent || null }; + if (id) { + filters.id = { $ne: id }; + + if (isUndefined(name)) { + const existingFolder = await strapi.entityService.findOne(folderModel, id); + filters.name = get('name', existingFolder); + } } - const exists = await getService('folder').exists({ id: folderId }); - - return exists; -}; - -const isNameUniqueInFolder = async function(name) { - const { exists } = getService('folder'); - const doesExist = await exists({ parent: this.parent.parent || null, name }); + const doesExist = await exists(filters); return !doesExist; } @@ -32,7 +34,7 @@ const validateCreateFolderSchema = yup .matches(NO_SLASH_REGEX, 'name cannot contain slashes') .matches(NO_SPACES_AROUND, 'name cannot start or end with a whitespace') .required() - .test('is-folder-unique', 'name already taken', isNameUniqueInFolder), + .test('is-folder-unique', 'folder already exists', isNameUniqueInFolder()), parent: yup .strapiID() .nullable() @@ -41,24 +43,25 @@ const validateCreateFolderSchema = yup .noUnknown() .required(); -const validateUpdateFolderSchema = yup - .object() - .shape({ - name: yup - .string() - .min(1) - .matches(NO_SLASH_REGEX, 'name cannot contain slashes') - .matches(NO_SPACES_AROUND, 'name cannot start or end with a whitespace') - .test('is-folder-unique', 'name already taken', isNameUniqueInFolder), - parent: yup - .strapiID() - .nullable() - .test('folder-exists', 'parent folder does not exist', folderExists), - }) - .noUnknown() - .required(); +const validateUpdateFolderSchema = id => + yup + .object() + .shape({ + name: yup + .string() + .min(1) + .matches(NO_SLASH_REGEX, 'name cannot contain slashes') + .matches(NO_SPACES_AROUND, 'name cannot start or end with a whitespace') + .test('is-folder-unique', 'folder already exists', isNameUniqueInFolder(id)), + parent: yup + .strapiID() + .nullable() + .test('folder-exists', 'parent folder does not exist', folderExists), + }) + .noUnknown() + .required(); module.exports = { validateCreateFolder: validateYupSchema(validateCreateFolderSchema), - validateUpdateFolder: validateYupSchema(validateUpdateFolderSchema), + validateUpdateFolder: id => validateYupSchema(validateUpdateFolderSchema(id)), }; diff --git a/packages/core/upload/server/controllers/validation/admin/utils.js b/packages/core/upload/server/controllers/validation/admin/utils.js new file mode 100644 index 0000000000..3834f598c6 --- /dev/null +++ b/packages/core/upload/server/controllers/validation/admin/utils.js @@ -0,0 +1,18 @@ +'use strict'; + +const { isNil } = require('lodash/fp'); +const { getService } = require('../../../utils'); + +const folderExists = async folderId => { + if (isNil(folderId)) { + return true; + } + + const exists = await getService('folder').exists({ id: folderId }); + + return exists; +}; + +module.exports = { + folderExists, +}; diff --git a/packages/core/upload/server/routes/admin.js b/packages/core/upload/server/routes/admin.js index c959ab6ee2..9d1fbd9115 100644 --- a/packages/core/upload/server/routes/admin.js +++ b/packages/core/upload/server/routes/admin.js @@ -171,5 +171,21 @@ module.exports = { ], }, }, + { + method: 'POST', + path: '/actions/bulk-move', + handler: 'admin-folder-file.moveMany', + config: { + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['plugin::upload.read'], + }, + }, + ], + }, + }, ], }; diff --git a/packages/core/upload/tests/admin/folder-file.test.e2e.js b/packages/core/upload/tests/admin/folder-file.test.e2e.js new file mode 100644 index 0000000000..8d399158a2 --- /dev/null +++ b/packages/core/upload/tests/admin/folder-file.test.e2e.js @@ -0,0 +1,433 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const { createTestBuilder } = require('../../../../../test/helpers/builder'); +const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); +const { createAuthRequest } = require('../../../../../test/helpers/request'); + +let strapi; +let rq; +let data = { + folders: [], + files: [], +}; + +const createFolder = async (name, parent = null) => { + const res = await rq({ + method: 'POST', + url: '/upload/folders', + body: { name, parent }, + }); + return res.body.data; +}; + +const createAFile = async (parent = null) => { + const res = await rq({ + method: 'POST', + url: '/upload', + formData: { + files: fs.createReadStream(path.join(__dirname, '../utils/rec.jpg')), + fileInfo: JSON.stringify({ folder: parent }), + }, + }); + return res.body[0]; +}; + +describe('Bulk actions fot folders & files', () => { + const builder = createTestBuilder(); + + beforeAll(async () => { + strapi = await createStrapiInstance(); + rq = await createAuthRequest({ strapi }); + }); + + afterAll(async () => { + await rq({ + method: 'POST', + url: '/upload/actions/bulk-delete', + body: { + folderIds: data.folders.map(f => f.id), + }, + }); + + await strapi.destroy(); + await builder.cleanup(); + }); + + describe('delete', () => { + test('Can delete folders and files', async () => { + const folder1 = await createFolder('folder-a-1', null); + const folder1a = await createFolder('folder-a-1a', folder1.id); + const folder1b = await createFolder('folder-a-1b', folder1.id); + const folder1a1 = await createFolder('folder-a-1a1', folder1a.id); + const file1 = await createAFile(null); + const file1b = await createAFile(folder1b.id); + const file1a = await createAFile(folder1a.id); + const file1a1 = await createAFile(folder1a1.id); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-delete', + body: { + fileIds: [file1.id], + folderIds: [folder1a.id], + }, + }); + + expect(res.body.data).toMatchObject({ + files: [ + { + alternativeText: null, + caption: null, + createdAt: expect.anything(), + ext: '.jpg', + folderPath: '/', + formats: null, + hash: expect.anything(), + height: 20, + id: file1.id, + mime: 'image/jpeg', + name: 'rec.jpg', + previewUrl: null, + provider: 'local', + provider_metadata: null, + size: 0.27, + updatedAt: expect.anything(), + url: expect.anything(), + width: 20, + }, + ], + folders: [ + { + id: folder1a.id, + name: 'folder-a-1a', + path: expect.anything(), + uid: expect.anything(), + createdAt: expect.anything(), + updatedAt: expect.anything(), + }, + ], + }); + + const resFolder = await rq({ + method: 'GET', + url: '/upload/folders?pagination[pageSize]=100', + }); + + const existingfoldersIds = resFolder.body.results.map(f => f.id); + expect(existingfoldersIds).toEqual(expect.not.arrayContaining([folder1a.id, folder1a1.id])); + expect(existingfoldersIds).toEqual(expect.arrayContaining([folder1.id, folder1b.id])); + + const resFiles = await rq({ + method: 'GET', + url: '/upload/files', + qs: { + pageSize: 100, + }, + }); + + const existingfilesIds = resFiles.body.results.map(f => f.id); + expect(existingfilesIds).toEqual( + expect.not.arrayContaining([file1.id, file1a.id, file1a1.id]) + ); + expect(existingfilesIds).toEqual(expect.arrayContaining([file1b.id])); + + data.folders.push(folder1, folder1b); + data.files.push(file1b); + }); + }); + + describe('move', () => { + test('Can move folders and files into another folder', async () => { + const folder1 = await createFolder('folder-b-1', null); + const folder1a = await createFolder('folder-b-1a', folder1.id); + const folder1b = await createFolder('folder-b-1b', folder1.id); + const folder1a1 = await createFolder('folder-b-1a1', folder1a.id); + const file1 = await createAFile(null); + const file1a = await createAFile(folder1a.id); + const file1b = await createAFile(folder1b.id); + const file1a1 = await createAFile(folder1a1.id); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: folder1b.id, + fileIds: [file1a.id], + folderIds: [folder1a.id], + }, + }); + + expect(res.body.data).toMatchObject({ + files: [ + { + alternativeText: null, + caption: null, + createdAt: expect.anything(), + ext: '.jpg', + folderPath: folder1b.path, + formats: null, + hash: expect.anything(), + height: 20, + id: file1a.id, + mime: 'image/jpeg', + name: 'rec.jpg', + previewUrl: null, + provider: 'local', + provider_metadata: null, + size: 0.27, + updatedAt: expect.anything(), + url: expect.anything(), + width: 20, + }, + ], + folders: [ + { + id: folder1a.id, + name: 'folder-b-1a', + path: `${folder1b.path}/${folder1a.uid}`, + uid: expect.anything(), + createdAt: expect.anything(), + updatedAt: expect.anything(), + }, + ], + }); + + const { + body: { results: folderResults }, + } = await rq({ + method: 'GET', + url: '/upload/folders?pagination[pageSize]=100&populate=parent', + qs: { + pagination: { pageSize: 100 }, + populate: 'parent', + sort: 'id:asc', + filters: { id: { $in: [folder1.id, folder1a.id, folder1b.id, folder1a1.id] } }, + }, + }); + + expect(folderResults[0]).toMatchObject({ ...folder1, parent: null }); + expect(folderResults[1]).toMatchObject({ + ...folder1a, + path: `${folder1b.path}/${folder1a.uid}`, + parent: { id: folder1b.id }, + updatedAt: expect.anything(), + }); + expect(folderResults[2]).toMatchObject({ ...folder1b, parent: { id: folder1.id } }); + expect(folderResults[3]).toMatchObject({ + ...folder1a1, + path: `${folder1b.path}/${folder1a.uid}/${folder1a1.uid}`, + parent: { id: folder1a.id }, + }); + + const { + body: { results: fileResults }, + } = await rq({ + method: 'GET', + url: '/upload/files', + qs: { + pageSize: 100, + populate: 'folder', + sort: 'id:asc', + filters: { id: { $in: [file1.id, file1a.id, file1b.id, file1a1.id] } }, + }, + }); + + expect(fileResults[0]).toMatchObject({ ...file1, folder: null }); + expect(fileResults[1]).toMatchObject({ + ...file1a, + folderPath: folder1b.path, + folder: { id: folder1b.id }, + updatedAt: expect.anything(), + }); + expect(fileResults[2]).toMatchObject({ ...file1b, folder: { id: folder1b.id } }); + expect(fileResults[3]).toMatchObject({ + ...file1a1, + folderPath: `${folder1b.path}/${folder1a.uid}/${folder1a1.uid}`, + folder: { id: folder1a1.id }, + }); + + data.folders.push(...folderResults); + data.files.push(...fileResults); + }); + test('Can move folders and files to the root level', async () => { + const folder1 = await createFolder('folder-c-1', null); + const folder1a = await createFolder('folder-c-1a', folder1.id); + const folder1a1 = await createFolder('folder-c-1a1', folder1a.id); + const file1 = await createAFile(null); + const file1a = await createAFile(folder1a.id); + const file1a1 = await createAFile(folder1a1.id); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: null, + fileIds: [file1a.id], + folderIds: [folder1a.id], + }, + }); + + expect(res.body.data).toMatchObject({ + files: [ + { + alternativeText: null, + caption: null, + createdAt: expect.anything(), + ext: '.jpg', + folderPath: '/', + formats: null, + hash: expect.anything(), + height: 20, + id: file1a.id, + mime: 'image/jpeg', + name: 'rec.jpg', + previewUrl: null, + provider: 'local', + provider_metadata: null, + size: 0.27, + updatedAt: expect.anything(), + url: expect.anything(), + width: 20, + }, + ], + folders: [ + { + id: folder1a.id, + name: 'folder-c-1a', + path: `/${folder1a.uid}`, + uid: expect.anything(), + createdAt: expect.anything(), + updatedAt: expect.anything(), + }, + ], + }); + + const { + body: { results: folderResults }, + } = await rq({ + method: 'GET', + url: '/upload/folders?pagination[pageSize]=100&populate=parent', + qs: { + pagination: { pageSize: 100 }, + populate: 'parent', + sort: 'id:asc', + filters: { id: { $in: [folder1.id, folder1a.id, folder1a1.id] } }, + }, + }); + + expect(folderResults[0]).toMatchObject({ ...folder1, parent: null }); + expect(folderResults[1]).toMatchObject({ + ...folder1a, + path: `/${folder1a.uid}`, + parent: null, + updatedAt: expect.anything(), + }); + expect(folderResults[2]).toMatchObject({ + ...folder1a1, + path: `/${folder1a.uid}/${folder1a1.uid}`, + parent: { id: folder1a.id }, + }); + + const { + body: { results: fileResults }, + } = await rq({ + method: 'GET', + url: '/upload/files', + qs: { + pageSize: 100, + populate: 'folder', + sort: 'id:asc', + filters: { id: { $in: [file1.id, file1a.id, file1a1.id] } }, + }, + }); + + expect(fileResults[0]).toMatchObject({ ...file1, folder: null }); + expect(fileResults[1]).toMatchObject({ + ...file1a, + folderPath: '/', + folder: null, + updatedAt: expect.anything(), + }); + expect(fileResults[2]).toMatchObject({ + ...file1a1, + folderPath: `/${folder1a.uid}/${folder1a1.uid}`, + folder: { id: folder1a1.id }, + }); + + data.folders.push(...folderResults); + data.files.push(...fileResults); + }); + + test('Cannot move a folder inside itself (0 level)', async () => { + const folder1 = await createFolder('folder-d-1', null); + data.folders.push(folder1); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: folder1.id, + folderIds: [folder1.id], + }, + }); + expect(res.status).toBe(400); + expect(res.body.error.message).toBe('folders cannot be moved inside themselves: folder-d-1'); + }); + + test('Cannot move a folder inside itself (1 level)', async () => { + const folder1 = await createFolder('folder-e-1', null); + const folder1a = await createFolder('folder-e-1a', folder1.id); + data.folders.push(folder1, folder1a); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: folder1a.id, + folderIds: [folder1.id], + }, + }); + expect(res.status).toBe(400); + expect(res.body.error.message).toBe('folders cannot be moved inside themselves: folder-e-1'); + }); + + test('Cannot move a folder inside itself (2 levels)', async () => { + const folder1 = await createFolder('folder-f-1', null); + const folder1a = await createFolder('folder-f-1a', folder1.id); + const folder1a1 = await createFolder('folder-f-1a1', folder1a.id); + data.folders.push(folder1, folder1a, folder1a1); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: folder1a1.id, + folderIds: [folder1.id], + }, + }); + expect(res.status).toBe(400); + expect(res.body.error.message).toBe('folders cannot be moved inside themselves: folder-f-1'); + }); + + test('Cannot move a folder if it creates a duplicate', async () => { + const folder1 = await createFolder('folder-g-1', null); + const folder1a = await createFolder('folder-g-1a', folder1.id); + const folder2 = await createFolder('folder-g-1a', null); + data.folders.push(folder1, folder1a, folder2); + + const res = await rq({ + method: 'POST', + url: '/upload/actions/bulk-move', + body: { + destinationFolderId: folder1.id, + folderIds: [folder2.id], + }, + }); + expect(res.status).toBe(400); + expect(res.body.error.message).toBe('some folders already exists: folder-g-1a'); + }); + }); +}); diff --git a/packages/core/upload/tests/admin/folder.test.e2e.js b/packages/core/upload/tests/admin/folder.test.e2e.js index 977cd581f1..4d85843d2b 100644 --- a/packages/core/upload/tests/admin/folder.test.e2e.js +++ b/packages/core/upload/tests/admin/folder.test.e2e.js @@ -126,7 +126,7 @@ describe('Folder', () => { }); expect(res.status).toBe(400); - expect(res.body.error.message).toBe('name already taken'); + expect(res.body.error.message).toBe('folder already exists'); }); test('Cannot create a folder with duplicated name inside a folder', async () => { @@ -140,7 +140,7 @@ describe('Folder', () => { }); expect(res.status).toBe(400); - expect(res.body.error.message).toBe('name already taken'); + expect(res.body.error.message).toBe('folder already exists'); }); test('Cannot create a folder inside a folder that does not exist', async () => { @@ -245,88 +245,6 @@ describe('Folder', () => { }); }); - describe('delete', () => { - test('Can delete folders and belonging files', async () => { - const folder1 = await createFolder('folder1', null); - const folder1a = await createFolder('folder1a', folder1.id); - const folder1b = await createFolder('folder1b', folder1.id); - const folder1a1 = await createFolder('folder1a1', folder1a.id); - const file1 = await createAFile(null); - const file1b = await createAFile(folder1b.id); - const file1a = await createAFile(folder1a.id); - const file1a1 = await createAFile(folder1a1.id); - - const res = await rq({ - method: 'POST', - url: '/upload/actions/bulk-delete', - body: { - fileIds: [file1.id], - folderIds: [folder1a.id], - }, - }); - - expect(res.body.data).toMatchObject({ - files: [ - { - alternativeText: null, - caption: null, - createdAt: expect.anything(), - ext: '.jpg', - folderPath: '/', - formats: null, - hash: expect.anything(), - height: 20, - id: file1.id, - mime: 'image/jpeg', - name: 'rec.jpg', - previewUrl: null, - provider: 'local', - provider_metadata: null, - size: 0.27, - updatedAt: expect.anything(), - url: expect.anything(), - width: 20, - }, - ], - folders: [ - { - id: folder1a.id, - name: 'folder1a', - path: expect.anything(), - uid: expect.anything(), - createdAt: expect.anything(), - updatedAt: expect.anything(), - }, - ], - }); - - const resFolder = await rq({ - method: 'GET', - url: '/upload/folders?pagination[pageSize]=100', - }); - - const existingfoldersIds = resFolder.body.results.map(f => f.id); - expect(existingfoldersIds).toEqual(expect.not.arrayContaining([folder1a.id, folder1a1.id])); - expect(existingfoldersIds).toEqual(expect.arrayContaining([folder1.id, folder1b.id])); - - const resFiles = await rq({ - method: 'GET', - url: '/upload/files', - qs: { - pageSize: 100, - }, - }); - - const existingfilesIds = resFiles.body.results.map(f => f.id); - expect(existingfilesIds).toEqual( - expect.not.arrayContaining([file1.id, file1a.id, file1a1.id]) - ); - expect(existingfilesIds).toEqual(expect.arrayContaining([file1b.id])); - - data.folders.push(folder1, folder1b); - }); - }); - describe('update', () => { test('rename a folder', async () => { const folder = await createFolder('folder-name', null); @@ -346,7 +264,7 @@ describe('Folder', () => { data.folders.push(res.body.data); }); - test('cannot rename a folder if duplicated', async () => { + test('cannot move and rename a folder if duplicated', async () => { const folder0 = await createFolder('folder-a-0', null); const folder1 = await createFolder('folder-a-1', null); const folder00 = await createFolder('folder-a-00', folder0.id); @@ -362,11 +280,29 @@ describe('Folder', () => { }); expect(res.status).toBe(400); - expect(res.body.error.message).toBe('name already taken'); + expect(res.body.error.message).toBe('folder already exists'); + }); + + test('cannot move a folder if duplicated', async () => { + const folder0 = await createFolder('folder-b-0', null); + const folder1 = await createFolder('folder-b-samename', null); + await createFolder('folder-b-samename', folder0.id); + data.folders.push(folder0, folder1); + + const res = await rq({ + method: 'PUT', + url: `/upload/folders/${folder1.id}`, + body: { + parent: folder0.id, + }, + }); + + expect(res.status).toBe(400); + expect(res.body.error.message).toBe('folder already exists'); }); test('cannot move a folder to a folder that does not exist', async () => { - const folder = await createFolder('folder-b-0', null); + const folder = await createFolder('folder-c-0', null); data.folders.push(folder); const res = await rq({