1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-14 09:20:06 +00:00

Merge pull request #71 from sasjs/return-file-in-response

Return file in response
This commit is contained in:
Muhammad Saad
2022-02-20 05:24:48 +04:00
committed by GitHub
8 changed files with 89 additions and 30 deletions

View File

@@ -539,7 +539,9 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ExecuteReturnJsonResponse' anyOf:
- {$ref: '#/components/schemas/ExecuteReturnJsonResponse'}
- {type: string, format: byte}
description: 'Execute SAS code.' description: 'Execute SAS code.'
summary: 'Run SAS Code and returns log' summary: 'Run SAS Code and returns log'
tags: tags:
@@ -1054,7 +1056,9 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: string anyOf:
- {type: string}
- {type: string, format: byte}
description: "Trigger a SAS program using it's location in the _program parameter.\nEnable debugging using the _debug parameter.\nAdditional URL parameters are turned into SAS macro variables.\nAny files provided are placed into the session and\ncorresponding _WEBIN_XXX variables are created." description: "Trigger a SAS program using it's location in the _program parameter.\nEnable debugging using the _debug parameter.\nAdditional URL parameters are turned into SAS macro variables.\nAny files provided are placed into the session and\ncorresponding _WEBIN_XXX variables are created."
summary: 'Execute Stored Program, return raw content' summary: 'Execute Stored Program, return raw content'
tags: tags:
@@ -1078,7 +1082,9 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ExecuteReturnJsonResponse' anyOf:
- {$ref: '#/components/schemas/ExecuteReturnJsonResponse'}
- {type: string, format: byte}
examples: examples:
'Example 1': 'Example 1':
value: {status: success, _webout: 'webout content', log: [], httpHeaders: {Content-type: application/zip, Cache-Control: 'public, max-age=1000'}} value: {status: success, _webout: 'webout content', log: [], httpHeaders: {Content-type: application/zip, Cache-Control: 'public, max-age=1000'}}

View File

@@ -25,7 +25,7 @@ export class CodeController {
public async executeSASCode( public async executeSASCode(
@Request() request: express.Request, @Request() request: express.Request,
@Body() body: ExecuteSASCodePayload @Body() body: ExecuteSASCodePayload
): Promise<ExecuteReturnJsonResponse> { ): Promise<ExecuteReturnJsonResponse | Buffer> {
return executeSASCode(request, body) return executeSASCode(request, body)
} }
} }
@@ -41,6 +41,11 @@ const executeSASCode = async (req: any, { code }: ExecuteSASCodePayload) => {
true true
)) as ExecuteReturnJson )) as ExecuteReturnJson
if (webout instanceof Buffer) {
;(req as any).sasHeaders = httpHeaders
return webout
}
return { return {
status: 'success', status: 'success',
_webout: webout, _webout: webout,

View File

@@ -1,7 +1,13 @@
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import { getSessionController } from './' import { getSessionController } from './'
import { readFile, fileExists, createFile, moveFile } from '@sasjs/utils' import {
readFile,
fileExists,
createFile,
moveFile,
readFileBinary
} from '@sasjs/utils'
import { PreProgramVars, TreeNode } from '../../types' import { PreProgramVars, TreeNode } from '../../types'
import { import {
extractHeaders, extractHeaders,
@@ -16,12 +22,12 @@ export interface ExecutionVars {
export interface ExecuteReturnRaw { export interface ExecuteReturnRaw {
httpHeaders: HTTPHeaders httpHeaders: HTTPHeaders
result: string result: string | Buffer
} }
export interface ExecuteReturnJson { export interface ExecuteReturnJson {
httpHeaders: HTTPHeaders httpHeaders: HTTPHeaders
webout: string webout: string | Buffer
log?: string log?: string
} }
@@ -138,15 +144,17 @@ ${program}`
} }
const log = (await fileExists(logPath)) ? await readFile(logPath) : '' const log = (await fileExists(logPath)) ? await readFile(logPath) : ''
const webout = (await fileExists(weboutPath))
? await readFile(weboutPath)
: ''
const headersContent = (await fileExists(headersPath)) const headersContent = (await fileExists(headersPath))
? await readFile(headersPath) ? await readFile(headersPath)
: '' : ''
const httpHeaders: HTTPHeaders = headersContent const httpHeaders: HTTPHeaders = extractHeaders(headersContent)
? extractHeaders(headersContent) const fileResponse: boolean = httpHeaders.hasOwnProperty('content-type')
: {}
const webout = (await fileExists(weboutPath))
? fileResponse
? await readFileBinary(weboutPath)
: await readFile(weboutPath)
: ''
const debugValue = const debugValue =
typeof vars._debug === 'string' ? parseInt(vars._debug) : vars._debug typeof vars._debug === 'string' ? parseInt(vars._debug) : vars._debug
@@ -165,10 +173,11 @@ ${program}`
return { return {
httpHeaders, httpHeaders,
result: result: fileResponse
(debugValue && debugValue >= 131) || session.crashed ? webout
? `<html><body>${webout}<div style="text-align:left"><hr /><h2>SAS Log</h2><pre>${log}</pre></div></body></html>` : (debugValue && debugValue >= 131) || session.crashed
: webout ? `<html><body>${webout}<div style="text-align:left"><hr /><h2>SAS Log</h2><pre>${log}</pre></div></body></html>`
: webout
} }
} }

View File

@@ -59,7 +59,7 @@ export class STPController {
public async executeReturnRaw( public async executeReturnRaw(
@Request() request: express.Request, @Request() request: express.Request,
@Query() _program: string @Query() _program: string
): Promise<string> { ): Promise<string | Buffer> {
return executeReturnRaw(request, _program) return executeReturnRaw(request, _program)
} }
@@ -87,7 +87,7 @@ export class STPController {
@Request() request: express.Request, @Request() request: express.Request,
@Body() body?: ExecuteReturnJsonPayload, @Body() body?: ExecuteReturnJsonPayload,
@Query() _program?: string @Query() _program?: string
): Promise<ExecuteReturnJsonResponse> { ): Promise<ExecuteReturnJsonResponse | Buffer> {
const program = _program ?? body?._program const program = _program ?? body?._program
return executeReturnJson(request, program!) return executeReturnJson(request, program!)
} }
@@ -96,7 +96,7 @@ export class STPController {
const executeReturnRaw = async ( const executeReturnRaw = async (
req: express.Request, req: express.Request,
_program: string _program: string
): Promise<string> => { ): Promise<string | Buffer> => {
const query = req.query as ExecutionVars const query = req.query as ExecutionVars
const sasCodePath = const sasCodePath =
path path
@@ -113,7 +113,11 @@ const executeReturnRaw = async (
req.res?.set(httpHeaders) req.res?.set(httpHeaders)
return result as string if (result instanceof Buffer) {
;(req as any).sasHeaders = httpHeaders
}
return result
} catch (err: any) { } catch (err: any) {
throw { throw {
code: 400, code: 400,
@@ -127,7 +131,7 @@ const executeReturnRaw = async (
const executeReturnJson = async ( const executeReturnJson = async (
req: any, req: any,
_program: string _program: string
): Promise<ExecuteReturnJsonResponse> => { ): Promise<ExecuteReturnJsonResponse | Buffer> => {
const sasCodePath = const sasCodePath =
path path
.join(getTmpFilesFolderPath(), _program) .join(getTmpFilesFolderPath(), _program)
@@ -145,6 +149,11 @@ const executeReturnJson = async (
true true
)) as ExecuteReturnJson )) as ExecuteReturnJson
if (webout instanceof Buffer) {
;(req as any).sasHeaders = httpHeaders
return webout
}
return { return {
status: 'success', status: 'success',
_webout: webout, _webout: webout,

View File

@@ -12,6 +12,12 @@ runRouter.post('/execute', async (req, res) => {
try { try {
const response = await controller.executeSASCode(req, body) const response = await controller.executeSASCode(req, body)
if (response instanceof Buffer) {
res.writeHead(200, (req as any).sasHeaders)
return res.end(response)
}
res.send(response) res.send(response)
} catch (err: any) { } catch (err: any) {
const statusCode = err.code const statusCode = err.code

View File

@@ -14,6 +14,12 @@ stpRouter.get('/execute', async (req, res) => {
try { try {
const response = await controller.executeReturnRaw(req, query._program) const response = await controller.executeReturnRaw(req, query._program)
if (response instanceof Buffer) {
res.writeHead(200, (req as any).sasHeaders)
return res.end(response)
}
res.send(response) res.send(response)
} catch (err: any) { } catch (err: any) {
const statusCode = err.code const statusCode = err.code
@@ -40,6 +46,12 @@ stpRouter.post(
body, body,
query?._program query?._program
) )
if (response instanceof Buffer) {
res.writeHead(200, (req as any).sasHeaders)
return res.end(response)
}
res.send(response) res.send(response)
} catch (err: any) { } catch (err: any) {
const statusCode = err.code const statusCode = err.code

View File

@@ -4,20 +4,20 @@ export interface HTTPHeaders {
[key: string]: string [key: string]: string
} }
export const extractHeaders = (content: string): HTTPHeaders => { export const extractHeaders = (content?: string): HTTPHeaders => {
const headersObj: HTTPHeaders = {} const headersObj: HTTPHeaders = {}
const headersArr = content const headersArr = content
.split('\n') ?.split('\n')
.map((line) => line.trim()) .map((line) => line.trim())
.filter((line) => !!line) .filter((line) => !!line)
headersArr.forEach((headerStr) => { headersArr?.forEach((headerStr) => {
const [key, value] = headerStr.split(':').map((data) => data.trim()) const [key, value] = headerStr.split(':').map((data) => data.trim())
if (value && headerUtils.validateHeader(key, value)) { if (value && headerUtils.validateHeader(key, value)) {
headersObj[key] = value headersObj[key.toLowerCase()] = value
} else { } else {
delete headersObj[key] delete headersObj[key.toLowerCase()]
} }
}) })

View File

@@ -12,8 +12,8 @@ describe('extractHeaders', () => {
`) `)
expect(headers).toEqual({ expect(headers).toEqual({
'Content-type': 'application/zip', 'content-type': 'application/zip',
'Cache-Control': 'public, max-age=1000' 'cache-control': 'public, max-age=1000'
}) })
}) })
@@ -25,7 +25,7 @@ describe('extractHeaders', () => {
Content-type: Content-type:
`) `)
expect(headers).toEqual({ 'Cache-Control': 'public, max-age=1000' }) expect(headers).toEqual({ 'cache-control': 'public, max-age=1000' })
}) })
it('should return only valid http headers', () => { it('should return only valid http headers', () => {
@@ -37,4 +37,16 @@ describe('extractHeaders', () => {
expect(headers).toEqual({}) expect(headers).toEqual({})
}) })
it('should return http headers if empty', () => {
const headers = extractHeaders('')
expect(headers).toEqual({})
})
it('should return http headers if not provided', () => {
const headers = extractHeaders()
expect(headers).toEqual({})
})
}) })