mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
Merge branch 'master' into homepage-sasjs-executor
Conflicts: api/src/app.ts api/src/routes/index.ts api/src/routes/spec/routes.spec.ts
This commit is contained in:
@@ -1,11 +1,16 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import indexRouter from './routes'
|
import webRouter from './routes/web'
|
||||||
import path from 'path'
|
import apiRouter from './routes/api'
|
||||||
import { getWebBuildFolderPath } from './utils'
|
import { getWebBuildFolderPath } from './utils'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
app.use(express.json({ limit: '50mb' }))
|
app.use(express.json({ limit: '50mb' }))
|
||||||
app.use('/', indexRouter)
|
|
||||||
|
app.use('/', webRouter)
|
||||||
|
app.use('/SASjsApi', apiRouter)
|
||||||
|
app.use(express.json({ limit: '50mb' }))
|
||||||
|
|
||||||
app.use(express.static(getWebBuildFolderPath()))
|
app.use(express.static(getWebBuildFolderPath()))
|
||||||
|
|
||||||
export default app
|
export default app
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export class ExecutionController {
|
|||||||
autoExec?: string,
|
autoExec?: string,
|
||||||
session?: Session,
|
session?: Session,
|
||||||
vars?: any,
|
vars?: any,
|
||||||
otherArgs?: any
|
otherArgs?: any,
|
||||||
|
returnJson?: boolean
|
||||||
) {
|
) {
|
||||||
if (program) {
|
if (program) {
|
||||||
if (!(await fileExists(program))) {
|
if (!(await fileExists(program))) {
|
||||||
@@ -91,6 +92,7 @@ ${program}`
|
|||||||
(key: string) => key.toLowerCase() === '_debug'
|
(key: string) => key.toLowerCase() === '_debug'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let jsonResult
|
||||||
if ((debug && vars[debug] >= 131) || stderr) {
|
if ((debug && vars[debug] >= 131) || stderr) {
|
||||||
webout = `<html><body>
|
webout = `<html><body>
|
||||||
${webout}
|
${webout}
|
||||||
@@ -99,13 +101,15 @@ ${webout}
|
|||||||
<pre>${log}</pre>
|
<pre>${log}</pre>
|
||||||
</div>
|
</div>
|
||||||
</body></html>`
|
</body></html>`
|
||||||
|
} else if (returnJson) {
|
||||||
|
jsonResult = { result: webout, log: log }
|
||||||
}
|
}
|
||||||
|
|
||||||
session.inUse = false
|
session.inUse = false
|
||||||
|
|
||||||
sessionController.deleteSession(session)
|
sessionController.deleteSession(session)
|
||||||
|
|
||||||
return Promise.resolve(webout)
|
return Promise.resolve(jsonResult || webout)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildDirectorytree() {
|
buildDirectorytree() {
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ export const createFileTree = async (
|
|||||||
)
|
)
|
||||||
|
|
||||||
await asyncForEach(members, async (member: FolderMember | ServiceMember) => {
|
await asyncForEach(members, async (member: FolderMember | ServiceMember) => {
|
||||||
const name = member.name
|
let name = member.name
|
||||||
|
|
||||||
|
if (member.type === 'service') name += '.sas'
|
||||||
|
|
||||||
if (member.type === MemberType.folder) {
|
if (member.type === MemberType.folder) {
|
||||||
await createFolder(path.join(destinationPath, name)).catch((err) =>
|
await createFolder(path.join(destinationPath, name)).catch((err) =>
|
||||||
|
|||||||
85
api/src/routes/api/drive.ts
Normal file
85
api/src/routes/api/drive.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import path from 'path'
|
||||||
|
import { createFileTree, getTreeExample, DriveController, ExecutionController } from '../../controllers'
|
||||||
|
import { isFileTree, isFileQuery } from '../../types'
|
||||||
|
import { getTmpFilesFolderPath } from '../../utils'
|
||||||
|
|
||||||
|
const driveRouter = express.Router()
|
||||||
|
|
||||||
|
driveRouter.post('/deploy', async (req, res) => {
|
||||||
|
if (!isFileTree(req.body.fileTree)) {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Provided not supported data format.',
|
||||||
|
example: getTreeExample()
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await createFileTree(
|
||||||
|
req.body.fileTree.members,
|
||||||
|
req.body.appLoc ? req.body.appLoc.replace(/^\//, '').split('/') : []
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
res.status(200).send({
|
||||||
|
status: 'success',
|
||||||
|
message: 'Files deployed successfully to @sasjs/server.'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
res
|
||||||
|
.status(500)
|
||||||
|
.send({ status: 'failure', message: 'Deployment failed!', ...err })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
driveRouter.get('/files', async (req, res) => {
|
||||||
|
if (isFileQuery(req.query)) {
|
||||||
|
const filePath = path
|
||||||
|
.join(getTmpFilesFolderPath(), req.query.filePath)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep)
|
||||||
|
await new DriveController()
|
||||||
|
.readFile(filePath)
|
||||||
|
.then((fileContent) => {
|
||||||
|
res.status(200).send({ status: 'success', fileContent: fileContent })
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'File request failed.',
|
||||||
|
...(typeof err === 'object' ? err : { details: err })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Invalid Request: Expected parameter filePath was not provided'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
driveRouter.patch('/files', async (req, res) => {
|
||||||
|
const filePath = path
|
||||||
|
.join(getTmpFilesFolderPath(), req.body.filePath)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep)
|
||||||
|
await new DriveController()
|
||||||
|
.updateFile(filePath, req.body.fileContent)
|
||||||
|
.then(() => {
|
||||||
|
res.status(200).send({ status: 'success' })
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'File request failed.',
|
||||||
|
...(typeof err === 'object' ? err : { details: err })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
driveRouter.get('/fileTree', async (req, res) => {
|
||||||
|
const tree = new ExecutionController().buildDirectorytree()
|
||||||
|
res.status(200).send({ status: 'success', tree })
|
||||||
|
})
|
||||||
|
|
||||||
|
export default driveRouter
|
||||||
10
api/src/routes/api/index.ts
Normal file
10
api/src/routes/api/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import driveRouter from './drive'
|
||||||
|
import stpRouter from './stp'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
router.use('/drive', driveRouter)
|
||||||
|
router.use('/stp', stpRouter)
|
||||||
|
|
||||||
|
export default router
|
||||||
114
api/src/routes/api/spec/drive.spec.ts
Normal file
114
api/src/routes/api/spec/drive.spec.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import request from 'supertest'
|
||||||
|
import app from '../../../app'
|
||||||
|
import { getTreeExample } from '../../../controllers/deploy'
|
||||||
|
import { getTmpFilesFolderPath } from '../../../utils/file'
|
||||||
|
import { folderExists, fileExists, readFile, deleteFolder } from '@sasjs/utils'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
describe('files', () => {
|
||||||
|
describe('deploy', () => {
|
||||||
|
const shouldFailAssertion = async (payload: any) => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post('/SASjsApi/drive/deploy')
|
||||||
|
.send(payload)
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(400)
|
||||||
|
expect(res.body).toEqual({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Provided not supported data format.',
|
||||||
|
example: getTreeExample()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should respond with payload example if valid payload was not provided', async () => {
|
||||||
|
await shouldFailAssertion(null)
|
||||||
|
await shouldFailAssertion(undefined)
|
||||||
|
await shouldFailAssertion('data')
|
||||||
|
await shouldFailAssertion({})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
userId: 1,
|
||||||
|
title: 'test is cool'
|
||||||
|
})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
membersWRONG: []
|
||||||
|
})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
members: {}
|
||||||
|
})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
nameWRONG: 'jobs',
|
||||||
|
type: 'folder',
|
||||||
|
members: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
name: 'jobs',
|
||||||
|
type: 'WRONG',
|
||||||
|
members: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
await shouldFailAssertion({
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
name: 'jobs',
|
||||||
|
type: 'folder',
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
name: 'extract',
|
||||||
|
type: 'folder',
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
name: 'makedata1',
|
||||||
|
type: 'service',
|
||||||
|
codeWRONG: '%put Hello World!;'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should respond with payload example if valid payload was not provided', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post('/SASjsApi/drive/deploy')
|
||||||
|
.send({ fileTree: getTreeExample() })
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(200)
|
||||||
|
expect(res.text).toEqual(
|
||||||
|
'{"status":"success","message":"Files deployed successfully to @sasjs/server."}'
|
||||||
|
)
|
||||||
|
await expect(folderExists(getTmpFilesFolderPath())).resolves.toEqual(true)
|
||||||
|
|
||||||
|
const testJobFolder = path.join(
|
||||||
|
getTmpFilesFolderPath(),
|
||||||
|
'jobs',
|
||||||
|
'extract'
|
||||||
|
)
|
||||||
|
await expect(folderExists(testJobFolder)).resolves.toEqual(true)
|
||||||
|
|
||||||
|
const testJobFile =
|
||||||
|
path.join(
|
||||||
|
testJobFolder,
|
||||||
|
getTreeExample().members[0].members[0].members[0].name
|
||||||
|
) + '.sas'
|
||||||
|
|
||||||
|
console.log(`[testJobFile]`, testJobFile)
|
||||||
|
|
||||||
|
await expect(fileExists(testJobFile)).resolves.toEqual(true)
|
||||||
|
|
||||||
|
await expect(readFile(testJobFile)).resolves.toEqual(
|
||||||
|
getTreeExample().members[0].members[0].members[0].code
|
||||||
|
)
|
||||||
|
|
||||||
|
await deleteFolder(getTmpFilesFolderPath())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
59
api/src/routes/api/stp.ts
Normal file
59
api/src/routes/api/stp.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import { isExecutionQuery } from '../../types'
|
||||||
|
import path from 'path'
|
||||||
|
import { getTmpFilesFolderPath, makeFilesNamesMap } from '../../utils'
|
||||||
|
import { ExecutionController, FileUploadController } from '../../controllers'
|
||||||
|
|
||||||
|
const stpRouter = express.Router()
|
||||||
|
|
||||||
|
const fileUploadController = new FileUploadController()
|
||||||
|
|
||||||
|
stpRouter.post(
|
||||||
|
'/execute',
|
||||||
|
fileUploadController.preuploadMiddleware,
|
||||||
|
fileUploadController.getMulterUploadObject().any(),
|
||||||
|
async (req: any, res: any) => {
|
||||||
|
if (isExecutionQuery(req.body)) {
|
||||||
|
let sasCodePath =
|
||||||
|
path
|
||||||
|
.join(getTmpFilesFolderPath(), req.body._program)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep) + '.sas'
|
||||||
|
|
||||||
|
let filesNamesMap = null
|
||||||
|
|
||||||
|
if (req.files && req.files.length > 0) {
|
||||||
|
filesNamesMap = makeFilesNamesMap(req.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
await new ExecutionController()
|
||||||
|
.execute(
|
||||||
|
sasCodePath,
|
||||||
|
undefined,
|
||||||
|
req.sasSession,
|
||||||
|
{ ...req.query, ...req.body },
|
||||||
|
{ filesNamesMap: filesNamesMap },
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.then((result: {}) => {
|
||||||
|
res.status(200).send({
|
||||||
|
status: 'success',
|
||||||
|
...result
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err: {} | string) => {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Job execution failed.',
|
||||||
|
...(typeof err === 'object' ? err : { details: err })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: `Please provide the location of SAS code`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default stpRouter
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import path from 'path'
|
|
||||||
import {
|
|
||||||
createFileTree,
|
|
||||||
getTreeExample,
|
|
||||||
DriveController,
|
|
||||||
ExecutionController,
|
|
||||||
FileUploadController
|
|
||||||
} from '../controllers'
|
|
||||||
import { isExecutionQuery, isFileQuery, isFileTree } from '../types'
|
|
||||||
import {
|
|
||||||
getTmpFilesFolderPath,
|
|
||||||
getWebBuildFolderPath,
|
|
||||||
makeFilesNamesMap
|
|
||||||
} from '../utils'
|
|
||||||
|
|
||||||
const router = express.Router()
|
|
||||||
|
|
||||||
const fileUploadController = new FileUploadController()
|
|
||||||
|
|
||||||
router.get('/', async (_, res) => {
|
|
||||||
res.sendFile(path.join(getWebBuildFolderPath(), 'index.html'))
|
|
||||||
})
|
|
||||||
|
|
||||||
router.post('/deploy', async (req, res) => {
|
|
||||||
if (!isFileTree(req.body.fileTree)) {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Provided not supported data format.',
|
|
||||||
example: getTreeExample()
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await createFileTree(
|
|
||||||
req.body.fileTree.members,
|
|
||||||
req.body.appLoc ? req.body.appLoc.replace(/^\//, '').split('/') : []
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
res.status(200).send({
|
|
||||||
status: 'success',
|
|
||||||
message: 'Files deployed successfully to @sasjs/server.'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
res
|
|
||||||
.status(500)
|
|
||||||
.send({ status: 'failure', message: 'Deployment failed!', ...err })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/SASjsApi/files', async (req, res) => {
|
|
||||||
if (isFileQuery(req.query)) {
|
|
||||||
const filePath = path
|
|
||||||
.join(getTmpFilesFolderPath(), req.query.filePath)
|
|
||||||
.replace(new RegExp('/', 'g'), path.sep)
|
|
||||||
await new DriveController()
|
|
||||||
.readFile(filePath)
|
|
||||||
.then((fileContent) => {
|
|
||||||
res.status(200).send({ status: 'success', fileContent: fileContent })
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'File request failed.',
|
|
||||||
...(typeof err === 'object' ? err : { details: err })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Invalid Request: Expected parameter filePath was not provided'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.post('/SASjsApi/files', async (req, res) => {
|
|
||||||
const filePath = path
|
|
||||||
.join(getTmpFilesFolderPath(), req.body.filePath)
|
|
||||||
.replace(new RegExp('/', 'g'), path.sep)
|
|
||||||
await new DriveController()
|
|
||||||
.updateFile(filePath, req.body.fileContent)
|
|
||||||
.then(() => {
|
|
||||||
res.status(200).send({ status: 'success' })
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'File request failed.',
|
|
||||||
...(typeof err === 'object' ? err : { details: err })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/SASjsApi/executor', async (req, res) => {
|
|
||||||
const tree = new ExecutionController().buildDirectorytree()
|
|
||||||
res.status(200).send({ status: 'success', tree })
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/SASjsExecutor/do', async (req, res) => {
|
|
||||||
if (isExecutionQuery(req.query)) {
|
|
||||||
let sasCodePath = path
|
|
||||||
.join(getTmpFilesFolderPath(), req.query._program)
|
|
||||||
.replace(new RegExp('/', 'g'), path.sep)
|
|
||||||
|
|
||||||
// If no extension provided, add .sas extension
|
|
||||||
sasCodePath += '.sas'
|
|
||||||
|
|
||||||
await new ExecutionController()
|
|
||||||
.execute(sasCodePath, undefined, undefined, { ...req.query })
|
|
||||||
.then((result: {}) => {
|
|
||||||
res.status(200).send(result)
|
|
||||||
})
|
|
||||||
.catch((err: {} | string) => {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Job execution failed.',
|
|
||||||
...(typeof err === 'object' ? err : { details: err })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: `Please provide the location of SAS code`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
'/SASjsExecutor/do',
|
|
||||||
fileUploadController.preuploadMiddleware,
|
|
||||||
fileUploadController.getMulterUploadObject().any(),
|
|
||||||
async (req: any, res: any) => {
|
|
||||||
if (isExecutionQuery(req.query)) {
|
|
||||||
let sasCodePath = path
|
|
||||||
.join(getTmpFilesFolderPath(), req.query._program)
|
|
||||||
.replace(new RegExp('/', 'g'), path.sep)
|
|
||||||
|
|
||||||
// If no extension provided, add .sas extension
|
|
||||||
sasCodePath += '.sas'
|
|
||||||
|
|
||||||
let filesNamesMap = null
|
|
||||||
|
|
||||||
if (req.files && req.files.length > 0) {
|
|
||||||
filesNamesMap = makeFilesNamesMap(req.files)
|
|
||||||
}
|
|
||||||
|
|
||||||
await new ExecutionController()
|
|
||||||
.execute(
|
|
||||||
sasCodePath,
|
|
||||||
undefined,
|
|
||||||
req.sasSession,
|
|
||||||
{ ...req.query, ...req.body },
|
|
||||||
{ filesNamesMap: filesNamesMap }
|
|
||||||
)
|
|
||||||
.then((result: {}) => {
|
|
||||||
res.status(200).send(result)
|
|
||||||
})
|
|
||||||
.catch((err: {} | string) => {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Job execution failed.',
|
|
||||||
...(typeof err === 'object' ? err : { details: err })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
res.status(400).send({
|
|
||||||
status: 'failure',
|
|
||||||
message: `Please provide the location of SAS code`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export default router
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import request from 'supertest'
|
|
||||||
import app from '../../app'
|
|
||||||
import { getTreeExample } from '../../controllers/deploy'
|
|
||||||
import { getTmpFilesFolderPath } from '../../utils/file'
|
|
||||||
import { folderExists, fileExists, readFile, deleteFolder } from '@sasjs/utils'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
describe('deploy', () => {
|
|
||||||
const shouldFailAssertion = async (payload: any) => {
|
|
||||||
const res = await request(app).post('/deploy').send(payload)
|
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(400)
|
|
||||||
expect(res.body).toEqual({
|
|
||||||
status: 'failure',
|
|
||||||
message: 'Provided not supported data format.',
|
|
||||||
example: getTreeExample()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should respond with payload example if valid payload was not provided', async () => {
|
|
||||||
await shouldFailAssertion(null)
|
|
||||||
await shouldFailAssertion(undefined)
|
|
||||||
await shouldFailAssertion('data')
|
|
||||||
await shouldFailAssertion({})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
userId: 1,
|
|
||||||
title: 'test is cool'
|
|
||||||
})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
membersWRONG: []
|
|
||||||
})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
members: {}
|
|
||||||
})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
nameWRONG: 'jobs',
|
|
||||||
type: 'folder',
|
|
||||||
members: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
name: 'jobs',
|
|
||||||
type: 'WRONG',
|
|
||||||
members: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
await shouldFailAssertion({
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
name: 'jobs',
|
|
||||||
type: 'folder',
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
name: 'extract',
|
|
||||||
type: 'folder',
|
|
||||||
members: [
|
|
||||||
{
|
|
||||||
name: 'makedata1',
|
|
||||||
type: 'service',
|
|
||||||
codeWRONG: '%put Hello World!;'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with payload example if valid payload was not provided', async () => {
|
|
||||||
const res = await request(app)
|
|
||||||
.post('/deploy')
|
|
||||||
.send({ fileTree: getTreeExample() })
|
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(200)
|
|
||||||
expect(res.text).toEqual(
|
|
||||||
'{"status":"success","message":"Files deployed successfully to @sasjs/server."}'
|
|
||||||
)
|
|
||||||
await expect(folderExists(getTmpFilesFolderPath())).resolves.toEqual(true)
|
|
||||||
|
|
||||||
const testJobFolder = path.join(getTmpFilesFolderPath(), 'jobs', 'extract')
|
|
||||||
await expect(folderExists(testJobFolder)).resolves.toEqual(true)
|
|
||||||
|
|
||||||
const testJobFile = path.join(
|
|
||||||
testJobFolder,
|
|
||||||
getTreeExample().members[0].members[0].members[0].name
|
|
||||||
)
|
|
||||||
|
|
||||||
await expect(fileExists(testJobFile)).resolves.toEqual(true)
|
|
||||||
|
|
||||||
await expect(readFile(testJobFile)).resolves.toEqual(
|
|
||||||
getTreeExample().members[0].members[0].members[0].code
|
|
||||||
)
|
|
||||||
|
|
||||||
await deleteFolder(getTmpFilesFolderPath())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
8
api/src/routes/web/index.ts
Normal file
8
api/src/routes/web/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import webRouter from './web'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
router.use('/', webRouter)
|
||||||
|
|
||||||
|
export default router
|
||||||
43
api/src/routes/web/web.ts
Normal file
43
api/src/routes/web/web.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import { isExecutionQuery } from '../../types'
|
||||||
|
import path from 'path'
|
||||||
|
import {
|
||||||
|
getTmpFilesFolderPath,
|
||||||
|
getWebBuildFolderPath,
|
||||||
|
} from '../../utils'
|
||||||
|
import { ExecutionController } from '../../controllers'
|
||||||
|
|
||||||
|
const webRouter = express.Router()
|
||||||
|
|
||||||
|
webRouter.get('/', async (_, res) => {
|
||||||
|
res.sendFile(path.join(getWebBuildFolderPath(), 'index.html'))
|
||||||
|
})
|
||||||
|
|
||||||
|
webRouter.get('/SASjsExecutor/do', async (req, res) => {
|
||||||
|
if (isExecutionQuery(req.query)) {
|
||||||
|
let sasCodePath =
|
||||||
|
path
|
||||||
|
.join(getTmpFilesFolderPath(), req.query._program)
|
||||||
|
.replace(new RegExp('/', 'g'), path.sep) + '.sas'
|
||||||
|
|
||||||
|
await new ExecutionController()
|
||||||
|
.execute(sasCodePath, undefined, undefined, { ...req.query })
|
||||||
|
.then((result: {}) => {
|
||||||
|
res.status(200).send(result)
|
||||||
|
})
|
||||||
|
.catch((err: {} | string) => {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Job execution failed.',
|
||||||
|
...(typeof err === 'object' ? err : { details: err })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.status(400).send({
|
||||||
|
status: 'failure',
|
||||||
|
message: `Please provide the location of SAS code`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default webRouter
|
||||||
Reference in New Issue
Block a user