From f6046b15ae30cd8ace685cf283339871de658b7d Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 10 Jun 2021 20:12:23 +0300 Subject: [PATCH] feat(api): set up endpoint for sas code execution --- src/controllers/index.ts | 46 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 12 +++-------- src/routes/index.ts | 23 ++++++++++++++++++++ src/types/index.ts | 2 ++ src/types/request.ts | 6 ++++++ src/types/sas.ts | 4 ++++ 6 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 src/controllers/index.ts create mode 100644 src/routes/index.ts create mode 100644 src/types/index.ts create mode 100644 src/types/request.ts create mode 100644 src/types/sas.ts diff --git a/src/controllers/index.ts b/src/controllers/index.ts new file mode 100644 index 0000000..3529901 --- /dev/null +++ b/src/controllers/index.ts @@ -0,0 +1,46 @@ +import { execFile } from 'child_process' +import { readFile, generateTimestamp, deleteFile } from '@sasjs/utils' +import path from 'path' +import { ExecutionResult, RequestQuery } from '../types' + +// FIXME +const sasExePath = `C:\\Program Files\\SASHome\\SASFoundation\\9.4\\sas.exe` +const baseSasLogPath = 'C:\\Users\\YuryShkoda\\projects\\server\\sas\\logs' +const baseSasCodePath = `sas` + +// TODO: create utils isSasFile + +export const processSas = async ( + query: RequestQuery +): Promise => + new Promise((resolve, reject) => { + let sasCodePath = query._program + sasCodePath = path.join(baseSasCodePath, `${sasCodePath}.sas`) + sasCodePath = sasCodePath.replace(new RegExp('/', 'g'), path.sep) + + const sasFile: string = sasCodePath.split(path.sep).pop() || 'default' + + const sasLogPath = [ + baseSasLogPath, + path.sep, + sasFile.replace(/\.sas/g, ''), + '-', + generateTimestamp(), + '.log' + ].join('') + + execFile( + sasExePath, + ['-SYSIN', sasCodePath, '-log', sasLogPath, '-nosplash'], + async (err, _, stderr) => { + if (err) reject(err) + if (stderr) reject(stderr) + + const log = await readFile(sasLogPath) + + deleteFile(sasLogPath) + + resolve({ log: log, logPath: sasLogPath }) + } + ) + }) diff --git a/src/index.ts b/src/index.ts index c3ad22c..ccb0751 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,11 @@ -// Express App Setup import express from 'express' -import bodyParser from 'body-parser' +import indexRouter from './routes' const app = express() -app.use(bodyParser.json()) - -// Express route handlers -app.get('/', (req, res) => { - res.send('Hey from @sasjs/server API!') -}) +app.use('/', indexRouter) const port = 5000 app.listen(port, () => { - console.log(`⚡️[server]: Server is running at https://localhost:${port}`) + console.log(`⚡️[server]: Server is running at http://localhost:${port}`) }) diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..bf8af62 --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1,23 @@ +import express from 'express' +import { processSas } from '../controllers' +import { ExecutionResult, RequestQuery, isRequestQuery } from '../types' + +const router = express.Router() + +router.get('/', async (req, res) => { + const query = req.query + + if (!isRequestQuery(query)) { + res.send('Welcome to @sasjs/server API') + + return + } + + const result: ExecutionResult = await processSas(query) + + res.send(`Executed!
+

Log is located:

${result.logPath}
+

Log:

`) +}) + +export default router diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..51251cc --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './sas' +export * from './request' diff --git a/src/types/request.ts b/src/types/request.ts new file mode 100644 index 0000000..04d1573 --- /dev/null +++ b/src/types/request.ts @@ -0,0 +1,6 @@ +export interface RequestQuery { + _program: string +} + +export const isRequestQuery = (arg: any): arg is RequestQuery => + arg && !Array.isArray(arg) && typeof arg._program === 'string' diff --git a/src/types/sas.ts b/src/types/sas.ts new file mode 100644 index 0000000..26d49b5 --- /dev/null +++ b/src/types/sas.ts @@ -0,0 +1,4 @@ +export interface ExecutionResult { + log: string + logPath: string +}