mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 03:34:35 +00:00
Merge pull request #113 from sasjs/issue-108
feat(drive): GET folder contents API added
This commit is contained in:
@@ -612,7 +612,6 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: 'No content'
|
description: 'No content'
|
||||||
description: "It's optional to either provide `_filePath` in url as query parameter\nOr provide `filePath` in body as form field.\nBut it's required to provide else API will respond with Bad Request."
|
|
||||||
summary: 'Get file from SASjs Drive'
|
summary: 'Get file from SASjs Drive'
|
||||||
tags:
|
tags:
|
||||||
- Drive
|
- Drive
|
||||||
@@ -623,19 +622,10 @@ paths:
|
|||||||
-
|
-
|
||||||
in: query
|
in: query
|
||||||
name: _filePath
|
name: _filePath
|
||||||
required: false
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: /Public/somefolder/some.file
|
example: /Public/somefolder/some.file
|
||||||
requestBody:
|
|
||||||
required: false
|
|
||||||
content:
|
|
||||||
multipart/form-data:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
filePath:
|
|
||||||
type: string
|
|
||||||
delete:
|
delete:
|
||||||
operationId: DeleteFile
|
operationId: DeleteFile
|
||||||
responses:
|
responses:
|
||||||
@@ -765,6 +755,36 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- file
|
- file
|
||||||
|
/SASjsApi/drive/folder:
|
||||||
|
get:
|
||||||
|
operationId: GetFolder
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
folders: {items: {type: string}, type: array}
|
||||||
|
files: {items: {type: string}, type: array}
|
||||||
|
required:
|
||||||
|
- folders
|
||||||
|
- files
|
||||||
|
type: object
|
||||||
|
summary: 'Get folder contents from SASjs Drive'
|
||||||
|
tags:
|
||||||
|
- Drive
|
||||||
|
security:
|
||||||
|
-
|
||||||
|
bearerAuth: []
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
in: query
|
||||||
|
name: _folderPath
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: /Public/somefolder
|
||||||
/SASjsApi/drive/filetree:
|
/SASjsApi/drive/filetree:
|
||||||
get:
|
get:
|
||||||
operationId: GetFileTree
|
operationId: GetFileTree
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
copySASjsCore,
|
copySASjsCore,
|
||||||
getWebBuildFolderPath,
|
getWebBuildFolderPath,
|
||||||
loadAppStreamConfig,
|
loadAppStreamConfig,
|
||||||
setProcessVariables
|
setProcessVariables,
|
||||||
|
setupFolders
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
@@ -42,6 +43,7 @@ const onError: ErrorRequestHandler = (err, req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default setProcessVariables().then(async () => {
|
export default setProcessVariables().then(async () => {
|
||||||
|
await setupFolders()
|
||||||
await copySASjsCore()
|
await copySASjsCore()
|
||||||
|
|
||||||
// loading these modules after setting up variables due to
|
// loading these modules after setting up variables due to
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ import {
|
|||||||
fileExists,
|
fileExists,
|
||||||
moveFile,
|
moveFile,
|
||||||
createFolder,
|
createFolder,
|
||||||
deleteFile as deleteFileOnSystem
|
deleteFile as deleteFileOnSystem,
|
||||||
|
folderExists,
|
||||||
|
listFilesAndSubFoldersInFolder,
|
||||||
|
listFilesInFolder,
|
||||||
|
listSubFoldersInFolder,
|
||||||
|
isFolder
|
||||||
} from '@sasjs/utils'
|
} from '@sasjs/utils'
|
||||||
import { createFileTree, ExecutionController, getTreeExample } from './internal'
|
import { createFileTree, ExecutionController, getTreeExample } from './internal'
|
||||||
|
|
||||||
@@ -89,9 +94,6 @@ export class DriveController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's optional to either provide `_filePath` in url as query parameter
|
|
||||||
* Or provide `filePath` in body as form field.
|
|
||||||
* But it's required to provide else API will respond with Bad Request.
|
|
||||||
*
|
*
|
||||||
* @summary Get file from SASjs Drive
|
* @summary Get file from SASjs Drive
|
||||||
* @query _filePath Location of SAS program
|
* @query _filePath Location of SAS program
|
||||||
@@ -100,11 +102,20 @@ export class DriveController {
|
|||||||
@Get('/file')
|
@Get('/file')
|
||||||
public async getFile(
|
public async getFile(
|
||||||
@Request() request: express.Request,
|
@Request() request: express.Request,
|
||||||
|
@Query() _filePath: string
|
||||||
@Query() _filePath?: string,
|
|
||||||
@FormField() filePath?: string
|
|
||||||
) {
|
) {
|
||||||
return getFile(request, (_filePath ?? filePath)!)
|
return getFile(request, _filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get folder contents from SASjs Drive
|
||||||
|
* @query _folderPath Location of SAS program
|
||||||
|
* @example _folderPath "/Public/somefolder"
|
||||||
|
*/
|
||||||
|
@Get('/folder')
|
||||||
|
public async getFolder(@Query() _folderPath?: string) {
|
||||||
|
return getFolder(_folderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -221,7 +232,7 @@ const getFile = async (req: express.Request, filePath: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(await fileExists(filePathFull))) {
|
if (!(await fileExists(filePathFull))) {
|
||||||
throw new Error('File does not exist.')
|
throw new Error("File doesn't exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const extension = path.extname(filePathFull).toLowerCase()
|
const extension = path.extname(filePathFull).toLowerCase()
|
||||||
@@ -232,6 +243,36 @@ const getFile = async (req: express.Request, filePath: string) => {
|
|||||||
req.res?.sendFile(path.resolve(filePathFull))
|
req.res?.sendFile(path.resolve(filePathFull))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFolder = async (folderPath?: string) => {
|
||||||
|
const driveFilesPath = getTmpFilesFolderPath()
|
||||||
|
|
||||||
|
if (folderPath) {
|
||||||
|
const folderPathFull = path
|
||||||
|
.join(getTmpFilesFolderPath(), folderPath)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep)
|
||||||
|
|
||||||
|
if (!folderPathFull.includes(driveFilesPath)) {
|
||||||
|
throw new Error('Cannot get folder outside drive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await folderExists(folderPathFull))) {
|
||||||
|
throw new Error("Folder doesn't exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await isFolder(folderPathFull))) {
|
||||||
|
throw new Error('Not a Folder.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const files: string[] = await listFilesInFolder(folderPathFull)
|
||||||
|
const folders: string[] = await listSubFoldersInFolder(folderPathFull)
|
||||||
|
return { files, folders }
|
||||||
|
}
|
||||||
|
|
||||||
|
const files: string[] = await listFilesInFolder(driveFilesPath)
|
||||||
|
const folders: string[] = await listSubFoldersInFolder(driveFilesPath)
|
||||||
|
return { files, folders }
|
||||||
|
}
|
||||||
|
|
||||||
const deleteFile = async (filePath: string) => {
|
const deleteFile = async (filePath: string) => {
|
||||||
const driveFilesPath = getTmpFilesFolderPath()
|
const driveFilesPath = getTmpFilesFolderPath()
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { DriveController } from '../../controllers/'
|
|||||||
import {
|
import {
|
||||||
deployValidation,
|
deployValidation,
|
||||||
fileBodyValidation,
|
fileBodyValidation,
|
||||||
fileParamValidation
|
fileParamValidation,
|
||||||
|
folderParamValidation
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
const controller = new DriveController()
|
const controller = new DriveController()
|
||||||
@@ -44,12 +45,24 @@ driveRouter.post('/deploy', async (req, res) => {
|
|||||||
|
|
||||||
driveRouter.get('/file', async (req, res) => {
|
driveRouter.get('/file', async (req, res) => {
|
||||||
const { error: errQ, value: query } = fileParamValidation(req.query)
|
const { error: errQ, value: query } = fileParamValidation(req.query)
|
||||||
const { error: errB, value: body } = fileBodyValidation(req.body)
|
|
||||||
|
|
||||||
if (errQ && errB) return res.status(400).send(errQ.details[0].message)
|
if (errQ) return res.status(400).send(errQ.details[0].message)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await controller.getFile(req, query._filePath, body.filePath)
|
await controller.getFile(req, query._filePath)
|
||||||
|
} catch (err: any) {
|
||||||
|
res.status(403).send(err.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
driveRouter.get('/folder', async (req, res) => {
|
||||||
|
const { error: errQ, value: query } = folderParamValidation(req.query)
|
||||||
|
|
||||||
|
if (errQ) return res.status(400).send(errQ.details[0].message)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await controller.getFolder(query._folderPath)
|
||||||
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(403).send(err.toString())
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import {
|
|||||||
readFile,
|
readFile,
|
||||||
deleteFolder,
|
deleteFolder,
|
||||||
generateTimestamp,
|
generateTimestamp,
|
||||||
copy
|
copy,
|
||||||
|
createFolder,
|
||||||
|
createFile
|
||||||
} from '@sasjs/utils'
|
} from '@sasjs/utils'
|
||||||
import * as fileUtilModules from '../../../utils/file'
|
import * as fileUtilModules from '../../../utils/file'
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ const user = {
|
|||||||
isActive: true
|
isActive: true
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('files', () => {
|
describe('drive', () => {
|
||||||
let con: Mongoose
|
let con: Mongoose
|
||||||
let mongoServer: MongoMemoryServer
|
let mongoServer: MongoMemoryServer
|
||||||
const controller = new UserController()
|
const controller = new UserController()
|
||||||
@@ -69,6 +71,7 @@ describe('files', () => {
|
|||||||
await mongoServer.stop()
|
await mongoServer.stop()
|
||||||
await deleteFolder(tmpFolder)
|
await deleteFolder(tmpFolder)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('deploy', () => {
|
describe('deploy', () => {
|
||||||
const shouldFailAssertion = async (payload: any) => {
|
const shouldFailAssertion = async (payload: any) => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -172,17 +175,126 @@ describe('files', () => {
|
|||||||
|
|
||||||
await expect(readFile(testJobFile)).resolves.toEqual(exampleService.code)
|
await expect(readFile(testJobFile)).resolves.toEqual(exampleService.code)
|
||||||
|
|
||||||
await deleteFolder(getTmpFilesFolderPath())
|
await deleteFolder(path.join(getTmpFilesFolderPath(), 'public'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('folder', () => {
|
||||||
|
describe('get', () => {
|
||||||
|
const getFolderApi = '/SASjsApi/drive/folder'
|
||||||
|
|
||||||
|
it('should get root SAS folder on drive', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(200)
|
||||||
|
expect(res.body).toEqual({ files: [], folders: [] })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get a SAS folder on drive having _folderPath as query param', async () => {
|
||||||
|
const pathToDrive = fileUtilModules.getTmpFilesFolderPath()
|
||||||
|
|
||||||
|
const dirLevel1 = 'level1'
|
||||||
|
const dirLevel2 = 'level2'
|
||||||
|
const fileLevel1 = 'file1'
|
||||||
|
const fileLevel2 = 'file2'
|
||||||
|
|
||||||
|
await createFolder(path.join(pathToDrive, dirLevel1, dirLevel2))
|
||||||
|
await createFile(
|
||||||
|
path.join(pathToDrive, dirLevel1, fileLevel1),
|
||||||
|
'some file content'
|
||||||
|
)
|
||||||
|
await createFile(
|
||||||
|
path.join(pathToDrive, dirLevel1, dirLevel2, fileLevel2),
|
||||||
|
'some file content'
|
||||||
|
)
|
||||||
|
|
||||||
|
const res1 = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.query({ _folderPath: '/' })
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
|
||||||
|
expect(res1.statusCode).toEqual(200)
|
||||||
|
expect(res1.body).toEqual({ files: [], folders: [dirLevel1] })
|
||||||
|
|
||||||
|
const res2 = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.query({ _folderPath: dirLevel1 })
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
|
||||||
|
expect(res2.statusCode).toEqual(200)
|
||||||
|
expect(res2.body).toEqual({ files: [fileLevel1], folders: [dirLevel2] })
|
||||||
|
|
||||||
|
const res3 = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.query({ _folderPath: `${dirLevel1}/${dirLevel2}` })
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
|
||||||
|
expect(res3.statusCode).toEqual(200)
|
||||||
|
expect(res3.body).toEqual({ files: [fileLevel2], folders: [] })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Unauthorized if access token is not present', async () => {
|
||||||
|
const res = await request(app).get(getFolderApi).expect(401)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Unauthorized')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Forbidden if folder is not present', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _folderPath: `/my/path/code-${generateTimestamp()}` })
|
||||||
|
.expect(403)
|
||||||
|
|
||||||
|
expect(res.text).toEqual(`Error: Folder doesn't exist.`)
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Forbidden if folderPath outside Drive', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _folderPath: '/../path/code.sas' })
|
||||||
|
.expect(403)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Error: Cannot get folder outside drive.')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Forbidden if folderPath is of a file', async () => {
|
||||||
|
const fileToCopyPath = path.join(__dirname, 'files', 'sample.sas')
|
||||||
|
const filePath = '/my/path/code.sas'
|
||||||
|
|
||||||
|
const pathToCopy = path.join(
|
||||||
|
fileUtilModules.getTmpFilesFolderPath(),
|
||||||
|
filePath
|
||||||
|
)
|
||||||
|
await copy(fileToCopyPath, pathToCopy)
|
||||||
|
|
||||||
|
const res = await request(app)
|
||||||
|
.get(getFolderApi)
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _folderPath: filePath })
|
||||||
|
.expect(403)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Error: Not a Folder.')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('file', () => {
|
describe('file', () => {
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
it('should create a SAS file on drive having filePath as form field', async () => {
|
it('should create a SAS file on drive having filePath as form field', async () => {
|
||||||
|
const pathToUpload = `/my/path/code-1.sas`
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/drive/file')
|
.post('/SASjsApi/drive/file')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.field('filePath', '/my/path/code.sas')
|
.field('filePath', pathToUpload)
|
||||||
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(200)
|
expect(res.statusCode).toEqual(200)
|
||||||
@@ -192,10 +304,12 @@ describe('files', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create a SAS file on drive having _filePath as query param', async () => {
|
it('should create a SAS file on drive having _filePath as query param', async () => {
|
||||||
|
const pathToUpload = `/my/path/code-2.sas`
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/drive/file')
|
.post('/SASjsApi/drive/file')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.query({ _filePath: '/my/path/code1.sas' })
|
.query({ _filePath: pathToUpload })
|
||||||
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(200)
|
expect(res.statusCode).toEqual(200)
|
||||||
@@ -217,7 +331,7 @@ describe('files', () => {
|
|||||||
|
|
||||||
it('should respond with Forbidden if file is already present', async () => {
|
it('should respond with Forbidden if file is already present', async () => {
|
||||||
const fileToAttachPath = path.join(__dirname, 'files', 'sample.sas')
|
const fileToAttachPath = path.join(__dirname, 'files', 'sample.sas')
|
||||||
const pathToUpload = '/my/path/code.sas'
|
const pathToUpload = `/my/path/code-${generateTimestamp()}.sas`
|
||||||
|
|
||||||
const pathToCopy = path.join(
|
const pathToCopy = path.join(
|
||||||
fileUtilModules.getTmpFilesFolderPath(),
|
fileUtilModules.getTmpFilesFolderPath(),
|
||||||
@@ -386,7 +500,7 @@ describe('files', () => {
|
|||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch('/SASjsApi/drive/file')
|
.patch('/SASjsApi/drive/file')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.field('filePath', `/my/path/code-${generateTimestamp()}.sas`)
|
.field('filePath', `/my/path/code-3.sas`)
|
||||||
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
.attach('file', path.join(__dirname, 'files', 'sample.sas'))
|
||||||
.expect(403)
|
.expect(403)
|
||||||
|
|
||||||
@@ -427,9 +541,9 @@ describe('files', () => {
|
|||||||
const pathToUpload = '/my/path/code.exe'
|
const pathToUpload = '/my/path/code.exe'
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch(`/SASjsApi/drive/file?_filePath=${pathToUpload}`)
|
.patch('/SASjsApi/drive/file')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
// .field('filePath', pathToUpload)
|
.query({ _filePath: pathToUpload })
|
||||||
.attach('file', fileToAttachPath)
|
.attach('file', fileToAttachPath)
|
||||||
.expect(400)
|
.expect(400)
|
||||||
|
|
||||||
@@ -483,6 +597,79 @@ describe('files', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('get', () => {
|
||||||
|
it('should get a SAS file on drive having _filePath as query param', async () => {
|
||||||
|
const fileToCopyPath = path.join(__dirname, 'files', 'sample.sas')
|
||||||
|
const fileToCopyContent = await readFile(fileToCopyPath)
|
||||||
|
const filePath = '/my/path/code.sas'
|
||||||
|
|
||||||
|
const pathToCopy = path.join(
|
||||||
|
fileUtilModules.getTmpFilesFolderPath(),
|
||||||
|
filePath
|
||||||
|
)
|
||||||
|
await copy(fileToCopyPath, pathToCopy)
|
||||||
|
|
||||||
|
const res = await request(app)
|
||||||
|
.get('/SASjsApi/drive/file')
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _filePath: filePath })
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(200)
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
expect(res.text).toEqual(fileToCopyContent)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Unauthorized if access token is not present', async () => {
|
||||||
|
const res = await request(app).get('/SASjsApi/drive/file').expect(401)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Unauthorized')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Forbidden if file is not present', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get('/SASjsApi/drive/file')
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _filePath: `/my/path/code-4.sas` })
|
||||||
|
.expect(403)
|
||||||
|
|
||||||
|
expect(res.text).toEqual(`Error: File doesn't exist.`)
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Forbidden if filePath outside Drive', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get('/SASjsApi/drive/file')
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _filePath: '/../path/code.sas' })
|
||||||
|
.expect(403)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Error: Cannot get file outside drive.')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should respond with Bad Request if filePath doesn't has correct extension", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.patch('/SASjsApi/drive/file')
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.query({ _filePath: '/my/path/code.exe' })
|
||||||
|
.expect(400)
|
||||||
|
|
||||||
|
expect(res.text).toEqual('Invalid file extension')
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with Bad Request if filePath is missing', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post('/SASjsApi/drive/file')
|
||||||
|
.auth(accessToken, { type: 'bearer' })
|
||||||
|
.expect(400)
|
||||||
|
|
||||||
|
expect(res.text).toEqual(`"_filePath" is required`)
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export * from './parseLogToArray'
|
|||||||
export * from './removeTokensInDB'
|
export * from './removeTokensInDB'
|
||||||
export * from './saveTokensInDB'
|
export * from './saveTokensInDB'
|
||||||
export * from './setProcessVariables'
|
export * from './setProcessVariables'
|
||||||
|
export * from './setupFolders'
|
||||||
export * from './sleep'
|
export * from './sleep'
|
||||||
export * from './upload'
|
export * from './upload'
|
||||||
export * from './validation'
|
export * from './validation'
|
||||||
|
|||||||
7
api/src/utils/setupFolders.ts
Normal file
7
api/src/utils/setupFolders.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createFolder } from '@sasjs/utils'
|
||||||
|
import { getTmpFilesFolderPath } from './file'
|
||||||
|
|
||||||
|
export const setupFolders = async () => {
|
||||||
|
const drivePath = getTmpFilesFolderPath()
|
||||||
|
await createFolder(drivePath)
|
||||||
|
}
|
||||||
@@ -98,6 +98,11 @@ export const fileParamValidation = (data: any): Joi.ValidationResult =>
|
|||||||
_filePath: filePathSchema
|
_filePath: filePathSchema
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
|
export const folderParamValidation = (data: any): Joi.ValidationResult =>
|
||||||
|
Joi.object({
|
||||||
|
_folderPath: Joi.string()
|
||||||
|
}).validate(data)
|
||||||
|
|
||||||
export const runSASValidation = (data: any): Joi.ValidationResult =>
|
export const runSASValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
code: Joi.string().required()
|
code: Joi.string().required()
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
### Get contents of folder
|
||||||
|
GET http://localhost:5000/SASjsApi/drive/folder?_path=/Public/app/react-seed-app/services/web
|
||||||
|
|
||||||
###
|
###
|
||||||
POST http://localhost:5000/SASjsApi/drive/deploy
|
POST http://localhost:5000/SASjsApi/drive/deploy
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJjbGllbnRJRDEiLCJ1c2VybmFtZSI6InVzZXJuYW1lMSIsImlzYWRtaW4iOmZhbHNlLCJpc2FjdGl2ZSI6dHJ1ZSwiaWF0IjoxNjM1ODA0MDc2LCJleHAiOjE2MzU4OTA0NzZ9.Cx1F54ILgAUtnkit0Wg1K1YVO2RdNjOnTKdPhUtDm5I
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJjbGllbnRJRDEiLCJ1c2VybmFtZSI6InVzZXJuYW1lMSIsImlzYWRtaW4iOmZhbHNlLCJpc2FjdGl2ZSI6dHJ1ZSwiaWF0IjoxNjM1ODA0MDc2LCJleHAiOjE2MzU4OTA0NzZ9.Cx1F54ILgAUtnkit0Wg1K1YVO2RdNjOnTKdPhUtDm5I
|
||||||
|
|||||||
Reference in New Issue
Block a user