Code Monkey home page Code Monkey logo

server's Introduction

SASjs Server

All Contributors

SASjs Server provides a NodeJS wrapper for calling the SAS binary executable. It can be installed on an actual SAS server, or locally on your desktop. It provides:

  • Virtual filesystem for storing SAS programs and other content
  • Ability to execute Stored Programs from a URL
  • Ability to create web apps using simple Desktop SAS
  • REST API with Swagger Docs

One major benefit of using SASjs Server alongside other components of the SASjs framework such as the CLI, Adapter and Core library, is that the projects you create can be very easily ported to SAS 9 (Stored Process server) or Viya (Job Execution server).

SASjs Server is available in two modes - Desktop (without authentication) and Server (with authentication, and a database)

Installation

Installation can be made programmatically using command line, or by manually downloading and running the executable.

Programmatic

Fetch the relevant package from github using curl, eg as follows (for linux):

curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
unzip linux.zip

The app can then be launched with ./api-linux and prompts followed (if ENV vars not set).

Manual

  1. Download the relevant package from the releases page
  2. Trigger by double clicking (windows) or executing from commandline.

You are presented with two prompts (if not set as ENV vars):

  • Location of your sas.exe / sas.sh executable
  • Path to a filesystem location for Stored Programs and temporary files

ENV Var configuration

When launching the app, it will make use of specific environment variables. These can be set in the following places:

  • Configured globally in /etc/environment file
  • Export in terminal or shell script (export VAR=VALUE)
  • Prepended in the command
  • Enter in the .env file alongside the executable

Example contents of a .env file:

#
## Core Settings
#


# MODE options: [desktop|server] default: `desktop`
# Desktop mode is single user and designed for workstation use
# Server mode is multi-user and suitable for intranet / internet use
MODE=

# A comma separated string that defines the available runTimes.
# Priority is given to the runtime that comes first in the string.
# Possible options at the moment are sas, js, py and r

# This string sets the priority of the available analytic runtimes
# Valid runtimes are SAS (sas), JavaScript (js), Python (py) and R (r)
# For each option provided, there should be a corresponding path,
# eg SAS_PATH, NODE_PATH, PYTHON_PATH or RSCRIPT_PATH
# Priority is given to runtimes earlier in the string
# Example options:  [sas,js,py | js,py | sas | sas,js | r | sas,r]
RUN_TIMES=

# Path to SAS executable (sas.exe / sas.sh)
SAS_PATH=/path/to/sas/executable.exe

# Path to Node.js executable
NODE_PATH=~/.nvm/versions/node/v16.14.0/bin/node

# Path to Python executable
PYTHON_PATH=/usr/bin/python

# Path to R executable
R_PATH=/usr/bin/Rscript

# Path to working directory
# This location is for SAS WORK, staged files, DRIVE, configuration etc
SASJS_ROOT=./sasjs_root


# This location is for files, sasjs packages and appStreamConfig.json
DRIVE_LOCATION=./sasjs_root/drive


# options: [http|https] default: http
PROTOCOL=

# default: 5000
PORT=

# options: [sas9|sasviya]
# If not present, mocking function is disabled
MOCK_SERVERTYPE=

# default: /api/mocks
# Path to mocking folder, for generic responses, it's sub directories should be: sas9, viya, sasjs
# Server will automatically use subdirectory accordingly
STATIC_MOCK_LOCATION=

#
## Additional SAS Options
#


# On windows use SAS_OPTIONS and on unix use SASV9_OPTIONS
# Any options set here are automatically applied in the SAS session
# See: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostunx/p0wrdmqp8k0oyyn1xbx3bp3qy2wl.htm
# And: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostwin/p0drw76qo0gig2n1kcoliekh605k.htm#p09y7hx0grw1gin1giuvrjyx61m6
SAS_OPTIONS= -NOXCMD
SASV9_OPTIONS= -NOXCMD


#
## Additional Web Server Options
#

# ENV variables for PROTOCOL: `https`
PRIVATE_KEY=privkey.pem (required)
CERT_CHAIN=certificate.pem (required)
CA_ROOT=fullchain.pem (optional)

## ENV variables required for MODE: `server`
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority

# options: [mongodb|cosmos_mongodb] default: mongodb
DB_TYPE=

# AUTH_PROVIDERS options: [ldap] default: ``
AUTH_PROVIDERS=

## ENV variables required for AUTH_MECHANISM: `ldap`
LDAP_URL= <LDAP_SERVER_URL>
LDAP_BIND_DN= <cn=admin,ou=system,dc=cloudron>
LDAP_BIND_PASSWORD = <password>
LDAP_USERS_BASE_DN = <ou=users,dc=cloudron>
LDAP_GROUPS_BASE_DN = <ou=groups,dc=cloudron>

# options: [disable|enable] default: `disable` for `server` & `enable` for `desktop`
# If enabled, be sure to also configure the WHITELIST of third party servers.
CORS=

# options: <http://localhost:3000 https://abc.com ...> space separated urls
WHITELIST=

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

# HELMET Content Security Policy
# Path to a json file containing HELMET `contentSecurityPolicy` directives
# Docs: https://helmetjs.github.io/#reference
#
# Example config:
# {
#   "img-src": ["'self'", "data:"],
#   "script-src": ["'self'", "'unsafe-inline'"],
#   "script-src-attr": ["'self'", "'unsafe-inline'"]
# }
HELMET_CSP_CONFIG_PATH=./csp.config.json

# To prevent brute force attack on login route we have implemented rate limiter
# Only valid for MODE: server
# Following are configurable env variable rate limiter

# After this, access is blocked for 1 day
MAX_WRONG_ATTEMPTS_BY_IP_PER_DAY = <number> default: 100;


# After this, access is blocked for an hour
# Store number for 24 days since first fail
# Once a successful login is attempted, it resets
MAX_CONSECUTIVE_FAILS_BY_USERNAME_AND_IP = <number> default: 10;

# Name of the admin user that will be created on startup if not exists already
# Default is `secretuser`
ADMIN_USERNAME=secretuser

# Temporary password for the ADMIN_USERNAME, which is in place until the first login
# Default is `secretpassword`
ADMIN_PASSWORD_INITIAL=secretpassword

# Specify whether app has to reset the ADMIN_USERNAME's password or not
# Default is NO. Possible options are YES and NO
# If ADMIN_PASSWORD_RESET is YES then the ADMIN_USERNAME will be prompted to change the password from ADMIN_PASSWORD_INITIAL on their next login. This will repeat on every server restart, unless the option is removed / set to NO.
ADMIN_PASSWORD_RESET=NO

# LOG_FORMAT_MORGAN options: [combined|common|dev|short|tiny] default: `common`
# Docs: https://www.npmjs.com/package/morgan#predefined-formats
LOG_FORMAT_MORGAN=

# This location is for server logs with classical UNIX logrotate behavior
LOG_LOCATION=./sasjs_root/logs

Persisting the Session

Normally the server process will stop when your terminal dies. To keep it going you can use the following suggested approaches:

  1. Linux Background Job
  2. NPM package pm2

Background Job

Trigger the command using NOHUP, redirecting the output commands, eg nohup ./api-linux > server.log 2>&1 &.

You can now see the job running using the jobs command. To ensure that it will still run when your terminal is closed, execute the disown command. To kill it later, use the kill -9 <pid> command. You can see your sessions using top -u <userid>. Type c to see the commands being run against each pid.

PM2

Install the npm package pm2 (npm install pm2@latest -g) and execute, eg as follows:

export SAS_PATH=/opt/sas9/SASHome/SASFoundation/9.4/sasexe/sas
export PORT=5001
export SASJS_ROOT=./sasjs_root

pm2 start api-linux

To get the logs (and some useful commands):

pm2 [list|ls|status]
pm2 logs
pm2 logs --lines 200

Managing processes:

pm2 restart app_name
pm2 reload app_name
pm2 stop app_name
pm2 delete app_name

Instead of app_name you can pass:

  • all to act on all processes
  • id to act on a specific process id

Server Version

The following credentials can be used for the initial connection to SASjs/server. It is highly recommended to change these on first use.

  • CLIENTID: clientID1
  • USERNAME: secretuser
  • PASSWORD: secretpassword

Contributors โœจ

Thanks goes to these wonderful people (emoji key):


Saad Jutt

๐Ÿ’ป โš ๏ธ

Sabir Hassan

๐Ÿ’ป โš ๏ธ

Yury Shkoda

๐Ÿ’ป โš ๏ธ

Mihajlo Medjedovic

๐Ÿ’ป โš ๏ธ

Allan Bowe

๐Ÿ’ป ๐Ÿ“–

Vladislav Parhomchik

โš ๏ธ

Koen Knapen

๐Ÿ““

This project follows the all-contributors specification. Contributions of any kind welcome!

server's People

Contributors

allanbowe avatar allcontributors[bot] avatar jahanzaibrao15 avatar medjedovicm avatar saadjutt01 avatar sabhas avatar semantic-release-bot avatar sharky618 avatar yuryshkoda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

server's Issues

lengths of username/password

currently we enforce a minimum length of 6 for both username / password. this is too long for a username, and too short for a password.

We should enable the following:

  • Minimum 3 for username
  • Minimum 10 for password

There should probably be a max also?

Perhaps the Logon screen can display the rules.. ?

SASjs Drive

After deploying Stored Programs to SASjs Server, it is helpful to be able to navigate / edit / execute those programs.

As such, we need the following functionality under the /SASjsDrive endpoint:

  • Explorer window (folders & files)
  • Ability to VIEW files (default action when clicking is to VIEW)
  • Ability to EDIT files (button action)
  • Ability to EXECUTE files (button action)

Explorer

It should be possible to expand and collapse folders, to see a list of content. It should be clear from the list what the content type is (eg Stored Program or Subfolder etc)

Viewer

The file content should be displayed in a content box, with buttons to EDIT and to EXECUTE

Editor

In edit mode, when edits are made, the EXECUTE button should be greyed out. After clicking SAVE, the EXECUTE button can be re-enabled.

Executor

When clicking EXECUTE, a NEW window (or tab) is opened with the file location in the _program url param. For instance, if opening /Public/subfolder/someprog then the url would be: /SASjsExecutor?_program=/Public/subfolder/someprog.

unsupported format when pushing angular app

Unsure whether this belongs to /server or /cli. When running sasjs cbd -t server on the https://github.com/sasjs/angular-seed-app repo, the following error is returned:

 ERROR  Auth error: {                                                                                                                                               23:28:48
  "status": "failure",
  "message": "Provided not supported data format.",

The cause of the error is not shown.

The message should also be updated, eg: "The format of the build-pack JSON is invalid", ideally it would show WHY (or WHERE) it was invalid.

Create dummy SAS executable

To avoid the need for having a SAS exe, we can emulate one ourselves

This can be placed in the sasPath, and all it needs to do is read in the SAS Program and write it to the LOG location.

To emulate the "hot session" behaviour in #18, this program will also need to remove the initial SYSIN program, then wait for it to re-appear

Definition of done:

  • an executable JS file that:
    • removes the code.sas file (location in the SYSIN command variable)
    • loops until it reappears
    • Copies the file to log.log (location in the LOG command variable)
    • exit with return code
  • a test that calls "SAS" and confirms that the result contains the input string

Server fails when CORS enabled and no WHITELIST is present

To reproduce, deploy an app with CORS=enable and WHITELIST=

Expected - the app would still work when the domain is the same

Nice to have - if WHITELIST is empty then ALL domains are accessible (it would be fine to throw an ERROR or WARNING in the console for this)

Refactor Session

Instead of having multiple variables for Session e.g. ready, inUse, consumed, completed.

Define state and it's types.

enforce utf-8

We should update the -CFG file to ensure that SASjs sessions are always UTF-8

This will improve forwards compatibility with Viya as well as addressing the shortcomings of default WLATIN1 (character set incompatibilities)

appStream Config

Developing apps will require a workflow as follows:

  1. Build app locally, designate the source in streamConfig. This folder is deployed to $appLoc/services/$streamConfig.streamWebFolder.
  2. Configure the app remotely, as follows:
{
  "AppStreamName1": {
    "appLoc": "/some/other/location/services/web",
    "mainLogo": "/path/to/logo/in/sasjs/drive"
  },
  "AppStreamName2": {
    "appLoc": "$/some/location/services/webv",
    "mainLogo": "/path/to/logo/in/sasjs/drive"
  }
}

The above could be modified using an API also.

The User Experience will then be as follows:

  1. Navigate to "serverUrl:/appstream"
  2. They are presented with a series of logos
  3. Clicking the logo opens the app at /appStream/$appStreamName

Enable `/SASjsLogon`

SASjs Server is composed of a "web" and an "api" component. All web content is public, with requests being made to the API (same as other clients). The API supports a single authentication mechanism - Authorization Code Flow. There are three supported providers for this:

  • Self Hosted (Username/Password)
  • Viya authorization-flow
  • Auth0 authorization-flow

In each case, the client is provided the CLIENT_ID, and the client uses this to fetch an AUTH_CODE. The backend (API) uses this to fetch the ACCESS/REFRESH tokens which are provided to the client in JWT form. This JWT is provided to each API request either in the header, or in a cookie. The client must call the /SASjsApi/auth/refresh endpoint before ACCESS_TOKEN expiry to obtain a refreshed JWT.

This ticket covers the Self Hosted (username/password) flow.

When starting SASjs Server for the first time, a default admin user/password is created. This enables immediate use of the APIs.

The following components are necessary:

  • A /SASjsLogon application, allowing a user to simply log in with username and password
  • A /SASjsApi/oauth/authorize endpoint, accepting a valid login, plus a CLIENT_ID, and returning an AUTH_CODE (used by frontend)
  • A /SASjsApi/oauth/token endpoint, accepting a client/secret/auth code and returning access / refresh tokens (used by backend, and maybe by other apps in future)
  • A /SASjsApi/auth/submit endpoint, accepting a CLIENT_ID and AUTH_CODE and returning a JWT (calls the previous endpoint at backend)
  • A /SASjsApi/auth/refresh endpoint for refreshing the JWT
  • A /SASjsApi/user/changePassword endpoint allowing a password to be changed
  • A /SASjsApi/user/add endpoint allowing a new user to be created, with the following attributes: UserId, UserName, Password, isAdmin, isActive. Only admins can create new users. The response object contains a randomly generated password for the new user.
  • A lookup table for commonly accessed attributes such as userName, UserId, Refresh Token (a user id could have multiple refresh tokens, one for each client id).
  • The ability to authenticate each API request so that only "active" users are allowed

The following is not necessary (for this ticket):

  • Auth0 authentication
  • Viya authentication
  • Forcing the user to change their password on first login

Enable `stpsrv_header()`

The stpsrv_header() function is used in SAS 9 to alter the http headers sent to the frontend (along with the request body).
Examples from official documentation: https://documentation.sas.com/doc/en/itechcdc/9.4/stpug/srvhead.htm (could be used as test cases)

In sasjs/server we can achieve this by using the fcmp function described here: https://core.sasjs.io/mfs__httpheader_8sas.html

This involves setting a macro variable in the autoexec ( %let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/../stpsrv_header.txt;) which the location of a text file that is written to the session folder.

If the file exists in the session folder, response headers are set according to the file contents. If the file has duplicates, the last one is used. If the final record has an empty VALUE then the header is removed.

The actual response depends on the way that the request is invoked.

  • If GET /SASjsApi/stp/execute then the actual http response headers are set accordingly
  • If POST /SASjsApi/stp/execute then the header table is simply inserted into the JSON response (alongside log and webout), eg: {headers:[{header1:value1},{header2:value2}],log:{},webout:{}}
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/../stpsrv_header.txt;

%mfs_httpheader(Content-type,application/csv)
%mfs_httpheader(Expires,%str(Thu, 18 Nov 1999 12:23:34 GMT))

data _null_;
  infile "&sasjs_stpsrv_header_loc";
  input;
  putlog _infile_;
run;

upload button on AppStream page

Currently to upload a new app to SASjs server it is necessary to open the swagger docs

It would be nice if there was an upload, or "+" button on the AppStream page itself that did the same thing

web component missing UI

Should have link to /SASjsAPI/
Should have loggedin user's section at top right.
Should have requests modal similar to react seed app.

make SASjs CORE macros available to SASjs Server sessions

When running in Studio, it's a pain to provide the relevant CORE macros each time.

Given that we are on the server, and we have access to the filesystem, we could instead just put the macros there. Then make them available to all SAS sessions in a single line of code.

The steps would be:

  • Create a "core" subfolder in the DRIVE_PATH
  • copy ALL the files from the folders below, into the above folder. They would have to be provided some other way in the final (built) version.
    • node_modules/@sasjs/core/base
    • node_modules/@sasjs/core/ddl
    • node_modules/@sasjs/core/fcmp
    • node_modules/@sasjs/core/lua
    • node_modules/@sasjs/core/server
  • add the following line of code to the SAS autoexec:
    • options insert=(SASAUTOS="$(DRIVE_PATH)/core"); -> replacing $DRIVE_PATH with the actual path

`_WEBIN_FILENAME` needs to contain the file name

When uploading files to SASjs server we should pass the filenames into the _WEBIN_FILENAME series of variables.

Currently these do not appear to be populated.

The information is visible from the request header, eg:

image

In the above scenario we'd expect the following code generated:

%let _webin_filename=DC687632.MPE_X_TEST.xlsx;
%let _webin_filename1=DC687632.MPE_X_TEST.xlsx;
%let _webin_filename2=DC687632.MPE_X_TEST.xlsx.csv;

support request file inputs

When files are uploaded to SAS (/SASjsExecutor endpoint) we need to make them available to the SAS session. Ideally they will be loaded into SASWORK, but until we find a nice way to handle a running SAS Session, we should proceed as follows:

  1. Create a temporary directory in which to store the files
  2. Insert some SAS code to enable the session to reference those files
  3. After execution, delete the directory.

The SAS code to be inserted consists of the following variables:

  • _WEBIN_FILE_COUNT - contains an integer, from 0 to the number of files to being provided. This variable is always created.
  • _WEBIN_FILENAME1 - The full path to the first file (including the filename). The path to the second file will be _WEBIN_FILENAME2 (and so on, same for the below)
  • _WEBIN_FILEREF1 - An 8 letter code, starting with an underscore and finishing with 7 random alphanumeric characters
  • _WEBIN_NAME1 - the value that was specified in the NAME attribute by the frontend

We will also create the fileref(s) using the filename statement.

To illustrate with an example - we are uploading two files, F1 and F2. The following SAS code will be generated, and inserted at the beginning of the executed program:

filename _SJS0001 "/some/temp/location/file1";
filename _SJS0002 "/some/temp/location/file2";
%let _WEBIN_FILE_COUNT=2;
%let _WEBIN_FILENAME1=/some/temp/location/file1;
%let _WEBIN_FILENAME2=/some/temp/location/file2;
%let _WEBIN_FILEREF1=_SJS0001;
%let _WEBIN_FILEREF2=_SJS0002;
%let _WEBIN_NAME1=F1;
%let _WEBIN_NAME1=F2;

If there are no files uploaded, only the following code will be generated:

%let _WEBIN_FILE_COUNT=0;

Split `/SASjsApi/stp/execute` between GET and POST

To fit our migration use case (and the streaming app use case), we need to continue to support the approach whereby the webout result is streamed directly to the browser.

To ease the integration with the adapter (and other API clients) we should also support a JSON response.

This ticket proposes that GET requests should result in a streamed output, and POST requests result in JSON output. This is now documented in https://server.sasjs.io/api

Change LOG response format

Currently, the LOG is returned as a single encoded string.

This can cause memory problems when we have very large logs. It is also problematic to parse within certain instances of SAS.

Instead, we should send the log in an array (ie, line by line).

This affects the following endpoints:

image

There is likely to also be an impact on the adapter / cli, which ingests the log int the request history.

example format:

{
  log:{
    "1":"reallyh long ",
    "2":" log|",
    "3":"sasoasdfasdfasdfasdf"
  }
}

Fix vulnerabilities

We should have no security vulnerabilities. If there is a way we can test for this as part of a merge request / github action, we should do so:

image

adjust `/SASjsApi/drive/file` payload

Due to limitations in SAS, we need to avoid the use of very wide strings.

Currently in POST /SASjsApi/drive/file we send a payload in the following format:

{
  "filePath": "/Public/somefolder/some.file",
  "fileContent": "Contents of the File"
}

Instead, fileContent should be sent as a raw file in the request body. This will also enable forward compatibility when we upload other file types (such as images). The filePath can be a URL parameter.

Change WEBOUT repsonse format when content-type is `application/json`

It would be super convenient for SAS programmers (as consumers of the sasjs/server apis) to be able to use the JSON libname engine for reading the webout response.

Currently that is not possible for large webouts, due to a 32767 character string length limitation.

So, when the HTTP header content-type: application/json is set AND the request is POST (not GET) SASjs/stp/execute, AND the webout contains valid JSON, the webout response should be passed back as an object rather than stringified text.

The description of the swagger API should also be updated to reflect the above logic.

Support Streaming Apps

It is common for SAS Developers to be denied access to the SAS Web Server.

To enable web development regardless, SASjs enables 'streaming apps' on both SAS 9 and Viya. This involves loading web content to the logical SAS folder (eg metadata or SAS drive) and adjusting the source files to use the modified paths.

We ought to support the same approach for SASjs server, enabling both frontend and backend to be deployed using sasjs deploy, where streamConfig.streamWeb==true.

SASAUTOS is not functional on deployed version

The path inserted into SASAUTOS is not valid in the released version of the app (it works only in development mode)

To validate, run the following code in Studio on a published version:

%put %sysfunc(getoption(SASAUTOS));

And check whether that folder exists / has the core macros.

Enable 'systemInit' program for sasjs services

In order to execute SAS 9 code on SASjs Server we will need to enable some additional functions. This will require the addition of some system precode when executing each service.

Currently we already insert some dynamic lines of code before each service, eg:

filename _webout "C:\Users\YuryShkoda\projects\server\tmp\webouts\testJob-20210819080309.json";
%let var2=val2;
%let varname=value;
* Job Variables start;
...

The above contains:

  • execution specific dynamic variable (from sasjs/server), ie temporary webout location
  • invocation specific variables (from the user), ie macro variables
  • actual job code (from the filesystem / database), ie the static SAS code

We now need to insert two more things:

  • An additional entry in the execution-specific part, ie the temporary header file location
  • An additional section between the above, and the invocation specific variables (a 'system init' program)

The systemInit program will be stored in the repo as follows:

/**
  @file
  @brief The systemInit program
  @details This program is inserted into every sasjs/server program invocation,
  _before_ any user-provided content.

  <h4> SAS Macros </h4>
  @li mcf_stpsrv_header.sas

**/


proc fcmp outcat=work.sasjs.utils;
%mcf_stpsrv_header()
quit;

During npm run build of sasjs/server this systemInit program will be compiled, and the compiled version will be inserted whenever deploying jobs/services/tests/etc.

So, a job that looks like this in the database/filesystem:

* Job Variables start;
*Job Variables end;
* Dependencies start;
* Dependencies end;
* Programs start;
*Programs end;
* Job start;
data _null_;
  file _webout;
  put 'test Jobhere is some end user SAS code';
run;
* Job end;

Will look like this when it is executed in SAS:

/* runtime vars */
%let sasjs_stpsrv_header_loc = C:\Users\YuryShkoda\projects\server\tmp\webouts\header-20210819080309.txt;
filename _webout "C:\Users\YuryShkoda\projects\server\tmp\webouts\testJob-20210819080309.json";

/* compiled systemInit */
SYSTEM INIT COMPILED PROGRAM

/* dynamic user-provided vars */
%let var2=val2;
%let varname=value;

/* actual job code */
* Job Variables start;
*Job Variables end;
* Dependencies start;
* Dependencies end;
* Programs start;
*Programs end;
* Job start;
data _null_;
  file _webout;
  put 'test Jobhere is some end user SAS code';
run;
* Job end;

mocked api

For development in non-SAS environments (eg running tests in github pipelines) it will be very helpful to have mocked versions of the Viya endpoints as they pertain to SASjs.

This will enable a more robust (and faster) test suite for the CLI and adapter, as well as various web apps. It will also mean that the tests can still be easily run against an actual viya instance on an ad-hoc basis.

The following entry will be enabled in the .env file:

# optional - enable mocked server endpoints for testing
# options: [disable|enable] default: `disable`
MOCK=

When enabled, various Viya / SAS 9 endpoints will be made available on SASjs server. The endpoints will provide a mix of actual and hard-coded responses, and varying degrees of actual / mocked functionality, in order to fit the needs of the test cases we have so far.

Definition of done:

  • The sasjs/adapter and sasjs/cli test suites can be executed using the "SASVIYA" serverType and the sasjs/server serverUrl.
  • The sasjs/adapter and sasjs/cli test suites can be executed using the "SAS9" serverType And sasjs/server serverUrl

execute request is failing

and gives following result
{"status":"failure","message":"Job execution failed.","error":"TypeError [ERR_INVALID_ARG_TYPE]: The \"file\" argument must be of type string. Received undefined"}

Mobile Friendly Headers

Currently it is not possible to navigate to SASjs Studio on a mobile (unless the mobile is tipped sideways)

It should be possible to navigate the menus on small screens

SAS_OPTIONS variable

It is a common requirement to alter the startup options for a SAS session. There are many ways to achieve this as illustrated by teh diagram below:
image

In our case we should use the SAS_OPTIONS environment variable. This will override all other settings (except for the invocation arguments).

Excerpt from docs:

SAS_OPTIONS operating system environment variable
This environment variable, if defined, contains a string of option specifications for any other SAS system options that you want to process each time you invoke SAS. For example, this environment variable might contain -obs 2m -sasinitialfolder c:\myfolder -linesize max.

Implementation

Enable an optional SAS_OPTIONS variable in the .env file

make hover on WEBOUT tab

When hovering over webout it would be nice to have a tooltip with the following text:

"Displays content from the _webout fileref"

remove NOSPLASH if server is linux

The NOSPLASH argument is not valid on linux - we should remove it

For analytium, the binary is here: /opt/sas/sas9/SASHome/SASFoundation/9.4/sas

Enable HOT sessions

For maximum responsiveness we should pre-spawn SAS instances so that they can execute immediately when the file arrives.

We can do this by implementing the following code in the autoexec (which will wait 15 minutes for the file to appear before continuing):

data _null_;
  /* remove the dummy SYSIN */
  length fname $8;
  rc=filename(fname,getoption('SYSIN') );
  if rc = 0 and fexist(fname) then rc=fdelete(fname);
  rc=filename(fname);
  /* now wait for the real SYSIN */
  slept=0;
  do until ( fileexist(getoption('SYSIN')) or slept>(60*15) );
    slept=slept+sleep(0.1,1);
  end;
run;

SAS can then be invoked as follows:

# make dedicated folder
mkdir /tmp/mydir

# create autoexec
cat > /tmp/mydir/autoexec.sas <<'EOL'
data _null_;
  /* remove the dummy SYSIN */
  length fname $8;
  rc=filename(fname,getoption('SYSIN') );
  if rc = 0 and fexist(fname) then rc=fdelete(fname);
  rc=filename(fname);
  /* now wait for the real SYSIN */
  slept=0;
  do until ( fileexist(getoption('SYSIN')) or slept>(60*15) );
    slept=slept+sleep(0.1,1);
  end;
run;
EOL

# create dummy SYSIN
touch /tmp/mydir/code.sas

# invoke SAS
/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe/sas -LOG /tmp/mydir/demo.log  -SYSIN /tmp/mydir/code.sas -AUTOEXEC /tmp/mydir/autoexec.sas -WORK /tmp/mydir

In another shell, execute the following:

# create sasfile
cat > /tmp/mydir/code.sas <<'EOL'
%put start NOW;
EOL

And the first shell should finish & execute.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.