1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-07 06:30:06 +00:00

chore: merging with sessions - sas request with file upload

This commit is contained in:
Mihajlo Medjedovic
2021-10-14 15:44:28 +00:00
parent 3f39cab357
commit 17a7a26fc3
8 changed files with 355 additions and 27 deletions

View File

@@ -5,6 +5,7 @@ import { configuration } from '../../package.json'
import { promisify } from 'util'
import { execFile } from 'child_process'
import { Session } from '../types'
import { generateFileUploadSasCode } from '../utils'
const execFilePromise = promisify(execFile)
export class ExecutionController {
@@ -12,7 +13,8 @@ export class ExecutionController {
program = '',
autoExec?: string,
session?: Session,
vars?: any
vars?: any,
otherArgs?: any
) {
if (program) {
if (!(await fileExists(program))) {
@@ -39,10 +41,26 @@ export class ExecutionController {
let webout = path.join(session.path, 'webout.txt')
await createFile(webout, '')
program = `
%let sasjsprocessmode=Stored Program;
filename _webout "${webout}";
${program}`
program = `filename _webout "${webout}";\n${program}`
// if no files are uploaded filesNamesMap will be undefined
if (otherArgs && otherArgs.filesNamesMap) {
const uploadSasCode = generateFileUploadSasCode(
otherArgs.filesNamesMap,
session.path
)
const code = path.join(session.path, 'code.sas')
//If sas code for the file is generated it will be appended to the top of sasCode
if (uploadSasCode.length > 0) {
program = `${uploadSasCode}` + program
}
}
let code = path.join(session.path, 'code.sas')
if (!(await fileExists(code))) {
await createFile(code, program)
}
@@ -64,7 +82,9 @@ export class ExecutionController {
if (await fileExists(log)) log = await readFile(log)
else log = ''
if (stderr) return Promise.reject({ error: stderr, log: log })
// if (stderr) {
// return Promise.reject({ error: stderr, log: log })
// }
if (await fileExists(webout)) webout = await readFile(webout)
else webout = ''
@@ -73,7 +93,7 @@ export class ExecutionController {
(key: string) => key.toLowerCase() === '_debug'
)
if (debug && vars[debug] >= 131) {
if (debug && vars[debug] >= 131 || stderr) {
webout = `<html><body>
${webout}
<div style="text-align:left">
@@ -82,7 +102,7 @@ ${webout}
</div>
</body></html>`
}
session.inUse = false
sessionController.deleteSession(session)

View File

@@ -70,7 +70,7 @@ export class SessionController {
this.scheduleSessionDestroy(session)
this.executionController.execute('', autoExec, session).catch(() => {})
this.executionController.execute('', autoExec, session).catch((err) => {})
this.sessions.push(session)

View File

@@ -1,12 +1,41 @@
import express from 'express'
import { createFileTree, getTreeExample } from '../controllers'
import { createFileTree, getSessionController, getTreeExample } from '../controllers'
import { ExecutionResult, isRequestQuery, isFileTree } from '../types'
import path from 'path'
import { getTmpFilesFolderPath } from '../utils'
import { getTmpFilesFolderPath, getTmpFolderPath, makeFilesNamesMap } from '../utils'
import { ExecutionController } from '../controllers'
import { uuidv4 } from '@sasjs/utils'
const multer = require('multer')
const router = express.Router()
const storage = multer.diskStorage({
destination: function (req: any, file: any, cb: any) {
//Sending the intercepted files to the sessions subfolder
cb(null, req.sasSession.path)
},
filename: function (req: any, file: any, cb: any) {
//req_file prefix + unique hash added to sas request files
cb(null, `req_file_${uuidv4().replace(/-/gm, '')}`)
}
})
const upload = multer({ storage: storage })
//It will intercept request and generate uniqe uuid to be used as a subfolder name
//that will store the files uploaded
const preuploadMiddleware = async (req: any, res: any, next: any) => {
let session
const sessionController = getSessionController()
session = await sessionController.getSession()
session.inUse = true
req.sasSession = session
next()
}
router.get('/', async (_, res) => {
res.status(200).send('Welcome to @sasjs/server API')
})
@@ -46,9 +75,12 @@ router.get('/SASjsExecutor', async (req, res) => {
router.get('/SASjsExecutor/do', async (req, res) => {
if (isRequestQuery(req.query)) {
const sasCodePath = path
let sasCodePath = path
.join(getTmpFilesFolderPath(), req.query._program)
.replace(new RegExp('/', 'g'), path.sep)
// If no extension provided, add .sas extension
sasCodePath += !sasCodePath.includes('.') ? '.sas' : ''
await new ExecutionController()
.execute(sasCodePath, undefined, undefined, { ...req.query })
@@ -70,4 +102,39 @@ router.get('/SASjsExecutor/do', async (req, res) => {
}
})
router.post('/SASjsExecutor/do', preuploadMiddleware, upload.any(), async (req: any, res: any) => {
if (isRequestQuery(req.query)) {
let sasCodePath = path
.join(getTmpFilesFolderPath(), req.query._program)
.replace(new RegExp('/', 'g'), path.sep)
// If no extension provided, add .sas extension
sasCodePath += !sasCodePath.includes('.') ? '.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 }, { 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

View File

@@ -1,2 +1,3 @@
export * from './file'
export * from './sleep'
export * from './upload'

86
src/utils/upload.ts Normal file
View File

@@ -0,0 +1,86 @@
import path from 'path'
import fs from 'fs'
import { getTmpSessionsFolderPath } from '.'
/**
* It will create a object that maps hashed file names to the original names
* @param files array of files to be mapped
* @returns object
*/
export const makeFilesNamesMap = (files: any) => {
if (!files) return null
const filesNamesMap: any = {}
for (let file of files) {
filesNamesMap[file.filename] = file.fieldname
}
return filesNamesMap
}
/**
* Generates the sas code that reference uploaded files in the concurrent request
* @param filesNamesMap object that maps hashed file names and original file names
* @param sasUploadFolder name of the folder that is created for the purpose of files in concurrent request
* @returns generated sas code
*/
export const generateFileUploadSasCode = (
filesNamesMap: any,
sasSessionFolder: string
): string => {
const uploadFilesDirPath = sasSessionFolder
let uploadSasCode = ''
let fileCount = 0
let uploadedFilesMap: {
fileref: string
filepath: string
filename: string
count: number
}[] = []
fs.readdirSync(uploadFilesDirPath).forEach((fileName) => {
let fileCountString = fileCount < 100 ? '0' + fileCount : fileCount
fileCountString = fileCount < 10 ? '00' + fileCount : fileCount
if (fileName.includes('req_file')) {
fileCount++
uploadedFilesMap.push({
fileref: `_sjs${fileCountString}`,
filepath: `${uploadFilesDirPath}/${fileName}`,
filename: filesNamesMap[fileName],
count: fileCount
})
}
})
for (let uploadedMap of uploadedFilesMap) {
uploadSasCode += `\nfilename ${uploadedMap.fileref} "${uploadedMap.filepath}";`
}
uploadSasCode += `\n%let _WEBIN_FILE_COUNT=${fileCount};`
for (let uploadedMap of uploadedFilesMap) {
uploadSasCode += `\n%let _WEBIN_FILENAME${uploadedMap.count}=${uploadedMap.filepath};`
}
for (let uploadedMap of uploadedFilesMap) {
uploadSasCode += `\n%let _WEBIN_FILEREF${uploadedMap.count}=${uploadedMap.fileref};`
}
for (let uploadedMap of uploadedFilesMap) {
uploadSasCode += `\n%let _WEBIN_NAME${uploadedMap.count}=${uploadedMap.filename};`
}
if (fileCount > 0) {
uploadSasCode += `\n%let _WEBIN_NAME=&_WEBIN_NAME1;`
uploadSasCode += `\n%let _WEBIN_FILEREF=&_WEBIN_FILEREF1;`
uploadSasCode += `\n%let _WEBIN_FILENAME=&_WEBIN_FILENAME1;`
}
uploadSasCode += `\n`
return uploadSasCode
}