mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
feat: mocking sas9 responses with JS STP
This commit is contained in:
@@ -28,6 +28,7 @@ interface ExecuteFileParams {
|
||||
returnJson?: boolean
|
||||
session?: Session
|
||||
runTime: RunTimeType
|
||||
forceStringResult?: boolean
|
||||
}
|
||||
|
||||
interface ExecuteProgramParams extends Omit<ExecuteFileParams, 'programPath'> {
|
||||
@@ -42,7 +43,8 @@ export class ExecutionController {
|
||||
otherArgs,
|
||||
returnJson,
|
||||
session,
|
||||
runTime
|
||||
runTime,
|
||||
forceStringResult
|
||||
}: ExecuteFileParams) {
|
||||
const program = await readFile(programPath)
|
||||
|
||||
@@ -53,7 +55,8 @@ export class ExecutionController {
|
||||
otherArgs,
|
||||
returnJson,
|
||||
session,
|
||||
runTime
|
||||
runTime,
|
||||
forceStringResult
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,7 +66,8 @@ export class ExecutionController {
|
||||
vars,
|
||||
otherArgs,
|
||||
session: sessionByFileUpload,
|
||||
runTime
|
||||
runTime,
|
||||
forceStringResult
|
||||
}: ExecuteProgramParams): Promise<ExecuteReturnRaw> {
|
||||
const sessionController = getSessionController(runTime)
|
||||
|
||||
@@ -104,7 +108,7 @@ export class ExecutionController {
|
||||
const fileResponse: boolean = httpHeaders.hasOwnProperty('content-type')
|
||||
|
||||
const webout = (await fileExists(weboutPath))
|
||||
? fileResponse
|
||||
? fileResponse && !forceStringResult
|
||||
? await readFileBinary(weboutPath)
|
||||
: await readFile(weboutPath)
|
||||
: ''
|
||||
|
||||
@@ -110,17 +110,13 @@ export const processProgram = async (
|
||||
|
||||
// create a stream that will write to console outputs to log file
|
||||
const writeStream = fs.createWriteStream(logPath)
|
||||
|
||||
// waiting for the open event so that we can have underlying file descriptor
|
||||
await once(writeStream, 'open')
|
||||
|
||||
execFileSync(executablePath, [codePath], {
|
||||
stdio: ['ignore', writeStream, writeStream]
|
||||
})
|
||||
|
||||
// copy the code file to log and end write stream
|
||||
writeStream.end(program)
|
||||
|
||||
session.completed = true
|
||||
console.log('session completed', session)
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -2,9 +2,14 @@ import { readFile } from '@sasjs/utils'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import { Request, Post, Get } from 'tsoa'
|
||||
import fs from 'fs'
|
||||
import fse from 'fs-extra'
|
||||
import dotenv from 'dotenv'
|
||||
import { ExecutionController } from './internal'
|
||||
import {
|
||||
getPreProgramVariables,
|
||||
getRunTimeAndFilePath,
|
||||
makeFilesNamesMap
|
||||
} from '../utils'
|
||||
import { MulterFile } from '../types/Upload'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
@@ -76,17 +81,37 @@ export class MockSas9Controller {
|
||||
}
|
||||
}
|
||||
|
||||
let program = req.query._program?.toString() || undefined
|
||||
const program = req.query._program ?? req.body?._program
|
||||
let filePath: string[] = ['generic', 'sas-stored-process']
|
||||
|
||||
if (program) {
|
||||
filePath = `${program}`.replace('/', '').split('/')
|
||||
return await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
this.mocksPath,
|
||||
'sas9',
|
||||
...filePath
|
||||
])
|
||||
const vars = { ...req.query, ...req.body, _requestMethod: req.method }
|
||||
const otherArgs = {}
|
||||
|
||||
try {
|
||||
const { codePath, runTime } = await getRunTimeAndFilePath(
|
||||
program + '.js'
|
||||
)
|
||||
|
||||
const result = await new ExecutionController().executeFile({
|
||||
programPath: codePath,
|
||||
preProgramVariables: getPreProgramVariables(req),
|
||||
vars: vars,
|
||||
otherArgs: otherArgs,
|
||||
runTime,
|
||||
forceStringResult: true
|
||||
})
|
||||
|
||||
return {
|
||||
content: result.result as string
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err', err)
|
||||
}
|
||||
|
||||
return {
|
||||
content: 'No webout returned.'
|
||||
}
|
||||
}
|
||||
|
||||
return await getMockResponseFromFile([
|
||||
@@ -115,66 +140,38 @@ export class MockSas9Controller {
|
||||
}
|
||||
}
|
||||
|
||||
let program = req.query._program?.toString() || ''
|
||||
program = program.replace('/', '')
|
||||
let debug = req.query._debug?.toString()
|
||||
const program = req.query._program ?? req.body?._program
|
||||
const vars = {
|
||||
...req.query,
|
||||
...req.body,
|
||||
_requestMethod: req.method,
|
||||
_driveLoc: process.driveLoc
|
||||
}
|
||||
const filesNamesMap = req.files?.length
|
||||
? makeFilesNamesMap(req.files as MulterFile[])
|
||||
: null
|
||||
const otherArgs = { filesNamesMap: filesNamesMap }
|
||||
const { codePath, runTime } = await getRunTimeAndFilePath(program + '.js')
|
||||
try {
|
||||
const result = await new ExecutionController().executeFile({
|
||||
programPath: codePath,
|
||||
preProgramVariables: getPreProgramVariables(req),
|
||||
vars: vars,
|
||||
otherArgs: otherArgs,
|
||||
runTime,
|
||||
session: req.sasjsSession,
|
||||
forceStringResult: true
|
||||
})
|
||||
|
||||
let fileContents = ''
|
||||
|
||||
if (program.includes('runner') && debug === 'log') {
|
||||
if (req.files && req.files.length > 0) {
|
||||
const regexRequest = /cli-tests-request-sas9-.*?\d*/g
|
||||
const uploadFilePath = (req.files as any)[0].path
|
||||
|
||||
fileContents = fs.readFileSync(uploadFilePath, 'utf8')
|
||||
|
||||
let matched = fileContents.match(regexRequest)?.[0]
|
||||
|
||||
if (matched) {
|
||||
const testsFolderPath = path.join(
|
||||
process.cwd(),
|
||||
this.mocksPath,
|
||||
'sas9',
|
||||
'User Folders',
|
||||
'cli-tests',
|
||||
'sasdemo',
|
||||
matched
|
||||
)
|
||||
|
||||
if (!fs.existsSync(testsFolderPath)) fs.mkdirSync(testsFolderPath)
|
||||
|
||||
fse.copySync(
|
||||
path.join(
|
||||
process.cwd(),
|
||||
this.mocksPath,
|
||||
'sas9',
|
||||
'User Folders',
|
||||
'sasdemo',
|
||||
'services'
|
||||
),
|
||||
path.join(testsFolderPath, 'services')
|
||||
)
|
||||
}
|
||||
return {
|
||||
content: result.result as string
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err', err)
|
||||
}
|
||||
|
||||
const content = await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
this.mocksPath,
|
||||
'sas9',
|
||||
...program.split('/')
|
||||
])
|
||||
|
||||
content.content += fileContents
|
||||
|
||||
if (content.error) {
|
||||
return content
|
||||
}
|
||||
|
||||
const parsedContent = parseJsonIfValid(content.content)
|
||||
|
||||
return {
|
||||
content: parsedContent
|
||||
content: 'No webout returned.'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,23 +260,6 @@ export class MockSas9Controller {
|
||||
private isPublicAccount = () => this.loggedIn?.toLowerCase() === 'public'
|
||||
}
|
||||
|
||||
/**
|
||||
* If JSON is valid it will be parsed otherwise will return text unaltered
|
||||
* @param content string to be parsed
|
||||
* @returns JSON or string
|
||||
*/
|
||||
const parseJsonIfValid = (content: string) => {
|
||||
let fileContent = ''
|
||||
|
||||
try {
|
||||
fileContent = JSON.parse(content)
|
||||
} catch (err: any) {
|
||||
fileContent = content
|
||||
}
|
||||
|
||||
return fileContent
|
||||
}
|
||||
|
||||
const getMockResponseFromFile = async (
|
||||
filePath: string[]
|
||||
): Promise<MockFileRead> => {
|
||||
|
||||
@@ -2,10 +2,10 @@ import express from 'express'
|
||||
import { generateCSRFToken } from '../../middlewares'
|
||||
import { WebController } from '../../controllers'
|
||||
import { MockSas9Controller } from '../../controllers/mock-sas9'
|
||||
import fs from 'fs'
|
||||
import multer from 'multer'
|
||||
import path from 'path'
|
||||
import dotenv from 'dotenv'
|
||||
import { FileUploadController } from '../../controllers/internal'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
@@ -14,6 +14,7 @@ const webController = new WebController()
|
||||
// Mock controller must be singleton because it keeps the states
|
||||
// for example `isLoggedIn` and potentially more in future mocks
|
||||
const controller = new MockSas9Controller()
|
||||
const fileUploadController = new FileUploadController()
|
||||
|
||||
const mockPath = process.env.STATIC_MOCK_LOCATION || 'mocks'
|
||||
|
||||
@@ -68,26 +69,25 @@ sas9WebRouter.get('/SASStoredProcess/do/', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
sas9WebRouter.post('/SASStoredProcess/do/', upload.any(), async (req, res) => {
|
||||
const response = await controller.sasStoredProcessDoPost(req)
|
||||
sas9WebRouter.post(
|
||||
'/SASStoredProcess/do/',
|
||||
fileUploadController.preUploadMiddleware,
|
||||
fileUploadController.getMulterUploadObject().any(),
|
||||
async (req, res) => {
|
||||
const response = await controller.sasStoredProcessDoPost(req)
|
||||
|
||||
if (req.files) {
|
||||
;(req.files as any).forEach((file: any) => {
|
||||
fs.renameSync(file.path, file.destination + '/' + file.fieldname)
|
||||
})
|
||||
}
|
||||
if (response.redirect) {
|
||||
res.redirect(response.redirect)
|
||||
return
|
||||
}
|
||||
|
||||
if (response.redirect) {
|
||||
res.redirect(response.redirect)
|
||||
return
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
sas9WebRouter.get('/SASLogon/login', async (req, res) => {
|
||||
const response = await controller.loginGet()
|
||||
|
||||
@@ -18,10 +18,12 @@ export const getPreProgramVariables = (req: Request): PreProgramVars => {
|
||||
|
||||
if (cookies.length) httpHeaders.push(`cookie: ${cookies.join('; ')}`)
|
||||
|
||||
//In desktop mode when mocking mode is enabled, user was undefined.
|
||||
//So this is workaround.
|
||||
return {
|
||||
username: user!.username,
|
||||
userId: user!.userId,
|
||||
displayName: user!.displayName,
|
||||
username: user ? user.username : 'demo',
|
||||
userId: user ? user.userId : 0,
|
||||
displayName: user ? user.displayName : 'demo',
|
||||
serverUrl: protocol + host,
|
||||
httpHeaders
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ export const setProcessVariables = async () => {
|
||||
process.rLoc = process.env.R_PATH
|
||||
} else {
|
||||
const { sasLoc, nodeLoc, pythonLoc, rLoc } = await getDesktopFields()
|
||||
|
||||
process.sasLoc = sasLoc
|
||||
process.nodeLoc = nodeLoc
|
||||
process.pythonLoc = pythonLoc
|
||||
|
||||
Reference in New Issue
Block a user