diff --git a/api/public/swagger.yaml b/api/public/swagger.yaml index 5d3e6af..90af057 100644 --- a/api/public/swagger.yaml +++ b/api/public/swagger.yaml @@ -250,6 +250,21 @@ components: - folderPath type: object additionalProperties: false + RenamePayload: + properties: + oldPath: + type: string + description: 'Old path of file/folder' + example: /Public/someFolder + newPath: + type: string + description: 'New path of file/folder' + example: /Public/newFolder + required: + - oldPath + - newPath + type: object + additionalProperties: false TreeNode: properties: name: @@ -1034,6 +1049,41 @@ paths: application/json: schema: $ref: '#/components/schemas/AddFolderPayload' + /SASjsApi/drive/rename: + post: + operationId: Rename + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/FileFolderResponse' + examples: + 'Example 1': + value: {status: success} + '409': + description: 'Folder already exists' + content: + application/json: + schema: + $ref: '#/components/schemas/FileFolderResponse' + examples: + 'Example 1': + value: {status: failure, message: 'rename request failed.'} + summary: 'Renames a file/folder in SASjs Drive' + tags: + - Drive + security: + - + bearerAuth: [] + parameters: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RenamePayload' /SASjsApi/drive/filetree: get: operationId: GetFileTree diff --git a/api/src/controllers/drive.ts b/api/src/controllers/drive.ts index d99a4d2..41acb31 100644 --- a/api/src/controllers/drive.ts +++ b/api/src/controllers/drive.ts @@ -72,6 +72,19 @@ interface AddFolderPayload { folderPath: string } +interface RenamePayload { + /** + * Old path of file/folder + * @example "/Public/someFolder" + */ + oldPath: string + /** + * New path of file/folder + * @example "/Public/newFolder" + */ + newPath: string +} + const fileTreeExample = getTreeExample() const successDeployResponse: DeployResponse = { @@ -241,6 +254,24 @@ export class DriveController { return updateFile((_filePath ?? filePath)!, file) } + /** + * @summary Renames a file/folder in SASjs Drive + * + */ + @Example({ + status: 'success' + }) + @Response(409, 'Folder already exists', { + status: 'failure', + message: 'rename request failed.' + }) + @Post('/rename') + public async rename( + @Body() body: RenamePayload + ): Promise { + return rename(body.oldPath, body.newPath) + } + /** * @summary Fetch file tree within SASjs Drive. * @@ -418,6 +449,46 @@ const addFolder = async (folderPath: string): Promise => { return { status: 'success' } } +const rename = async ( + oldPath: string, + newPath: string +): Promise => { + const drivePath = getFilesFolder() + + const oldPathFull = path + .join(drivePath, oldPath) + .replace(new RegExp('/', 'g'), path.sep) + + const newPathFull = path + .join(drivePath, newPath) + .replace(new RegExp('/', 'g'), path.sep) + + if (!oldPathFull.includes(drivePath)) { + throw new Error('Old path is outside drive.') + } + + if (!newPathFull.includes(drivePath)) { + throw new Error('New path is outside drive.') + } + + if (await folderExists(oldPathFull)) { + if (await folderExists(newPathFull)) { + throw new Error('Folder already exists.') + } else moveFile(oldPathFull, newPathFull) + + return { status: 'success' } + } + + if (await fileExists(oldPathFull)) { + if (await fileExists(newPathFull)) { + throw new Error('File already exists.') + } else moveFile(oldPath, newPathFull) + return { status: 'success' } + } + + throw new Error('No file/folder found for provided path.') +} + const updateFile = async ( filePath: string, multerFile: Express.Multer.File diff --git a/api/src/routes/api/drive.ts b/api/src/routes/api/drive.ts index 90fc707..1af8f3e 100644 --- a/api/src/routes/api/drive.ts +++ b/api/src/routes/api/drive.ts @@ -13,7 +13,8 @@ import { fileParamValidation, folderBodyValidation, folderParamValidation, - isZipFile + isZipFile, + renameBodyValidation } from '../../utils' const controller = new DriveController() @@ -232,6 +233,19 @@ driveRouter.patch( } ) +driveRouter.post('/rename', async (req, res) => { + const { error, value: body } = renameBodyValidation(req.body) + + if (error) return res.status(400).send(error.details[0].message) + + try { + const response = await controller.rename(body) + res.send(response) + } catch (err: any) { + res.status(403).send(err.toString()) + } +}) + driveRouter.get('/fileTree', async (req, res) => { try { const response = await controller.getFileTree() diff --git a/api/src/utils/validation.ts b/api/src/utils/validation.ts index 0dae10c..307e67a 100644 --- a/api/src/utils/validation.ts +++ b/api/src/utils/validation.ts @@ -140,12 +140,18 @@ export const fileParamValidation = (data: any): Joi.ValidationResult => export const folderParamValidation = (data: any): Joi.ValidationResult => Joi.object({ - _folderPath: Joi.string() + _folderPath: Joi.string().required() }).validate(data) export const folderBodyValidation = (data: any): Joi.ValidationResult => Joi.object({ - folderPath: Joi.string() + folderPath: Joi.string().required() + }).validate(data) + +export const renameBodyValidation = (data: any): Joi.ValidationResult => + Joi.object({ + oldPath: Joi.string().required(), + newPath: Joi.string().required() }).validate(data) export const runCodeValidation = (data: any): Joi.ValidationResult =>