diff --git a/.all-contributorsrc b/.all-contributorsrc index 187ba80..cba9f96 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -106,6 +106,16 @@ "userTesting", "doc" ] + }, + { + "login": "saramartinelli1992", + "name": "Sara", + "avatar_url": "https://avatars.githubusercontent.com/u/100193908?v=4", + "profile": "https://github.com/saramartinelli1992", + "contributions": [ + "userTesting", + "platform" + ] } ], "contributorsPerLine": 7, diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 0000000..cea3c67 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,12 @@ +## Expected behaviour +*Describe what should be happening* + +## Current behaviour +*Describe what is actually happening* + +## Environment info +**Client tech stack**: *Angular, React, Vue, VanillaJS, NodeJS etc.* +**Server type**: SASJS|SASVIYA|SAS9 +**Login mechanism**: Default|Redirected +**Debug**: true|false +**Use Compute Api (relevant only on VIYA)**: true|false diff --git a/.github/reviewer-lottery.yml b/.github/reviewer-lottery.yml index e96566d..0c3b845 100644 --- a/.github/reviewer-lottery.yml +++ b/.github/reviewer-lottery.yml @@ -5,7 +5,3 @@ groups: - YuryShkoda - medjedovicm - sabhas - - name: SASjs QA - reviewers: 1 - usernames: - - VladislavParhomchik diff --git a/.github/vpn/config.ovpn b/.github/vpn/config.ovpn index abd1a74..85139b0 100644 --- a/.github/vpn/config.ovpn +++ b/.github/vpn/config.ovpn @@ -1,30 +1,25 @@ -cipher AES-256-CBC -setenv FORWARD_COMPATIBLE 1 +# Client client -server-poll-timeout 4 -nobind -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 443 tcp -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 1194 udp -remote vpn.analytium.co.uk 1194 udp +tls-client dev tun -dev-type tun -ns-cert-type server -setenv opt tls-version-min 1.0 or-highest -reneg-sec 604800 -sndbuf 0 -rcvbuf 0 -# NOTE: LZO commands are pushed by the Access Server at connect time. -# NOTE: The below line doesn't disable LZO. -comp-lzo no -verb 3 -setenv PUSH_PEER_INFO +# this will connect with whatever proto DNS tells us (https://community.openvpn.net/openvpn/ticket/934) +proto tcp +remote vpn.4gl.io 7494 +resolv-retry infinite +cipher AES-256-CBC +auth SHA256 +script-security 2 +keepalive 10 120 +remote-cert-tls server +# Keys ca ca.crt cert user.crt key user.key tls-auth tls.key 1 + +# Security +nobind +persist-key +persist-tun +verb 3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5988625..8f293d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [lts/fermium] + node-version: [lts/hydrogen] steps: - uses: actions/checkout@v2 @@ -39,12 +39,6 @@ jobs: env: CI: true - - name: Install SSH Key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.DCGITLAB_KEY }} - known_hosts: 'placeholder' - - name: Write VPN Files run: | echo "$CA_CRT" > .github/vpn/ca.crt @@ -63,13 +57,16 @@ jobs: sudo apt install apt-transport-https sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub sudo apt-key add openvpn-repo-pkg-key.pub - sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-focal.list + sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-jammy.list sudo apt update - sudo apt install openvpn3=16~beta+focal + sudo apt install openvpn3=17~betaUb22042+jammy - name: Start Open VPN 3 run: openvpn3 session-start --config .github/vpn/config.ovpn + - name: install pm2 + run: npm i -g pm2 + - name: Deploy sasjs-tests run: | npm install -g replace-in-files-cli @@ -80,12 +77,12 @@ jobs: replace-in-files --regex='"userName".*' --replacement='"userName":"${{ secrets.SASJS_USERNAME }}",' ./public/config.json replace-in-files --regex='"password".*' --replacement='"password":"${{ secrets.SASJS_PASSWORD }}",' ./public/config.json replace-in-files --regex='"serverType".*' --replacement='"serverType":"SASJS",' ./public/config.json - npm run update:adapter && npm run build - scp -o stricthostkeychecking=no -r ./build/* ${{ secrets.DCGITLAB_DEPLOY_PATH_VIYA }} + npm run update:adapter + pm2 start --name sasjs-test npm -- start - name: Run cypress on sasjs run: | - replace-in-files --regex='"sasjsTestsUrl".*' --replacement='"sasjsTestsUrl":"${{ secrets.SASJS_TEST_URL_VIYA }}",' ./cypress.json + replace-in-files --regex='"sasjsTestsUrl".*' --replacement='"sasjsTestsUrl":"http://localhost:3000",' ./cypress.json replace-in-files --regex='"username".*' --replacement='"username":"${{ secrets.SASJS_USERNAME }}",' ./cypress.json replace-in-files --regex='"password".*' --replacement='"password":"${{ secrets.SASJS_PASSWORD }}",' ./cypress.json sh ./sasjs-tests/sasjs-cypress-run.sh ${{ secrets.MATRIX_TOKEN }} https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/generateDocs.yml b/.github/workflows/generateDocs.yml new file mode 100644 index 0000000..9fe2db2 --- /dev/null +++ b/.github/workflows/generateDocs.yml @@ -0,0 +1,44 @@ +name: Generate docs and Push to docs Branch + +on: + push: + branches: + - master + +jobs: + generate_and_push_docs: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [lts/hydrogen] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install Dependencies + run: npm ci + + - name: Ensure docs folder exists + run: | + rm -rf docs || true # avoid error if docs folder does not exist + mkdir docs + + - name: Generate Docs + run: npm run typedoc + + - name: Push generated docs + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GH_TOKEN }} + publish_branch: gh-pages + publish_dir: ./docs + cname: adapter.sasjs.io + diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 549657d..94629f9 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - node-version: [lts/fermium] + node-version: [lts/hydrogen] steps: - uses: actions/checkout@v2 @@ -34,9 +34,10 @@ jobs: run: npm run build - name: Semantic Release - uses: cycjimmy/semantic-release-action@v2 + uses: cycjimmy/semantic-release-action@v3 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Send Slack message - run: curl -X POST --data-urlencode "payload={\"channel\":\"#sasjs\", \"username\":\"GitHub CI\", \"text\":\"New version of @sasjs/adapter has been released! \n Please deploy and run `dctests` with new adapter to make sure everything is still in place.\", \"icon_emoji\":\":rocket:\"}" ${{ secrets.SLACK_WEBHOOK }} + + - name: Send Matrix message + run: curl -XPOST -d "{\"msgtype\":\"m.text\", \"body\":\"New version of @sasjs/adapter has been released! \n Please deploy and run 'dctests' with new adapter to make sure everything is still in place.\"}" https://matrix.4gl.io/_matrix/client/r0/rooms/!jRebyiGmHZlpfDwYXN:4gl.io/send/m.room.message?access_token=${{ secrets.MATRIX_TOKEN }} diff --git a/.gitignore b/.gitignore index 41db3ac..68c8cae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ node_modules build +docs + .env /coverage -.DS_Store +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 8e404e2..b08ef9c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ SASjs is a open-source framework for building Web Apps on SASยฎ platforms. You c 1 - `npm install @sasjs/adapter` - for use in a nodeJS project (recommended) -2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@3/index.min.js) and use a copy of the latest JS file +2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@4/index.min.js) and use a copy of the latest JS file 3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash. @@ -153,6 +153,14 @@ The response object will contain returned tables and columns. Table names are a The adapter will also cache the logs (if debug enabled) and even the work tables. For performance, it is best to keep debug mode off. +### Session Manager + +To execute a script on Viya a session has to be created first which is time-consuming (~15sec). That is why a Session Manager has been created which is implementing the following logic: + +1. When the first session is requested, we also create one more session (hot session) for future requests. Please notice two pending POST requests to create a session within the same context:  +2. When a subsequent request for a session is received and there is a hot session available (not expired), this session is returned and an asynchronous request to create another hot session is sent. Please notice that there is a pending POST request to create a new session while a job has been already finished execution (POST request with status 201):  +3. When a subsequent request for a session is received and there is no available hot session, 2 requests are sent asynchronously to create a session. The first created session will be returned and another session will be reserved for future requests. + ### Variable Types The SAS type (char/numeric) of the values is determined according to a set of rules: @@ -332,7 +340,7 @@ If you find this library useful, help us grow our star graph! ## Contributors โจ -[](#contributors-) +[](#contributors-) Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -341,18 +349,21 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Executes code on a SAS9 server.
-an array of code lines to execute.
-the user name to log into the current SAS server.
-the password to log into the current SAS server.
-Returns an object containing server URL.
-Updates server URL which is not null.
-URL of the server to be set.
-Generated using TypeDoc
A client for interfacing with the SAS Viya REST API.
-A helper method used to call appendRequest method of RequestClient
-response from sasjs request
-name of program
-a boolean that indicates whether debug was enabled or not
-Creates a compute context on the given server.
-the name of the context to be created.
-the name of the launcher context used by the compute service.
-the ID of the account to run the servers for this context.
-the lines of code to execute during session initialization.
-an access token for an authorized user.
-an optional list of authorized user IDs.
-Creates a file. Path to or URI of the parent folder is required.
-the name of the new file.
-the content of the new file in Buffer.
-the full path to the parent folder. If not - provided, the parentFolderUri must be provided.
-the URI (eg /folders/folders/UUID) of the parent - folder. If not provided, the parentFolderPath must be provided.
-an access token for authorizing the request.
-Creates a folder. Path to or URI of the parent folder is required.
-the name of the new folder.
-the full path to the parent folder. If not - provided, the parentFolderUri must be provided.
-the URI (eg /folders/folders/UUID) of the parent - folder. If not provided, the parentFolderPath must be provided.
-an access token for authorizing the request.
-flag that indicates if target folder already exists, it and all subfolders have to be deleted.
-Creates a Job in the specified folder (or folder uri).
-the name of the new job to be created.
-the SAS code for the new job.
-the location of the new job.
-the URI location of the new job. The function is a -little faster if the folder URI is supplied instead of the path.
-Creates a launcher context on the given server.
-the name of the context to be created.
-the description of the context to be created.
-launch type of the context to be created.
-an access token for an authorized user.
-Creates a session on the given context.
-the name of the context to create a session on.
-an access token for an authorized user.
-Deletes the client representing the supplied ID.
-the client ID to authenticate with.
-an access token for authorizing the request.
-Deletes a compute context on the given server.
-the name of the context to be deleted.
-an access token for an authorized user.
-For performance (and in case of accidental error) the deleteFolder function does not actually delete the folder (and all its content and subfolder content). Instead the folder is simply moved to the recycle bin. Deletion time will be added to the folder name.
the full path (eg /Public/example/deleteThis) of the folder to be deleted.
an access token for authorizing the request.
-Updates a compute context on the given server.
-the original name of the context to be updated.
-an object with the properties to be updated.
-an access token for an authorized user.
-Executes a job via the SAS Viya Compute API.
-the relative path to the job.
-the name of the context where the job is to be executed.
-sets the _debug flag in the job arguments.
-any data to be passed in as input to the job.
-a boolean indicating if the function should wait for a result.
-a boolean indicating whether to expect a _webout response.
-an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
-a boolean that indicates whether the function should print (PID) of the started job.
-an object that represents macro variables.
-Executes a job via the SAS Viya Job Execution API
-the relative or absolute path to the job.
-the name of the context where the job is to be executed.
-sets the _debug flag in the job arguments.
-any data to be passed in as input to the job.
-Executes code on the current SAS Viya server.
-the path to the file being submitted for execution.
-an array of code lines to execute.
-the context to execute the code in.
-an object containing an access token, refresh token, client ID and secret.
-execution data.
-when set to true, the log will be returned.
-when set to true, the automatic _webout fileref will be checked for content, and that content returned. This fileref is used when the Job contains a SASjs web request (as opposed to executing arbitrary SAS code).
-when set to true, function will return the session
-an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
-a boolean that indicates whether the function should print (PID) of the started job.
-an object that represents macro variables.
-Exchanges the auth code for an access token for the given client.
-the client ID to authenticate with.
-the client secret to authenticate with.
-the auth code received from the server.
-Performs a login redirect and returns an auth code for the given client.
-the client ID to authenticate with.
-Returns a JSON representation of a compute context.
-an id of the context to return.
-an access token for an authorized user.
-Returns a JSON representation of a compute context.
-the name of the context to return.
-an access token for an authorized user.
-Returns all available compute contexts on this server.
-an access token for an authorized user.
-Returns an object containing the server URL and root folder name.
-Returns default(system) compute contexts.
-Returns all compute contexts on this server that the user has access to.
-an access token, refresh token, client and secret for an authorized user.
-Fetches a folder. Path to the folder is required.
-the absolute path to the folder.
-an access token for authorizing the request.
-Returns a list of jobs in the currently set root folder.
-Returns all available launcher contexts on this server.
-an access token for an authorized user.
-Lists children folders for given Viya folder.
-the full path (eg /Public/example/myFolder) or URI of the source folder listed. Providing URI instead of path will save one extra request.
an access token for authorizing the request.
-Moves Viya folder to a new location. The folder may be renamed at the same time.
-the full path (eg /Public/example/myFolder) or URI of the source folder to be moved. Providing URI instead of path will save one extra request.
the full path or URI of the parent folder to which the sourceFolder will be moved (eg /Public/newDestination). To move a folder, a user has to have write permissions in targetParentFolder. Providing URI instead of the path will save one extra request.
the name of the "moved" folder. If left blank, the original folder name will be used (eg myFolder in /Public/newDestination/myFolder for the example above). Optional field.
an access token for authorizing the request.
-Exchanges the refresh token for an access token for the given client.
-the client ID to authenticate with.
-the client secret to authenticate with.
-the refresh token received from the server.
-Updates server URL and root folder name, if it was not set.
-the URL of the server.
-the name for root folder.
-Generated using TypeDoc
SASjs is a JavaScript adapter for SAS.
-Checks whether a session is active, or login is required.
-isLoggedIn, and a string userName.Creates a compute context on the given server.
-the name of the context to be created.
-the name of the launcher context used by the compute service.
-the ID of the account to run the servers for this context as.
-the lines of code to execute during session initialization.
-an access token for an authorized user.
-an optional list of authorized user IDs.
-Creates a file in the logical SAS folder tree
-name of the file to be created.
-content of the file to be created.
-the full path (eg /Public/example/myFolder) of the parent folder.
the URI of the parent folder.
-the access token to authorizing the request.
-a client for interfacing with SAS API.
-Creates a folder in the logical SAS folder tree
-name of the folder to be created.
-the full path (eg /Public/example/myFolder) of the parent folder.
the URI of the parent folder.
-the access token to authorizing the request.
-a client for interfacing with SAS API.
-flag that indicates if target folder already exists, it and all subfolders have to be deleted. Applicable for SAS VIYA only.
-Creates a launcher context on the given server.
-the name of the context to be created.
-the description of the context to be created.
-launch type of the context to be created.
-an access token for an authorized user.
-Deletes a compute context on the given server.
-the name of the context to be deleted.
-an access token for an authorized user.
-For performance (and in case of accidental error) the deleteFolder function does not actually delete the folder (and all its content and subfolder content). Instead the folder is simply moved to the recycle bin. Deletion time will be added to the folder name.
the full path (eg /Public/example/deleteThis) of the folder to be deleted.
an access token for authorizing the request.
-Creates the folders and services at the given location appLoc on the given server serverUrl.
the JSON specifying the folders and services to be created.
-the base folder in which to create the new folders and -services. If not provided, is taken from SASjsConfig.
-the server on which to deploy the folders and services. -If not provided, is taken from SASjsConfig.
-an optional access token to be passed in when -using this function from the command line.
-flag that indicates if target folder already exists, it and all subfolders have to be deleted.
-Creates the folders and services at the given location appLoc on the given server serverUrl.
the JSON specifying the folders and services to be created.
-the base folder in which to create the new folders and -services. If not provided, is taken from SASjsConfig.
-a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
-Updates a compute context on the given server.
-the original name of the context to be deleted.
-an object with the properties to be updated.
-an access token for an authorized user.
-Executes code against a SAS 9 server. Requires a runner to be present in -the users home directory in metadata.
-lines of sas code from the file to run.
-a string representing the password.
-Executes sas code in a SAS Viya compute session.
-name of the file to run. It will be converted to path to the file being submitted for execution.
-lines of sas code from the file to run.
-context name on which code will be run on the server.
-(optional) the access token, refresh token, client and secret for authorizing the request.
-(optional) if true, global debug config will be overriden
-Fetches content of the log file
-url of the log file.
-an access token for an authorized user.
-Exchanges the auth code for an access token for the given client.
-the client ID to authenticate with.
-the client secret to authenticate with.
-the auth code received from the server.
-Returns a JSON representation of a compute context.
-an id of the context to return.
-an access token for an authorized user.
-Returns a JSON representation of a compute context.
-the name of the context to return.
-an access token for an authorized user.
-Gets compute contexts.
-an access token for an authorized user.
-Gets default(system) launcher contexts.
-Gets executable compute contexts.
-an access token, refresh token, client and secret for an authorized user.
-Fetches a folder from the SAS file system.
-path of the folder to be fetched.
-the access token to authorize the request.
-Gets launcher contexts.
-an access token for an authorized user.
-this method returns an array of SASjsRequest
-SASjsRequest[]
-Returns the current SASjs configuration.
-Returns the username of the user currently logged in.
-Lists children folders for given Viya folder.
-the full path (eg /Public/example/myFolder) or URI of the source folder listed. Providing URI instead of path will save one extra request.
an access token for authorizing the request.
-Logs into the SAS server with the supplied credentials.
-a string representing the username.
-a string representing the password.
-a string representing the client ID.
-Logs out of the configured SAS server.
-Moves folder to a new location. The folder may be renamed at the same time.
-the full path (eg /Public/example/myFolder) or URI of the source folder to be moved. Providing URI instead of path will save one extra request.
the full path or URI of the parent folder to which the sourceFolder will be moved (eg /Public/newDestination). To move a folder, a user has to have write permissions in targetParentFolder. Providing URI instead of path will save one extra request.
the name of the "moved" folder. If left blank, the original folder name will be used (eg myFolder in /Public/newDestination/myFolder for the example above). Optional field.
an access token for authorizing the request.
-Exchanges the refresh token for an access token for the given client.
-the client ID to authenticate with.
-the client secret to authenticate with.
-the refresh token received from the server.
-Makes a request to program specified in SASjob (could be a Viya Job, a
-SAS 9 Stored Process, or a SASjs Server Stored Program). The response
-object will always contain table names in lowercase, and column names in
-uppercase. Values are returned formatted by default, unformatted
-values can be configured as an option in the %webout macro.
the path to the SAS program (ultimately resolves to
- the SAS _program parameter to run a Job Definition or SAS 9 Stored
- Process). Is prepended at runtime with the value of appLoc.
a JSON object containing one or more tables to be sent to
-SAS. For an example of the table structure, see the project README. This
-value can be null if no inputs are required.
provide any changes to the config here, for instance to
-enable/disable debug. Any change provided will override the global config,
-for that particular function call.
a function that is called if the
-user is not logged in (eg to display a login form). The request will be
-resubmitted after successful login.
-When using a loginRequiredCallback, the call to the request will look, for example, like so:
-await request(sasJobPath, data, config, () => setIsLoggedIn(false))
-If you are not passing in any data and configuration, it will look like so:
-await request(sasJobPath, {}, {}, () => setIsLoggedIn(false))
a array of predefined values that are used -to provide extra attributes (same names as those values) to be added in response -Supported values are declared in ExtraResponseAttributes type.
-Sets the debug state. Turning this on will enable additional logging in the adapter.
-boolean indicating debug state (on/off).
-Sets the SASjs configuration.
-SASjs configuration.
-Kicks off execution of the given job via the compute API.
-the path to the SAS program (ultimately resolves to
- the SAS _program parameter to run a Job Definition or SAS 9 Stored
- Process). Is prepended at runtime with the value of appLoc.
a JSON object containing one or more tables to be sent to
-SAS. Can be null if no inputs required.
provide any changes to the config here, for instance to
-enable/disable debug. Any change provided will override the global config,
-for that particular function call.
a valid client, secret, refresh and access tokens that are authorised to execute compute jobs. -The access token is not required when the user is authenticated via the browser.
-a boolean that indicates whether the function needs to wait for execution to complete.
-an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
-a boolean that indicates whether the function should print (PID) of the started job.
-an object that represents macro variables.
-an object representing the compute session created for the given job.
-Uploads a file to the given service.
-the path to the SAS program (ultimately resolves to
- the SAS _program parameter to run a Job Definition or SAS 9 Stored
- Process). Is prepended at runtime with the value of appLoc.
array of files to be uploaded, including File object and file name.
-request URL parameters.
-provide any changes to the config here, for instance to
-enable/disable debug. Any change provided will override the global config,
-for that particular function call.
a function that is called if the -user is not logged in (eg to display a login form). The request will be -resubmitted after successful login.
-Generated using TypeDoc
Exchanges the auth code for an access token for the given client.
-the client ID to authenticate with.
-the auth code received from the server.
-Performs a login authenticate and returns an auth code for the given client.
-a string representing the username.
-a string representing the password.
-the client ID to authenticate with.
-Exchanges the refresh token for an access token.
-the refresh token received from the server.
-Generated using TypeDoc
Specifies the configuration for the SASjs instance - eg where and how to -connect to SAS.
-The appLoc is the parent folder under which the SAS services (STPs or Job
-Execution Services) are stored. We recommend that each app is stored in
-a dedicated parent folder (the appLoc) and the services are grouped inside
-subfolders within the appLoc - allowing functionality to be restricted
-according to those groups at backend.
-When using appLoc, the paths provided in the request function should be
-without a leading slash (/).
The name of the compute context to use when calling the Viya services directly. -Example value: 'SAS Job Execution compute context'
-Set to true to enable additional debugging.
Optional setting to configure HTTPS Agent.
-By providing key, cert, ca to connect with server
-Other options can be set rejectUnauthorized and requestCert
Supported login mechanisms are - Redirected and Default
-The location of the Stored Process Web Application. By default the adapter -will use '/SASStoredProcess/do' on SAS 9.
-The location of the STP Process Web Application. By default the adapter -will use '/SASjsApi/stp/execute' on SAS JS.
-The location of the Job Execution Web Application. By default the adapter -will use '/SASJobExecution' on SAS Viya.
-Optional setting to configure request history limit. Increasing this limit -may affect browser performance, especially with debug (logs) enabled.
-Can be SAS9 or SASVIYA.
The location (including http protocol and port) of the SAS Server. -Can be omitted, eg if serving directly from the SAS Web Server or being -streamed.
-If it's false adapter will use the JES API as connection approach. To enhance VIYA
-performance, set to true and provide a contextName on which to run
-the code. When running on a named context, the code executes under the
-user identity. When running as a Job Execution service, the code runs
-under the identity in the JES context. If useComputeApi is null or undefined, the service will run as a Job, except
-triggered using the APIs instead of the Job Execution Web Service broker.
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
SASjs is a open-source framework for building Web Apps on SASยฎ platforms. You can use as much or as little of it as you like. This repository contains the JS adapter, the part that handles the to/from SAS communication on the client side. There are 3 ways to install it:
-1 - npm install @sasjs/adapter - for use in a nodeJS project (recommended)
2 - Download and use a copy of the latest JS file
-3 - Reference directly from the CDN - in which case click here and select "SRI" to get the script tag with the integrity hash.
-If you are short on time and just need to build an app quickly, then check out this video and the react-seed-app which provides some boilerplate.
-For more information on building web apps with SAS, check out sasjs.io
- - -Ok ok. Deploy this example.html file to your web server, and update servertype to SAS9 or SASVIYA depending on your backend.
The backend part can be deployed as follows:
-%let appLoc=/Public/app/readme; /* Metadata or Viya Folder per SASjs config */
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc; /* compile macros (can also be downloaded & compiled seperately) */
filename ft15f001 temp;
parmcards4;
%webout(FETCH) /* receive all data as SAS datasets */
proc sql;
create table areas as select make,mean(invoice) as avprice
from sashelp.cars
where type in (select type from work.fromjs)
group by 1;
%webout(OPEN)
%webout(OBJ,areas)
%webout(CLOSE)
;;;;
%mp_createwebservice(path=&appLoc/common,name=getdata)
-
-You now have a simple web app with a backend service!
- - -The SASjs adapter is a JS library and a set of SAS Macros that handle the communication between the frontend app and backend SAS services.
-There are three parts to consider:
-To install the library you can simply run npm i @sasjs/adapter or include a <script> tag with a reference to our CDN.
Full technical documentation is available here. The main parts are:
- - -The following code will instantiate an instance of the adapter:
-let sasJs = new SASjs.default(
{
appLoc: "/Your/SAS/Folder",
serverType:"SAS9"
}
);
-
-If you've installed it via NPM, you can import it as a default import like so:
- import SASjs from '@sasjs/adapter';
-
-You can then instantiate it with:
-const sasJs = new SASjs({your config})
-
-More on the config later.
- - -All authentication from the adapter is done against SASLogon. There are two approaches that can be taken, which are configured using the LoginMechanism attribute of the sasJs config object (above):
LoginMechanism:'Redirected' - this approach enables authentication through a SASLogon window, supporting complex authentication flows (such as 2FA) and avoids the need to handle passwords in the application itself. The styling of the window can be modified using CSS.LoginMechanism:'Default' - this approach requires that the username and password are captured, and used within the .login() method. This can be helpful for development, or automated testing.Sample code for logging in with the Default approach:
sasJs.logIn('USERNAME','PASSWORD'
).then((response) => {
if (response.isLoggedIn === true) {
console.log('do stuff')
} else {
console.log('do other stuff')
}
}
-
-More examples of using authentication, and more, can be found in the SASjs Seed Apps on github.
- - -A simple request can be sent to SAS in the following fashion:
-sasJs.request("/path/to/my/service", dataObject)
.then((response) => {
// all tables are in the response object, eg:
console.log(response.tablewith2cols1row[0].COL1.value)
})
-
-We supply the path to the SAS service, and a data object. The data object can be null (for services with no input), or can contain one or more tables in the following format:
-let dataObject={
"tablewith2cols1row": [{
"col1": "val1",
"col2": 42
}],
"tablewith1col2rows": [{
"col": "row1"
}, {
"col": "row2"
}]
};
-
-There are optional parameters such as a config object and a callback login function.
-The response object will contain returned tables and columns. Table names are always lowercase, and column names uppercase.
-The adapter will also cache the logs (if debug enabled) and even the work tables. For performance, it is best to keep debug mode off.
- - -The SAS type (char/numeric) of the values is determined according to a set of rules:
-null is set to either '.' or '' depending on the assigned or derived type per the above rules. If entire column is null then the type will be numeric.The following table illustrates the formats applied to columns under various scenarios:
-| JS Values | -SAS Format | -
|---|---|
| 'a', 'a' | -$char1. | -
| 0, '_' | -best. | -
| 'Z', 0 | -best. | -
| 'a', 'aaa' | -$char3. | -
| null, 'a', 'aaa' | -$char3. | -
| null, 'a', 0 | -best. | -
| null, null | -best. | -
| null, '' | -$char1. | -
| null, 'a' | -$char1. | -
| 'a' | -$char1. | -
| 'a', null | -$char1. | -
| 'a', null, 0 | -best. | -
Validation is also performed on the values. The following combinations will throw errors:
-| JS Values | -SAS Format | -
|---|---|
| null, 'aaaa', 0 | -Error: mixed types. 'aaaa' is not a special missing value. | -
| 0, 'a', '!' | -Error: mixed types. '!' is not a special missing value | -
| 1.1, '.', 0 | -Error: mixed types. For regular nulls, use null |
-
The auto-detect functionality above is thwarted in the following scenarios:
-null values (is considered numeric)To cater for these scenarios, an optional array of formats can be passed along with the data to ensure that SAS will read them in correctly.
-To understand these formats, it should be noted that the JSON data is NOT passed directly (as JSON) to SAS. It is first converted into CSV, and the header row is actually an infile statement in disguise. It looks a bit like this:
CHARVAR1:$char4. CHARVAR2:$char1. NUMVAR:best.
-LOAD,,0
-ABCD,X,.
-
-To provide overrides to this header row, the tables object can be constructed as follows (with a leading '$' in the table name):
-let specialData={
"tablewith2cols2rows": [
{"col1": "val1","specialMissingsCol": "A"},
{"col1": "val2","specialMissingsCol": "_"}
],
"$tablewith2cols2rows":{"formats":{"specialMissingsCol":"best."}
}
};
-
-It is not necessary to provide formats for ALL the columns, only the ones that need to be overridden.
- - -The SAS side is handled by a number of macros in the macro core library.
-The following snippet shows the process of SAS tables arriving / leaving:
-/* fetch all input tables sent from frontend - they arrive as work tables */
%webout(FETCH)
/* some sas code */
data a b c;
set from js;
run;
%webout(OPEN) /* Open the JSON to be returned */
%webout(OBJ,a) /* Rows in table `a` are objects (easy to use) */
%webout(ARR,b) /* Rows in table `b` are arrays (compact) */
%webout(OBJ,c,fmt=N) /* Table `c` is sent unformatted (raw) */
%webout(OBJ,c,label=d) /* Rename as `d` on JS side */
%webout(CLOSE) /* Close the JSON and add default variables */
-
-By default, special SAS numeric missings (_a-Z) are converted to null in the JSON. If you'd like to preserve these, use the missing=STRING option as follows:
%webout(OBJ,a,missing=STRING)
-
-In this case, special missings (such as .a, .b) are converted to javascript string values ('A', 'B').
Where an entire column is made up of special missing numerics, there would be no way to distinguish it from a single-character column by looking at the values. To cater for this scenario, it is possible to export the variable types (and other attributes such as label and format) by adding a showmeta param to the webout() macro as follows:
%webout(OBJ,a,missing=STRING,showmeta=YES)
-
-
-
- Configuration on the client side involves passing an object on startup, which can also be passed with each request. Technical documentation on the SASjsConfig class is available here. The main config items are:
-appLoc - this is the folder (eg in metadata or SAS Drive) under which the SAS services are created.serverType - either SAS9, SASVIYA or SASJS. The SASJS server type is for use with sasjs/server.serverUrl - the location (including http protocol and port) of the SAS Server. Can be omitted, eg if serving directly from the SAS Web Server, or in streaming mode.debug - if true then SAS Logs and extra debug information is returned.LoginMechanism - either Default or Redirected. See SAS Logon section.useComputeApi - Only relevant when the serverType is SASVIYA. If true the Compute API is used. If false the JES API is used. If null or undefined the Web approach is used.contextName - Compute context on which the requests will be called. If missing or not provided, defaults to Job Execution Compute context.requestHistoryLimit - Request history limit. Increasing this limit may affect browser performance, especially with debug (logs) enabled. Default is 10.The adapter supports a number of approaches for interfacing with Viya (serverType is SASVIYA). For maximum performance, be sure to configure your compute context with reuseServerProcesses as true and a system account in runServerAs. This functionality is available since Viya 3.5. This configuration is supported when creating contexts using the CLI.
In this setup, all requests are routed through the JES web app, at YOURSERVER/SASJobExecution?_program=/your/program. This is the most reliable method, and also the slowest. One request is made to the JES app, and remaining requests (getting job uri, session spawning, passing parameters, running the program, fetching the log) are handled by the SAS server inside the JES app.
{
appLoc:"/Your/Path",
serverType:"SASVIYA",
contextName: 'yourComputeContext'
}
-
-Note - to use the web approach, the useComputeApi property must be undefined or null.
Here we are running Jobs using the Job Execution Service except this time we are making the requests directly using the REST API instead of through the JES Web App. This is helpful when we need to call web services outside of a browser (eg with the SASjs CLI or other commandline tools). To save one network request, the adapter prefetches the JOB URIs and passes them in the __job parameter. Depending on your network bandwidth, it may or may not be faster than the JES Web approach.
This approach (useComputeApi: false) also ensures that jobs are displayed in Environment Manager.
{
appLoc:"/Your/Path",
serverType:"SASVIYA",
useComputeApi: false,
contextName: 'yourComputeContext'
}
-
-
-
- This approach is by far the fastest, as a result of the optimisations we have built into the adapter. With this configuration, in the first sasjs request, we take a URI map of the services in the target folder, and create a session manager. This manager will spawn a additional session every time a request is made. Subsequent requests will use the existing 'hot' session, if it exists. Sessions are always deleted after every use, which actually makes this less resource intensive than a typical JES web app, in which all sessions are kept alive by default for 15 minutes.
-With this approach (useComputeApi: true), the requests/logs will not appear in the list in Environment manager.
{
appLoc:"/Your/Path",
serverType:"SASVIYA",
useComputeApi: true,
contextName: "yourComputeContext"
}
-
-
-
- For more information and examples specific to this adapter you can check out the user guide or the technical documentation.
-For more information on building web apps in general, check out these resources or contact the author directly.
-If you are a SAS 9 or SAS Viya customer you can also request a copy of Data Controller - free for up to 5 users, this tool makes use of all parts of the SASjs framework.
- - -If you find this library useful, help us grow our star graph!
-Thanks goes to these wonderful people (emoji key):
- - - -This project follows the all-contributors specification. Contributions of any kind welcome!
-Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
Generated using TypeDoc
A client for interfacing with the SAS9 REST API.
-