@sasjs/adapter 4.11.1 → 4.11.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.
Files changed (56) hide show
  1. package/SASViyaApiClient.d.ts +6 -0
  2. package/SASjs.d.ts +1 -1
  3. package/api/viya/writeStream.d.ts +1 -1
  4. package/auth/getTokenRequestErrorPrefix.d.ts +2 -2
  5. package/file/generateFileUploadForm.d.ts +1 -1
  6. package/file/generateTableUploadForm.d.ts +1 -1
  7. package/index-dev.js +77056 -3
  8. package/index.js +77056 -2
  9. package/job-execution/JobExecutor.d.ts +1 -1
  10. package/minified_sas9.js +16558 -2
  11. package/node/SASViyaApiClient.d.ts +6 -0
  12. package/node/SASjs.d.ts +1 -1
  13. package/node/api/viya/writeStream.d.ts +1 -1
  14. package/node/auth/getTokenRequestErrorPrefix.d.ts +2 -2
  15. package/node/file/generateFileUploadForm.d.ts +1 -1
  16. package/node/file/generateTableUploadForm.d.ts +1 -1
  17. package/node/index.js +28150 -2
  18. package/node/job-execution/JobExecutor.d.ts +1 -1
  19. package/node/request/RequestClient.d.ts +11 -2
  20. package/node/types/PollOptions.d.ts +1 -1
  21. package/node/types/RequestClient.d.ts +1 -1
  22. package/node/types/WriteStream.d.ts +6 -3
  23. package/node/utils/getFormData.d.ts +1 -1
  24. package/package.json +13 -11
  25. package/request/RequestClient.d.ts +11 -2
  26. package/spec/SAS9ApiClient.spec.d.ts +1 -0
  27. package/spec/SASjsApiClient.spec.d.ts +1 -0
  28. package/src/SAS9ApiClient.ts +1 -1
  29. package/src/SASViyaApiClient.ts +1 -1
  30. package/src/SASjs.ts +2 -2
  31. package/src/SASjsApiClient.ts +1 -1
  32. package/src/api/viya/writeStream.ts +10 -5
  33. package/src/auth/refreshTokensForViya.ts +1 -1
  34. package/src/file/generateFileUploadForm.ts +1 -1
  35. package/src/file/generateTableUploadForm.ts +1 -1
  36. package/src/job-execution/Sas9JobExecutor.ts +1 -1
  37. package/src/job-execution/SasjsJobExecutor.ts +5 -3
  38. package/src/job-execution/WebJobExecutor.ts +5 -3
  39. package/src/request/RequestClient.ts +73 -81
  40. package/src/request/Sas9RequestClient.ts +5 -5
  41. package/src/test/SAS_server_app.ts +10 -2
  42. package/src/types/WriteStream.ts +9 -3
  43. package/src/utils/convertToCsv.ts +5 -1
  44. package/src/utils/getFormData.ts +2 -2
  45. package/src/utils/validateInput.ts +16 -13
  46. package/types/PollOptions.d.ts +1 -1
  47. package/types/RequestClient.d.ts +1 -1
  48. package/types/WriteStream.d.ts +6 -3
  49. package/types/errors/spec/SAS9AuthError.spec.d.ts +1 -0
  50. package/utils/getFormData.d.ts +1 -1
  51. package/utils/spec/parseSasViyaLog.spec.d.ts +1 -0
  52. package/utils/spec/parseViyaDebugResponse.spec.d.ts +1 -0
  53. package/index-dev.js.LICENSE.txt +0 -110
  54. package/index.js.LICENSE.txt +0 -110
  55. package/minified_sas9.js.LICENSE.txt +0 -15
  56. package/node/index.js.LICENSE.txt +0 -106
@@ -1,6 +1,6 @@
1
1
  import { AuthConfig, ServerType } from '@sasjs/utils/types';
2
2
  import { ExtraResponseAttributes } from '@sasjs/utils/types';
3
- export declare type ExecuteFunction = () => Promise<any>;
3
+ export type ExecuteFunction = () => Promise<any>;
4
4
  export interface JobExecutor {
5
5
  execute: (sasJob: string, data: any, config: any, loginRequiredCallback?: any, authConfig?: AuthConfig, extraResponseAttributes?: ExtraResponseAttributes[]) => Promise<any>;
6
6
  resendWaitingRequests: () => Promise<void>;
@@ -42,6 +42,13 @@ export declare class RequestClient implements HttpClient {
42
42
  etag: string;
43
43
  status: number;
44
44
  }>;
45
+ /**
46
+ * @param contentType Newer version of Axios is more strict so if you don't
47
+ * set the contentType to `form data` while sending a FormData object
48
+ * application/json will be used by default, axios won’t treat it as FormData.
49
+ * Instead, it serializes data as JSON—resulting in a payload like
50
+ * {"sometable":{}} and we lose the multipart/form-data formatting.
51
+ */
45
52
  post<T>(url: string, data: any, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
46
53
  [key: string]: string | number;
47
54
  }, additionalSettings?: {
@@ -79,7 +86,9 @@ export declare class RequestClient implements HttpClient {
79
86
  * @returns - formatted string.
80
87
  */
81
88
  private parseInterceptedBody;
82
- private defaultInterceptionCallBack;
89
+ private handleAxiosResponse;
90
+ private handleAxiosError;
91
+ private formatHeaders;
83
92
  /**
84
93
  * Sets verbose mode.
85
94
  * @param verboseMode - value of the verbose mode, can be true, false or bleached(without extra colors).
@@ -90,7 +99,7 @@ export declare class RequestClient implements HttpClient {
90
99
  * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
91
100
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
92
101
  */
93
- enableVerboseMode: (successCallBack?: (axiosResponse: AxiosResponse | AxiosError) => AxiosResponse<any, any> | AxiosError<unknown, any>, errorCallBack?: (axiosResponse: AxiosResponse | AxiosError) => AxiosResponse<any, any> | AxiosError<unknown, any>) => void;
102
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any>) => void;
94
103
  /**
95
104
  * Turns off verbose mode to log every HTTP response.
96
105
  */
@@ -5,4 +5,4 @@ export interface PollOptions {
5
5
  streamLog?: boolean;
6
6
  logFolderPath?: string;
7
7
  }
8
- export declare type PollStrategy = PollOptions[];
8
+ export type PollStrategy = PollOptions[];
@@ -43,4 +43,4 @@ export interface SasjsParsedResponse<T> {
43
43
  status: number;
44
44
  printOutput?: string;
45
45
  }
46
- export declare type VerboseMode = boolean | 'bleached';
46
+ export type VerboseMode = boolean | 'bleached';
@@ -1,4 +1,7 @@
1
- export interface WriteStream {
2
- write: (content: string, callback: (err?: Error) => any) => void;
3
- path: string;
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { WriteStream as FsWriteStream } from 'fs';
4
+ export interface WriteStream extends FsWriteStream {
5
+ write(chunk: any, encoding?: BufferEncoding | ((error: Error | null | undefined) => void), cb?: (error: Error | null | undefined) => void): boolean;
6
+ path: string | Buffer;
4
7
  }
@@ -1,2 +1,2 @@
1
- import * as NodeFormData from 'form-data';
1
+ import NodeFormData from 'form-data';
2
2
  export declare const getFormData: () => NodeFormData | FormData;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "nodeVersionMessage": "echo \u001b[33m make sure you are running node lts version \u001b[0m",
7
7
  "preinstall": "npm run nodeVersionMessage",
8
8
  "prebuild": "npm run nodeVersionMessage",
9
- "build": "rimraf build && rimraf node && mkdir node && copyfiles -u 1 \"./src/**/*\" ./node && webpack && rimraf build/src && rimraf node",
9
+ "build": "npx rimraf build && npx rimraf node && mkdir node && copyfiles -u 1 \"./src/**/*\" ./node && webpack && npx rimraf build/src && npx rimraf node",
10
10
  "package:lib": "npm run build && copyfiles ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
11
11
  "publish:lib": "npm run build && cd build && npm publish",
12
12
  "lint:fix": "npx prettier --loglevel silent --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --write \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --write \"cypress/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
@@ -45,44 +45,46 @@
45
45
  "license": "ISC",
46
46
  "devDependencies": {
47
47
  "@cypress/webpack-preprocessor": "5.9.1",
48
+ "@types/cors": "^2.8.17",
48
49
  "@types/express": "4.17.13",
49
- "@types/jest": "27.4.0",
50
+ "@types/jest": "29.5.14",
50
51
  "@types/mime": "2.0.3",
51
52
  "@types/pem": "1.9.6",
52
53
  "@types/tough-cookie": "4.0.2",
53
54
  "copyfiles": "2.4.1",
55
+ "cors": "^2.8.5",
54
56
  "cp": "0.2.0",
55
57
  "cypress": "7.7.0",
56
58
  "dotenv": "16.0.0",
57
59
  "express": "4.17.3",
58
- "jest": "27.4.7",
59
- "jest-extended": "2.0.0",
60
+ "jest": "29.7.0",
61
+ "jest-environment-jsdom": "^29.7.0",
62
+ "jest-extended": "4.0.2",
60
63
  "node-polyfill-webpack-plugin": "1.1.4",
61
64
  "path": "0.12.7",
62
65
  "pem": "1.14.5",
63
66
  "prettier": "2.8.7",
64
67
  "process": "0.11.10",
65
- "rimraf": "^6.0.1",
66
68
  "semantic-release": "19.0.3",
67
69
  "terser-webpack-plugin": "5.3.6",
68
- "ts-jest": "27.1.3",
70
+ "ts-jest": "29.2.6",
69
71
  "ts-loader": "9.4.0",
70
72
  "tslint": "6.1.3",
71
73
  "tslint-config-prettier": "1.18.0",
72
74
  "typedoc": "0.23.24",
73
75
  "typedoc-plugin-rename-defaults": "0.6.4",
74
- "typescript": "4.8.3",
76
+ "typescript": "4.9.5",
75
77
  "webpack": "5.76.2",
76
78
  "webpack-cli": "4.9.2"
77
79
  },
78
80
  "main": "index.js",
79
81
  "dependencies": {
80
- "@sasjs/utils": "^3.5.1",
81
- "axios": "0.27.2",
82
- "axios-cookiejar-support": "1.0.1",
82
+ "@sasjs/utils": "3.5.2",
83
+ "axios": "1.8.2",
84
+ "axios-cookiejar-support": "5.0.5",
83
85
  "form-data": "4.0.0",
84
86
  "https": "1.0.0",
85
87
  "tough-cookie": "4.1.3"
86
88
  },
87
- "version": "4.11.1"
89
+ "version": "4.11.3"
88
90
  }
@@ -42,6 +42,13 @@ export declare class RequestClient implements HttpClient {
42
42
  etag: string;
43
43
  status: number;
44
44
  }>;
45
+ /**
46
+ * @param contentType Newer version of Axios is more strict so if you don't
47
+ * set the contentType to `form data` while sending a FormData object
48
+ * application/json will be used by default, axios won’t treat it as FormData.
49
+ * Instead, it serializes data as JSON—resulting in a payload like
50
+ * {"sometable":{}} and we lose the multipart/form-data formatting.
51
+ */
45
52
  post<T>(url: string, data: any, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
46
53
  [key: string]: string | number;
47
54
  }, additionalSettings?: {
@@ -79,7 +86,9 @@ export declare class RequestClient implements HttpClient {
79
86
  * @returns - formatted string.
80
87
  */
81
88
  private parseInterceptedBody;
82
- private defaultInterceptionCallBack;
89
+ private handleAxiosResponse;
90
+ private handleAxiosError;
91
+ private formatHeaders;
83
92
  /**
84
93
  * Sets verbose mode.
85
94
  * @param verboseMode - value of the verbose mode, can be true, false or bleached(without extra colors).
@@ -90,7 +99,7 @@ export declare class RequestClient implements HttpClient {
90
99
  * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
91
100
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
92
101
  */
93
- enableVerboseMode: (successCallBack?: (axiosResponse: AxiosResponse | AxiosError) => AxiosResponse<any, any> | AxiosError<unknown, any>, errorCallBack?: (axiosResponse: AxiosResponse | AxiosError) => AxiosResponse<any, any> | AxiosError<unknown, any>) => void;
102
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any>) => void;
94
103
  /**
95
104
  * Turns off verbose mode to log every HTTP response.
96
105
  */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,6 @@
1
1
  import * as https from 'https'
2
2
  import { generateTimestamp } from '@sasjs/utils/time'
3
- import * as NodeFormData from 'form-data'
3
+ import NodeFormData from 'form-data'
4
4
  import { Sas9RequestClient } from './request/Sas9RequestClient'
5
5
  import { isUrl } from './utils'
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { isRelativePath, isUri, isUrl } from './utils'
2
- import * as NodeFormData from 'form-data'
2
+ import NodeFormData from 'form-data'
3
3
  import {
4
4
  Job,
5
5
  Session,
package/src/SASjs.ts CHANGED
@@ -1170,8 +1170,8 @@ export default class SASjs {
1170
1170
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
1171
1171
  */
1172
1172
  public enableVerboseMode(
1173
- successCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse,
1174
- errorCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse
1173
+ successCallBack?: (response: AxiosResponse) => AxiosResponse,
1174
+ errorCallBack?: (response: AxiosError) => AxiosError
1175
1175
  ) {
1176
1176
  this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
1177
1177
  }
@@ -1,4 +1,4 @@
1
- import * as NodeFormData from 'form-data'
1
+ import NodeFormData from 'form-data'
2
2
  import { AuthConfig, ServerType, ServicePackSASjs } from '@sasjs/utils/types'
3
3
  import { prefixMessage } from '@sasjs/utils/error'
4
4
  import { ExecutionQuery } from './types'
@@ -3,9 +3,14 @@ import { WriteStream } from '../../types'
3
3
  export const writeStream = async (
4
4
  stream: WriteStream,
5
5
  content: string
6
- ): Promise<void> =>
7
- stream.write(content + '\n', (e: any) => {
8
- if (e) return Promise.reject(e)
9
-
10
- return Promise.resolve()
6
+ ): Promise<boolean> => {
7
+ return new Promise((resolve, reject) => {
8
+ stream.write(content + '\n', (err: Error | null | undefined) => {
9
+ if (err) {
10
+ reject(err) // Reject on write error
11
+ } else {
12
+ resolve(true) // Resolve on successful write
13
+ }
14
+ })
11
15
  })
16
+ }
@@ -1,6 +1,6 @@
1
1
  import { SasAuthResponse, ServerType } from '@sasjs/utils/types'
2
2
  import { prefixMessage } from '@sasjs/utils/error'
3
- import * as NodeFormData from 'form-data'
3
+ import NodeFormData from 'form-data'
4
4
  import { RequestClient } from '../request/RequestClient'
5
5
  import { isNode } from '../utils'
6
6
  import { getTokenRequestErrorPrefix } from './getTokenRequestErrorPrefix'
@@ -1,4 +1,4 @@
1
- import * as NodeFormData from 'form-data'
1
+ import NodeFormData from 'form-data'
2
2
  import { convertToCSV } from '../utils/convertToCsv'
3
3
  import { isNode } from '../utils'
4
4
 
@@ -1,4 +1,4 @@
1
- import * as NodeFormData from 'form-data'
1
+ import NodeFormData from 'form-data'
2
2
  import { convertToCSV, isFormatsTable } from '../utils/convertToCsv'
3
3
  import { splitChunks } from '../utils/splitChunks'
4
4
 
@@ -1,6 +1,6 @@
1
1
  import * as https from 'https'
2
2
  import { ServerType } from '@sasjs/utils/types'
3
- import * as NodeFormData from 'form-data'
3
+ import NodeFormData from 'form-data'
4
4
  import { ErrorResponse } from '../types/errors'
5
5
  import { convertToCSV, isRelativePath } from '../utils'
6
6
  import { BaseJobExecutor } from './JobExecutor'
@@ -1,4 +1,4 @@
1
- import * as NodeFormData from 'form-data'
1
+ import NodeFormData from 'form-data'
2
2
  import {
3
3
  AuthConfig,
4
4
  ExtraResponseAttributes,
@@ -73,8 +73,10 @@ export class SasjsJobExecutor extends BaseJobExecutor {
73
73
  /* The NodeFormData object does not set the request header - so, set it */
74
74
  const contentType =
75
75
  formData instanceof NodeFormData && typeof FormData === 'undefined'
76
- ? `multipart/form-data; boundary=${formData.getBoundary()}`
77
- : undefined
76
+ ? `multipart/form-data; boundary=${
77
+ formData.getHeaders()['content-type']
78
+ }`
79
+ : 'multipart/form-data'
78
80
 
79
81
  const requestPromise = new Promise((resolve, reject) => {
80
82
  this.requestClient!.post(
@@ -1,4 +1,4 @@
1
- import * as NodeFormData from 'form-data'
1
+ import NodeFormData from 'form-data'
2
2
  import {
3
3
  AuthConfig,
4
4
  ExtraResponseAttributes,
@@ -150,8 +150,10 @@ export class WebJobExecutor extends BaseJobExecutor {
150
150
  /* The NodeFormData object does not set the request header - so, set it */
151
151
  const contentType =
152
152
  formData instanceof NodeFormData && typeof FormData === 'undefined'
153
- ? `multipart/form-data; boundary=${formData.getBoundary()}`
154
- : undefined
153
+ ? `multipart/form-data; boundary=${
154
+ formData.getHeaders()['content-type']
155
+ }`
156
+ : 'multipart/form-data'
155
157
 
156
158
  const requestPromise = new Promise((resolve, reject) => {
157
159
  this.requestClient!.post(
@@ -2,9 +2,9 @@ import {
2
2
  AxiosError,
3
3
  AxiosInstance,
4
4
  AxiosRequestConfig,
5
+ AxiosRequestHeaders,
5
6
  AxiosResponse
6
7
  } from 'axios'
7
- import axios from 'axios'
8
8
  import * as https from 'https'
9
9
  import { CsrfToken } from '..'
10
10
  import { isAuthorizeFormRequired, isLogInRequired } from '../auth'
@@ -160,7 +160,7 @@ export class RequestClient implements HttpClient {
160
160
  const requestConfig: AxiosRequestConfig = {
161
161
  headers,
162
162
  responseType: contentType === 'text/plain' ? 'text' : 'json',
163
- withCredentials: true
163
+ withXSRFToken: true
164
164
  }
165
165
 
166
166
  if (contentType === 'text/plain') {
@@ -191,6 +191,13 @@ export class RequestClient implements HttpClient {
191
191
  })
192
192
  }
193
193
 
194
+ /**
195
+ * @param contentType Newer version of Axios is more strict so if you don't
196
+ * set the contentType to `form data` while sending a FormData object
197
+ * application/json will be used by default, axios won’t treat it as FormData.
198
+ * Instead, it serializes data as JSON—resulting in a payload like
199
+ * {"sometable":{}} and we lose the multipart/form-data formatting.
200
+ */
194
201
  public async post<T>(
195
202
  url: string,
196
203
  data: any,
@@ -207,7 +214,7 @@ export class RequestClient implements HttpClient {
207
214
  return this.httpClient
208
215
  .post<T>(url, data, {
209
216
  headers,
210
- withCredentials: true,
217
+ withXSRFToken: true,
211
218
  ...additionalSettings
212
219
  })
213
220
  .then((response) => {
@@ -234,7 +241,7 @@ export class RequestClient implements HttpClient {
234
241
  }
235
242
 
236
243
  return this.httpClient
237
- .put<T>(url, data, { headers, withCredentials: true })
244
+ .put<T>(url, data, { headers, withXSRFToken: true })
238
245
  .then((response) => {
239
246
  throwIfError(response)
240
247
  return this.parseResponse<T>(response)
@@ -253,7 +260,7 @@ export class RequestClient implements HttpClient {
253
260
  const headers = this.getHeaders(accessToken, 'application/json')
254
261
 
255
262
  return this.httpClient
256
- .delete<T>(url, { headers, withCredentials: true })
263
+ .delete<T>(url, { headers, withXSRFToken: true })
257
264
  .then((response) => {
258
265
  throwIfError(response)
259
266
  return this.parseResponse<T>(response)
@@ -271,7 +278,7 @@ export class RequestClient implements HttpClient {
271
278
  const headers = this.getHeaders(accessToken, 'application/json')
272
279
 
273
280
  return this.httpClient
274
- .patch<T>(url, data, { headers, withCredentials: true })
281
+ .patch<T>(url, data, { headers, withXSRFToken: true })
275
282
  .then((response) => {
276
283
  throwIfError(response)
277
284
  return this.parseResponse<T>(response)
@@ -413,95 +420,67 @@ export class RequestClient implements HttpClient {
413
420
  return bodyLines.join('\n')
414
421
  }
415
422
 
416
- private defaultInterceptionCallBack = (
417
- axiosResponse: AxiosResponse | AxiosError
418
- ) => {
423
+ private handleAxiosResponse = (response: AxiosResponse) => {
424
+ const { status, config, request, data } = response
425
+
426
+ const reqHeaders = request?._header ?? 'Not provided\n'
427
+ const rawHeaders = request?.res?.rawHeaders ?? ['Not provided']
428
+
429
+ const resHeaders = this.formatHeaders(rawHeaders)
430
+ const parsedResBody = this.parseInterceptedBody(data)
431
+
432
+ process.logger?.info(`HTTP Request (first 50 lines):
433
+ ${reqHeaders}${this.parseInterceptedBody(config.data)}
434
+
435
+ HTTP Response Code: ${this.prettifyString(status)}
436
+
437
+ HTTP Response (first 50 lines):
438
+ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
439
+ `)
440
+
441
+ return response
442
+ }
443
+
444
+ private handleAxiosError = (error: AxiosError) => {
419
445
  // Message indicating absent value.
420
446
  const noValueMessage = 'Not provided'
447
+ const { response, request, config } = error
421
448
 
422
449
  // Fallback request object that can be safely used to form request summary.
423
- type FallbackRequest = { _header?: string; res: { rawHeaders: string[] } }
424
450
  // _header is not present in responses with status 1**
425
451
  // rawHeaders are not present in responses with status 1**
426
- let fallbackRequest: FallbackRequest = {
452
+ let fallbackRequest = {
427
453
  _header: `${noValueMessage}\n`,
428
454
  res: { rawHeaders: [noValueMessage] }
429
455
  }
430
456
 
431
- // Fallback response object that can be safely used to form response summary.
432
- type FallbackResponse = {
433
- status?: number | string
434
- request?: FallbackRequest
435
- config: { data?: string }
436
- data?: unknown
437
- }
438
- let fallbackResponse: FallbackResponse = axiosResponse
439
-
440
- if (axios.isAxiosError(axiosResponse)) {
441
- const { response, request, config } = axiosResponse
442
-
443
- // Try to use axiosResponse.response to form response summary.
444
- if (response) {
445
- fallbackResponse = response
446
- } else {
447
- // Try to use axiosResponse.request to form request summary.
448
- if (request) {
449
- const { _header, _currentRequest } = request
450
-
451
- // Try to use axiosResponse.request._header to form request summary.
452
- if (_header) {
453
- fallbackRequest._header = _header
454
- }
455
- // Try to use axiosResponse.request._currentRequest._header to form request summary.
456
- else if (_currentRequest && _currentRequest._header) {
457
- fallbackRequest._header = _currentRequest._header
458
- }
459
-
460
- const { res } = request
461
-
462
- // Try to use axiosResponse.request.res.rawHeaders to form request summary.
463
- if (res && res.rawHeaders) {
464
- fallbackRequest.res.rawHeaders = res.rawHeaders
465
- }
466
- }
467
-
468
- // Fallback config that can be safely used to form response summary.
469
- const fallbackConfig = { data: noValueMessage }
470
-
471
- fallbackResponse = {
472
- status: noValueMessage,
473
- request: fallbackRequest,
474
- config: config || fallbackConfig,
475
- data: noValueMessage
476
- }
457
+ if (request) {
458
+ fallbackRequest = {
459
+ _header:
460
+ request._header ?? request._currentRequest?._header ?? noValueMessage,
461
+ res: { rawHeaders: request.res?.rawHeaders ?? [noValueMessage] }
477
462
  }
478
463
  }
479
464
 
480
- const { status, config, request, data: resData } = fallbackResponse
481
- const { data: reqData } = config
482
- const { _header: reqHeaders, res } = request || fallbackRequest
483
- const { rawHeaders } = res
484
-
485
- // Converts an array of strings into a single string with the following format:
486
- // <headerName>: <headerValue>
487
- const resHeaders = rawHeaders.reduce(
488
- (acc: string, value: string, i: number) => {
489
- if (i % 2 === 0) {
490
- acc += `${i === 0 ? '' : '\n'}${value}`
491
- } else {
492
- acc += `: ${value}`
493
- }
494
-
495
- return acc
465
+ // Fallback response object that can be safely used to form response summary.
466
+ let fallbackResponse = response || {
467
+ status: noValueMessage,
468
+ request: fallbackRequest,
469
+ config: config || {
470
+ data: noValueMessage,
471
+ headers: {} as AxiosRequestHeaders
496
472
  },
497
- ''
498
- )
473
+ data: noValueMessage
474
+ }
475
+
476
+ const { status, request: req, data: resData } = fallbackResponse
477
+ const { _header: reqHeaders, res } = req
499
478
 
479
+ const resHeaders = this.formatHeaders(res.rawHeaders)
500
480
  const parsedResBody = this.parseInterceptedBody(resData)
501
481
 
502
- // HTTP response summary.
503
482
  process.logger?.info(`HTTP Request (first 50 lines):
504
- ${reqHeaders}${this.parseInterceptedBody(reqData)}
483
+ ${reqHeaders}${this.parseInterceptedBody(config?.data)}
505
484
 
506
485
  HTTP Response Code: ${this.prettifyString(status)}
507
486
 
@@ -509,7 +488,20 @@ HTTP Response (first 50 lines):
509
488
  ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
510
489
  `)
511
490
 
512
- return axiosResponse
491
+ return error
492
+ }
493
+
494
+ // Converts an array of strings into a single string with the following format:
495
+ // <headerName>: <headerValue>
496
+ private formatHeaders = (rawHeaders: string[]): string => {
497
+ return rawHeaders.reduce((acc, value, i) => {
498
+ if (i % 2 === 0) {
499
+ acc += `${i === 0 ? '' : '\n'}${value}`
500
+ } else {
501
+ acc += `: ${value}`
502
+ }
503
+ return acc
504
+ }, '')
513
505
  }
514
506
 
515
507
  /**
@@ -529,8 +521,8 @@ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
529
521
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
530
522
  */
531
523
  public enableVerboseMode = (
532
- successCallBack = this.defaultInterceptionCallBack,
533
- errorCallBack = this.defaultInterceptionCallBack
524
+ successCallBack = this.handleAxiosResponse,
525
+ errorCallBack = this.handleAxiosError
534
526
  ) => {
535
527
  this.httpInterceptor = this.httpClient.interceptors.response.use(
536
528
  successCallBack,
@@ -645,7 +637,7 @@ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
645
637
  // Fetching root and creating CSRF cookie
646
638
  await this.httpClient
647
639
  .get('/', {
648
- withCredentials: true
640
+ withXSRFToken: true
649
641
  })
650
642
  .then((response) => {
651
643
  const cookie =
@@ -1,6 +1,6 @@
1
1
  import * as https from 'https'
2
2
  import { AxiosRequestConfig } from 'axios'
3
- import axiosCookieJarSupport from 'axios-cookiejar-support'
3
+ import { wrapper } 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,8 +17,8 @@ export class Sas9RequestClient extends RequestClient {
17
17
  this.httpClient.defaults.validateStatus = (status) =>
18
18
  status >= 200 && status < 303
19
19
 
20
- if (axiosCookieJarSupport) {
21
- axiosCookieJarSupport(this.httpClient)
20
+ if (wrapper) {
21
+ wrapper(this.httpClient)
22
22
  this.httpClient.defaults.jar = new tough.CookieJar()
23
23
  }
24
24
  }
@@ -50,7 +50,7 @@ export class Sas9RequestClient extends RequestClient {
50
50
  const requestConfig: AxiosRequestConfig = {
51
51
  headers,
52
52
  responseType: contentType === 'text/plain' ? 'text' : 'json',
53
- withCredentials: true
53
+ withXSRFToken: true
54
54
  }
55
55
  if (contentType === 'text/plain') {
56
56
  requestConfig.transformResponse = undefined
@@ -103,7 +103,7 @@ export class Sas9RequestClient extends RequestClient {
103
103
  }
104
104
 
105
105
  return this.httpClient
106
- .post<T>(url, data, { headers, withCredentials: true })
106
+ .post<T>(url, data, { headers, withXSRFToken: true })
107
107
  .then(async (response) => {
108
108
  if (response.status === 302) {
109
109
  return await this.get(
@@ -1,7 +1,15 @@
1
1
  import express = require('express')
2
+ import cors from 'cors'
2
3
 
3
4
  export const app = express()
4
5
 
6
+ app.use(
7
+ cors({
8
+ origin: 'http://localhost', // Allow requests only from this origin
9
+ credentials: true // Allow credentials (cookies, auth headers, etc.)
10
+ })
11
+ )
12
+
5
13
  export const mockedAuthResponse = {
6
14
  access_token: 'access_token',
7
15
  token_type: 'bearer',
@@ -12,11 +20,11 @@ export const mockedAuthResponse = {
12
20
  jti: 'jti'
13
21
  }
14
22
 
15
- app.get('/', function (req: any, res: any) {
23
+ app.get('/', (req: any, res: any) => {
16
24
  res.send('Hello World')
17
25
  })
18
26
 
19
- app.post('/SASLogon/oauth/token', function (req: any, res: any) {
27
+ app.post('/SASLogon/oauth/token', (req: any, res: any) => {
20
28
  let valid = true
21
29
 
22
30
  // capture the encoded form data
@@ -1,4 +1,10 @@
1
- export interface WriteStream {
2
- write: (content: string, callback: (err?: Error) => any) => void
3
- path: string
1
+ import { WriteStream as FsWriteStream } from 'fs'
2
+
3
+ export interface WriteStream extends FsWriteStream {
4
+ write(
5
+ chunk: any,
6
+ encoding?: BufferEncoding | ((error: Error | null | undefined) => void),
7
+ cb?: (error: Error | null | undefined) => void
8
+ ): boolean
9
+ path: string | Buffer
4
10
  }