From 8cc4270e484dcb4d411d438e91a4092d6e547289 Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 22 Jun 2021 13:19:11 +0200 Subject: [PATCH 1/7] fix: web approach contextname, upload file: context name and debug parameter --- README.md | 10 ++++++---- src/FileUploader.ts | 22 ++++++++++++++++------ src/SASjs.ts | 13 ++++++------- src/job-execution/WebJobExecutor.ts | 21 ++++++++++++--------- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 9d267fd..522c5fa 100644 --- a/README.md +++ b/README.md @@ -172,8 +172,8 @@ Configuration on the client side involves passing an object on startup, which ca * `serverType` - either `SAS9` or `SASVIYA`. * `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. -* `useComputeApi` - if `true` and the serverType is `SASVIYA` then the REST APIs will be called directly (rather than using the JES web service). -* `contextName` - if missing or blank, and `useComputeApi` is `true` and `serverType` is `SASVIYA` then the JES API will be used. +* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](https://github.com/sasjs/adapter#using-the-compute-api) is used. If `false` the [JES API](https://github.com/sasjs/adapter#using-the-jes-api) is used. If `null` or `undefined` the [Web](https://github.com/sasjs/adapter#using-jes-web-app) approach is used. +* `contextName` - Context on which the reqeusts will be called. The adapter supports a number of approaches for interfacing with Viya (`serverType` is `SASVIYA`). For maximum performance, be sure to [configure your compute context](https://sasjs.io/guide-viya/#shared-account-and-server-re-use) 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](https://sasjs.io/sasjs-cli-context/#sasjs-context-create). @@ -184,7 +184,8 @@ In this setup, all requests are routed through the JES web app, at `YOURSERVER/S ``` { appLoc:"/Your/Path", - serverType:"SASVIYA" + serverType:"SASVIYA", + contextName: 'yourComputeContext' } ``` @@ -195,7 +196,8 @@ Here we are running Jobs using the Job Execution Service except this time we are { appLoc:"/Your/Path", serverType:"SASVIYA", - useComputeApi: true + useComputeApi: false, + contextName: 'yourComputeContext' } ``` diff --git a/src/FileUploader.ts b/src/FileUploader.ts index c9cecc6..74b70b1 100644 --- a/src/FileUploader.ts +++ b/src/FileUploader.ts @@ -2,15 +2,19 @@ import { isUrl } from './utils' import { UploadFile } from './types/UploadFile' import { ErrorResponse, LoginRequiredError } from './types/errors' import { RequestClient } from './request/RequestClient' +import { ServerType } from '@sasjs/utils/types' +import SASjs from './SASjs' +import { Server } from 'https' +import { SASjsConfig } from './types' +import { config } from 'process' export class FileUploader { constructor( - private appLoc: string, - serverUrl: string, + private sasjsConfig: SASjsConfig, private jobsPath: string, private requestClient: RequestClient ) { - if (serverUrl) isUrl(serverUrl) + if (this.sasjsConfig.serverUrl) isUrl(this.sasjsConfig.serverUrl) } public uploadFile(sasJob: string, files: UploadFile[], params: any) { @@ -29,12 +33,17 @@ export class FileUploader { } } - const program = this.appLoc - ? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '') + const program = this.sasjsConfig.appLoc + ? this.sasjsConfig.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '') : sasJob const uploadUrl = `${this.jobsPath}/?${ '_program=' + program - }${paramsString}` + }${paramsString}${ + this.sasjsConfig.serverType === ServerType.SasViya && + this.sasjsConfig.contextName + ? '&_contextname=' + this.sasjsConfig.contextName + : '' + }` const formData = new FormData() @@ -44,6 +53,7 @@ export class FileUploader { const csrfToken = this.requestClient.getCsrfToken('file') if (csrfToken) formData.append('_csrf', csrfToken.value) + if (this.sasjsConfig.debug) formData.append('_debug', '131') const headers = { 'cache-control': 'no-cache', diff --git a/src/SASjs.ts b/src/SASjs.ts index 9304a51..151d033 100644 --- a/src/SASjs.ts +++ b/src/SASjs.ts @@ -544,12 +544,7 @@ export default class SASjs { public uploadFile(sasJob: string, files: UploadFile[], params: any) { const fileUploader = this.fileUploader || - new FileUploader( - this.sasjsConfig.appLoc, - this.sasjsConfig.serverUrl, - this.jobsPath, - this.requestClient! - ) + new FileUploader(this.sasjsConfig, this.jobsPath, this.requestClient!) return fileUploader.uploadFile(sasJob, files, params) } @@ -595,7 +590,11 @@ export default class SASjs { const validationResult = this.validateInput(data) if (validationResult.status) { - if (config.serverType === ServerType.SasViya && config.contextName) { + if ( + config.serverType !== ServerType.Sas9 && + config.useComputeApi !== undefined && + config.useComputeApi !== null + ) { if (config.useComputeApi) { return await this.computeJobExecutor!.execute( sasJob, diff --git a/src/job-execution/WebJobExecutor.ts b/src/job-execution/WebJobExecutor.ts index 61f215c..76f0aad 100644 --- a/src/job-execution/WebJobExecutor.ts +++ b/src/job-execution/WebJobExecutor.ts @@ -39,15 +39,18 @@ export class WebJobExecutor extends BaseJobExecutor { ? config.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '') : sasJob : sasJob - const jobUri = - config.serverType === ServerType.SasViya - ? await this.getJobUri(sasJob) - : '' - const apiUrl = `${config.serverUrl}${this.jobsPath}/?${ - jobUri.length > 0 - ? '__program=' + program + '&_job=' + jobUri - : '_program=' + program - }` + let apiUrl = `${config.serverUrl}${this.jobsPath}/?${'_program=' + program}` + + if (config.serverType === ServerType.SasViya) { + const jobUri = + config.serverType === ServerType.SasViya + ? await this.getJobUri(sasJob) + : '' + + apiUrl += jobUri.length > 0 ? '&_job=' + jobUri : '' + + apiUrl += config.contextName ? `&_contextname=${config.contextName}` : '' + } let requestParams = { ...this.getRequestParams(config) From abc15fb3ab64a4f1d887bdbc869ac8eba9710694 Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 22 Jun 2021 13:23:18 +0200 Subject: [PATCH 2/7] chore: fix file upload call --- src/SASjs.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SASjs.ts b/src/SASjs.ts index 151d033..159ed32 100644 --- a/src/SASjs.ts +++ b/src/SASjs.ts @@ -934,8 +934,7 @@ export default class SASjs { } this.fileUploader = new FileUploader( - this.sasjsConfig.appLoc, - this.sasjsConfig.serverUrl, + this.sasjsConfig, this.jobsPath, this.requestClient ) From 5d7cfe1e6cc35149fe1f6d52b8ba5ad16501ac0a Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 22 Jun 2021 16:51:14 +0200 Subject: [PATCH 3/7] chore: fixing useComputeApi defaults --- src/FileUploader.ts | 16 ++++++++-------- src/SASjs.ts | 2 +- src/types/SASjsConfig.ts | 12 ++++-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/FileUploader.ts b/src/FileUploader.ts index 74b70b1..78a426b 100644 --- a/src/FileUploader.ts +++ b/src/FileUploader.ts @@ -38,12 +38,7 @@ export class FileUploader { : sasJob const uploadUrl = `${this.jobsPath}/?${ '_program=' + program - }${paramsString}${ - this.sasjsConfig.serverType === ServerType.SasViya && - this.sasjsConfig.contextName - ? '&_contextname=' + this.sasjsConfig.contextName - : '' - }` + }${paramsString}` const formData = new FormData() @@ -54,6 +49,7 @@ export class FileUploader { const csrfToken = this.requestClient.getCsrfToken('file') if (csrfToken) formData.append('_csrf', csrfToken.value) if (this.sasjsConfig.debug) formData.append('_debug', '131') + if (this.sasjsConfig.serverType === ServerType.SasViya && this.sasjsConfig.contextName) formData.append('_contextname', this.sasjsConfig.contextName) const headers = { 'cache-control': 'no-cache', @@ -63,8 +59,12 @@ export class FileUploader { return this.requestClient .post(uploadUrl, formData, undefined, 'application/json', headers) - .then((res) => - typeof res.result === 'string' ? JSON.parse(res.result) : res.result + .then((res) => { + let result + + result = typeof res.result === 'string' ? JSON.parse(res.result) : res.result + //TODO: append to SASjs requests + } ) .catch((err: Error) => { if (err instanceof LoginRequiredError) { diff --git a/src/SASjs.ts b/src/SASjs.ts index 159ed32..557d23f 100644 --- a/src/SASjs.ts +++ b/src/SASjs.ts @@ -24,7 +24,7 @@ const defaultConfig: SASjsConfig = { serverType: ServerType.SasViya, debug: false, contextName: 'SAS Job Execution compute context', - useComputeApi: false, + useComputeApi: null, allowInsecureRequests: false } diff --git a/src/types/SASjsConfig.ts b/src/types/SASjsConfig.ts index e83ece0..2bdd84c 100644 --- a/src/types/SASjsConfig.ts +++ b/src/types/SASjsConfig.ts @@ -40,23 +40,19 @@ export class SASjsConfig { */ debug: boolean = true /** - * The name of the compute context to use when calling the Viya APIs directly. + * The name of the compute context to use when calling the Viya services directly. * Example value: 'SAS Job Execution compute context' - * If set to missing or empty, and useComputeApi is true, the adapter will use - * the JES APIs. If provided, the Job Code will be executed in pooled - * compute sessions on this named context. */ contextName: string = '' /** - * Set to `false` to use the Job Execution Web Service. To enhance VIYA + * 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 no `contextName` is provided, - * and `useComputeApi` is `true`, then the service will run as a Job, except + * 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. */ - useComputeApi = false + useComputeApi: boolean | null = null /** * Defaults to `false`. * When set to `true`, the adapter will allow requests to SAS servers that use a self-signed SSL certificate. From 8cf249e8fdcab55e09a00c12b66230648162a6bb Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 22 Jun 2021 16:51:43 +0200 Subject: [PATCH 4/7] style: lint --- src/FileUploader.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/FileUploader.ts b/src/FileUploader.ts index 78a426b..29a7e0c 100644 --- a/src/FileUploader.ts +++ b/src/FileUploader.ts @@ -49,7 +49,11 @@ export class FileUploader { const csrfToken = this.requestClient.getCsrfToken('file') if (csrfToken) formData.append('_csrf', csrfToken.value) if (this.sasjsConfig.debug) formData.append('_debug', '131') - if (this.sasjsConfig.serverType === ServerType.SasViya && this.sasjsConfig.contextName) formData.append('_contextname', this.sasjsConfig.contextName) + if ( + this.sasjsConfig.serverType === ServerType.SasViya && + this.sasjsConfig.contextName + ) + formData.append('_contextname', this.sasjsConfig.contextName) const headers = { 'cache-control': 'no-cache', @@ -62,10 +66,10 @@ export class FileUploader { .then((res) => { let result - result = typeof res.result === 'string' ? JSON.parse(res.result) : res.result + result = + typeof res.result === 'string' ? JSON.parse(res.result) : res.result //TODO: append to SASjs requests - } - ) + }) .catch((err: Error) => { if (err instanceof LoginRequiredError) { return Promise.reject( From d53d1e1e6a5b306d27712309ecec8e10fbd7f9b7 Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Tue, 22 Jun 2021 17:05:13 +0200 Subject: [PATCH 5/7] chore: tests fix --- src/FileUploader.ts | 2 ++ src/test/FileUploader.spec.ts | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/FileUploader.ts b/src/FileUploader.ts index 29a7e0c..148f534 100644 --- a/src/FileUploader.ts +++ b/src/FileUploader.ts @@ -68,6 +68,8 @@ export class FileUploader { result = typeof res.result === 'string' ? JSON.parse(res.result) : res.result + + return result //TODO: append to SASjs requests }) .catch((err: Error) => { diff --git a/src/test/FileUploader.spec.ts b/src/test/FileUploader.spec.ts index 4b6f678..b04130d 100644 --- a/src/test/FileUploader.spec.ts +++ b/src/test/FileUploader.spec.ts @@ -3,7 +3,7 @@ */ import { FileUploader } from '../FileUploader' -import { UploadFile } from '../types' +import { SASjsConfig, UploadFile } from '../types' import { RequestClient } from '../request/RequestClient' import axios from 'axios' jest.mock('axios') @@ -32,9 +32,13 @@ const prepareFilesAndParams = () => { } describe('FileUploader', () => { + const config: SASjsConfig = { + ...new SASjsConfig(), + appLoc: '/sample/apploc' + } + const fileUploader = new FileUploader( - '/sample/apploc', - 'https://sample.server.com', + config, '/jobs/path', new RequestClient('https://sample.server.com') ) From 291e23e40afd2148524cae49d70bbc0d449fb644 Mon Sep 17 00:00:00 2001 From: Allan Bowe <4420615+allanbowe@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:53:12 +0300 Subject: [PATCH 6/7] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 522c5fa..7bda0df 100644 --- a/README.md +++ b/README.md @@ -172,14 +172,14 @@ Configuration on the client side involves passing an object on startup, which ca * `serverType` - either `SAS9` or `SASVIYA`. * `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. -* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](https://github.com/sasjs/adapter#using-the-compute-api) is used. If `false` the [JES API](https://github.com/sasjs/adapter#using-the-jes-api) is used. If `null` or `undefined` the [Web](https://github.com/sasjs/adapter#using-jes-web-app) approach is used. -* `contextName` - Context on which the reqeusts will be called. +* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](#using-the-compute-api) is used. If `false` the [JES API](#using-the-jes-api) is used. If `null` or `undefined` the [Web](#using-jes-web-app) approach is used. +* `contextName` - Compute context on which the requests will be called. If missing or not provided, defaults to `Job Execution Compute context`. The adapter supports a number of approaches for interfacing with Viya (`serverType` is `SASVIYA`). For maximum performance, be sure to [configure your compute context](https://sasjs.io/guide-viya/#shared-account-and-server-re-use) 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](https://sasjs.io/sasjs-cli-context/#sasjs-context-create). ### Using JES Web App -In this setup, all requests are routed through the JES web app, at `YOURSERVER/SASJobExecution`. 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 made on the SAS server by the JES app. +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. ``` { @@ -190,7 +190,7 @@ In this setup, all requests are routed through the JES web app, at `YOURSERVER/S ``` ### Using the JES API -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. +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. ``` { @@ -202,7 +202,7 @@ Here we are running Jobs using the Job Execution Service except this time we are ``` ### Using the Compute API -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 - which spawns an extra session. The next time a request is made, the adapter will use the 'hot' session. Sessions are 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. +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. ``` { @@ -227,4 +227,4 @@ If you are a SAS 9 or SAS Viya customer you can also request a copy of [Data Con If you find this library useful, help us grow our star graph! -![](https://starchart.cc/sasjs/adapter.svg) \ No newline at end of file +![](https://starchart.cc/sasjs/adapter.svg) From 9bd7d8497529b6e2aeeb4e8f349bf9f1e0be570c Mon Sep 17 00:00:00 2001 From: Mihajlo Date: Wed, 23 Jun 2021 11:14:54 +0200 Subject: [PATCH 7/7] style: lint --- src/test/FileUploader.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/FileUploader.spec.ts b/src/test/FileUploader.spec.ts index b04130d..2222d04 100644 --- a/src/test/FileUploader.spec.ts +++ b/src/test/FileUploader.spec.ts @@ -36,7 +36,7 @@ describe('FileUploader', () => { ...new SASjsConfig(), appLoc: '/sample/apploc' } - + const fileUploader = new FileUploader( config, '/jobs/path',