@sasjs/adapter 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { AuthConfig, ExtraResponseAttributes, ServerType } from '@sasjs/utils/types';
2
+ import { RequestClient } from '../request/RequestClient';
3
+ import { BaseJobExecutor } from './JobExecutor';
4
+ export declare class SasJsJobExecutor extends BaseJobExecutor {
5
+ private jobsPath;
6
+ private requestClient;
7
+ constructor(serverUrl: string, serverType: ServerType, jobsPath: string, requestClient: RequestClient);
8
+ execute(sasJob: string, data: any, config: any, loginRequiredCallback?: any, authConfig?: AuthConfig, extraResponseAttributes?: ExtraResponseAttributes[]): Promise<unknown>;
9
+ private getRequestParams;
10
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './ComputeJobExecutor';
2
+ export * from './FileUploader';
2
3
  export * from './JesJobExecutor';
3
4
  export * from './JobExecutor';
4
5
  export * from './Sas9JobExecutor';
6
+ export * from './SasJsJobExecutor';
5
7
  export * from './WebJobExecutor';
6
- export * from './FileUploader';
@@ -35,10 +35,11 @@ export interface HttpClient {
35
35
  export declare class RequestClient implements HttpClient {
36
36
  protected baseUrl: string;
37
37
  private requests;
38
+ private requestsLimit;
38
39
  protected csrfToken: CsrfToken;
39
40
  protected fileUploadCsrfToken: CsrfToken | undefined;
40
41
  protected httpClient: AxiosInstance;
41
- constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions);
42
+ constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number);
42
43
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
43
44
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
44
45
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
@@ -16,7 +16,7 @@ export declare class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string;
19
+ pathSASJS: string;
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -60,7 +60,7 @@ export declare class SASjsConfig {
60
60
  */
61
61
  useComputeApi: boolean | null;
62
62
  /**
63
- * Optional settings to configure HTTPS Agent.
63
+ * Optional setting to configure HTTPS Agent.
64
64
  * By providing `key`, `cert`, `ca` to connect with server
65
65
  * Other options can be set `rejectUnauthorized` and `requestCert`
66
66
  */
@@ -69,6 +69,11 @@ export declare class SASjsConfig {
69
69
  * Supported login mechanisms are - Redirected and Default
70
70
  */
71
71
  loginMechanism: LoginMechanism;
72
+ /**
73
+ * Optional setting to configure request history limit. Increasing this limit
74
+ * may affect browser performance, especially with debug (logs) enabled.
75
+ */
76
+ requestHistoryLimit?: number;
72
77
  }
73
78
  export declare enum LoginMechanism {
74
79
  Default = "Default",
@@ -2,7 +2,7 @@
2
2
  * Converts the given JSON object array to a CSV string.
3
3
  * @param data - the array of JSON objects to convert.
4
4
  */
5
- export declare const convertToCSV: (data: any, sasFormats?: {
5
+ export declare const convertToCSV: (data: any[], sasFormats?: {
6
6
  formats: {
7
7
  [key: string]: string;
8
8
  };
package/package.json CHANGED
@@ -75,10 +75,10 @@
75
75
  "dependencies": {
76
76
  "@sasjs/utils": "2.35.0",
77
77
  "axios": "0.26.0",
78
- "axios-cookiejar-support": "2.0.3",
78
+ "axios-cookiejar-support": "1.0.1",
79
79
  "form-data": "4.0.0",
80
80
  "https": "1.0.0",
81
81
  "tough-cookie": "4.0.0"
82
82
  },
83
- "version": "3.5.0"
83
+ "version": "3.7.0"
84
84
  }
@@ -35,10 +35,11 @@ export interface HttpClient {
35
35
  export declare class RequestClient implements HttpClient {
36
36
  protected baseUrl: string;
37
37
  private requests;
38
+ private requestsLimit;
38
39
  protected csrfToken: CsrfToken;
39
40
  protected fileUploadCsrfToken: CsrfToken | undefined;
40
41
  protected httpClient: AxiosInstance;
41
- constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions);
42
+ constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number);
42
43
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
43
44
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
44
45
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
package/src/SASjs.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  ComputeJobExecutor,
28
28
  JesJobExecutor,
29
29
  Sas9JobExecutor,
30
+ SasJsJobExecutor,
30
31
  FileUploader
31
32
  } from './job-execution'
32
33
  import { ErrorResponse } from './types/errors'
@@ -62,6 +63,7 @@ export default class SASjs {
62
63
  private computeJobExecutor: JobExecutor | null = null
63
64
  private jesJobExecutor: JobExecutor | null = null
64
65
  private sas9JobExecutor: JobExecutor | null = null
66
+ private sasJsJobExecutor: JobExecutor | null = null
65
67
 
66
68
  constructor(config?: Partial<SASjsConfig>) {
67
69
  this.sasjsConfig = {
@@ -76,6 +78,12 @@ export default class SASjs {
76
78
  return this.requestClient?.getCsrfToken(type)
77
79
  }
78
80
 
81
+ /**
82
+ * Executes the sas code against SAS9 server
83
+ * @param linesOfCode - lines of sas code from the file to run.
84
+ * @param username - a string representing the username.
85
+ * @param password - a string representing the password.
86
+ */
79
87
  public async executeScriptSAS9(
80
88
  linesOfCode: string[],
81
89
  userName: string,
@@ -90,6 +98,38 @@ export default class SASjs {
90
98
  )
91
99
  }
92
100
 
101
+ /**
102
+ * Executes the sas code against SASViya server
103
+ * @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
104
+ * @param linesOfCode - lines of sas code from the file to run.
105
+ * @param contextName - context name on which code will be run on the server.
106
+ * @param authConfig - (optional) the access token, refresh token, client and secret for authorizing the request.
107
+ * @param debug - (optional) if true, global debug config will be overriden
108
+ */
109
+ public async executeScriptSASViya(
110
+ fileName: string,
111
+ linesOfCode: string[],
112
+ contextName: string,
113
+ authConfig?: AuthConfig,
114
+ debug?: boolean
115
+ ) {
116
+ this.isMethodSupported('executeScriptSASViya', [ServerType.SasViya])
117
+ if (!contextName) {
118
+ throw new Error(
119
+ 'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
120
+ )
121
+ }
122
+
123
+ return await this.sasViyaApiClient!.executeScript(
124
+ fileName,
125
+ linesOfCode,
126
+ contextName,
127
+ authConfig,
128
+ null,
129
+ debug ? debug : this.sasjsConfig.debug
130
+ )
131
+ }
132
+
93
133
  /**
94
134
  * Gets compute contexts.
95
135
  * @param accessToken - an access token for an authorized user.
@@ -253,38 +293,6 @@ export default class SASjs {
253
293
  return await this.sasViyaApiClient!.createSession(contextName, accessToken)
254
294
  }
255
295
 
256
- /**
257
- * Executes the sas code against given sas server
258
- * @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
259
- * @param linesOfCode - lines of sas code from the file to run.
260
- * @param contextName - context name on which code will be run on the server.
261
- * @param authConfig - (optional) the access token, refresh token, client and secret for authorizing the request.
262
- * @param debug - (optional) if true, global debug config will be overriden
263
- */
264
- public async executeScriptSASViya(
265
- fileName: string,
266
- linesOfCode: string[],
267
- contextName: string,
268
- authConfig?: AuthConfig,
269
- debug?: boolean
270
- ) {
271
- this.isMethodSupported('executeScriptSASViya', [ServerType.SasViya])
272
- if (!contextName) {
273
- throw new Error(
274
- 'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
275
- )
276
- }
277
-
278
- return await this.sasViyaApiClient!.executeScript(
279
- fileName,
280
- linesOfCode,
281
- contextName,
282
- authConfig,
283
- null,
284
- debug ? debug : this.sasjsConfig.debug
285
- )
286
- }
287
-
288
296
  /**
289
297
  * Creates a folder in the logical SAS folder tree
290
298
  * @param folderName - name of the folder to be created.
@@ -675,7 +683,16 @@ export default class SASjs {
675
683
  const validationResult = this.validateInput(data)
676
684
 
677
685
  if (validationResult.status) {
678
- if (
686
+ if (config.serverType === ServerType.Sasjs) {
687
+ return await this.sasJsJobExecutor!.execute(
688
+ sasJob,
689
+ data,
690
+ config,
691
+ loginRequiredCallback,
692
+ authConfig,
693
+ extraResponseAttributes
694
+ )
695
+ } else if (
679
696
  config.serverType !== ServerType.Sas9 &&
680
697
  config.useComputeApi !== undefined &&
681
698
  config.useComputeApi !== null
@@ -1017,7 +1034,8 @@ export default class SASjs {
1017
1034
  : RequestClient
1018
1035
  this.requestClient = new RequestClientClass(
1019
1036
  this.sasjsConfig.serverUrl,
1020
- this.sasjsConfig.httpsAgentOptions
1037
+ this.sasjsConfig.httpsAgentOptions,
1038
+ this.sasjsConfig.requestHistoryLimit
1021
1039
  )
1022
1040
  } else {
1023
1041
  this.requestClient.setConfig(
@@ -1031,7 +1049,7 @@ export default class SASjs {
1031
1049
  ? this.sasjsConfig.pathSASViya
1032
1050
  : this.sasjsConfig.serverType === ServerType.Sas9
1033
1051
  ? this.sasjsConfig.pathSAS9
1034
- : this.sasjsConfig.pathSASJS || ''
1052
+ : this.sasjsConfig.pathSASJS
1035
1053
 
1036
1054
  this.authManager = new AuthManager(
1037
1055
  this.sasjsConfig.serverUrl,
@@ -1096,6 +1114,13 @@ export default class SASjs {
1096
1114
  this.sasViyaApiClient!
1097
1115
  )
1098
1116
 
1117
+ this.sasJsJobExecutor = new SasJsJobExecutor(
1118
+ this.sasjsConfig.serverUrl,
1119
+ this.sasjsConfig.serverType!,
1120
+ this.jobsPath,
1121
+ this.requestClient
1122
+ )
1123
+
1099
1124
  this.sas9JobExecutor = new Sas9JobExecutor(
1100
1125
  this.sasjsConfig.serverUrl,
1101
1126
  this.sasjsConfig.serverType!,
@@ -5,8 +5,11 @@ export const generateFileUploadForm = (
5
5
  data: any
6
6
  ): FormData => {
7
7
  for (const tableName in data) {
8
+ if (!Array.isArray(data[tableName])) continue
9
+
8
10
  const name = tableName
9
11
  const csv = convertToCSV(data[tableName])
12
+
10
13
  if (csv === 'ERROR: LARGE STRING LENGTH') {
11
14
  throw new Error(
12
15
  'The max length of a string value in SASjs is 32765 characters.'
@@ -0,0 +1,123 @@
1
+ import {
2
+ AuthConfig,
3
+ ExtraResponseAttributes,
4
+ ServerType
5
+ } from '@sasjs/utils/types'
6
+ import {
7
+ ErrorResponse,
8
+ JobExecutionError,
9
+ LoginRequiredError
10
+ } from '../types/errors'
11
+ import { RequestClient } from '../request/RequestClient'
12
+ import {
13
+ isRelativePath,
14
+ appendExtraResponseAttributes,
15
+ getValidJson
16
+ } from '../utils'
17
+ import { BaseJobExecutor } from './JobExecutor'
18
+ import { parseWeboutResponse } from '../utils/parseWeboutResponse'
19
+
20
+ export class SasJsJobExecutor extends BaseJobExecutor {
21
+ constructor(
22
+ serverUrl: string,
23
+ serverType: ServerType,
24
+ private jobsPath: string,
25
+ private requestClient: RequestClient
26
+ ) {
27
+ super(serverUrl, serverType)
28
+ }
29
+
30
+ async execute(
31
+ sasJob: string,
32
+ data: any,
33
+ config: any,
34
+ loginRequiredCallback?: any,
35
+ authConfig?: AuthConfig,
36
+ extraResponseAttributes: ExtraResponseAttributes[] = []
37
+ ) {
38
+ const loginCallback = loginRequiredCallback || (() => Promise.resolve())
39
+ const program = isRelativePath(sasJob)
40
+ ? config.appLoc
41
+ ? config.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
42
+ : sasJob
43
+ : sasJob
44
+ let apiUrl = `${config.serverUrl}${this.jobsPath}/?${'_program=' + program}`
45
+
46
+ const requestParams = this.getRequestParams(config)
47
+
48
+ const requestPromise = new Promise((resolve, reject) => {
49
+ this.requestClient!.post(
50
+ apiUrl,
51
+ { ...requestParams, ...data },
52
+ authConfig?.access_token
53
+ )
54
+ .then(async (res: any) => {
55
+ const resObj = {
56
+ result: res.result._webout,
57
+ log: res.result.log
58
+ }
59
+ this.requestClient!.appendRequest(resObj, sasJob, config.debug)
60
+
61
+ let jsonResponse = res.result
62
+
63
+ if (config.debug) {
64
+ const webout = parseWeboutResponse(res.result._webout, apiUrl)
65
+ jsonResponse = getValidJson(webout)
66
+ } else {
67
+ jsonResponse = getValidJson(res.result._webout)
68
+ }
69
+
70
+ const responseObject = appendExtraResponseAttributes(
71
+ { result: jsonResponse },
72
+ extraResponseAttributes
73
+ )
74
+ resolve(responseObject)
75
+ })
76
+ .catch(async (e: Error) => {
77
+ if (e instanceof JobExecutionError) {
78
+ this.requestClient!.appendRequest(e, sasJob, config.debug)
79
+ reject(new ErrorResponse(e?.message, e))
80
+ }
81
+
82
+ if (e instanceof LoginRequiredError) {
83
+ this.appendWaitingRequest(() => {
84
+ return this.execute(
85
+ sasJob,
86
+ data,
87
+ config,
88
+ loginRequiredCallback,
89
+ authConfig,
90
+ extraResponseAttributes
91
+ ).then(
92
+ (res: any) => {
93
+ resolve(res)
94
+ },
95
+ (err: any) => {
96
+ reject(err)
97
+ }
98
+ )
99
+ })
100
+
101
+ await loginCallback()
102
+ } else {
103
+ reject(new ErrorResponse(e?.message, e))
104
+ }
105
+ })
106
+ })
107
+
108
+ return requestPromise
109
+ }
110
+
111
+ private getRequestParams(config: any): any {
112
+ const requestParams: any = {}
113
+
114
+ if (config.debug) {
115
+ requestParams['_omittextlog'] = 'false'
116
+ requestParams['_omitsessionresults'] = 'false'
117
+
118
+ requestParams['_debug'] = 131
119
+ }
120
+
121
+ return requestParams
122
+ }
123
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './ComputeJobExecutor'
2
+ export * from './FileUploader'
2
3
  export * from './JesJobExecutor'
3
4
  export * from './JobExecutor'
4
5
  export * from './Sas9JobExecutor'
6
+ export * from './SasJsJobExecutor'
5
7
  export * from './WebJobExecutor'
6
- export * from './FileUploader'
@@ -56,6 +56,7 @@ export interface HttpClient {
56
56
 
57
57
  export class RequestClient implements HttpClient {
58
58
  private requests: SASjsRequest[] = []
59
+ private requestsLimit: number = 10
59
60
 
60
61
  protected csrfToken: CsrfToken = { headerName: '', value: '' }
61
62
  protected fileUploadCsrfToken: CsrfToken | undefined
@@ -63,9 +64,11 @@ export class RequestClient implements HttpClient {
63
64
 
64
65
  constructor(
65
66
  protected baseUrl: string,
66
- httpsAgentOptions?: https.AgentOptions
67
+ httpsAgentOptions?: https.AgentOptions,
68
+ requestsLimit?: number
67
69
  ) {
68
70
  this.createHttpClient(baseUrl, httpsAgentOptions)
71
+ if (requestsLimit) this.requestsLimit = requestsLimit
69
72
  }
70
73
 
71
74
  public setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions) {
@@ -149,7 +152,7 @@ export class RequestClient implements HttpClient {
149
152
  SASWORK: sasWork
150
153
  })
151
154
 
152
- if (this.requests.length > 20) {
155
+ if (this.requests.length > this.requestsLimit) {
153
156
  this.requests.splice(0, 1)
154
157
  }
155
158
  }
@@ -1,6 +1,6 @@
1
1
  import * as https from 'https'
2
2
  import { AxiosRequestConfig } from 'axios'
3
- import * as axiosCookieJarSupport from 'axios-cookiejar-support'
3
+ import axiosCookieJarSupport from 'axios-cookiejar-support'
4
4
  import * as tough from 'tough-cookie'
5
5
  import { prefixMessage } from '@sasjs/utils/error'
6
6
  import { RequestClient, throwIfError } from './RequestClient'
@@ -17,7 +17,7 @@ export class Sas9RequestClient extends RequestClient {
17
17
  status >= 200 && status < 303
18
18
 
19
19
  if (axiosCookieJarSupport) {
20
- axiosCookieJarSupport.wrapper(this.httpClient)
20
+ axiosCookieJarSupport(this.httpClient)
21
21
  this.httpClient.defaults.jar = new tough.CookieJar()
22
22
  }
23
23
  }
@@ -16,7 +16,7 @@ export class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string = ''
19
+ pathSASJS: string = ''
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -60,7 +60,7 @@ export class SASjsConfig {
60
60
  */
61
61
  useComputeApi: boolean | null = null
62
62
  /**
63
- * Optional settings to configure HTTPS Agent.
63
+ * Optional setting to configure HTTPS Agent.
64
64
  * By providing `key`, `cert`, `ca` to connect with server
65
65
  * Other options can be set `rejectUnauthorized` and `requestCert`
66
66
  */
@@ -69,6 +69,11 @@ export class SASjsConfig {
69
69
  * Supported login mechanisms are - Redirected and Default
70
70
  */
71
71
  loginMechanism: LoginMechanism = LoginMechanism.Default
72
+ /**
73
+ * Optional setting to configure request history limit. Increasing this limit
74
+ * may affect browser performance, especially with debug (logs) enabled.
75
+ */
76
+ requestHistoryLimit?: number = 10
72
77
  }
73
78
 
74
79
  export enum LoginMechanism {
@@ -3,7 +3,7 @@
3
3
  * @param data - the array of JSON objects to convert.
4
4
  */
5
5
  export const convertToCSV = (
6
- data: any,
6
+ data: any[],
7
7
  sasFormats?: { formats: { [key: string]: string } }
8
8
  ) => {
9
9
  let formats = sasFormats?.formats
@@ -76,7 +76,7 @@ export const convertToCSV = (
76
76
  return byteSize
77
77
  }
78
78
  })
79
- .sort((a: number, b: number) => b - a)[0]
79
+ .sort((a: any, b: any) => b - a)[0]
80
80
 
81
81
  if (longestValueForField && longestValueForField > 32765) {
82
82
  invalidString = true
@@ -16,7 +16,7 @@ export declare class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string;
19
+ pathSASJS: string;
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -60,7 +60,7 @@ export declare class SASjsConfig {
60
60
  */
61
61
  useComputeApi: boolean | null;
62
62
  /**
63
- * Optional settings to configure HTTPS Agent.
63
+ * Optional setting to configure HTTPS Agent.
64
64
  * By providing `key`, `cert`, `ca` to connect with server
65
65
  * Other options can be set `rejectUnauthorized` and `requestCert`
66
66
  */
@@ -69,6 +69,11 @@ export declare class SASjsConfig {
69
69
  * Supported login mechanisms are - Redirected and Default
70
70
  */
71
71
  loginMechanism: LoginMechanism;
72
+ /**
73
+ * Optional setting to configure request history limit. Increasing this limit
74
+ * may affect browser performance, especially with debug (logs) enabled.
75
+ */
76
+ requestHistoryLimit?: number;
72
77
  }
73
78
  export declare enum LoginMechanism {
74
79
  Default = "Default",
@@ -2,7 +2,7 @@
2
2
  * Converts the given JSON object array to a CSV string.
3
3
  * @param data - the array of JSON objects to convert.
4
4
  */
5
- export declare const convertToCSV: (data: any, sasFormats?: {
5
+ export declare const convertToCSV: (data: any[], sasFormats?: {
6
6
  formats: {
7
7
  [key: string]: string;
8
8
  };