@sasjs/adapter 3.6.1 → 3.7.3

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.
@@ -3,5 +3,4 @@ export * from './FileUploader';
3
3
  export * from './JesJobExecutor';
4
4
  export * from './JobExecutor';
5
5
  export * from './Sas9JobExecutor';
6
- export * from './SasJsJobExecutor';
7
6
  export * from './WebJobExecutor';
@@ -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;
@@ -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",
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  "https": "1.0.0",
81
81
  "tough-cookie": "4.0.0"
82
82
  },
83
- "version": "3.6.1"
83
+ "version": "3.7.3"
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,7 +27,6 @@ import {
27
27
  ComputeJobExecutor,
28
28
  JesJobExecutor,
29
29
  Sas9JobExecutor,
30
- SasJsJobExecutor,
31
30
  FileUploader
32
31
  } from './job-execution'
33
32
  import { ErrorResponse } from './types/errors'
@@ -63,7 +62,6 @@ export default class SASjs {
63
62
  private computeJobExecutor: JobExecutor | null = null
64
63
  private jesJobExecutor: JobExecutor | null = null
65
64
  private sas9JobExecutor: JobExecutor | null = null
66
- private sasJsJobExecutor: JobExecutor | null = null
67
65
 
68
66
  constructor(config?: Partial<SASjsConfig>) {
69
67
  this.sasjsConfig = {
@@ -79,7 +77,8 @@ export default class SASjs {
79
77
  }
80
78
 
81
79
  /**
82
- * Executes the sas code against SAS9 server
80
+ * Executes code against a SAS 9 server. Requires a runner to be present in
81
+ * the users home directory in metadata.
83
82
  * @param linesOfCode - lines of sas code from the file to run.
84
83
  * @param username - a string representing the username.
85
84
  * @param password - a string representing the password.
@@ -99,7 +98,7 @@ export default class SASjs {
99
98
  }
100
99
 
101
100
  /**
102
- * Executes the sas code against SASViya server
101
+ * Executes sas code in a SAS Viya compute session.
103
102
  * @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
104
103
  * @param linesOfCode - lines of sas code from the file to run.
105
104
  * @param contextName - context name on which code will be run on the server.
@@ -643,7 +642,8 @@ export default class SASjs {
643
642
  }
644
643
 
645
644
  /**
646
- * Makes a request to the SAS Service specified in `SASjob`. The response
645
+ * Makes a request to program specified in `SASjob` (could be a Viya Job, a
646
+ * SAS 9 Stored Process, or a SASjs Server Stored Program). The response
647
647
  * object will always contain table names in lowercase, and column names in
648
648
  * uppercase. Values are returned formatted by default, unformatted
649
649
  * values can be configured as an option in the `%webout` macro.
@@ -652,7 +652,8 @@ export default class SASjs {
652
652
  * the SAS `_program` parameter to run a Job Definition or SAS 9 Stored
653
653
  * Process). Is prepended at runtime with the value of `appLoc`.
654
654
  * @param data - a JSON object containing one or more tables to be sent to
655
- * SAS. Can be `null` if no inputs required.
655
+ * SAS. For an example of the table structure, see the project README. This
656
+ * value can be `null` if no inputs are required.
656
657
  * @param config - provide any changes to the config here, for instance to
657
658
  * enable/disable `debug`. Any change provided will override the global config,
658
659
  * for that particular function call.
@@ -682,9 +683,10 @@ export default class SASjs {
682
683
 
683
684
  const validationResult = this.validateInput(data)
684
685
 
686
+ // status is true if the data passes validation checks above
685
687
  if (validationResult.status) {
686
688
  if (config.serverType === ServerType.Sasjs) {
687
- return await this.sasJsJobExecutor!.execute(
689
+ return await this.webJobExecutor!.execute(
688
690
  sasJob,
689
691
  data,
690
692
  config,
@@ -693,7 +695,7 @@ export default class SASjs {
693
695
  extraResponseAttributes
694
696
  )
695
697
  } else if (
696
- config.serverType !== ServerType.Sas9 &&
698
+ config.serverType === ServerType.SasViya &&
697
699
  config.useComputeApi !== undefined &&
698
700
  config.useComputeApi !== null
699
701
  ) {
@@ -1034,7 +1036,8 @@ export default class SASjs {
1034
1036
  : RequestClient
1035
1037
  this.requestClient = new RequestClientClass(
1036
1038
  this.sasjsConfig.serverUrl,
1037
- this.sasjsConfig.httpsAgentOptions
1039
+ this.sasjsConfig.httpsAgentOptions,
1040
+ this.sasjsConfig.requestHistoryLimit
1038
1041
  )
1039
1042
  } else {
1040
1043
  this.requestClient.setConfig(
@@ -1113,13 +1116,6 @@ export default class SASjs {
1113
1116
  this.sasViyaApiClient!
1114
1117
  )
1115
1118
 
1116
- this.sasJsJobExecutor = new SasJsJobExecutor(
1117
- this.sasjsConfig.serverUrl,
1118
- this.sasjsConfig.serverType!,
1119
- this.jobsPath,
1120
- this.requestClient
1121
- )
1122
-
1123
1119
  this.sas9JobExecutor = new Sas9JobExecutor(
1124
1120
  this.sasjsConfig.serverUrl,
1125
1121
  this.sasjsConfig.serverType!,
@@ -1,9 +1,19 @@
1
+ import * as NodeFormData from 'form-data'
1
2
  import { convertToCSV } from '../utils/convertToCsv'
2
3
 
4
+ /**
5
+ * One of the approaches SASjs takes to send tables-formatted JSON (see README)
6
+ * to SAS is as multipart form data, where each table is provided as a specially
7
+ * formatted CSV file.
8
+ * @param formData Different objects are used depending on whether the adapter is
9
+ * running in the browser, or in the CLI
10
+ * @param data Special, tables-formatted JSON (see README)
11
+ * @returns Populated formData
12
+ */
3
13
  export const generateFileUploadForm = (
4
- formData: FormData,
14
+ formData: FormData | NodeFormData,
5
15
  data: any
6
- ): FormData => {
16
+ ): FormData | NodeFormData => {
7
17
  for (const tableName in data) {
8
18
  if (!Array.isArray(data[tableName])) continue
9
19
 
@@ -1,7 +1,11 @@
1
+ import * as NodeFormData from 'form-data'
1
2
  import { convertToCSV } from '../utils/convertToCsv'
2
3
  import { splitChunks } from '../utils/splitChunks'
3
4
 
4
- export const generateTableUploadForm = (formData: FormData, data: any) => {
5
+ export const generateTableUploadForm = (
6
+ formData: FormData | NodeFormData,
7
+ data: any
8
+ ) => {
5
9
  const sasjsTables = []
6
10
  const requestParams: any = {}
7
11
  let tableCounter = 0
@@ -1,3 +1,4 @@
1
+ import * as NodeFormData from 'form-data'
1
2
  import {
2
3
  AuthConfig,
3
4
  ExtraResponseAttributes,
@@ -108,7 +109,12 @@ export class WebJobExecutor extends BaseJobExecutor {
108
109
  ...this.getRequestParams(config)
109
110
  }
110
111
 
111
- let formData = new FormData()
112
+ /**
113
+ * Use the available form data object (FormData in Browser, NodeFormData in
114
+ * Node)
115
+ */
116
+ let formData =
117
+ typeof FormData === 'undefined' ? new NodeFormData() : new FormData()
112
118
 
113
119
  if (data) {
114
120
  const stringifiedData = JSON.stringify(data)
@@ -143,14 +149,30 @@ export class WebJobExecutor extends BaseJobExecutor {
143
149
  }
144
150
  }
145
151
 
152
+ /* The NodeFormData object does not set the request header - so, set it */
153
+ const contentType =
154
+ formData instanceof NodeFormData
155
+ ? `multipart/form-data; boundary=${formData.getBoundary()}`
156
+ : undefined
157
+
146
158
  const requestPromise = new Promise((resolve, reject) => {
147
- this.requestClient!.post(apiUrl, formData, authConfig?.access_token)
159
+ this.requestClient!.post(
160
+ apiUrl,
161
+ formData,
162
+ authConfig?.access_token,
163
+ contentType
164
+ )
148
165
  .then(async (res: any) => {
166
+ const parsedSasjsServerLog =
167
+ this.serverType === ServerType.Sasjs
168
+ ? res.result.log.map((logLine: any) => logLine.line).join('\n')
169
+ : res.result.log
170
+
149
171
  const resObj =
150
172
  this.serverType === ServerType.Sasjs
151
173
  ? {
152
174
  result: res.result._webout,
153
- log: res.result.log
175
+ log: parsedSasjsServerLog
154
176
  }
155
177
  : res
156
178
  this.requestClient!.appendRequest(resObj, sasJob, config.debug)
@@ -173,8 +195,12 @@ export class WebJobExecutor extends BaseJobExecutor {
173
195
  : res.result
174
196
  break
175
197
  case ServerType.Sasjs:
176
- const webout = parseWeboutResponse(res.result._webout, apiUrl)
177
- jsonResponse = getValidJson(webout)
198
+ if (typeof res.result._webout === 'object') {
199
+ jsonResponse = res.result._webout
200
+ } else {
201
+ const webout = parseWeboutResponse(res.result._webout, apiUrl)
202
+ jsonResponse = getValidJson(webout)
203
+ }
178
204
  break
179
205
  }
180
206
  } else if (this.serverType === ServerType.Sasjs) {
@@ -3,5 +3,4 @@ export * from './FileUploader'
3
3
  export * from './JesJobExecutor'
4
4
  export * from './JobExecutor'
5
5
  export * from './Sas9JobExecutor'
6
- export * from './SasJsJobExecutor'
7
6
  export * from './WebJobExecutor'
@@ -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
  }
@@ -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 {
@@ -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",
@@ -1,10 +0,0 @@
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,10 +0,0 @@
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,123 +0,0 @@
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
- }