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

feat(executor): improved api response

This commit is contained in:
Yury Shkoda
2021-10-05 17:24:20 +03:00
parent 52275ba67d
commit 707b503942
4 changed files with 74 additions and 82 deletions

View File

@@ -1,27 +1,21 @@
import { import { readFile, deleteFile, fileExists, createFile } from '@sasjs/utils'
readFile,
generateTimestamp,
deleteFile,
fileExists,
createFile
} from '@sasjs/utils'
import path from 'path' import path from 'path'
import { ExecutionResult, ExecutionQuery } from '../types' import { ExecutionResult, ExecutionQuery } from '../types'
import { import {
getTmpFilesFolderPath, getTmpFilesFolderPath,
getTmpLogFolderPath, getTmpLogFolderPath,
getTmpWeboutFolderPath getTmpWeboutFolderPath,
generateUniqueFileName
} from '../utils' } from '../utils'
import { configuration } from '../../package.json' import { configuration } from '../../package.json'
import { promisify } from 'util' import { promisify } from 'util'
import { execFile } from 'child_process' import { execFile } from 'child_process'
const execFilePromise = promisify(execFile) const execFilePromise = promisify(execFile)
export const processSas = async ( export const processSas = async (query: ExecutionQuery): Promise<any> => {
query: ExecutionQuery const sasCodePath = path
): Promise<ExecutionResult> => { .join(getTmpFilesFolderPath(), query._program)
let sasCodePath = path.join(getTmpFilesFolderPath(), query._program) .replace(new RegExp('/', 'g'), path.sep)
sasCodePath = sasCodePath.replace(new RegExp('/', 'g'), path.sep)
if (!(await fileExists(sasCodePath))) { if (!(await fileExists(sasCodePath))) {
return Promise.reject('SAS file does not exist.') return Promise.reject('SAS file does not exist.')
@@ -29,71 +23,73 @@ export const processSas = async (
const sasFile: string = sasCodePath.split(path.sep).pop() || 'default' const sasFile: string = sasCodePath.split(path.sep).pop() || 'default'
const logArgs = [] const sasLogPath = path.join(
let sasLogPath getTmpLogFolderPath(),
generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.log')
if (query._debug) { )
sasLogPath = path.join(
getTmpLogFolderPath(),
[sasFile.replace(/\.sas/g, ''), '-', generateTimestamp(), '.log'].join('')
)
logArgs.push('-log')
logArgs.push(sasLogPath)
}
const sasWeboutPath = path.join( const sasWeboutPath = path.join(
getTmpWeboutFolderPath(), getTmpWeboutFolderPath(),
[sasFile.replace(/\.sas/g, ''), '-', generateTimestamp(), '.json'].join('') generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.json')
) )
let sasCode = await readFile(sasCodePath) let sasCode = await readFile(sasCodePath)
const originalSasCode = sasCode
if (query.macroVars) { const vars: any = query
const macroVars = query.macroVars.macroVars Object.keys(query).forEach(
(key: string) => (sasCode = `%let ${key}=${vars[key]};\n${sasCode}`)
Object.keys(macroVars).forEach( )
(key: string) => (sasCode = `%let ${key}=${macroVars[key]};\n${sasCode}`)
)
}
sasCode = `filename _webout "${sasWeboutPath}";\n${sasCode}` sasCode = `filename _webout "${sasWeboutPath}";\n${sasCode}`
await createFile(sasCodePath, sasCode) const tmpSasCodePath = sasCodePath.replace(
sasFile,
generateUniqueFileName(sasFile)
)
await createFile(tmpSasCodePath, sasCode)
const { stdout, stderr } = await execFilePromise(configuration.sasPath, [ const { stdout, stderr } = await execFilePromise(configuration.sasPath, [
'-SYSIN', '-SYSIN',
sasCodePath, tmpSasCodePath,
...logArgs, '-log',
'-nosplash' sasLogPath,
]) '-nosplash' // FIXME: should be configurable
]).catch((err) => ({ stderr: err, stdout: '' }))
if (stderr) return Promise.reject(stderr) let log = ''
if (sasLogPath && (await fileExists(sasLogPath))) {
if (await fileExists(sasWeboutPath)) { log = await readFile(sasLogPath)
const webout = await readFile(sasWeboutPath)
try {
const weboutJson = JSON.parse(webout)
if (sasLogPath && (await fileExists(sasLogPath))) {
return Promise.resolve({
webout: weboutJson,
log: await readFile(sasLogPath),
logPath: sasLogPath
})
} else {
return Promise.resolve({
webout: weboutJson
})
}
} catch (error) {
return Promise.reject(`Error while parsing Webout. Details: ${error}`)
}
} else {
return Promise.reject(`Webout wasn't created.`)
} }
// await createFile(sasCodePath, originalSasCode) await deleteFile(sasLogPath)
// await deleteFile(sasLogPath) await deleteFile(tmpSasCodePath)
if (stderr) return Promise.reject({ error: stderr, log: log })
if (await fileExists(sasWeboutPath)) {
let webout = await readFile(sasWeboutPath)
await deleteFile(sasWeboutPath)
const debug = Object.keys(query).find(
(key: string) => key.toLowerCase() === '_debug'
)
if (debug && (query as any)[debug] >= 131) {
webout = `<html><body>
>>weboutBEGIN<< ${webout} >>weboutEND<<
<div style="text-align:left">
<hr /><h2>SAS Log</h2>
<pre>${log}</pre>
</div>
</body></html>`
}
return Promise.resolve(webout)
} else {
return Promise.resolve({
log: log
})
}
} }

View File

@@ -53,27 +53,13 @@ router.get('/SASjsExecutor', async (req, res) => {
res.status(200).send({ status: 'success', tree: {} }) res.status(200).send({ status: 'success', tree: {} })
}) })
// SAS:
// https://sas.analytium.co.uk:8343/SASStoredProcess/do?_action=form,properties,execute,noba[…]blic%2Fapp%2Fdata-combiner%2Fservices%2Fcommon%2Fappinit
// https://sas.analytium.co.uk:8343/SASStoredProcess/
// https://sas.analytium.co.uk:8343/SASStoredProcess/do?&_program=%2FPublic%2Fapp%2Fdata-combiner%2Fservices%2Fcommon%2Fappinit&_DEBUG=131
// https://sas.analytium.co.uk:8343/SASStoredProcess/do?_program=%2FPublic%2Fapp%2Fdata-comb[…]ction=update%2Cnewwindow%2Cnobanner&_updatekey=895432774
// SASjs:
// http://localhost:5000/SASjsExecutor?_program=%2FPublic%2Fapp%2Fdata-combiner%2Fservices%2Fcommon%2Fappinit
// http://localhost:5000/SASjsExecutor
// http://localhost:5000/SASjsExecutor?_program=%2FPublic%2Fapp%2Fdata-combiner%2Fservices%2Fcommon%2Fappinit&_DEBUG=131
router.get('/SASjsExecutor/do', async (req, res) => { router.get('/SASjsExecutor/do', async (req, res) => {
const queryEntries = Object.keys(req.query).map((entry: string) => const queryEntries = Object.keys(req.query).map((entry: string) =>
entry.toLowerCase() entry.toLowerCase()
) )
const isDebug = queryEntries.find((entry: string) => entry === '_debug')
? true
: false
if (isRequestQuery(req.query)) { if (isRequestQuery(req.query)) {
await processSas({ ...req.query, _debug: isDebug }) await processSas({ ...req.query })
.then((result) => { .then((result) => {
res.status(200).send(result) res.status(200).send(result)
}) })
@@ -81,7 +67,7 @@ router.get('/SASjsExecutor/do', async (req, res) => {
res.status(400).send({ res.status(400).send({
status: 'failure', status: 'failure',
message: 'Job execution failed.', message: 'Job execution failed.',
error: err ...err
}) })
}) })
} else { } else {

View File

@@ -1,5 +1,5 @@
export interface ExecutionResult { export interface ExecutionResult {
webout: object webout?: string
log?: string log?: string
logPath?: string logPath?: string
} }

View File

@@ -1,5 +1,5 @@
import path from 'path' import path from 'path'
import { getRealPath } from '@sasjs/utils' import { getRealPath, generateTimestamp } from '@sasjs/utils'
export const getTmpFolderPath = () => export const getTmpFolderPath = () =>
getRealPath(path.join(__dirname, '..', '..', 'tmp')) getRealPath(path.join(__dirname, '..', '..', 'tmp'))
@@ -11,3 +11,13 @@ export const getTmpLogFolderPath = () => path.join(getTmpFolderPath(), 'logs')
export const getTmpWeboutFolderPath = () => export const getTmpWeboutFolderPath = () =>
path.join(getTmpFolderPath(), 'webouts') path.join(getTmpFolderPath(), 'webouts')
export const generateUniqueFileName = (fileName: string, extension = '') =>
[
fileName,
'-',
Math.round(Math.random() * 100000),
'-',
generateTimestamp(),
extension
].join('')