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

Compare commits

...

20 Commits

Author SHA1 Message Date
semantic-release-bot
48c1ada1b6 chore(release): 0.35.0 [skip ci]
# [0.35.0](https://github.com/sasjs/server/compare/v0.34.2...v0.35.0) (2023-05-03)

### Bug Fixes

* **editor:** fixed log/webout/print tabs ([d2de9dc](d2de9dc13e))
* **execute:** added atribute indicating stp api ([e78f87f](e78f87f5c0))
* **execute:** fixed adding print output ([9aaffce](9aaffce820))
* **execution:** removed empty webout from response ([6dd2f4f](6dd2f4f876))
* **webout:** fixed adding empty webout to response payload ([31df72a](31df72ad88))

### Features

* **editor:** parse print output in response payload ([eb42683](eb42683fff))
2023-05-03 09:34:56 +00:00
Allan Bowe
0532488b55 Merge pull request #360 from sasjs/issue-354
Support print destination natively
2023-05-03 10:31:06 +01:00
Yury Shkoda
d458b5bb81 chore: cleanup 2023-05-03 10:56:17 +03:00
Yury Shkoda
958ab9cad2 chore(execution): add includePrintOutput to ExecuteFileParams 2023-05-03 10:46:21 +03:00
Yury Shkoda
78ceed13e1 docs(code): updated execute endpoint info 2023-05-02 16:01:00 +03:00
Yury Shkoda
a17814fc90 chore(stp): removed redundant argument 2023-05-02 15:53:13 +03:00
Yury Shkoda
9aaffce820 fix(execute): fixed adding print output 2023-05-02 15:49:44 +03:00
Yury Shkoda
e78f87f5c0 fix(execute): added atribute indicating stp api 2023-05-02 15:18:05 +03:00
Yury Shkoda
bd1b58086d docs: left a comment regarding payload parts 2023-05-02 12:10:17 +03:00
Yury Shkoda
9f521634d9 chore(webout): added comment 2023-05-02 11:30:55 +03:00
Yury Shkoda
a696168443 Merge branch 'main' of github.com:sasjs/server into issue-354 2023-05-02 11:17:41 +03:00
Yury Shkoda
31df72ad88 fix(webout): fixed adding empty webout to response payload 2023-05-02 11:17:12 +03:00
semantic-release-bot
d2239f75c2 chore(release): 0.34.2 [skip ci]
## [0.34.2](https://github.com/sasjs/server/compare/v0.34.1...v0.34.2) (2023-05-01)

### Bug Fixes

* use custom logic for handling sequence ids ([dba53de](dba53de646))
2023-05-01 15:32:32 +00:00
Allan Bowe
45428892cc Merge pull request #362 from sasjs/remove-mongoose-sequence
fix: use custom logic for handling sequence ids
2023-05-01 16:28:47 +01:00
ac27a9b894 chore: remove residue 2023-05-01 19:54:43 +05:00
dba53de646 fix: use custom logic for handling sequence ids 2023-05-01 19:28:51 +05:00
Yury Shkoda
eb42683fff feat(editor): parse print output in response payload 2023-05-01 08:18:49 +03:00
Yury Shkoda
d2de9dc13e fix(editor): fixed log/webout/print tabs 2023-05-01 07:28:23 +03:00
Yury Shkoda
6dd2f4f876 fix(execution): removed empty webout from response 2023-04-28 17:25:30 +03:00
Yury Shkoda
c0f38ba7c9 wip(print-output): added print output to response payload 2023-04-28 15:09:44 +03:00
16 changed files with 196 additions and 144 deletions

View File

@@ -1,3 +1,26 @@
# [0.35.0](https://github.com/sasjs/server/compare/v0.34.2...v0.35.0) (2023-05-03)
### Bug Fixes
* **editor:** fixed log/webout/print tabs ([d2de9dc](https://github.com/sasjs/server/commit/d2de9dc13ef2e980286dd03cca5e22cea443ed0c))
* **execute:** added atribute indicating stp api ([e78f87f](https://github.com/sasjs/server/commit/e78f87f5c00038ea11261dffb525ac8f1024e40b))
* **execute:** fixed adding print output ([9aaffce](https://github.com/sasjs/server/commit/9aaffce82051d81bf39adb69942bb321e9795141))
* **execution:** removed empty webout from response ([6dd2f4f](https://github.com/sasjs/server/commit/6dd2f4f87673336135bc7a6de0d2e143e192c025))
* **webout:** fixed adding empty webout to response payload ([31df72a](https://github.com/sasjs/server/commit/31df72ad88fe2c771d0ef8445d6db9dd147c40c9))
### Features
* **editor:** parse print output in response payload ([eb42683](https://github.com/sasjs/server/commit/eb42683fff701bd5b4d2b68760fe0c3ecad573dd))
## [0.34.2](https://github.com/sasjs/server/compare/v0.34.1...v0.34.2) (2023-05-01)
### Bug Fixes
* use custom logic for handling sequence ids ([dba53de](https://github.com/sasjs/server/commit/dba53de64664c9d8a40fe69de6281c53d1c73641))
## [0.34.1](https://github.com/sasjs/server/compare/v0.34.0...v0.34.1) (2023-04-28) ## [0.34.1](https://github.com/sasjs/server/compare/v0.34.0...v0.34.1) (2023-04-28)

View File

@@ -158,7 +158,7 @@ CORS=
WHITELIST= WHITELIST=
# HELMET Cross Origin Embedder Policy # HELMET Cross Origin Embedder Policy
# Sets the Cross-Origin-Embedder-Policy header to require-corp when `true` # Sets the Cross-Origin-Embedder-Policy header to require-corp when `true`
# options: [true|false] default: true # options: [true|false] default: true
# Docs: https://helmetjs.github.io/#reference (`crossOriginEmbedderPolicy`) # Docs: https://helmetjs.github.io/#reference (`crossOriginEmbedderPolicy`)
HELMET_COEP= HELMET_COEP=

82
api/package-lock.json generated
View File

@@ -21,7 +21,6 @@
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"ldapjs": "2.3.3", "ldapjs": "2.3.3",
"mongoose": "^6.0.12", "mongoose": "^6.0.12",
"mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"rate-limiter-flexible": "2.4.1", "rate-limiter-flexible": "2.4.1",
@@ -43,7 +42,6 @@
"@types/jest": "^26.0.24", "@types/jest": "^26.0.24",
"@types/jsonwebtoken": "^8.5.5", "@types/jsonwebtoken": "^8.5.5",
"@types/ldapjs": "^2.2.4", "@types/ldapjs": "^2.2.4",
"@types/mongoose-sequence": "^3.0.6",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^15.12.2", "@types/node": "^15.12.2",
@@ -3216,25 +3214,6 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true "dev": true
}, },
"node_modules/@types/mongoose": {
"version": "5.11.97",
"resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
"integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
"deprecated": "Mongoose publishes its own types, so you do not need to install this package.",
"dev": true,
"dependencies": {
"mongoose": "*"
}
},
"node_modules/@types/mongoose-sequence": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/mongoose-sequence/-/mongoose-sequence-3.0.6.tgz",
"integrity": "sha512-S6DD4rSlSnUI9BQvR/ACtekpylSIm0pEKayG9NqOlkUo3Q/AZLBmdi0IozSGPQ8JcB2ZSm81nLdZPhTqyOqrQg==",
"dev": true,
"dependencies": {
"@types/mongoose": "^5.10.5"
}
},
"node_modules/@types/morgan": { "node_modules/@types/morgan": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@@ -3672,14 +3651,6 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dependencies": {
"lodash": "^4.17.14"
}
},
"node_modules/async-mutex": { "node_modules/async-mutex": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
@@ -8125,7 +8096,8 @@
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
}, },
"node_modules/lodash.escaperegexp": { "node_modules/lodash.escaperegexp": {
"version": "4.1.2", "version": "4.1.2",
@@ -8583,18 +8555,6 @@
"url": "https://opencollective.com/mongoose" "url": "https://opencollective.com/mongoose"
} }
}, },
"node_modules/mongoose-sequence": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/mongoose-sequence/-/mongoose-sequence-5.3.1.tgz",
"integrity": "sha512-kQB1ctCdAQT8YdQzoHV0CpBRsO4RNVy03SOkzM6TQKBbGBs1ZgVS4UlKsuvBPaiPt9q5tKgQZvorGJ1awbHDqA==",
"dependencies": {
"async": "^2.5.0",
"lodash": "^4.17.20"
},
"peerDependencies": {
"mongoose": ">=4"
}
},
"node_modules/mongoose/node_modules/ms": { "node_modules/mongoose/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -14014,24 +13974,6 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true "dev": true
}, },
"@types/mongoose": {
"version": "5.11.97",
"resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
"integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
"dev": true,
"requires": {
"mongoose": "*"
}
},
"@types/mongoose-sequence": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/mongoose-sequence/-/mongoose-sequence-3.0.6.tgz",
"integrity": "sha512-S6DD4rSlSnUI9BQvR/ACtekpylSIm0pEKayG9NqOlkUo3Q/AZLBmdi0IozSGPQ8JcB2ZSm81nLdZPhTqyOqrQg==",
"dev": true,
"requires": {
"@types/mongoose": "^5.10.5"
}
},
"@types/morgan": { "@types/morgan": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@@ -14409,14 +14351,6 @@
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
}, },
"async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"requires": {
"lodash": "^4.17.14"
}
},
"async-mutex": { "async-mutex": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
@@ -17760,7 +17694,8 @@
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
}, },
"lodash.escaperegexp": { "lodash.escaperegexp": {
"version": "4.1.2", "version": "4.1.2",
@@ -18126,15 +18061,6 @@
} }
} }
}, },
"mongoose-sequence": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/mongoose-sequence/-/mongoose-sequence-5.3.1.tgz",
"integrity": "sha512-kQB1ctCdAQT8YdQzoHV0CpBRsO4RNVy03SOkzM6TQKBbGBs1ZgVS4UlKsuvBPaiPt9q5tKgQZvorGJ1awbHDqA==",
"requires": {
"async": "^2.5.0",
"lodash": "^4.17.20"
}
},
"morgan": { "morgan": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",

View File

@@ -61,7 +61,6 @@
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"ldapjs": "2.3.3", "ldapjs": "2.3.3",
"mongoose": "^6.0.12", "mongoose": "^6.0.12",
"mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"rate-limiter-flexible": "2.4.1", "rate-limiter-flexible": "2.4.1",
@@ -80,7 +79,6 @@
"@types/jest": "^26.0.24", "@types/jest": "^26.0.24",
"@types/jsonwebtoken": "^8.5.5", "@types/jsonwebtoken": "^8.5.5",
"@types/ldapjs": "^2.2.4", "@types/ldapjs": "^2.2.4",
"@types/mongoose-sequence": "^3.0.6",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^15.12.2", "@types/node": "^15.12.2",

View File

@@ -772,7 +772,7 @@ paths:
examples: examples:
'Example 1': 'Example 1':
value: [{clientId: someClientID1234, clientSecret: someRandomCryptoString, accessTokenExpiration: 86400}, {clientId: someOtherClientID, clientSecret: someOtherRandomCryptoString, accessTokenExpiration: 86400}] value: [{clientId: someClientID1234, clientSecret: someRandomCryptoString, accessTokenExpiration: 86400}, {clientId: someOtherClientID, clientSecret: someOtherRandomCryptoString, accessTokenExpiration: 86400}]
summary: 'Admin only task. Returns the list of all the clients *' summary: 'Admin only task. Returns the list of all the clients'
tags: tags:
- Client - Client
security: security:

View File

@@ -28,7 +28,14 @@ interface ExecuteCodePayload {
export class CodeController { export class CodeController {
/** /**
* Execute Code on the Specified Runtime * Execute Code on the Specified Runtime
* @summary Run Code and Return Webout Content and Log * @summary Run Code and Return Webout Content, Log and Print output
* The order of returned parts of the payload is:
* 1. Webout (if present)
* 2. Logs UUID (used as separator)
* 3. Log
* 4. Logs UUID (used as separator)
* 5. Print (if present and if the runtime is SAS)
* Please see @sasjs/server/api/src/controllers/internal/Execution.ts for more information
*/ */
@Post('/execute') @Post('/execute')
public async executeCode( public async executeCode(
@@ -55,7 +62,8 @@ const executeCode = async (
preProgramVariables: getPreProgramVariables(req), preProgramVariables: getPreProgramVariables(req),
vars: { ...req.query, _debug: 131 }, vars: { ...req.query, _debug: 131 },
otherArgs: { userAutoExec }, otherArgs: { userAutoExec },
runTime: runTime runTime: runTime,
includePrintOutput: true
}) })
return result return result

View File

@@ -33,6 +33,7 @@ interface ExecuteFileParams {
interface ExecuteProgramParams extends Omit<ExecuteFileParams, 'programPath'> { interface ExecuteProgramParams extends Omit<ExecuteFileParams, 'programPath'> {
program: string program: string
includePrintOutput?: boolean
} }
export class ExecutionController { export class ExecutionController {
@@ -67,7 +68,8 @@ export class ExecutionController {
otherArgs, otherArgs,
session: sessionByFileUpload, session: sessionByFileUpload,
runTime, runTime,
forceStringResult forceStringResult,
includePrintOutput
}: ExecuteProgramParams): Promise<ExecuteReturnRaw> { }: ExecuteProgramParams): Promise<ExecuteReturnRaw> {
const sessionController = getSessionController(runTime) const sessionController = getSessionController(runTime)
@@ -78,7 +80,6 @@ export class ExecutionController {
const logPath = path.join(session.path, 'log.log') const logPath = path.join(session.path, 'log.log')
const headersPath = path.join(session.path, 'stpsrv_header.txt') const headersPath = path.join(session.path, 'stpsrv_header.txt')
const weboutPath = path.join(session.path, 'webout.txt') const weboutPath = path.join(session.path, 'webout.txt')
const tokenFile = path.join(session.path, 'reqHeaders.txt') const tokenFile = path.join(session.path, 'reqHeaders.txt')
@@ -122,12 +123,30 @@ export class ExecutionController {
// it should be deleted by scheduleSessionDestroy // it should be deleted by scheduleSessionDestroy
session.inUse = false session.inUse = false
const resultParts = []
// INFO: webout can be a Buffer, that is why it's length should be checked to determine if it is empty
if (webout && webout.length !== 0) resultParts.push(webout)
resultParts.push(process.logsUUID)
resultParts.push(log)
if (includePrintOutput && runTime === RunTimeType.SAS) {
const printOutputPath = path.join(session.path, 'output.lst')
const printOutput = (await fileExists(printOutputPath))
? await readFile(printOutputPath)
: ''
if (printOutput) {
resultParts.push(process.logsUUID)
resultParts.push(printOutput)
}
}
return { return {
httpHeaders, httpHeaders,
result: result:
isDebugOn(vars) || session.crashed isDebugOn(vars) || session.crashed ? resultParts.join(`\n`) : webout
? `${webout}\n${process.logsUUID}\n${log}`
: webout
} }
} }

View File

@@ -3,8 +3,6 @@ import { Request, Security, Route, Tags, Post, Body, Get, Query } from 'tsoa'
import { ExecutionController, ExecutionVars } from './internal' import { ExecutionController, ExecutionVars } from './internal'
import { import {
getPreProgramVariables, getPreProgramVariables,
HTTPHeaders,
LogLine,
makeFilesNamesMap, makeFilesNamesMap,
getRunTimeAndFilePath getRunTimeAndFilePath
} from '../utils' } from '../utils'
@@ -39,6 +37,7 @@ export class STPController {
@Query() _program: string @Query() _program: string
): Promise<string | Buffer> { ): Promise<string | Buffer> {
const vars = request.query as ExecutionVars const vars = request.query as ExecutionVars
return execute(request, _program, vars) return execute(request, _program, vars)
} }

15
api/src/model/Counter.ts Normal file
View File

@@ -0,0 +1,15 @@
import mongoose, { Schema } from 'mongoose'
const CounterSchema = new Schema({
id: {
type: String,
required: true,
unique: true
},
seq: {
type: Number,
required: true
}
})
export default mongoose.model('Counter', CounterSchema)

View File

@@ -1,8 +1,7 @@
import mongoose, { Schema, model, Document, Model } from 'mongoose' import { Schema, model, Document, Model } from 'mongoose'
import { GroupDetailsResponse } from '../controllers' import { GroupDetailsResponse } from '../controllers'
import User, { IUser } from './User' import User, { IUser } from './User'
import { AuthProviderType } from '../utils' import { AuthProviderType, getSequenceNextValue } from '../utils'
const AutoIncrement = require('mongoose-sequence')(mongoose)
export const PUBLIC_GROUP_NAME = 'Public' export const PUBLIC_GROUP_NAME = 'Public'
@@ -44,6 +43,10 @@ const groupSchema = new Schema<IGroupDocument>({
required: true, required: true,
unique: true unique: true
}, },
groupId: {
type: Number,
unique: true
},
description: { description: {
type: String, type: String,
default: 'Group description.' default: 'Group description.'
@@ -59,9 +62,13 @@ const groupSchema = new Schema<IGroupDocument>({
users: [{ type: Schema.Types.ObjectId, ref: 'User' }] users: [{ type: Schema.Types.ObjectId, ref: 'User' }]
}) })
groupSchema.plugin(AutoIncrement, { inc_field: 'groupId' })
// Hooks // Hooks
groupSchema.pre('save', async function () {
if (this.isNew) {
this.groupId = await getSequenceNextValue('groupId')
}
})
groupSchema.post('save', function (group: IGroup, next: Function) { groupSchema.post('save', function (group: IGroup, next: Function) {
group.populate('users', 'id username displayName -_id').then(function () { group.populate('users', 'id username displayName -_id').then(function () {
next() next()

View File

@@ -1,6 +1,6 @@
import mongoose, { Schema, model, Document, Model } from 'mongoose' import { Schema, model, Document, Model } from 'mongoose'
const AutoIncrement = require('mongoose-sequence')(mongoose)
import { PermissionDetailsResponse } from '../controllers' import { PermissionDetailsResponse } from '../controllers'
import { getSequenceNextValue } from '../utils'
interface GetPermissionBy { interface GetPermissionBy {
user?: Schema.Types.ObjectId user?: Schema.Types.ObjectId
@@ -23,6 +23,10 @@ interface IPermissionModel extends Model<IPermission> {
} }
const permissionSchema = new Schema<IPermissionDocument>({ const permissionSchema = new Schema<IPermissionDocument>({
permissionId: {
type: Number,
unique: true
},
path: { path: {
type: String, type: String,
required: true required: true
@@ -39,7 +43,12 @@ const permissionSchema = new Schema<IPermissionDocument>({
group: { type: Schema.Types.ObjectId, ref: 'Group' } group: { type: Schema.Types.ObjectId, ref: 'Group' }
}) })
permissionSchema.plugin(AutoIncrement, { inc_field: 'permissionId' }) // Hooks
permissionSchema.pre('save', async function () {
if (this.isNew) {
this.permissionId = await getSequenceNextValue('permissionId')
}
})
// Static Methods // Static Methods
permissionSchema.static('get', async function (getBy: GetPermissionBy): Promise< permissionSchema.static('get', async function (getBy: GetPermissionBy): Promise<

View File

@@ -1,7 +1,6 @@
import mongoose, { Schema, model, Document, Model } from 'mongoose' import { Schema, model, Document, Model } from 'mongoose'
const AutoIncrement = require('mongoose-sequence')(mongoose)
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { AuthProviderType } from '../utils' import { AuthProviderType, getSequenceNextValue } from '../utils'
export interface UserPayload { export interface UserPayload {
/** /**
@@ -66,6 +65,10 @@ const userSchema = new Schema<IUserDocument>({
required: true, required: true,
unique: true unique: true
}, },
id: {
type: Number,
unique: true
},
password: { password: {
type: String, type: String,
required: true required: true
@@ -107,7 +110,15 @@ const userSchema = new Schema<IUserDocument>({
} }
] ]
}) })
userSchema.plugin(AutoIncrement, { inc_field: 'id' })
// Hooks
userSchema.pre('save', async function (next) {
if (this.isNew) {
this.id = await getSequenceNextValue('id')
}
next()
})
// Static Methods // Static Methods
userSchema.static('hashPassword', (password: string): string => { userSchema.static('hashPassword', (password: string): string => {

View File

@@ -0,0 +1,15 @@
import Counter from '../model/Counter'
export const getSequenceNextValue = async (seqName: string) => {
const seqDoc = await Counter.findOne({ id: seqName })
if (!seqDoc) {
await Counter.create({ id: seqName, seq: 1 })
return 1
}
seqDoc.seq += 1
await seqDoc.save()
return seqDoc.seq
}

View File

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

View File

@@ -62,6 +62,7 @@ const SASjsEditor = ({
selectedRunTime, selectedRunTime,
showDiff, showDiff,
webout, webout,
printOutput,
Dialog, Dialog,
handleChangeRunTime, handleChangeRunTime,
handleDiffEditorDidMount, handleDiffEditorDidMount,
@@ -153,30 +154,35 @@ const SASjsEditor = ({
> >
<TabList onChange={handleTabChange} centered> <TabList onChange={handleTabChange} centered>
<StyledTab label="Code" value="code" /> <StyledTab label="Code" value="code" />
<StyledTab {log && (
label={logWithErrorsOrWarnings ? '' : 'log'} <StyledTab
value="log" label={logWithErrorsOrWarnings ? '' : 'log'}
icon={ value="log"
logWithErrorsOrWarnings ? ( icon={
<LogTabWithIcons log={log as LogObject} /> logWithErrorsOrWarnings ? (
) : ( <LogTabWithIcons log={log as LogObject} />
'' ) : (
) ''
} )
onClick={() => { }
const logWrapper = document.querySelector(`#logWrapper`) onClick={() => {
const logWrapper = document.querySelector(`#logWrapper`)
if (logWrapper) logWrapper.scrollTop = 0 if (logWrapper) logWrapper.scrollTop = 0
}} }}
/> />
<StyledTab )}
label={ {webout && (
<Tooltip title="Displays content from the _webout fileref"> <StyledTab
<Typography>Webout</Typography> label={
</Tooltip> <Tooltip title="Displays content from the _webout fileref">
} <Typography>Webout</Typography>
value="webout" </Tooltip>
/> }
value="webout"
/>
)}
{printOutput && <StyledTab label="print" value="printOutput" />}
</TabList> </TabList>
</Box> </Box>
@@ -222,11 +228,20 @@ const SASjsEditor = ({
<LogComponent log={log} selectedRunTime={selectedRunTime} /> <LogComponent log={log} selectedRunTime={selectedRunTime} />
)} )}
</StyledTabPanel> </StyledTabPanel>
<StyledTabPanel value="webout"> {webout && (
<div> <StyledTabPanel value="webout">
<pre>{webout}</pre> <div>
</div> <pre>{webout}</pre>
</StyledTabPanel> </div>
</StyledTabPanel>
)}
{printOutput && (
<StyledTabPanel value="printOutput">
<div>
<pre>{printOutput}</pre>
</div>
</StyledTabPanel>
)}
</TabContext> </TabContext>
)} )}
<Dialog /> <Dialog />

View File

@@ -39,14 +39,14 @@ const useEditor = ({
const { Snackbar, setOpenSnackbar, setSnackbarMessage, setSnackbarSeverity } = const { Snackbar, setOpenSnackbar, setSnackbarMessage, setSnackbarSeverity } =
useSnackbar() useSnackbar()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [prevFileContent, setPrevFileContent] = useStateWithCallback('') const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
const [fileContent, setFileContent] = useState('') const [fileContent, setFileContent] = useState('')
const [log, setLog] = useState<LogObject | string>() const [log, setLog] = useState<LogObject | string>()
const [webout, setWebout] = useState('') const [webout, setWebout] = useState<string>()
const [printOutput, setPrintOutput] = useState<string>()
const [runTimes, setRunTimes] = useState<string[]>([]) const [runTimes, setRunTimes] = useState<string[]>([])
const [selectedRunTime, setSelectedRunTime] = useState<RunTimeType | string>( const [selectedRunTime, setSelectedRunTime] = useState<RunTimeType>(
'' RunTimeType.SAS
) )
const [selectedFileExtension, setSelectedFileExtension] = useState('') const [selectedFileExtension, setSelectedFileExtension] = useState('')
const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false) const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false)
@@ -169,25 +169,30 @@ const useEditor = ({
), ),
runTime: selectedRunTime runTime: selectedRunTime
}) })
.then((res: any) => { .then((res: { data: string }) => {
if (selectedRunTime === RunTimeType.SAS) { // INFO: the order of payload parts is set in @sasjs/server/api/src/controllers/internal/Execution.ts
const { errors, warnings, logLines } = parseErrorsAndWarnings( const resDataSplitted = res.data.split(SASJS_LOGS_SEPARATOR)
res.data.split(SASJS_LOGS_SEPARATOR)[1] const webout = resDataSplitted[0]
) const log = resDataSplitted[1]
const printOutput = resDataSplitted[2]
const log: LogObject = { if (selectedRunTime === RunTimeType.SAS) {
const { errors, warnings, logLines } = parseErrorsAndWarnings(log)
const logObject: LogObject = {
body: logLines.join(`\n`), body: logLines.join(`\n`),
errors, errors,
warnings, warnings,
linesCount: logLines.length linesCount: logLines.length
} }
setLog(log) setLog(logObject)
} else { } else {
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '') setLog(log)
} }
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '') setWebout(webout)
setPrintOutput(printOutput)
setTab('log') setTab('log')
// Scroll to bottom of log // Scroll to bottom of log
@@ -335,6 +340,7 @@ const useEditor = ({
selectedRunTime, selectedRunTime,
showDiff, showDiff,
webout, webout,
printOutput,
Dialog, Dialog,
handleChangeRunTime, handleChangeRunTime,
handleDiffEditorDidMount, handleDiffEditorDidMount,