1
0
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:
2022-10-17 18:31:08 +02:00
parent 6434123401
commit 36be3a7d5e
7 changed files with 96 additions and 114 deletions

View File

@@ -103,8 +103,9 @@ PORT=
# If not present, mocking function is disabled
MOCK_SERVERTYPE=
# Path to mocking folder, it's sub directories should be: sas9, viya, sasjs
# Server will automatically use sub directory accordingly
# default: /api/mocks
# Path to mocking folder, for generic responses, it's sub directories should be: sas9, viya, sasjs
# Server will automatically use subdirectory accordingly
STATIC_MOCK_LOCATION=
#

View File

@@ -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)
: ''

View File

@@ -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) {

View File

@@ -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> => {

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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