mirror of
https://github.com/sasjs/server.git
synced 2026-01-14 01:10:05 +00:00
chore: refactor ExecutionController class to remove code duplications
This commit is contained in:
@@ -46,7 +46,7 @@ const executeSASCode = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { webout, log, httpHeaders } =
|
const { webout, log, httpHeaders } =
|
||||||
(await new ExecutionController().executeSASProgram(
|
(await new ExecutionController().executeProgram(
|
||||||
code,
|
code,
|
||||||
getPreProgramVariables(req),
|
getPreProgramVariables(req),
|
||||||
{ ...req.query, _debug: 131 },
|
{ ...req.query, _debug: 131 },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import { execFileSync } from 'child_process'
|
||||||
import { getSASSessionController, getJSSessionController } from './'
|
import { getSASSessionController, getJSSessionController } from './'
|
||||||
import {
|
import {
|
||||||
readFile,
|
readFile,
|
||||||
@@ -15,7 +16,8 @@ import {
|
|||||||
getFilesFolder,
|
getFilesFolder,
|
||||||
getMacrosFolder,
|
getMacrosFolder,
|
||||||
HTTPHeaders,
|
HTTPHeaders,
|
||||||
isDebugOn
|
isDebugOn,
|
||||||
|
SASJSRunTimes
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
export interface ExecutionVars {
|
export interface ExecutionVars {
|
||||||
@@ -50,45 +52,53 @@ export class ExecutionController {
|
|||||||
const codePath =
|
const codePath =
|
||||||
path
|
path
|
||||||
.join(getFilesFolder(), programPath)
|
.join(getFilesFolder(), programPath)
|
||||||
.replace(new RegExp('/', 'g'), path.sep) + runTime
|
.replace(new RegExp('/', 'g'), path.sep) +
|
||||||
|
'.' +
|
||||||
if (await fileExists(programPath)) {
|
runTime
|
||||||
|
if (await fileExists(codePath)) {
|
||||||
const program = await readFile(codePath)
|
const program = await readFile(codePath)
|
||||||
|
|
||||||
if (runTime === 'sas') {
|
if (runTime === 'sas') {
|
||||||
return this.executeSASProgram(
|
return this.executeProgram(
|
||||||
program,
|
program,
|
||||||
preProgramVariables,
|
preProgramVariables,
|
||||||
vars,
|
vars,
|
||||||
otherArgs,
|
otherArgs,
|
||||||
returnJson,
|
returnJson,
|
||||||
session
|
session,
|
||||||
|
runTime
|
||||||
)
|
)
|
||||||
} else if (runTime === 'js') {
|
} else if (runTime === 'js') {
|
||||||
return this.executeJSProgram(
|
return this.executeProgram(
|
||||||
program,
|
program,
|
||||||
preProgramVariables,
|
preProgramVariables,
|
||||||
vars,
|
vars,
|
||||||
otherArgs,
|
otherArgs,
|
||||||
returnJson
|
returnJson,
|
||||||
|
session,
|
||||||
|
runTime
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
throw `${runTime} runtime is not implemented yet.`
|
throw `${runTime} runtime is not implemented yet.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw 'ExecutionController: Program file does not exist.'
|
throw `ExecutionController: ${programPath} file does not exist.`
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeSASProgram(
|
async executeProgram(
|
||||||
program: string,
|
program: string,
|
||||||
preProgramVariables: PreProgramVars,
|
preProgramVariables: PreProgramVars,
|
||||||
vars: ExecutionVars,
|
vars: ExecutionVars,
|
||||||
otherArgs?: any,
|
otherArgs?: any,
|
||||||
returnJson?: boolean,
|
returnJson?: boolean,
|
||||||
sessionByFileUpload?: Session
|
sessionByFileUpload?: Session,
|
||||||
|
runTime: string = 'sas'
|
||||||
): Promise<ExecuteReturnRaw | ExecuteReturnJson> {
|
): Promise<ExecuteReturnRaw | ExecuteReturnJson> {
|
||||||
const sessionController = getSASSessionController()
|
const sessionController =
|
||||||
|
runTime === SASJSRunTimes.JS
|
||||||
|
? getJSSessionController()
|
||||||
|
: getSASSessionController()
|
||||||
|
|
||||||
const session =
|
const session =
|
||||||
sessionByFileUpload ?? (await sessionController.getSession())
|
sessionByFileUpload ?? (await sessionController.getSession())
|
||||||
@@ -106,6 +116,93 @@ export class ExecutionController {
|
|||||||
preProgramVariables?.httpHeaders.join('\n') ?? ''
|
preProgramVariables?.httpHeaders.join('\n') ?? ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (runTime === SASJSRunTimes.JS) {
|
||||||
|
program = await this.createJSProgram(
|
||||||
|
program,
|
||||||
|
preProgramVariables,
|
||||||
|
vars,
|
||||||
|
weboutPath,
|
||||||
|
tokenFile
|
||||||
|
)
|
||||||
|
|
||||||
|
const codePath = path.join(session.path, 'code.js')
|
||||||
|
|
||||||
|
await createFile(codePath, program)
|
||||||
|
|
||||||
|
execFileSync('node', [codePath])
|
||||||
|
} else {
|
||||||
|
program = await this.createSASProgram(
|
||||||
|
program,
|
||||||
|
preProgramVariables,
|
||||||
|
vars,
|
||||||
|
session,
|
||||||
|
weboutPath,
|
||||||
|
tokenFile,
|
||||||
|
otherArgs
|
||||||
|
)
|
||||||
|
|
||||||
|
const codePath = path.join(session.path, 'code.sas')
|
||||||
|
|
||||||
|
// Creating this file in a RUNNING session will break out
|
||||||
|
// the autoexec loop and actually execute the program
|
||||||
|
// but - given it will take several milliseconds to create
|
||||||
|
// (which can mean SAS trying to run a partial program, or
|
||||||
|
// failing due to file lock) we first create the file THEN
|
||||||
|
// we rename it.
|
||||||
|
await createFile(codePath + '.bkp', program)
|
||||||
|
await moveFile(codePath + '.bkp', codePath)
|
||||||
|
|
||||||
|
// we now need to poll the session status
|
||||||
|
while (!session.completed) {
|
||||||
|
await delay(50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const log = (await fileExists(logPath)) ? await readFile(logPath) : ''
|
||||||
|
const headersContent = (await fileExists(headersPath))
|
||||||
|
? await readFile(headersPath)
|
||||||
|
: ''
|
||||||
|
const httpHeaders: HTTPHeaders = extractHeaders(headersContent)
|
||||||
|
const fileResponse: boolean =
|
||||||
|
httpHeaders.hasOwnProperty('content-type') &&
|
||||||
|
!returnJson && // not a POST Request
|
||||||
|
!isDebugOn(vars) // Debug is not enabled
|
||||||
|
|
||||||
|
const webout = (await fileExists(weboutPath))
|
||||||
|
? fileResponse
|
||||||
|
? await readFileBinary(weboutPath)
|
||||||
|
: await readFile(weboutPath)
|
||||||
|
: ''
|
||||||
|
|
||||||
|
// it should be deleted by scheduleSessionDestroy
|
||||||
|
session.inUse = false
|
||||||
|
|
||||||
|
if (returnJson) {
|
||||||
|
return {
|
||||||
|
httpHeaders,
|
||||||
|
webout,
|
||||||
|
log: isDebugOn(vars) || session.crashed ? log : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
httpHeaders,
|
||||||
|
result:
|
||||||
|
isDebugOn(vars) || session.crashed
|
||||||
|
? `<html><body>${webout}<div style="text-align:left"><hr /><h2>SAS Log</h2><pre>${log}</pre></div></body></html>`
|
||||||
|
: webout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createSASProgram(
|
||||||
|
program: string,
|
||||||
|
preProgramVariables: PreProgramVars,
|
||||||
|
vars: ExecutionVars,
|
||||||
|
session: Session,
|
||||||
|
weboutPath: string,
|
||||||
|
tokenFile: string,
|
||||||
|
otherArgs?: any
|
||||||
|
) {
|
||||||
const varStatments = Object.keys(vars).reduce(
|
const varStatments = Object.keys(vars).reduce(
|
||||||
(computed: string, key: string) =>
|
(computed: string, key: string) =>
|
||||||
`${computed}%let ${key}=${vars[key]};\n`,
|
`${computed}%let ${key}=${vars[key]};\n`,
|
||||||
@@ -161,100 +258,37 @@ ${program}`
|
|||||||
program = `${uploadSasCode}` + program
|
program = `${uploadSasCode}` + program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return program
|
||||||
const codePath = path.join(session.path, 'code.sas')
|
|
||||||
|
|
||||||
// Creating this file in a RUNNING session will break out
|
|
||||||
// the autoexec loop and actually execute the program
|
|
||||||
// but - given it will take several milliseconds to create
|
|
||||||
// (which can mean SAS trying to run a partial program, or
|
|
||||||
// failing due to file lock) we first create the file THEN
|
|
||||||
// we rename it.
|
|
||||||
await createFile(codePath + '.bkp', program)
|
|
||||||
await moveFile(codePath + '.bkp', codePath)
|
|
||||||
|
|
||||||
// we now need to poll the session status
|
|
||||||
while (!session.completed) {
|
|
||||||
await delay(50)
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = (await fileExists(logPath)) ? await readFile(logPath) : ''
|
|
||||||
const headersContent = (await fileExists(headersPath))
|
|
||||||
? await readFile(headersPath)
|
|
||||||
: ''
|
|
||||||
const httpHeaders: HTTPHeaders = extractHeaders(headersContent)
|
|
||||||
const fileResponse: boolean =
|
|
||||||
httpHeaders.hasOwnProperty('content-type') &&
|
|
||||||
!returnJson && // not a POST Request
|
|
||||||
!isDebugOn(vars) // Debug is not enabled
|
|
||||||
|
|
||||||
const webout = (await fileExists(weboutPath))
|
|
||||||
? fileResponse
|
|
||||||
? await readFileBinary(weboutPath)
|
|
||||||
: await readFile(weboutPath)
|
|
||||||
: ''
|
|
||||||
|
|
||||||
// it should be deleted by scheduleSessionDestroy
|
|
||||||
session.inUse = false
|
|
||||||
|
|
||||||
if (returnJson) {
|
|
||||||
return {
|
|
||||||
httpHeaders,
|
|
||||||
webout,
|
|
||||||
log: isDebugOn(vars) || session.crashed ? log : undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
httpHeaders,
|
|
||||||
result:
|
|
||||||
isDebugOn(vars) || session.crashed
|
|
||||||
? `<html><body>${webout}<div style="text-align:left"><hr /><h2>SAS Log</h2><pre>${log}</pre></div></body></html>`
|
|
||||||
: webout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeJSProgram(
|
private async createJSProgram(
|
||||||
program: string,
|
program: string,
|
||||||
preProgramVariables: PreProgramVars,
|
preProgramVariables: PreProgramVars,
|
||||||
vars: ExecutionVars,
|
vars: ExecutionVars,
|
||||||
otherArgs?: any,
|
weboutPath: string,
|
||||||
returnJson?: boolean,
|
tokenFile: string
|
||||||
sessionByFileUpload?: Session
|
) {
|
||||||
): Promise<ExecuteReturnRaw | ExecuteReturnJson> {
|
|
||||||
const sessionController = getJSSessionController()
|
|
||||||
|
|
||||||
const session =
|
|
||||||
sessionByFileUpload ?? (await sessionController.getSession())
|
|
||||||
|
|
||||||
const logPath = path.join(session.path, 'log.log')
|
|
||||||
const headersPath = path.join(session.path, 'stpsrv_header.txt')
|
|
||||||
const weboutPath = path.join(session.path, 'webout.txt')
|
|
||||||
const tokenFile = path.join(session.path, 'reqHeaders.txt')
|
|
||||||
|
|
||||||
await createFile(
|
|
||||||
tokenFile,
|
|
||||||
preProgramVariables?.httpHeaders.join('\n') ?? ''
|
|
||||||
)
|
|
||||||
|
|
||||||
const varStatments = Object.keys(vars).reduce(
|
const varStatments = Object.keys(vars).reduce(
|
||||||
(computed: string, key: string) =>
|
(computed: string, key: string) =>
|
||||||
`${computed}const ${key} = ${vars[key]};\n`,
|
`${computed}const ${key} = '${vars[key]}';\n`,
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
|
|
||||||
const preProgramVarStatments = `
|
const preProgramVarStatments = `
|
||||||
const _webout = ${weboutPath}
|
const weboutPath = '${weboutPath}';
|
||||||
const _sasjs_tokenfile = ${tokenFile};
|
const _sasjs_tokenfile = '${tokenFile}';
|
||||||
const _sasjs_username = ${preProgramVariables?.username};
|
const _sasjs_username = '${preProgramVariables?.username}';
|
||||||
const _sasjs_userid = ${preProgramVariables?.userId};
|
const _sasjs_userid = '${preProgramVariables?.userId}';
|
||||||
const _sasjs_displayname = ${preProgramVariables?.displayName};
|
const _sasjs_displayname = '${preProgramVariables?.displayName}';
|
||||||
const _metaperson = _sasjs_displayname;
|
const _metaperson = _sasjs_displayname;
|
||||||
const _metauser = _sasjs_username;
|
const _metauser = _sasjs_username;
|
||||||
const sasjsprocessmode = 'Stored Program';
|
const sasjsprocessmode = 'Stored Program';
|
||||||
`
|
`
|
||||||
|
|
||||||
program = `
|
program = `
|
||||||
|
/*require module for writing webout file*/
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
|
||||||
/* runtime vars */
|
/* runtime vars */
|
||||||
${varStatments}
|
${varStatments}
|
||||||
|
|
||||||
@@ -262,7 +296,11 @@ ${varStatments}
|
|||||||
${preProgramVarStatments}
|
${preProgramVarStatments}
|
||||||
|
|
||||||
/* actual job code */
|
/* actual job code */
|
||||||
${program}`
|
${program}
|
||||||
|
|
||||||
|
/* write webout file*/
|
||||||
|
fs.promises.writeFile(weboutPath, JSON.stringify(webout))
|
||||||
|
`
|
||||||
|
|
||||||
// todo: modify this commented block for js runtime
|
// todo: modify this commented block for js runtime
|
||||||
// if no files are uploaded filesNamesMap will be undefined
|
// if no files are uploaded filesNamesMap will be undefined
|
||||||
@@ -277,42 +315,7 @@ ${program}`
|
|||||||
// program = `${uploadSasCode}` + program
|
// program = `${uploadSasCode}` + program
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
return program
|
||||||
const codePath = path.join(session.path, 'code.sas')
|
|
||||||
await createFile(codePath, program)
|
|
||||||
|
|
||||||
// todo: execute code.js file
|
|
||||||
|
|
||||||
const log = (await fileExists(logPath)) ? await readFile(logPath) : ''
|
|
||||||
const headersContent = (await fileExists(headersPath))
|
|
||||||
? await readFile(headersPath)
|
|
||||||
: ''
|
|
||||||
const httpHeaders: HTTPHeaders = extractHeaders(headersContent)
|
|
||||||
const fileResponse: boolean =
|
|
||||||
httpHeaders.hasOwnProperty('content-type') &&
|
|
||||||
!returnJson && // not a POST Request
|
|
||||||
!isDebugOn(vars) // Debug is not enabled
|
|
||||||
|
|
||||||
const webout = (await fileExists(weboutPath))
|
|
||||||
? fileResponse
|
|
||||||
? await readFileBinary(weboutPath)
|
|
||||||
: await readFile(weboutPath)
|
|
||||||
: ''
|
|
||||||
|
|
||||||
if (returnJson) {
|
|
||||||
return {
|
|
||||||
httpHeaders,
|
|
||||||
webout,
|
|
||||||
log: isDebugOn(vars) ? log : undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
httpHeaders,
|
|
||||||
result: isDebugOn(vars)
|
|
||||||
? `<html><body>${webout}<div style="text-align:left"><hr /><h2>SAS Log</h2><pre>${log}</pre></div></body></html>`
|
|
||||||
: webout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildDirectoryTree() {
|
buildDirectoryTree() {
|
||||||
|
|||||||
Reference in New Issue
Block a user