1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-10 19:34:34 +00:00

Compare commits

...

16 Commits

Author SHA1 Message Date
semantic-release-bot
056a436e10 chore(release): 0.21.4 [skip ci]
## [0.21.4](https://github.com/sasjs/server/compare/v0.21.3...v0.21.4) (2022-09-21)

### Bug Fixes

* removing single quotes from _program value ([a0e7875](a0e7875ae6))
2022-09-21 20:02:09 +00:00
Allan Bowe
06d59c618c Merge pull request #287 from sasjs/varfix
fix: removing single quotes from _program value
2022-09-21 20:58:28 +01:00
Allan Bowe
a0e7875ae6 fix: removing single quotes from _program value 2022-09-21 19:57:32 +00:00
semantic-release-bot
24966e695a chore(release): 0.21.3 [skip ci]
## [0.21.3](https://github.com/sasjs/server/compare/v0.21.2...v0.21.3) (2022-09-21)

### Bug Fixes

* return same tokens if not expired ([330c020](330c020933))
2022-09-21 17:49:49 +00:00
Allan Bowe
5c40d8a342 Merge pull request #286 from sasjs/issue-279
fix: return same tokens if not expired
2022-09-21 18:46:07 +01:00
6f5566dabb chore: lint fix 2022-09-21 22:29:50 +05:00
d93470d183 chore: improve code 2022-09-21 22:27:27 +05:00
330c020933 fix: return same tokens if not expired 2022-09-21 22:12:03 +05:00
munja
a810f6c7cf chore(docs): updating swagger definitions 2022-09-21 11:08:12 +01:00
semantic-release-bot
5d6c6086b4 chore(release): 0.21.2 [skip ci]
## [0.21.2](https://github.com/sasjs/server/compare/v0.21.1...v0.21.2) (2022-09-20)

### Bug Fixes

* default content-type for sas programs should be text/plain ([9977c9d](9977c9d161))
* **studio:** inject program path to code before sending for execution ([edc2e2a](edc2e2a302))
2022-09-20 21:08:08 +00:00
Allan Bowe
0edcbdcefc Merge pull request #283 from sasjs/fix-default-content-type
fix: default content-type for sas programs should be text/plain
2022-09-20 22:04:27 +01:00
Allan Bowe
ea0222f218 Merge pull request #285 from sasjs/issue-280
fix(studio): inject program path to code before sending for execution
2022-09-20 22:04:16 +01:00
edc2e2a302 fix(studio): inject program path to code before sending for execution 2022-09-21 01:57:01 +05:00
Allan Bowe
efd2e1450e Merge pull request #284 from sasjs/apidocs
chore(docs): updating API docs
2022-09-20 12:25:12 +01:00
munja
1092a73c10 chore(docs): updating API docs 2022-09-20 12:20:50 +01:00
9977c9d161 fix: default content-type for sas programs should be text/plain 2022-09-20 02:32:22 +05:00
12 changed files with 126 additions and 30 deletions

View File

@@ -1,3 +1,25 @@
## [0.21.4](https://github.com/sasjs/server/compare/v0.21.3...v0.21.4) (2022-09-21)
### Bug Fixes
* removing single quotes from _program value ([a0e7875](https://github.com/sasjs/server/commit/a0e7875ae61cbb6e7d3995d2e36e7300b0daec86))
## [0.21.3](https://github.com/sasjs/server/compare/v0.21.2...v0.21.3) (2022-09-21)
### Bug Fixes
* return same tokens if not expired ([330c020](https://github.com/sasjs/server/commit/330c020933f1080261b38f07d6b627f6d7c62446))
## [0.21.2](https://github.com/sasjs/server/compare/v0.21.1...v0.21.2) (2022-09-20)
### Bug Fixes
* default content-type for sas programs should be text/plain ([9977c9d](https://github.com/sasjs/server/commit/9977c9d161947b11d45ab2513f99a5320a3f5a06))
* **studio:** inject program path to code before sending for execution ([edc2e2a](https://github.com/sasjs/server/commit/edc2e2a302ccea4985f3d6b83ef8c23620ab82b6))
## [0.21.1](https://github.com/sasjs/server/compare/v0.21.0...v0.21.1) (2022-09-19) ## [0.21.1](https://github.com/sasjs/server/compare/v0.21.0...v0.21.1) (2022-09-19)

View File

@@ -660,10 +660,10 @@ paths:
anyOf: anyOf:
- {type: string} - {type: string}
- {type: string, format: byte} - {type: string, format: byte}
description: 'Execute SAS code.' description: 'Execute Code on the Specified Runtime'
summary: 'Run SAS Code and returns log' summary: 'Run Code and Return Webout Content and Log'
tags: tags:
- CODE - Code
security: security:
- -
bearerAuth: [] bearerAuth: []
@@ -1658,8 +1658,8 @@ paths:
anyOf: anyOf:
- {type: string} - {type: string}
- {type: string, format: byte} - {type: string, format: byte}
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms" description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
summary: 'Execute a Stored Program, returns raw _webout content.' summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
tags: tags:
- STP - STP
security: security:
@@ -1667,7 +1667,7 @@ paths:
bearerAuth: [] bearerAuth: []
parameters: parameters:
- -
description: 'Location of SAS or JS code' description: 'Location of code in SASjs Drive'
in: query in: query
name: _program name: _program
required: true required: true
@@ -1685,8 +1685,8 @@ paths:
anyOf: anyOf:
- {type: string} - {type: string}
- {type: string, format: byte} - {type: string, format: byte}
description: "Trigger a SAS or JS program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms\n\nThe response will be a JSON object with the following root attributes:\nlog, webout, headers.\n\nThe webout attribute will be nested JSON ONLY if the response-header\ncontains a content-type of application/json AND it is valid JSON.\nOtherwise it will be a stringified version of the webout content." description: "Trigger a Stored Program using the _program URL parameter.\n\nAccepts URL parameters and file uploads. For more details, see docs:\n\nhttps://server.sasjs.io/storedprograms"
summary: 'Execute a Stored Program, return a JSON object' summary: 'Execute a Stored Program, returns _webout and (optionally) log.'
tags: tags:
- STP - STP
security: security:
@@ -1694,7 +1694,7 @@ paths:
bearerAuth: [] bearerAuth: []
parameters: parameters:
- -
description: 'Location of SAS or JS code' description: 'Location of code in SASjs Drive'
in: query in: query
name: _program name: _program
required: false required: false
@@ -1798,7 +1798,7 @@ tags:
name: Client name: Client
description: 'Operations about clients' description: 'Operations about clients'
- -
name: CODE name: Code
description: 'Execution of code (various runtimes are supported)' description: 'Execution of code (various runtimes are supported)'
- -
name: Drive name: Drive

View File

@@ -4,6 +4,7 @@ import { InfoJWT } from '../types'
import { import {
generateAccessToken, generateAccessToken,
generateRefreshToken, generateRefreshToken,
getTokensFromDB,
removeTokensInDB, removeTokensInDB,
saveTokensInDB saveTokensInDB
} from '../utils' } from '../utils'
@@ -73,6 +74,15 @@ const token = async (data: any): Promise<TokenResponse> => {
AuthController.deleteCode(userInfo.userId, clientId) AuthController.deleteCode(userInfo.userId, clientId)
// get tokens from DB
const existingTokens = await getTokensFromDB(userInfo.userId, clientId)
if (existingTokens) {
return {
accessToken: existingTokens.accessToken,
refreshToken: existingTokens.refreshToken
}
}
const accessToken = generateAccessToken(userInfo) const accessToken = generateAccessToken(userInfo)
const refreshToken = generateRefreshToken(userInfo) const refreshToken = generateRefreshToken(userInfo)

View File

@@ -24,11 +24,11 @@ interface ExecuteCodePayload {
@Security('bearerAuth') @Security('bearerAuth')
@Route('SASjsApi/code') @Route('SASjsApi/code')
@Tags('CODE') @Tags('Code')
export class CodeController { export class CodeController {
/** /**
* Execute SAS code. * Execute Code on the Specified Runtime
* @summary Run SAS Code and returns log * @summary Run Code and Return Webout Content and Log
*/ */
@Post('/execute') @Post('/execute')
public async executeCode( public async executeCode(

View File

@@ -92,6 +92,9 @@ export class SASSessionController extends SessionController {
path: sessionFolder path: sessionFolder
} }
const headersPath = path.join(session.path, 'stpsrv_header.txt')
await createFile(headersPath, 'Content-type: text/plain')
// we do not want to leave sessions running forever // we do not want to leave sessions running forever
// we clean them up after a predefined period, if unused // we clean them up after a predefined period, if unused
this.scheduleSessionDestroy(session) this.scheduleSessionDestroy(session)
@@ -170,7 +173,7 @@ ${autoExecContent}`
session.ready = true session.ready = true
} }
public async deleteSession(session: Session) { private async deleteSession(session: Session) {
// remove the temporary files, to avoid buildup // remove the temporary files, to avoid buildup
await deleteFolder(session.path) await deleteFolder(session.path)

View File

@@ -23,14 +23,14 @@ interface ExecutePostRequestPayload {
@Tags('STP') @Tags('STP')
export class STPController { export class STPController {
/** /**
* Trigger a SAS or JS program using the _program URL parameter. * Trigger a Stored Program using the _program URL parameter.
* *
* Accepts URL parameters and file uploads. For more details, see docs: * Accepts URL parameters and file uploads. For more details, see docs:
* *
* https://server.sasjs.io/storedprograms * https://server.sasjs.io/storedprograms
* *
* @summary Execute a Stored Program, returns raw _webout content. * @summary Execute a Stored Program, returns _webout and (optionally) log.
* @param _program Location of SAS or JS code * @param _program Location of code in SASjs Drive
* @example _program "/Projects/myApp/some/program" * @example _program "/Projects/myApp/some/program"
*/ */
@Get('/execute') @Get('/execute')
@@ -43,21 +43,15 @@ export class STPController {
} }
/** /**
* Trigger a SAS or JS program using the _program URL parameter. * Trigger a Stored Program using the _program URL parameter.
* *
* Accepts URL parameters and file uploads. For more details, see docs: * Accepts URL parameters and file uploads. For more details, see docs:
* *
* https://server.sasjs.io/storedprograms * https://server.sasjs.io/storedprograms
* *
* The response will be a JSON object with the following root attributes:
* log, webout, headers.
* *
* The webout attribute will be nested JSON ONLY if the response-header * @summary Execute a Stored Program, returns _webout and (optionally) log.
* contains a content-type of application/json AND it is valid JSON. * @param _program Location of code in SASjs Drive
* Otherwise it will be a stringified version of the webout content.
*
* @summary Execute a Stored Program, return a JSON object
* @param _program Location of SAS or JS code
* @example _program "/Projects/myApp/some/program" * @example _program "/Projects/myApp/some/program"
*/ */
@Post('/execute') @Post('/execute')

View File

@@ -7,7 +7,7 @@ import {
authenticateRefreshToken authenticateRefreshToken
} from '../../middlewares' } from '../../middlewares'
import { authorizeValidation, tokenValidation } from '../../utils' import { tokenValidation } from '../../utils'
import { InfoJWT } from '../../types' import { InfoJWT } from '../../types'
const authRouter = express.Router() const authRouter = express.Router()

View File

@@ -0,0 +1,34 @@
import jwt from 'jsonwebtoken'
import User from '../model/User'
export const getTokensFromDB = async (userId: number, clientId: string) => {
const user = await User.findOne({ id: userId })
if (!user) return
const currentTokenObj = user.tokens.find(
(tokenObj: any) => tokenObj.clientId === clientId
)
if (currentTokenObj) {
const accessToken = currentTokenObj.accessToken
const refreshToken = currentTokenObj.refreshToken
const verifiedAccessToken: any = jwt.verify(
accessToken,
process.secrets.ACCESS_TOKEN_SECRET
)
const verifiedRefreshToken: any = jwt.verify(
refreshToken,
process.secrets.REFRESH_TOKEN_SECRET
)
if (
verifiedAccessToken?.userId === userId &&
verifiedAccessToken?.clientId === clientId &&
verifiedRefreshToken?.userId === userId &&
verifiedRefreshToken?.clientId === clientId
)
return { accessToken, refreshToken }
}
}

View File

@@ -14,6 +14,7 @@ export * from './getDesktopFields'
export * from './getPreProgramVariables' export * from './getPreProgramVariables'
export * from './getRunTimeAndFilePath' export * from './getRunTimeAndFilePath'
export * from './getServerUrl' export * from './getServerUrl'
export * from './getTokensFromDB'
export * from './instantiateLogger' export * from './instantiateLogger'
export * from './isDebugOn' export * from './isDebugOn'
export * from './isPublicRoute' export * from './isPublicRoute'

View File

@@ -20,7 +20,7 @@
"description": "Operations about clients" "description": "Operations about clients"
}, },
{ {
"name": "CODE", "name": "Code",
"description": "Execution of code (various runtimes are supported)" "description": "Execution of code (various runtimes are supported)"
}, },
{ {

View File

@@ -1,3 +1,5 @@
import { RunTimeType } from '../../../context/appContext'
export const getLanguageFromExtension = (extension: string) => { export const getLanguageFromExtension = (extension: string) => {
if (extension === 'js') return 'javascript' if (extension === 'js') return 'javascript'
@@ -12,3 +14,26 @@ export const getSelection = (editor: any) => {
const selection = editor?.getModel().getValueInRange(editor?.getSelection()) const selection = editor?.getModel().getValueInRange(editor?.getSelection())
return selection ?? '' return selection ?? ''
} }
export const programPathInjection = (
code: string,
path: string,
runtime: RunTimeType
) => {
if (path) {
if (runtime === RunTimeType.JS) {
return `const _PROGRAM = '${path}';\n${code}`
}
if (runtime === RunTimeType.PY) {
return `_PROGRAM = '${path}';\n${code}`
}
if (runtime === RunTimeType.R) {
return `._PROGRAM = '${path}';\n${code}`
}
if (runtime === RunTimeType.SAS) {
return `%let _program = ${path};\n${code}`
}
}
return code
}

View File

@@ -10,7 +10,7 @@ import {
} from 'react' } from 'react'
import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor' import { DiffEditorDidMount, EditorDidMount, monaco } from 'react-monaco-editor'
import { SelectChangeEvent } from '@mui/material' import { SelectChangeEvent } from '@mui/material'
import { getSelection } from '../helper' import { getSelection, programPathInjection } from '../helper'
import { AppContext, RunTimeType } from '../../../../context/appContext' import { AppContext, RunTimeType } from '../../../../context/appContext'
import { AlertSeverityType } from '../../../../components/snackbar' import { AlertSeverityType } from '../../../../components/snackbar'
import { import {
@@ -151,7 +151,14 @@ const useEditor = ({
const runCode = (code: string) => { const runCode = (code: string) => {
setIsLoading(true) setIsLoading(true)
axios axios
.post(`/SASjsApi/code/execute`, { code, runTime: selectedRunTime }) .post(`/SASjsApi/code/execute`, {
code: programPathInjection(
code,
selectedFilePath,
selectedRunTime as RunTimeType
),
runTime: selectedRunTime
})
.then((res: any) => { .then((res: any) => {
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '') setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '') setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')