mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 11:24:35 +00:00
refactor: improve types and imports
This commit is contained in:
@@ -3,6 +3,7 @@ import { getTmpFilesFolderPath } from '../utils/file'
|
||||
import { createFolder, createFile, asyncForEach } from '@sasjs/utils'
|
||||
import path from 'path'
|
||||
|
||||
// REFACTOR: export FileTreeCpntroller
|
||||
export const createFileTree = async (
|
||||
members: [FolderMember, ServiceMember],
|
||||
parentFolders: string[] = []
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { getSessionController } from './session'
|
||||
import { readFile, deleteFile, fileExists, createFile } from '@sasjs/utils'
|
||||
import path from 'path'
|
||||
import { configuration } from '../../package.json'
|
||||
import { promisify } from 'util'
|
||||
import { execFile } from 'child_process'
|
||||
const execFilePromise = promisify(execFile)
|
||||
|
||||
export class ExecutionController {
|
||||
async execute(program = '', autoExec?: string, debug?: number) {
|
||||
console.log(`[ExecutionController]program: `, program)
|
||||
console.log(`[ExecutionController]autoExec: `, autoExec)
|
||||
|
||||
if (program) {
|
||||
if (!(await fileExists(program))) {
|
||||
throw 'SASjsServer/Executor: SAS file does not exist.'
|
||||
}
|
||||
|
||||
program = await readFile(program)
|
||||
}
|
||||
|
||||
const sessionController = getSessionController()
|
||||
const session = await sessionController.getSession()
|
||||
|
||||
console.log(`[ExecutionController]session: `, session)
|
||||
|
||||
let log = path.join(session.path, 'log.log')
|
||||
await createFile(log, '')
|
||||
|
||||
let webout = path.join(session.path, 'webout.txt')
|
||||
await createFile(webout, '')
|
||||
|
||||
const code = path.join(session.path, 'code')
|
||||
await createFile(code, program)
|
||||
|
||||
let additionalArgs: string[] = []
|
||||
if (autoExec) additionalArgs = ['-AUTOEXEC', autoExec]
|
||||
|
||||
const { stdout, stderr } = await execFilePromise(configuration.sasPath, [
|
||||
'-SYSIN',
|
||||
code,
|
||||
'-LOG',
|
||||
log,
|
||||
'-WORK',
|
||||
...additionalArgs,
|
||||
session.path,
|
||||
process.platform === 'win32' ? '-nosplash' : ''
|
||||
]).catch((err) => ({ stderr: err, stdout: '' }))
|
||||
|
||||
log = await readFile(log)
|
||||
|
||||
if (stderr) return Promise.reject({ error: stderr, log: log })
|
||||
|
||||
webout = await readFile(webout)
|
||||
|
||||
if (debug && debug >= 131) {
|
||||
webout = `<html><body>
|
||||
${webout}
|
||||
<div style="text-align:left">
|
||||
<hr /><h2>SAS Log</h2>
|
||||
<pre>${log}</pre>
|
||||
</div>
|
||||
</body></html>`
|
||||
}
|
||||
|
||||
return Promise.resolve(webout)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './sas'
|
||||
export * from './deploy'
|
||||
export * from './session'
|
||||
export * from './Session'
|
||||
export * from './Execution'
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { readFile, deleteFile, fileExists, createFile } from '@sasjs/utils'
|
||||
import path from 'path'
|
||||
import { ExecutionResult, ExecutionQuery } from '../types'
|
||||
import {
|
||||
getTmpFilesFolderPath,
|
||||
getTmpLogFolderPath,
|
||||
getTmpWeboutFolderPath,
|
||||
generateUniqueFileName
|
||||
} from '../utils'
|
||||
import { configuration } from '../../package.json'
|
||||
import { SessionController } from './session'
|
||||
import { promisify } from 'util'
|
||||
import { execFile } from 'child_process'
|
||||
const execFilePromise = promisify(execFile)
|
||||
|
||||
export const processSas = async (query: ExecutionQuery): Promise<any> => {
|
||||
const sasCodePath = path
|
||||
.join(getTmpFilesFolderPath(), query._program)
|
||||
.replace(new RegExp('/', 'g'), path.sep)
|
||||
|
||||
if (!(await fileExists(sasCodePath))) {
|
||||
return Promise.reject({ error: 'SAS file does not exist.' })
|
||||
}
|
||||
|
||||
// FIXME
|
||||
const sessionController = new SessionController()
|
||||
|
||||
sessionController.getSession()
|
||||
|
||||
return Promise.resolve('success')
|
||||
|
||||
// const sasFile: string = sasCodePath.split(path.sep).pop() || 'default'
|
||||
|
||||
// const sasLogPath = path.join(
|
||||
// getTmpLogFolderPath(),
|
||||
// generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.log')
|
||||
// )
|
||||
|
||||
// await createFile(sasLogPath, '')
|
||||
|
||||
// const sasWeboutPath = path.join(
|
||||
// getTmpWeboutFolderPath(),
|
||||
// generateUniqueFileName(sasFile.replace(/\.sas/g, ''), '.txt')
|
||||
// )
|
||||
|
||||
// await createFile(sasWeboutPath, '')
|
||||
|
||||
// let sasCode = await readFile(sasCodePath)
|
||||
|
||||
// const vars: any = query
|
||||
// Object.keys(query).forEach(
|
||||
// (key: string) => (sasCode = `%let ${key}=${vars[key]};\n${sasCode}`)
|
||||
// )
|
||||
|
||||
// sasCode = `filename _webout "${sasWeboutPath}";\n${sasCode}`
|
||||
|
||||
// const tmpSasCodePath = sasCodePath.replace(
|
||||
// sasFile,
|
||||
// generateUniqueFileName(sasFile)
|
||||
// )
|
||||
|
||||
// await createFile(tmpSasCodePath, sasCode)
|
||||
|
||||
// const { stdout, stderr } = await execFilePromise(configuration.sasPath, [
|
||||
// '-SYSIN',
|
||||
// tmpSasCodePath,
|
||||
// '-log',
|
||||
// sasLogPath,
|
||||
// process.platform === 'win32' ? '-nosplash' : ''
|
||||
// ]).catch((err) => ({ stderr: err, stdout: '' }))
|
||||
|
||||
// let log = ''
|
||||
// if (sasLogPath && (await fileExists(sasLogPath))) {
|
||||
// log = await readFile(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>
|
||||
// ${webout}
|
||||
// <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
|
||||
// })
|
||||
// }
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import { Session } from '../types'
|
||||
import { getTmpSessionsFolderPath, generateUniqueFileName } from '../utils'
|
||||
import { createFolder, createFile, generateTimestamp } from '@sasjs/utils'
|
||||
import path from 'path'
|
||||
import { ExecutionController } from './executor'
|
||||
|
||||
// /opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe/sas
|
||||
// -LOG /tmp/mydir/demo.log
|
||||
// -SYSIN /tmp/mydir/code.sas
|
||||
// -AUTOEXEC /tmp/mydir/autoexec.sas
|
||||
// -WORK /tmp/mydir
|
||||
|
||||
// 1. req (_program) for execution
|
||||
// 2. check available session
|
||||
// 2.3 spawn one more session
|
||||
// 2.3.1 create folder
|
||||
// 2.3.2 create autoexec
|
||||
// 2.3.3 create _program.sas (empty)
|
||||
// 2.3.4 /opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe/sas -LOG /tmp/sessionFolder/demo.log -SYSIN /tmp/sessionFolder/_program.sas -AUTOEXEC /tmp/sessionFolder/autoexec.sas -WORK /tmp/sessionFolder
|
||||
// 2.3.5 wait for _program.sas to be deleted
|
||||
// 2.3.6 add session to the session array
|
||||
// ---
|
||||
// 3.0 wait for session
|
||||
// 3.1 create _program.sas in sessionFolder
|
||||
// 3.2 poll session array
|
||||
|
||||
export class SessionController {
|
||||
private sessions: Session[] = []
|
||||
private executionController: ExecutionController
|
||||
|
||||
constructor() {
|
||||
this.executionController = new ExecutionController()
|
||||
}
|
||||
|
||||
public async getSession() {
|
||||
if (this.sessions.length) {
|
||||
const session: Session = this.sessions[0]
|
||||
|
||||
// TODO: check if session is not expired
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
return await this.createSession()
|
||||
}
|
||||
|
||||
private async createSession() {
|
||||
if (!this.sessions.length) {
|
||||
const sessionId = generateUniqueFileName(generateTimestamp())
|
||||
|
||||
const sessionFolder = path.join(
|
||||
await getTmpSessionsFolderPath(),
|
||||
sessionId
|
||||
)
|
||||
|
||||
const autoExecContent = `data _null_;
|
||||
/* remove the dummy SYSIN */
|
||||
length fname $8;
|
||||
rc=filename(fname,getoption('SYSIN') );
|
||||
if rc = 0 and fexist(fname) then rc=fdelete(fname);
|
||||
rc=filename(fname);
|
||||
/* now wait for the real SYSIN */
|
||||
slept=0;
|
||||
do until ( fileexist(getoption('SYSIN')) or slept>(60*15) );
|
||||
slept=slept+sleep(0.1,1);
|
||||
end;
|
||||
run;
|
||||
EOL`
|
||||
|
||||
const autoExec = path.join(sessionFolder, 'autoexec.sas')
|
||||
|
||||
await createFile(autoExec, autoExecContent)
|
||||
|
||||
console.log(`[SessionController about to create first session]`)
|
||||
|
||||
this.executionController.execute('', autoExec)
|
||||
|
||||
const creationTimeStamp = sessionId.split('-').pop() as string
|
||||
|
||||
const session: Session = {
|
||||
id: sessionId,
|
||||
available: true,
|
||||
creationTimeStamp: creationTimeStamp,
|
||||
deathTimeStamp: (
|
||||
parseInt(creationTimeStamp) +
|
||||
15 * 60 * 1000 -
|
||||
1000
|
||||
).toString(),
|
||||
path: sessionFolder
|
||||
}
|
||||
|
||||
console.log(`[SessionController]session: `, session)
|
||||
|
||||
this.sessions.push(session)
|
||||
|
||||
return session
|
||||
} else {
|
||||
return this.sessions[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getSessionController = () => {
|
||||
if (process.sessionController) return process.sessionController
|
||||
|
||||
process.sessionController = new SessionController()
|
||||
|
||||
return process.sessionController
|
||||
}
|
||||
2
src/types/Process.d.ts
vendored
2
src/types/Process.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
declare namespace NodeJS {
|
||||
export interface Process {
|
||||
sessionController?: import('../controllers/session').SessionController
|
||||
sessionController?: import('../controllers/Session').SessionController
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MacroVars } from '@sasjs/utils'
|
||||
|
||||
export interface ExecutionQuery {
|
||||
_program: string
|
||||
macroVars?: MacroVars
|
||||
@@ -1,7 +1,8 @@
|
||||
export interface Session {
|
||||
id: string
|
||||
available: boolean
|
||||
ready: boolean
|
||||
creationTimeStamp: string
|
||||
deathTimeStamp: string
|
||||
path: string
|
||||
inUse: boolean
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// TODO: uppercase types
|
||||
export * from './sas'
|
||||
export * from './request'
|
||||
export * from './fileTree'
|
||||
export * from './Execution'
|
||||
export * from './Request'
|
||||
export * from './FileTree'
|
||||
export * from './Session'
|
||||
|
||||
Reference in New Issue
Block a user