@sasjs/adapter 4.6.0 → 4.8.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.
@@ -36,10 +36,11 @@ export declare class RequestClient implements HttpClient {
36
36
  protected baseUrl: string;
37
37
  private requests;
38
38
  private requestsLimit;
39
+ private httpInterceptor?;
39
40
  protected csrfToken: CsrfToken;
40
41
  protected fileUploadCsrfToken: CsrfToken | undefined;
41
42
  protected httpClient: AxiosInstance;
42
- constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number);
43
+ constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number, verboseMode?: boolean);
43
44
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
44
45
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
45
46
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
@@ -93,6 +94,29 @@ export declare class RequestClient implements HttpClient {
93
94
  }>;
94
95
  uploadFile(url: string, content: string, accessToken?: string): Promise<any>;
95
96
  authorize: (response: string) => Promise<any>;
97
+ /**
98
+ * Adds colors to the string.
99
+ * @param str - string to be prettified.
100
+ * @returns - prettified string
101
+ */
102
+ private prettifyString;
103
+ /**
104
+ * Formats HTTP request/response body.
105
+ * @param body - HTTP request/response body.
106
+ * @returns - formatted string.
107
+ */
108
+ private parseInterceptedBody;
109
+ private defaultInterceptionCallBack;
110
+ /**
111
+ * Turns on verbose mode to log every HTTP response.
112
+ * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
113
+ * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
114
+ */
115
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>, errorCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>) => void;
116
+ /**
117
+ * Turns off verbose mode to log every HTTP response.
118
+ */
119
+ disableVerboseMode: () => void;
96
120
  protected getHeaders: (accessToken: string | undefined, contentType: string) => any;
97
121
  protected parseAndSetFileUploadCsrfToken: (response: AxiosResponse) => void;
98
122
  protected parseAndSetCsrfToken: (response: AxiosResponse) => void;
@@ -45,6 +45,10 @@ export declare class SASjsConfig {
45
45
  * Set to `true` to enable additional debugging.
46
46
  */
47
47
  debug: boolean;
48
+ /**
49
+ * Set to `true` to enable verbose mode that will log a summary of every HTTP response.
50
+ */
51
+ verbose?: boolean;
48
52
  /**
49
53
  * The name of the compute context to use when calling the Viya services directly.
50
54
  * Example value: 'SAS Job Execution compute context'
package/package.json CHANGED
@@ -84,5 +84,5 @@
84
84
  "https": "1.0.0",
85
85
  "tough-cookie": "4.1.3"
86
86
  },
87
- "version": "4.6.0"
87
+ "version": "4.8.0"
88
88
  }
@@ -36,10 +36,11 @@ export declare class RequestClient implements HttpClient {
36
36
  protected baseUrl: string;
37
37
  private requests;
38
38
  private requestsLimit;
39
+ private httpInterceptor?;
39
40
  protected csrfToken: CsrfToken;
40
41
  protected fileUploadCsrfToken: CsrfToken | undefined;
41
42
  protected httpClient: AxiosInstance;
42
- constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number);
43
+ constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number, verboseMode?: boolean);
43
44
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
44
45
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
45
46
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
@@ -93,6 +94,29 @@ export declare class RequestClient implements HttpClient {
93
94
  }>;
94
95
  uploadFile(url: string, content: string, accessToken?: string): Promise<any>;
95
96
  authorize: (response: string) => Promise<any>;
97
+ /**
98
+ * Adds colors to the string.
99
+ * @param str - string to be prettified.
100
+ * @returns - prettified string
101
+ */
102
+ private prettifyString;
103
+ /**
104
+ * Formats HTTP request/response body.
105
+ * @param body - HTTP request/response body.
106
+ * @returns - formatted string.
107
+ */
108
+ private parseInterceptedBody;
109
+ private defaultInterceptionCallBack;
110
+ /**
111
+ * Turns on verbose mode to log every HTTP response.
112
+ * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
113
+ * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
114
+ */
115
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>, errorCallBack?: (response: AxiosResponse) => AxiosResponse<any, any>) => void;
116
+ /**
117
+ * Turns off verbose mode to log every HTTP response.
118
+ */
119
+ disableVerboseMode: () => void;
96
120
  protected getHeaders: (accessToken: string | undefined, contentType: string) => any;
97
121
  protected parseAndSetFileUploadCsrfToken: (response: AxiosResponse) => void;
98
122
  protected parseAndSetCsrfToken: (response: AxiosResponse) => void;
package/src/SASjs.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  } from './job-execution'
32
32
  import { ErrorResponse } from './types/errors'
33
33
  import { LoginOptions, LoginResult } from './types/Login'
34
+ import { AxiosResponse } from 'axios'
34
35
 
35
36
  interface ExecuteScriptParams {
36
37
  linesOfCode: string[]
@@ -854,6 +855,7 @@ export default class SASjs {
854
855
  * @param pollOptions - an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { maxPollCount: 24 * 60 * 60, pollInterval: 1000 }. More information available at src/api/viya/pollJobState.ts.
855
856
  * @param printPid - a boolean that indicates whether the function should print (PID) of the started job.
856
857
  * @param variables - an object that represents macro variables.
858
+ * @param verboseMode - boolean to enable verbose mode (log every HTTP response).
857
859
  */
858
860
  public async startComputeJob(
859
861
  sasJob: string,
@@ -863,7 +865,8 @@ export default class SASjs {
863
865
  waitForResult?: boolean,
864
866
  pollOptions?: PollOptions,
865
867
  printPid = false,
866
- variables?: MacroVar
868
+ variables?: MacroVar,
869
+ verboseMode?: boolean
867
870
  ) {
868
871
  config = {
869
872
  ...this.sasjsConfig,
@@ -877,6 +880,9 @@ export default class SASjs {
877
880
  )
878
881
  }
879
882
 
883
+ if (verboseMode) this.requestClient?.enableVerboseMode()
884
+ else if (verboseMode === false) this.requestClient?.disableVerboseMode()
885
+
880
886
  return this.sasViyaApiClient?.executeComputeJob(
881
887
  sasJob,
882
888
  config.contextName,
@@ -970,7 +976,8 @@ export default class SASjs {
970
976
  this.requestClient = new RequestClientClass(
971
977
  this.sasjsConfig.serverUrl,
972
978
  this.sasjsConfig.httpsAgentOptions,
973
- this.sasjsConfig.requestHistoryLimit
979
+ this.sasjsConfig.requestHistoryLimit,
980
+ this.sasjsConfig.verbose
974
981
  )
975
982
  } else {
976
983
  this.requestClient.setConfig(
@@ -1134,4 +1141,23 @@ export default class SASjs {
1134
1141
  )
1135
1142
  }
1136
1143
  }
1144
+
1145
+ /**
1146
+ * Enables verbose mode that will log a summary of every HTTP response.
1147
+ * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
1148
+ * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
1149
+ */
1150
+ public enableVerboseMode(
1151
+ successCallBack?: (response: AxiosResponse) => AxiosResponse,
1152
+ errorCallBack?: (response: AxiosResponse) => AxiosResponse
1153
+ ) {
1154
+ this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
1155
+ }
1156
+
1157
+ /**
1158
+ * Turns off verbose mode to log every HTTP response.
1159
+ */
1160
+ public disableVerboseMode() {
1161
+ this.requestClient?.disableVerboseMode()
1162
+ }
1137
1163
  }
@@ -233,7 +233,8 @@ export default class SASjs {
233
233
  this.requestClient = new RequestClient(
234
234
  this.sasjsConfig.serverUrl,
235
235
  this.sasjsConfig.httpsAgentOptions,
236
- this.sasjsConfig.requestHistoryLimit
236
+ this.sasjsConfig.requestHistoryLimit,
237
+ this.sasjsConfig.verbose
237
238
  )
238
239
  } else {
239
240
  this.requestClient.setConfig(
@@ -11,7 +11,6 @@ import {
11
11
  import { RequestClient } from '../../request/RequestClient'
12
12
  import {
13
13
  isRelativePath,
14
- parseSasViyaDebugResponse,
15
14
  appendExtraResponseAttributes,
16
15
  convertToCSV
17
16
  } from '../../utils'
@@ -20,6 +20,7 @@ import {
20
20
  createAxiosInstance
21
21
  } from '../utils'
22
22
  import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
23
+ import { inspect } from 'util'
23
24
 
24
25
  export interface HttpClient {
25
26
  get<T>(
@@ -59,6 +60,7 @@ export interface HttpClient {
59
60
  export class RequestClient implements HttpClient {
60
61
  private requests: SASjsRequest[] = []
61
62
  private requestsLimit: number = 10
63
+ private httpInterceptor?: number
62
64
 
63
65
  protected csrfToken: CsrfToken = { headerName: '', value: '' }
64
66
  protected fileUploadCsrfToken: CsrfToken | undefined
@@ -67,10 +69,14 @@ export class RequestClient implements HttpClient {
67
69
  constructor(
68
70
  protected baseUrl: string,
69
71
  httpsAgentOptions?: https.AgentOptions,
70
- requestsLimit?: number
72
+ requestsLimit?: number,
73
+ verboseMode?: boolean
71
74
  ) {
72
75
  this.createHttpClient(baseUrl, httpsAgentOptions)
76
+
73
77
  if (requestsLimit) this.requestsLimit = requestsLimit
78
+
79
+ if (verboseMode) this.enableVerboseMode()
74
80
  }
75
81
 
76
82
  public setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions) {
@@ -180,6 +186,7 @@ export class RequestClient implements HttpClient {
180
186
  responseType: contentType === 'text/plain' ? 'text' : 'json',
181
187
  withCredentials: true
182
188
  }
189
+
183
190
  if (contentType === 'text/plain') {
184
191
  requestConfig.transformResponse = undefined
185
192
  }
@@ -389,6 +396,105 @@ export class RequestClient implements HttpClient {
389
396
  })
390
397
  }
391
398
 
399
+ /**
400
+ * Adds colors to the string.
401
+ * @param str - string to be prettified.
402
+ * @returns - prettified string
403
+ */
404
+ private prettifyString = (str: any) => inspect(str, { colors: true })
405
+
406
+ /**
407
+ * Formats HTTP request/response body.
408
+ * @param body - HTTP request/response body.
409
+ * @returns - formatted string.
410
+ */
411
+ private parseInterceptedBody = (body: any) => {
412
+ if (!body) return ''
413
+
414
+ let parsedBody
415
+
416
+ // Tries to parse body into JSON object.
417
+ if (typeof body === 'string') {
418
+ try {
419
+ parsedBody = JSON.parse(body)
420
+ } catch (error) {
421
+ parsedBody = body
422
+ }
423
+ } else {
424
+ parsedBody = body
425
+ }
426
+
427
+ const bodyLines = this.prettifyString(parsedBody).split('\n')
428
+
429
+ // Leaves first 50 lines
430
+ if (bodyLines.length > 51) {
431
+ bodyLines.splice(50)
432
+ bodyLines.push('...')
433
+ }
434
+
435
+ return bodyLines.join('\n')
436
+ }
437
+
438
+ private defaultInterceptionCallBack = (response: AxiosResponse) => {
439
+ const { status, config, request, data: resData } = response
440
+ const { data: reqData } = config
441
+ const { _header: reqHeaders, res } = request
442
+ const { rawHeaders } = res
443
+
444
+ // Converts an array of strings into a single string with the following format:
445
+ // <headerName>: <headerValue>
446
+ const resHeaders = rawHeaders.reduce(
447
+ (acc: string, value: string, i: number) => {
448
+ if (i % 2 === 0) {
449
+ acc += `${i === 0 ? '' : '\n'}${value}`
450
+ } else {
451
+ acc += `: ${value}`
452
+ }
453
+
454
+ return acc
455
+ },
456
+ ''
457
+ )
458
+
459
+ const parsedResBody = this.parseInterceptedBody(resData)
460
+
461
+ // HTTP response summary.
462
+ process.logger?.info(`HTTP Request (first 50 lines):
463
+ ${reqHeaders}${this.parseInterceptedBody(reqData)}
464
+
465
+ HTTP Response Code: ${this.prettifyString(status)}
466
+
467
+ HTTP Response (first 50 lines):
468
+ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
469
+ `)
470
+
471
+ return response
472
+ }
473
+
474
+ /**
475
+ * Turns on verbose mode to log every HTTP response.
476
+ * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
477
+ * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
478
+ */
479
+ public enableVerboseMode = (
480
+ successCallBack = this.defaultInterceptionCallBack,
481
+ errorCallBack = this.defaultInterceptionCallBack
482
+ ) => {
483
+ this.httpInterceptor = this.httpClient.interceptors.response.use(
484
+ successCallBack,
485
+ errorCallBack
486
+ )
487
+ }
488
+
489
+ /**
490
+ * Turns off verbose mode to log every HTTP response.
491
+ */
492
+ public disableVerboseMode = () => {
493
+ if (this.httpInterceptor) {
494
+ this.httpClient.interceptors.response.eject(this.httpInterceptor)
495
+ }
496
+ }
497
+
392
498
  protected getHeaders = (
393
499
  accessToken: string | undefined,
394
500
  contentType: string
@@ -45,6 +45,10 @@ export class SASjsConfig {
45
45
  * Set to `true` to enable additional debugging.
46
46
  */
47
47
  debug: boolean = true
48
+ /**
49
+ * Set to `true` to enable verbose mode that will log a summary of every HTTP response.
50
+ */
51
+ verbose?: boolean = true
48
52
  /**
49
53
  * The name of the compute context to use when calling the Viya services directly.
50
54
  * Example value: 'SAS Job Execution compute context'
@@ -45,6 +45,10 @@ export declare class SASjsConfig {
45
45
  * Set to `true` to enable additional debugging.
46
46
  */
47
47
  debug: boolean;
48
+ /**
49
+ * Set to `true` to enable verbose mode that will log a summary of every HTTP response.
50
+ */
51
+ verbose?: boolean;
48
52
  /**
49
53
  * The name of the compute context to use when calling the Viya services directly.
50
54
  * Example value: 'SAS Job Execution compute context'