diff --git a/src/controllers/sas.ts b/src/controllers/sas.ts index a63b5a6..1f145cc 100644 --- a/src/controllers/sas.ts +++ b/src/controllers/sas.ts @@ -1,27 +1,21 @@ -import { - readFile, - generateTimestamp, - deleteFile, - fileExists, - createFile -} from '@sasjs/utils' +import { readFile, deleteFile, fileExists, createFile } from '@sasjs/utils' import path from 'path' import { ExecutionResult, ExecutionQuery } from '../types' import { getTmpFilesFolderPath, getTmpLogFolderPath, - getTmpWeboutFolderPath + getTmpWeboutFolderPath, + generateUniqueFileName } from '../utils' import { configuration } from '../../package.json' import { promisify } from 'util' import { execFile } from 'child_process' const execFilePromise = promisify(execFile) -export const processSas = async ( - query: ExecutionQuery -): Promise => { - let sasCodePath = path.join(getTmpFilesFolderPath(), query._program) - sasCodePath = sasCodePath.replace(new RegExp('/', 'g'), path.sep) +export const processSas = async (query: ExecutionQuery): Promise => { + const sasCodePath = path + .join(getTmpFilesFolderPath(), query._program) + .replace(new RegExp('/', 'g'), path.sep) if (!(await fileExists(sasCodePath))) { 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 logArgs = [] - let sasLogPath - - if (query._debug) { - sasLogPath = path.join( - getTmpLogFolderPath(), - [sasFile.replace(/\.sas/g, ''), '-', generateTimestamp(), '.log'].join('') - ) - logArgs.push('-log') - logArgs.push(sasLogPath) - } + const sasLogPath = path.join( + getTmpLogFolderPath(), + generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.log') + ) const sasWeboutPath = path.join( getTmpWeboutFolderPath(), - [sasFile.replace(/\.sas/g, ''), '-', generateTimestamp(), '.json'].join('') + generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.json') ) let sasCode = await readFile(sasCodePath) - const originalSasCode = sasCode - if (query.macroVars) { - const macroVars = query.macroVars.macroVars - - Object.keys(macroVars).forEach( - (key: string) => (sasCode = `%let ${key}=${macroVars[key]};\n${sasCode}`) - ) - } + const vars: any = query + Object.keys(query).forEach( + (key: string) => (sasCode = `%let ${key}=${vars[key]};\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, [ '-SYSIN', - sasCodePath, - ...logArgs, - '-nosplash' - ]) + tmpSasCodePath, + '-log', + sasLogPath, + '-nosplash' // FIXME: should be configurable + ]).catch((err) => ({ stderr: err, stdout: '' })) - if (stderr) return Promise.reject(stderr) - - if (await fileExists(sasWeboutPath)) { - 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.`) + let log = '' + if (sasLogPath && (await fileExists(sasLogPath))) { + log = await readFile(sasLogPath) } - // 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 = ` + >>weboutBEGIN<< ${webout} >>weboutEND<< +
+

SAS Log

+
${log}
+
+ ` + } + + return Promise.resolve(webout) + } else { + return Promise.resolve({ + log: log + }) + } } diff --git a/src/routes/index.ts b/src/routes/index.ts index 7dc647a..bd27d0a 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -53,27 +53,13 @@ router.get('/SASjsExecutor', async (req, res) => { 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) => { const queryEntries = Object.keys(req.query).map((entry: string) => entry.toLowerCase() ) - const isDebug = queryEntries.find((entry: string) => entry === '_debug') - ? true - : false if (isRequestQuery(req.query)) { - await processSas({ ...req.query, _debug: isDebug }) + await processSas({ ...req.query }) .then((result) => { res.status(200).send(result) }) @@ -81,7 +67,7 @@ router.get('/SASjsExecutor/do', async (req, res) => { res.status(400).send({ status: 'failure', message: 'Job execution failed.', - error: err + ...err }) }) } else { diff --git a/src/types/sas.ts b/src/types/sas.ts index 1147687..e67f6da 100644 --- a/src/types/sas.ts +++ b/src/types/sas.ts @@ -1,5 +1,5 @@ export interface ExecutionResult { - webout: object + webout?: string log?: string logPath?: string } diff --git a/src/utils/file.ts b/src/utils/file.ts index 5a6a88d..bd3f759 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -1,5 +1,5 @@ import path from 'path' -import { getRealPath } from '@sasjs/utils' +import { getRealPath, generateTimestamp } from '@sasjs/utils' export const getTmpFolderPath = () => getRealPath(path.join(__dirname, '..', '..', 'tmp')) @@ -11,3 +11,13 @@ export const getTmpLogFolderPath = () => path.join(getTmpFolderPath(), 'logs') export const getTmpWeboutFolderPath = () => path.join(getTmpFolderPath(), 'webouts') + +export const generateUniqueFileName = (fileName: string, extension = '') => + [ + fileName, + '-', + Math.round(Math.random() * 100000), + '-', + generateTimestamp(), + extension + ].join('')