mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70655e74d3 | ||
|
|
cb82fea0d8 | ||
| b9a596616d | |||
|
|
72a5393be3 | ||
|
|
769a840e9f | ||
| 730c7c52ac | |||
| ee2db276bb | |||
|
|
d0a24aacb6 | ||
|
|
57dfdf89a4 | ||
|
|
393b5eaf99 | ||
|
|
7477326b22 | ||
|
|
76bf84316e | ||
|
|
e355276e44 | ||
|
|
a3a9e3bd9f | ||
|
|
9f06080348 | ||
|
|
4bbf9cfdb3 | ||
|
|
e8e71fcde9 | ||
|
|
e63271a67a | ||
| 7633608318 | |||
|
|
e67d27d264 | ||
|
|
53033ccc96 | ||
|
|
6131ed1cbe | ||
|
|
5d624e3399 | ||
| ee17d37aa1 | |||
| 572fe22d50 | |||
| 091268bf58 | |||
| 71a4a48443 | |||
| 3b188cd724 | |||
| eeba2328c0 | |||
| 0a0ba2cca5 | |||
|
|
a5ee2f2923 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,6 +5,8 @@ node_modules/
|
||||
.env*
|
||||
sas/
|
||||
sasjs_root/
|
||||
api/mocks/custom/*
|
||||
!api/mocks/custom/.keep
|
||||
tmp/
|
||||
build/
|
||||
sasjsbuild/
|
||||
|
||||
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,3 +1,52 @@
|
||||
# [0.19.0](https://github.com/sasjs/server/compare/v0.18.0...v0.19.0) (2022-09-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added mocking endpoints ([0a0ba2c](https://github.com/sasjs/server/commit/0a0ba2cca5db867de46fb2486d856a84ec68d3b4))
|
||||
|
||||
# [0.18.0](https://github.com/sasjs/server/compare/v0.17.5...v0.18.0) (2022-09-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add option for program launch in context menu ([ee2db27](https://github.com/sasjs/server/commit/ee2db276bb0bbd522f758e0b66f7e7b2f4afd9d5))
|
||||
|
||||
## [0.17.5](https://github.com/sasjs/server/compare/v0.17.4...v0.17.5) (2022-09-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* SASINITIALFOLDER split over 2 params, closes [#271](https://github.com/sasjs/server/issues/271) ([393b5ea](https://github.com/sasjs/server/commit/393b5eaf990049c39eecf2b9e8dd21a001b6e298))
|
||||
|
||||
## [0.17.4](https://github.com/sasjs/server/compare/v0.17.3...v0.17.4) (2022-09-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* invalid JS logic ([9f06080](https://github.com/sasjs/server/commit/9f06080348aed076f8188a26fb4890d38a5a3510))
|
||||
|
||||
## [0.17.3](https://github.com/sasjs/server/compare/v0.17.2...v0.17.3) (2022-09-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* making SASINITIALFOLDER option windows only. Closes [#267](https://github.com/sasjs/server/issues/267) ([e63271a](https://github.com/sasjs/server/commit/e63271a67a0deb3059a5f2bec1854efee5a6e5a5))
|
||||
|
||||
## [0.17.2](https://github.com/sasjs/server/compare/v0.17.1...v0.17.2) (2022-08-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* addition of SASINITIALFOLDER startup option. Closes [#260](https://github.com/sasjs/server/issues/260) ([a5ee2f2](https://github.com/sasjs/server/commit/a5ee2f292384f90e9d95d003d652311c0d91a7a7))
|
||||
|
||||
## [0.17.1](https://github.com/sasjs/server/compare/v0.17.0...v0.17.1) (2022-08-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typo mistake ([ee17d37](https://github.com/sasjs/server/commit/ee17d37aa188b0ca43cea0e89d6cd1a566b765cb))
|
||||
|
||||
# [0.17.0](https://github.com/sasjs/server/compare/v0.16.1...v0.17.0) (2022-08-25)
|
||||
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ PROTOCOL=
|
||||
# default: 5000
|
||||
PORT=
|
||||
|
||||
# options: [sas9|sasviya]
|
||||
# If not present, mocking function is disabled
|
||||
MOCK_SERVERTYPE=
|
||||
|
||||
#
|
||||
## Additional SAS Options
|
||||
|
||||
0
api/mocks/custom/.keep
Normal file
0
api/mocks/custom/.keep
Normal file
1
api/mocks/generic/sas9/logged-in
Normal file
1
api/mocks/generic/sas9/logged-in
Normal file
@@ -0,0 +1 @@
|
||||
You have signed in.
|
||||
1
api/mocks/generic/sas9/logged-out
Normal file
1
api/mocks/generic/sas9/logged-out
Normal file
@@ -0,0 +1 @@
|
||||
You have signed out.
|
||||
30
api/mocks/generic/sas9/login
Normal file
30
api/mocks/generic/sas9/login
Normal file
@@ -0,0 +1,30 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" dir="ltr" class="bg">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="initial-scale=1" />
|
||||
</head>
|
||||
|
||||
|
||||
<div class="content">
|
||||
<form id="credentials" class="minimal" action="/SASLogon/login?service=http%3A%2F%2Flocalhost:5004%2FSASStoredProcess%2Fj_spring_cas_security_check" method="post">
|
||||
<!--form container-->
|
||||
<input type="hidden" name="lt" value="LT-8-WGkt9EXwICBihaVbxGc92opjufTK1D" aria-hidden="true" />
|
||||
<input type="hidden" name="execution" value="e2s1" aria-hidden="true" />
|
||||
<input type="hidden" name="_eventId" value="submit" aria-hidden="true" />
|
||||
|
||||
<span class="userid">
|
||||
|
||||
<input id="username" name="username" tabindex="3" aria-labelledby="username1 message1 message2 message3" name="username" placeholder="User ID" type="text" autofocus="true" value="" maxlength="500" autocomplete="off" />
|
||||
</span>
|
||||
<span class="password">
|
||||
|
||||
<input id="password" name="password" tabindex="4" name="password" placeholder="Password" type="password" value="" maxlength="500" autocomplete="off" />
|
||||
</span>
|
||||
|
||||
<button type="submit" class="btn-submit" title="Sign In" tabindex="5" onClick="this.disabled=true;setSubmitUrl(this.form);this.form.submit();return false;">Sign In</button>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</html>
|
||||
1
api/mocks/generic/sas9/sas-stored-process
Normal file
1
api/mocks/generic/sas9/sas-stored-process
Normal file
@@ -0,0 +1 @@
|
||||
"title": "Log Off SAS Demo User"
|
||||
@@ -85,8 +85,10 @@ components:
|
||||
type: string
|
||||
_webout:
|
||||
anyOf:
|
||||
- type: string
|
||||
- $ref: '#/components/schemas/IRecordOfAny'
|
||||
-
|
||||
type: string
|
||||
-
|
||||
$ref: '#/components/schemas/IRecordOfAny'
|
||||
log:
|
||||
items:
|
||||
$ref: '#/components/schemas/LogLine'
|
||||
@@ -106,6 +108,7 @@ components:
|
||||
enum:
|
||||
- sas
|
||||
- js
|
||||
- py
|
||||
type: string
|
||||
ExecuteCodePayload:
|
||||
properties:
|
||||
@@ -135,9 +138,12 @@ components:
|
||||
members:
|
||||
items:
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/FolderMember'
|
||||
- $ref: '#/components/schemas/ServiceMember'
|
||||
- $ref: '#/components/schemas/FileMember'
|
||||
-
|
||||
$ref: '#/components/schemas/FolderMember'
|
||||
-
|
||||
$ref: '#/components/schemas/ServiceMember'
|
||||
-
|
||||
$ref: '#/components/schemas/FileMember'
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
@@ -186,9 +192,12 @@ components:
|
||||
members:
|
||||
items:
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/FolderMember'
|
||||
- $ref: '#/components/schemas/ServiceMember'
|
||||
- $ref: '#/components/schemas/FileMember'
|
||||
-
|
||||
$ref: '#/components/schemas/FolderMember'
|
||||
-
|
||||
$ref: '#/components/schemas/ServiceMember'
|
||||
-
|
||||
$ref: '#/components/schemas/FileMember'
|
||||
type: array
|
||||
required:
|
||||
- members
|
||||
@@ -374,7 +383,7 @@ components:
|
||||
autoExec:
|
||||
type: string
|
||||
description: 'User-specific auto-exec code'
|
||||
example: ''
|
||||
example: ""
|
||||
required:
|
||||
- displayName
|
||||
- username
|
||||
@@ -423,27 +432,13 @@ components:
|
||||
- description
|
||||
type: object
|
||||
additionalProperties: false
|
||||
_LeanDocument__LeanDocument_T__:
|
||||
FlattenMaps_T_:
|
||||
properties: {}
|
||||
type: object
|
||||
Pick__LeanDocument_T_.Exclude_keyof_LeanDocument_T_.Exclude_keyofDocument._id-or-id-or-__v_-or-%24isSingleNested__:
|
||||
properties:
|
||||
_id:
|
||||
$ref: '#/components/schemas/_LeanDocument__LeanDocument_T__'
|
||||
description: 'This documents _id.'
|
||||
__v:
|
||||
description: 'This documents __v.'
|
||||
id:
|
||||
description: 'The string version of this documents _id.'
|
||||
type: object
|
||||
description: 'From T, pick a set of properties whose keys are in the union K'
|
||||
Omit__LeanDocument_this_.Exclude_keyofDocument._id-or-id-or-__v_-or-%24isSingleNested_:
|
||||
$ref: '#/components/schemas/Pick__LeanDocument_T_.Exclude_keyof_LeanDocument_T_.Exclude_keyofDocument._id-or-id-or-__v_-or-%24isSingleNested__'
|
||||
description: 'Construct a type with the properties of T except for those in type K.'
|
||||
LeanDocument_this_:
|
||||
$ref: '#/components/schemas/Omit__LeanDocument_this_.Exclude_keyofDocument._id-or-id-or-__v_-or-%24isSingleNested_'
|
||||
IGroup:
|
||||
$ref: '#/components/schemas/LeanDocument_this_'
|
||||
$ref: '#/components/schemas/FlattenMaps_T_'
|
||||
ObjectId:
|
||||
type: string
|
||||
InfoResponse:
|
||||
properties:
|
||||
mode:
|
||||
@@ -623,11 +618,7 @@ paths:
|
||||
$ref: '#/components/schemas/TokenResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
accessToken: someRandomCryptoString,
|
||||
refreshToken: someRandomCryptoString
|
||||
}
|
||||
value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString}
|
||||
summary: 'Accepts client/auth code and returns access/refresh tokens'
|
||||
tags:
|
||||
- Auth
|
||||
@@ -651,16 +642,13 @@ paths:
|
||||
$ref: '#/components/schemas/TokenResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
accessToken: someRandomCryptoString,
|
||||
refreshToken: someRandomCryptoString
|
||||
}
|
||||
value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString}
|
||||
summary: 'Returns new access/refresh tokens'
|
||||
tags:
|
||||
- Auth
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
/SASjsApi/auth/logout:
|
||||
post:
|
||||
@@ -672,7 +660,8 @@ paths:
|
||||
tags:
|
||||
- Auth
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
/SASjsApi/client:
|
||||
post:
|
||||
@@ -686,16 +675,13 @@ paths:
|
||||
$ref: '#/components/schemas/ClientPayload'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
clientId: someFormattedClientID1234,
|
||||
clientSecret: someRandomCryptoString
|
||||
}
|
||||
value: {clientId: someFormattedClientID1234, clientSecret: someRandomCryptoString}
|
||||
summary: 'Create client with the following attributes: ClientId, ClientSecret. Admin only task.'
|
||||
tags:
|
||||
- Client
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -718,7 +704,8 @@ paths:
|
||||
tags:
|
||||
- CODE
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -738,11 +725,7 @@ paths:
|
||||
$ref: '#/components/schemas/DeployResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
status: success,
|
||||
message: 'Files deployed successfully to @sasjs/server.'
|
||||
}
|
||||
value: {status: success, message: 'Files deployed successfully to @sasjs/server.'}
|
||||
'400':
|
||||
description: 'Invalid Format'
|
||||
content:
|
||||
@@ -751,11 +734,7 @@ paths:
|
||||
$ref: '#/components/schemas/DeployResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
status: failure,
|
||||
message: 'Provided not supported data format.'
|
||||
}
|
||||
value: {status: failure, message: 'Provided not supported data format.'}
|
||||
'500':
|
||||
description: 'Execution Error'
|
||||
content:
|
||||
@@ -769,7 +748,8 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -789,11 +769,7 @@ paths:
|
||||
$ref: '#/components/schemas/DeployResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
status: success,
|
||||
message: 'Files deployed successfully to @sasjs/server.'
|
||||
}
|
||||
value: {status: success, message: 'Files deployed successfully to @sasjs/server.'}
|
||||
'400':
|
||||
description: 'Invalid Format'
|
||||
content:
|
||||
@@ -802,11 +778,7 @@ paths:
|
||||
$ref: '#/components/schemas/DeployResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
status: failure,
|
||||
message: 'Provided not supported data format.'
|
||||
}
|
||||
value: {status: failure, message: 'Provided not supported data format.'}
|
||||
'500':
|
||||
description: 'Execution Error'
|
||||
content:
|
||||
@@ -821,7 +793,8 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -845,9 +818,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- in: query
|
||||
-
|
||||
in: query
|
||||
name: _filePath
|
||||
required: true
|
||||
schema:
|
||||
@@ -870,9 +845,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- in: query
|
||||
-
|
||||
in: query
|
||||
name: _filePath
|
||||
required: true
|
||||
schema:
|
||||
@@ -904,9 +881,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: 'Location of file'
|
||||
-
|
||||
description: 'Location of file'
|
||||
in: query
|
||||
name: _filePath
|
||||
required: false
|
||||
@@ -940,7 +919,7 @@ paths:
|
||||
'Example 1':
|
||||
value: {status: success}
|
||||
'403':
|
||||
description: ''
|
||||
description: ""
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -953,9 +932,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: 'Location of SAS program'
|
||||
-
|
||||
description: 'Location of SAS program'
|
||||
in: query
|
||||
name: _filePath
|
||||
required: false
|
||||
@@ -996,9 +977,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- in: query
|
||||
-
|
||||
in: query
|
||||
name: _folderPath
|
||||
required: false
|
||||
schema:
|
||||
@@ -1021,9 +1004,11 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- in: query
|
||||
-
|
||||
in: query
|
||||
name: _folderPath
|
||||
required: true
|
||||
schema:
|
||||
@@ -1049,13 +1034,13 @@ paths:
|
||||
$ref: '#/components/schemas/FileFolderResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{ status: failure, message: 'Add folder request failed.' }
|
||||
value: {status: failure, message: 'Add folder request failed.'}
|
||||
summary: 'Create an empty folder in SASjs Drive'
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -1089,7 +1074,8 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -1111,7 +1097,8 @@ paths:
|
||||
tags:
|
||||
- Drive
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
/SASjsApi/user:
|
||||
get:
|
||||
@@ -1127,26 +1114,13 @@ paths:
|
||||
type: array
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
[
|
||||
{
|
||||
id: 123,
|
||||
username: johnusername,
|
||||
displayName: John,
|
||||
isAdmin: false
|
||||
},
|
||||
{
|
||||
id: 456,
|
||||
username: starkusername,
|
||||
displayName: Stark,
|
||||
isAdmin: true
|
||||
}
|
||||
]
|
||||
value: [{id: 123, username: johnusername, displayName: John, isAdmin: false}, {id: 456, username: starkusername, displayName: Stark, isAdmin: true}]
|
||||
summary: 'Get list of all users (username, displayname). All users can request this.'
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
post:
|
||||
operationId: CreateUser
|
||||
@@ -1159,19 +1133,13 @@ paths:
|
||||
$ref: '#/components/schemas/UserDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
id: 1234,
|
||||
displayName: 'John Snow',
|
||||
username: johnSnow01,
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||
summary: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.'
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -1194,9 +1162,11 @@ paths:
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The User's username"
|
||||
-
|
||||
description: 'The User''s username'
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
@@ -1214,21 +1184,16 @@ paths:
|
||||
$ref: '#/components/schemas/UserDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
id: 1234,
|
||||
displayName: 'John Snow',
|
||||
username: johnSnow01,
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The User's username"
|
||||
-
|
||||
description: 'The User''s username'
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
@@ -1250,9 +1215,11 @@ paths:
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The User's username"
|
||||
-
|
||||
description: 'The User''s username'
|
||||
in: path
|
||||
name: username
|
||||
required: true
|
||||
@@ -1283,9 +1250,11 @@ paths:
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: userId
|
||||
required: true
|
||||
@@ -1304,21 +1273,16 @@ paths:
|
||||
$ref: '#/components/schemas/UserDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
id: 1234,
|
||||
displayName: 'John Snow',
|
||||
username: johnSnow01,
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true}
|
||||
summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.'
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: userId
|
||||
required: true
|
||||
@@ -1341,9 +1305,11 @@ paths:
|
||||
tags:
|
||||
- User
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: userId
|
||||
required: true
|
||||
@@ -1374,19 +1340,13 @@ paths:
|
||||
type: array
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
[
|
||||
{
|
||||
groupId: 123,
|
||||
name: DCGroup,
|
||||
description: 'This group represents Data Controller Users'
|
||||
}
|
||||
]
|
||||
value: [{groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users'}]
|
||||
summary: 'Get list of all groups (groupName and groupDescription). All users can request this.'
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
post:
|
||||
operationId: CreateGroup
|
||||
@@ -1399,19 +1359,13 @@ paths:
|
||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
groupId: 123,
|
||||
name: DCGroup,
|
||||
description: 'This group represents Data Controller Users',
|
||||
isActive: true,
|
||||
users: []
|
||||
}
|
||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||
summary: 'Create a new group. Admin only.'
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -1433,9 +1387,11 @@ paths:
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The group's name"
|
||||
-
|
||||
description: 'The group''s name'
|
||||
in: path
|
||||
name: name
|
||||
required: true
|
||||
@@ -1455,9 +1411,11 @@ paths:
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The group's identifier"
|
||||
-
|
||||
description: 'The group''s identifier'
|
||||
in: path
|
||||
name: groupId
|
||||
required: true
|
||||
@@ -1475,14 +1433,16 @@ paths:
|
||||
schema:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/IGroup'}
|
||||
- { properties: { _id: {} }, required: [_id], type: object }
|
||||
- {properties: {_id: {$ref: '#/components/schemas/ObjectId'}}, required: [_id], type: object}
|
||||
summary: 'Delete a group. Admin task only.'
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The group's identifier"
|
||||
-
|
||||
description: 'The group''s identifier'
|
||||
in: path
|
||||
name: groupId
|
||||
required: true
|
||||
@@ -1502,21 +1462,16 @@ paths:
|
||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
groupId: 123,
|
||||
name: DCGroup,
|
||||
description: 'This group represents Data Controller Users',
|
||||
isActive: true,
|
||||
users: []
|
||||
}
|
||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||
summary: 'Add a user to a group. Admin task only.'
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The group's identifier"
|
||||
-
|
||||
description: 'The group''s identifier'
|
||||
in: path
|
||||
name: groupId
|
||||
required: true
|
||||
@@ -1524,7 +1479,8 @@ paths:
|
||||
format: double
|
||||
type: number
|
||||
example: '1234'
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: userId
|
||||
required: true
|
||||
@@ -1543,21 +1499,16 @@ paths:
|
||||
$ref: '#/components/schemas/GroupDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
groupId: 123,
|
||||
name: DCGroup,
|
||||
description: 'This group represents Data Controller Users',
|
||||
isActive: true,
|
||||
users: []
|
||||
}
|
||||
value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}
|
||||
summary: 'Remove a user to a group. Admin task only.'
|
||||
tags:
|
||||
- Group
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The group's identifier"
|
||||
-
|
||||
description: 'The group''s identifier'
|
||||
in: path
|
||||
name: groupId
|
||||
required: true
|
||||
@@ -1565,7 +1516,8 @@ paths:
|
||||
format: double
|
||||
type: number
|
||||
example: '1234'
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: userId
|
||||
required: true
|
||||
@@ -1585,14 +1537,7 @@ paths:
|
||||
$ref: '#/components/schemas/InfoResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
mode: desktop,
|
||||
cors: enable,
|
||||
whiteList: ['http://example.com', 'http://example2.com'],
|
||||
protocol: http,
|
||||
runTimes: [sas, js]
|
||||
}
|
||||
value: {mode: desktop, cors: enable, whiteList: ['http://example.com', 'http://example2.com'], protocol: http, runTimes: [sas, js]}
|
||||
summary: 'Get server info (mode, cors, whiteList, protocol).'
|
||||
tags:
|
||||
- Info
|
||||
@@ -1630,42 +1575,14 @@ paths:
|
||||
type: array
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
[
|
||||
{
|
||||
permissionId: 123,
|
||||
path: /SASjsApi/code/execute,
|
||||
type: Route,
|
||||
setting: Grant,
|
||||
user:
|
||||
{
|
||||
id: 1,
|
||||
username: johnSnow01,
|
||||
displayName: 'John Snow',
|
||||
isAdmin: false
|
||||
}
|
||||
},
|
||||
{
|
||||
permissionId: 124,
|
||||
path: /SASjsApi/code/execute,
|
||||
type: Route,
|
||||
setting: Grant,
|
||||
group:
|
||||
{
|
||||
groupId: 1,
|
||||
name: DCGroup,
|
||||
description: 'This group represents Data Controller Users',
|
||||
isActive: true,
|
||||
users: []
|
||||
}
|
||||
}
|
||||
]
|
||||
value: [{permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}, {permissionId: 124, path: /SASjsApi/code/execute, type: Route, setting: Grant, group: {groupId: 1, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []}}]
|
||||
description: "Get the list of permission rules applicable the authenticated user.\nIf the user is an admin, all rules are returned."
|
||||
summary: 'Get the list of permission rules. If the user is admin, all rules are returned.'
|
||||
tags:
|
||||
- Permission
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
post:
|
||||
operationId: CreatePermission
|
||||
@@ -1678,25 +1595,13 @@ paths:
|
||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
permissionId: 123,
|
||||
path: /SASjsApi/code/execute,
|
||||
type: Route,
|
||||
setting: Grant,
|
||||
user:
|
||||
{
|
||||
id: 1,
|
||||
username: johnSnow01,
|
||||
displayName: 'John Snow',
|
||||
isAdmin: false
|
||||
}
|
||||
}
|
||||
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
||||
summary: 'Create a new permission. Admin only.'
|
||||
tags:
|
||||
- Permission
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -1716,27 +1621,16 @@ paths:
|
||||
$ref: '#/components/schemas/PermissionDetailsResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
permissionId: 123,
|
||||
path: /SASjsApi/code/execute,
|
||||
type: Route,
|
||||
setting: Grant,
|
||||
user:
|
||||
{
|
||||
id: 1,
|
||||
username: johnSnow01,
|
||||
displayName: 'John Snow',
|
||||
isAdmin: false
|
||||
}
|
||||
}
|
||||
value: {permissionId: 123, path: /SASjsApi/code/execute, type: Route, setting: Grant, user: {id: 1, username: johnSnow01, displayName: 'John Snow', isAdmin: false}}
|
||||
summary: 'Update permission setting. Admin only'
|
||||
tags:
|
||||
- Permission
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The permission's identifier"
|
||||
-
|
||||
description: 'The permission''s identifier'
|
||||
in: path
|
||||
name: permissionId
|
||||
required: true
|
||||
@@ -1759,9 +1653,11 @@ paths:
|
||||
tags:
|
||||
- Permission
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: "The user's identifier"
|
||||
-
|
||||
description: 'The user''s identifier'
|
||||
in: path
|
||||
name: permissionId
|
||||
required: true
|
||||
@@ -1781,18 +1677,13 @@ paths:
|
||||
$ref: '#/components/schemas/UserResponse'
|
||||
examples:
|
||||
'Example 1':
|
||||
value:
|
||||
{
|
||||
id: 123,
|
||||
username: johnusername,
|
||||
displayName: John,
|
||||
isAdmin: false
|
||||
}
|
||||
value: {id: 123, username: johnusername, displayName: John, isAdmin: false}
|
||||
summary: 'Get session info (username).'
|
||||
tags:
|
||||
- Session
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters: []
|
||||
/SASjsApi/stp/execute:
|
||||
get:
|
||||
@@ -1811,9 +1702,11 @@ paths:
|
||||
tags:
|
||||
- STP
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: 'Location of SAS or JS code'
|
||||
-
|
||||
description: 'Location of SAS or JS code'
|
||||
in: query
|
||||
name: _program
|
||||
required: true
|
||||
@@ -1831,25 +1724,17 @@ paths:
|
||||
$ref: '#/components/schemas/ExecuteReturnJsonResponse'
|
||||
examples:
|
||||
'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'}}
|
||||
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."
|
||||
summary: 'Execute a Stored Program, return a JSON object'
|
||||
tags:
|
||||
- STP
|
||||
security:
|
||||
- bearerAuth: []
|
||||
-
|
||||
bearerAuth: []
|
||||
parameters:
|
||||
- description: 'Location of SAS or JS code'
|
||||
-
|
||||
description: 'Location of SAS or JS code'
|
||||
in: query
|
||||
name: _program
|
||||
required: false
|
||||
@@ -1887,18 +1772,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
user:
|
||||
{
|
||||
properties:
|
||||
{
|
||||
isAdmin: { type: boolean },
|
||||
displayName: { type: string },
|
||||
username: { type: string },
|
||||
id: { type: number, format: double }
|
||||
},
|
||||
required: [isAdmin, displayName, username, id],
|
||||
type: object
|
||||
}
|
||||
user: {properties: {isAdmin: {type: boolean}, displayName: {type: string}, username: {type: string}, id: {type: number, format: double}}, required: [isAdmin, displayName, username, id], type: object}
|
||||
loggedIn: {type: boolean}
|
||||
required:
|
||||
- user
|
||||
@@ -1954,27 +1828,39 @@ paths:
|
||||
security: []
|
||||
parameters: []
|
||||
servers:
|
||||
- url: /
|
||||
-
|
||||
url: /
|
||||
tags:
|
||||
- name: Auth
|
||||
-
|
||||
name: Auth
|
||||
description: 'Operations about auth'
|
||||
- name: Client
|
||||
-
|
||||
name: Client
|
||||
description: 'Operations about clients'
|
||||
- name: CODE
|
||||
-
|
||||
name: CODE
|
||||
description: 'Execution of code (various runtimes are supported)'
|
||||
- name: Drive
|
||||
-
|
||||
name: Drive
|
||||
description: 'Operations on SASjs Drive'
|
||||
- name: Group
|
||||
-
|
||||
name: Group
|
||||
description: 'Operations on groups and group memberships'
|
||||
- name: Info
|
||||
-
|
||||
name: Info
|
||||
description: 'Get Server Information'
|
||||
- name: Permission
|
||||
-
|
||||
name: Permission
|
||||
description: 'Operations about permissions'
|
||||
- name: Session
|
||||
-
|
||||
name: Session
|
||||
description: 'Get Session information'
|
||||
- name: STP
|
||||
-
|
||||
name: STP
|
||||
description: 'Execution of Stored Programs'
|
||||
- name: User
|
||||
-
|
||||
name: User
|
||||
description: 'Operations with users'
|
||||
- name: Web
|
||||
-
|
||||
name: Web
|
||||
description: 'Operations on Web'
|
||||
|
||||
@@ -101,12 +101,14 @@ ${autoExecContent}`
|
||||
session.path,
|
||||
'-AUTOEXEC',
|
||||
autoExecPath,
|
||||
isWindows() ? '-nologo' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-nosplash' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-icon' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-nodms' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-noterminal' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-nostatuswin' : '',
|
||||
isWindows() ? '-nologo' : ''
|
||||
process.sasLoc!.endsWith('sas.exe') ? '-SASINITIALFOLDER' : '',
|
||||
process.sasLoc!.endsWith('sas.exe') ? session.path : ''
|
||||
])
|
||||
.then(() => {
|
||||
session.completed = true
|
||||
|
||||
143
api/src/controllers/mock-sas9.ts
Normal file
143
api/src/controllers/mock-sas9.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { readFile } from '@sasjs/utils'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import { Request, Post, Get } from 'tsoa'
|
||||
|
||||
export interface Sas9Response {
|
||||
content: string
|
||||
redirect?: string
|
||||
error?: boolean
|
||||
}
|
||||
|
||||
export interface MockFileRead {
|
||||
content: string
|
||||
error?: boolean
|
||||
}
|
||||
|
||||
export class MockSas9Controller {
|
||||
private loggedIn: boolean = false
|
||||
|
||||
@Get('/SASStoredProcess')
|
||||
public async sasStoredProcess(): Promise<Sas9Response> {
|
||||
if (!this.loggedIn) {
|
||||
return {
|
||||
content: '',
|
||||
redirect: '/SASLogon/login'
|
||||
}
|
||||
}
|
||||
|
||||
return await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
'mocks',
|
||||
'generic',
|
||||
'sas9',
|
||||
'sas-stored-process'
|
||||
])
|
||||
}
|
||||
|
||||
@Post('/SASStoredProcess/do/')
|
||||
public async sasStoredProcessDo(
|
||||
@Request() req: express.Request
|
||||
): Promise<Sas9Response> {
|
||||
if (!this.loggedIn) {
|
||||
return {
|
||||
content: '',
|
||||
redirect: '/SASLogon/login'
|
||||
}
|
||||
}
|
||||
|
||||
let program = req.query._program?.toString() || ''
|
||||
program = program.replace('/', '')
|
||||
|
||||
const content = await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
'mocks',
|
||||
...program.split('/')
|
||||
])
|
||||
|
||||
if (content.error) {
|
||||
return content
|
||||
}
|
||||
|
||||
const parsedContent = parseJsonIfValid(content.content)
|
||||
|
||||
return {
|
||||
content: parsedContent
|
||||
}
|
||||
}
|
||||
|
||||
@Get('/SASLogon/login')
|
||||
public async loginGet(): Promise<Sas9Response> {
|
||||
return await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
'mocks',
|
||||
'generic',
|
||||
'sas9',
|
||||
'login'
|
||||
])
|
||||
}
|
||||
|
||||
@Post('/SASLogon/login')
|
||||
public async loginPost(): Promise<Sas9Response> {
|
||||
this.loggedIn = true
|
||||
|
||||
return await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
'mocks',
|
||||
'generic',
|
||||
'sas9',
|
||||
'logged-in'
|
||||
])
|
||||
}
|
||||
|
||||
@Get('/SASLogon/logout')
|
||||
public async logout(): Promise<Sas9Response> {
|
||||
this.loggedIn = false
|
||||
|
||||
return await getMockResponseFromFile([
|
||||
process.cwd(),
|
||||
'mocks',
|
||||
'generic',
|
||||
'sas9',
|
||||
'logged-out'
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> => {
|
||||
const filePathParsed = path.join(...filePath)
|
||||
let error: boolean = false
|
||||
|
||||
let file = await readFile(filePathParsed).catch((err: any) => {
|
||||
const errMsg = `Error reading mocked file on path: ${filePathParsed}\nError: ${err}`
|
||||
console.error(errMsg)
|
||||
|
||||
error = true
|
||||
|
||||
return errMsg
|
||||
})
|
||||
|
||||
return {
|
||||
content: file,
|
||||
error: error
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,25 @@
|
||||
import express from 'express'
|
||||
import sas9WebRouter from './sas9-web'
|
||||
import sasViyaWebRouter from './sasviya-web'
|
||||
import webRouter from './web'
|
||||
import { MOCK_SERVERTYPEType } from '../../utils'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
const { MOCK_SERVERTYPE } = process.env
|
||||
|
||||
switch (MOCK_SERVERTYPE) {
|
||||
case MOCK_SERVERTYPEType.SAS9: {
|
||||
router.use('/', sas9WebRouter)
|
||||
break
|
||||
}
|
||||
case MOCK_SERVERTYPEType.SASVIYA: {
|
||||
router.use('/', sasViyaWebRouter)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
router.use('/', webRouter)
|
||||
}
|
||||
}
|
||||
|
||||
export default router
|
||||
|
||||
88
api/src/routes/web/sas9-web.ts
Normal file
88
api/src/routes/web/sas9-web.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import express from 'express'
|
||||
import { WebController } from '../../controllers'
|
||||
import { MockSas9Controller } from '../../controllers/mock-sas9'
|
||||
|
||||
const sas9WebRouter = express.Router()
|
||||
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()
|
||||
|
||||
sas9WebRouter.get('/', async (req, res) => {
|
||||
let response
|
||||
try {
|
||||
response = await webController.home()
|
||||
} catch (_) {
|
||||
response = '<html><head></head><body>Web Build is not present</body></html>'
|
||||
} finally {
|
||||
const codeToInject = `<script>document.cookie = 'XSRF-TOKEN=${req.csrfToken()}; Max-Age=86400; SameSite=Strict; Path=/;'</script>`
|
||||
const injectedContent = response?.replace(
|
||||
'</head>',
|
||||
`${codeToInject}</head>`
|
||||
)
|
||||
|
||||
return res.send(injectedContent)
|
||||
}
|
||||
})
|
||||
|
||||
sas9WebRouter.get('/SASStoredProcess', async (req, res) => {
|
||||
const response = await controller.sasStoredProcess()
|
||||
|
||||
if (response.redirect) {
|
||||
res.redirect(response.redirect)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => {
|
||||
const response = await controller.sasStoredProcessDo(req)
|
||||
|
||||
if (response.redirect) {
|
||||
res.redirect(response.redirect)
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
sas9WebRouter.post('/SASLogon/login', async (req, res) => {
|
||||
const response = await controller.loginPost()
|
||||
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
sas9WebRouter.get('/SASLogon/logout', async (req, res) => {
|
||||
const response = await controller.logout()
|
||||
|
||||
try {
|
||||
res.send(response.content)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
export default sas9WebRouter
|
||||
32
api/src/routes/web/sasviya-web.ts
Normal file
32
api/src/routes/web/sasviya-web.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import express from 'express'
|
||||
import { WebController } from '../../controllers/web'
|
||||
|
||||
const sasViyaWebRouter = express.Router()
|
||||
const controller = new WebController()
|
||||
|
||||
sasViyaWebRouter.get('/', async (req, res) => {
|
||||
let response
|
||||
try {
|
||||
response = await controller.home()
|
||||
} catch (_) {
|
||||
response = '<html><head></head><body>Web Build is not present</body></html>'
|
||||
} finally {
|
||||
const codeToInject = `<script>document.cookie = 'XSRF-TOKEN=${req.csrfToken()}; Max-Age=86400; SameSite=Strict; Path=/;'</script>`
|
||||
const injectedContent = response?.replace(
|
||||
'</head>',
|
||||
`${codeToInject}</head>`
|
||||
)
|
||||
|
||||
return res.send(injectedContent)
|
||||
}
|
||||
})
|
||||
|
||||
sasViyaWebRouter.post('/SASJobExecution/', async (req, res) => {
|
||||
try {
|
||||
res.send({ test: 'test' })
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
export default sasViyaWebRouter
|
||||
@@ -16,7 +16,7 @@ export const getDesktopFields = async () => {
|
||||
nodeLoc = NODE_PATH ?? (await getNodeLocation())
|
||||
}
|
||||
|
||||
if (process.runTimes.includes(RunTimeType.JS)) {
|
||||
if (process.runTimes.includes(RunTimeType.PY)) {
|
||||
pythonLoc = PYTHON_PATH ?? (await getPythonLocation())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export enum MOCK_SERVERTYPEType {
|
||||
SAS9 = 'sas9',
|
||||
SASVIYA = 'sasviya'
|
||||
}
|
||||
|
||||
export enum ModeType {
|
||||
Server = 'server',
|
||||
Desktop = 'desktop'
|
||||
@@ -40,6 +45,8 @@ export enum ReturnCode {
|
||||
export const verifyEnvVariables = (): ReturnCode => {
|
||||
const errors: string[] = []
|
||||
|
||||
errors.push(...verifyMOCK_SERVERTYPE())
|
||||
|
||||
errors.push(...verifyMODE())
|
||||
|
||||
errors.push(...verifyPROTOCOL())
|
||||
@@ -66,6 +73,23 @@ export const verifyEnvVariables = (): ReturnCode => {
|
||||
return ReturnCode.Success
|
||||
}
|
||||
|
||||
const verifyMOCK_SERVERTYPE = (): string[] => {
|
||||
const errors: string[] = []
|
||||
const { MOCK_SERVERTYPE } = process.env
|
||||
|
||||
if (MOCK_SERVERTYPE) {
|
||||
const modeTypes = Object.values(MOCK_SERVERTYPEType)
|
||||
if (!modeTypes.includes(MOCK_SERVERTYPE as MOCK_SERVERTYPEType))
|
||||
errors.push(
|
||||
`- MOCK_SERVERTYPE '${MOCK_SERVERTYPE}'\n - valid options ${modeTypes}`
|
||||
)
|
||||
} else {
|
||||
process.env.MOCK_SERVERTYPE = undefined
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
const verifyMODE = (): string[] => {
|
||||
const errors: string[] = []
|
||||
const { MODE } = process.env
|
||||
|
||||
@@ -78,6 +78,18 @@ const TreeViewNode = ({
|
||||
mouseY: number
|
||||
} | null>(null)
|
||||
|
||||
const launchProgram = () => {
|
||||
const baseUrl = window.location.origin
|
||||
window.open(`${baseUrl}/SASjsApi/stp/execute?_program=${node.relativePath}`)
|
||||
}
|
||||
|
||||
const launchProgramWithDebug = () => {
|
||||
const baseUrl = window.location.origin
|
||||
window.open(
|
||||
`${baseUrl}/SASjsApi/stp/execute?_program=${node.relativePath}&_debug=131`
|
||||
)
|
||||
}
|
||||
|
||||
const handleContextMenu = (event: React.MouseEvent) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@@ -224,8 +236,8 @@ const TreeViewNode = ({
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{node.isFolder && (
|
||||
<div>
|
||||
{node.isFolder ? (
|
||||
<>
|
||||
<MenuItem onClick={handleNewFolderItemClick}>Add Folder</MenuItem>
|
||||
<MenuItem
|
||||
disabled={!node.relativePath}
|
||||
@@ -233,14 +245,21 @@ const TreeViewNode = ({
|
||||
>
|
||||
Add File
|
||||
</MenuItem>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MenuItem onClick={launchProgram}>Launch</MenuItem>
|
||||
<MenuItem onClick={launchProgramWithDebug}>
|
||||
Launch and Debug
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
{!!node.relativePath && (
|
||||
<>
|
||||
<MenuItem onClick={handleRenameItemClick}>Rename</MenuItem>
|
||||
<MenuItem onClick={handleDeleteItemClick}>Delete</MenuItem>
|
||||
</>
|
||||
)}
|
||||
<MenuItem disabled={!node.relativePath} onClick={handleRenameItemClick}>
|
||||
Rename
|
||||
</MenuItem>
|
||||
<MenuItem disabled={!node.relativePath} onClick={handleDeleteItemClick}>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user