@sasjs/adapter 3.7.0 → 3.7.4

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';
@@ -71,6 +71,8 @@ export declare class RequestClient implements HttpClient {
71
71
  }>;
72
72
  post<T>(url: string, data: any, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
73
73
  [key: string]: string | number;
74
+ }, additionalSettings?: {
75
+ [key: string]: string | number;
74
76
  }): Promise<{
75
77
  result: T;
76
78
  etag: string;
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.7.0"
83
+ "version": "3.7.4"
84
84
  }
@@ -71,6 +71,8 @@ export declare class RequestClient implements HttpClient {
71
71
  }>;
72
72
  post<T>(url: string, data: any, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
73
73
  [key: string]: string | number;
74
+ }, additionalSettings?: {
75
+ [key: string]: string | number;
74
76
  }): Promise<{
75
77
  result: T;
76
78
  etag: string;
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
  ) {
@@ -1114,13 +1116,6 @@ export default class SASjs {
1114
1116
  this.sasViyaApiClient!
1115
1117
  )
1116
1118
 
1117
- this.sasJsJobExecutor = new SasJsJobExecutor(
1118
- this.sasjsConfig.serverUrl,
1119
- this.sasjsConfig.serverType!,
1120
- this.jobsPath,
1121
- this.requestClient
1122
- )
1123
-
1124
1119
  this.sas9JobExecutor = new Sas9JobExecutor(
1125
1120
  this.sasjsConfig.serverUrl,
1126
1121
  this.sasjsConfig.serverType!,
@@ -37,7 +37,10 @@ export class SASjsApiClient {
37
37
  }>(
38
38
  'SASjsApi/drive/deploy',
39
39
  { fileTree: members, appLoc: appLoc },
40
- access_token
40
+ access_token,
41
+ undefined,
42
+ {},
43
+ { maxContentLength: Infinity, maxBodyLength: Infinity }
41
44
  )
42
45
 
43
46
  return Promise.resolve(result)
@@ -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'
@@ -207,7 +207,8 @@ export class RequestClient implements HttpClient {
207
207
  data: any,
208
208
  accessToken: string | undefined,
209
209
  contentType = 'application/json',
210
- overrideHeaders: { [key: string]: string | number } = {}
210
+ overrideHeaders: { [key: string]: string | number } = {},
211
+ additionalSettings: { [key: string]: string | number } = {}
211
212
  ): Promise<{ result: T; etag: string }> {
212
213
  const headers = {
213
214
  ...this.getHeaders(accessToken, contentType),
@@ -215,7 +216,11 @@ export class RequestClient implements HttpClient {
215
216
  }
216
217
 
217
218
  return this.httpClient
218
- .post<T>(url, data, { headers, withCredentials: true })
219
+ .post<T>(url, data, {
220
+ headers,
221
+ withCredentials: true,
222
+ ...additionalSettings
223
+ })
219
224
  .then((response) => {
220
225
  throwIfError(response)
221
226
 
@@ -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
- }