@sasjs/adapter 2.11.0 → 2.11.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.
@@ -53,7 +53,36 @@ export class WebJobExecutor extends BaseJobExecutor {
53
53
  let apiUrl = `${config.serverUrl}${this.jobsPath}/?${'_program=' + program}`
54
54
 
55
55
  if (config.serverType === ServerType.SasViya) {
56
- const jobUri = await this.getJobUri(sasJob)
56
+ let jobUri
57
+ try {
58
+ jobUri = await this.getJobUri(sasJob)
59
+ } catch (e: any) {
60
+ return new Promise(async (resolve, reject) => {
61
+ if (e instanceof LoginRequiredError) {
62
+ this.appendWaitingRequest(() => {
63
+ return this.execute(
64
+ sasJob,
65
+ data,
66
+ config,
67
+ loginRequiredCallback,
68
+ authConfig,
69
+ extraResponseAttributes
70
+ ).then(
71
+ (res: any) => {
72
+ resolve(res)
73
+ },
74
+ (err: any) => {
75
+ reject(err)
76
+ }
77
+ )
78
+ })
79
+
80
+ await loginCallback()
81
+ } else {
82
+ reject(new ErrorResponse(e?.message, e))
83
+ }
84
+ })
85
+ }
57
86
 
58
87
  apiUrl += jobUri.length > 0 ? '&_job=' + jobUri : ''
59
88
 
@@ -90,7 +119,7 @@ export class WebJobExecutor extends BaseJobExecutor {
90
119
  // file upload approach
91
120
  try {
92
121
  formData = generateFileUploadForm(formData, data)
93
- } catch (e) {
122
+ } catch (e: any) {
94
123
  return Promise.reject(new ErrorResponse(e?.message, e))
95
124
  }
96
125
  } else {
@@ -100,7 +129,7 @@ export class WebJobExecutor extends BaseJobExecutor {
100
129
  generateTableUploadForm(formData, data)
101
130
  formData = newFormData
102
131
  requestParams = { ...requestParams, ...params }
103
- } catch (e) {
132
+ } catch (e: any) {
104
133
  return Promise.reject(new ErrorResponse(e?.message, e))
105
134
  }
106
135
  }
@@ -115,6 +144,8 @@ export class WebJobExecutor extends BaseJobExecutor {
115
144
  const requestPromise = new Promise((resolve, reject) => {
116
145
  this.requestClient!.post(apiUrl, formData, undefined)
117
146
  .then(async (res: any) => {
147
+ this.requestClient!.appendRequest(res, sasJob, config.debug)
148
+
118
149
  let jsonResponse = res.result
119
150
 
120
151
  if (config.debug) {
@@ -135,8 +166,6 @@ export class WebJobExecutor extends BaseJobExecutor {
135
166
  }
136
167
  }
137
168
 
138
- this.appendRequest(res, sasJob, config.debug)
139
-
140
169
  const responseObject = appendExtraResponseAttributes(
141
170
  { result: jsonResponse },
142
171
  extraResponseAttributes
@@ -145,8 +174,7 @@ export class WebJobExecutor extends BaseJobExecutor {
145
174
  })
146
175
  .catch(async (e: Error) => {
147
176
  if (e instanceof JobExecutionError) {
148
- this.appendRequest(e, sasJob, config.debug)
149
-
177
+ this.requestClient!.appendRequest(e, sasJob, config.debug)
150
178
  reject(new ErrorResponse(e?.message, e))
151
179
  }
152
180
 
@@ -3,3 +3,4 @@ export * from './JesJobExecutor'
3
3
  export * from './JobExecutor'
4
4
  export * from './Sas9JobExecutor'
5
5
  export * from './WebJobExecutor'
6
+ export * from './FileUploader'
@@ -8,10 +8,11 @@ import {
8
8
  InternalServerError,
9
9
  JobExecutionError
10
10
  } from '../types/errors'
11
+ import { SASjsRequest } from '../types'
11
12
  import { parseWeboutResponse } from '../utils/parseWeboutResponse'
12
13
  import { prefixMessage } from '@sasjs/utils/error'
13
14
  import { SAS9AuthError } from '../types/errors/SAS9AuthError'
14
- import { getValidJson } from '../utils'
15
+ import { parseGeneratedCode, parseSourceCode } from '../utils'
15
16
 
16
17
  export interface HttpClient {
17
18
  get<T>(
@@ -47,27 +48,18 @@ export interface HttpClient {
47
48
  }
48
49
 
49
50
  export class RequestClient implements HttpClient {
51
+ private requests: SASjsRequest[] = []
52
+
50
53
  protected csrfToken: CsrfToken = { headerName: '', value: '' }
51
54
  protected fileUploadCsrfToken: CsrfToken | undefined
52
- protected httpClient: AxiosInstance
55
+ protected httpClient!: AxiosInstance
53
56
 
54
57
  constructor(protected baseUrl: string, allowInsecure = false) {
55
- const https = require('https')
56
- if (allowInsecure && https.Agent) {
57
- this.httpClient = axios.create({
58
- baseURL: baseUrl,
59
- httpsAgent: new https.Agent({
60
- rejectUnauthorized: !allowInsecure
61
- })
62
- })
63
- } else {
64
- this.httpClient = axios.create({
65
- baseURL: baseUrl
66
- })
67
- }
58
+ this.createHttpClient(baseUrl, allowInsecure)
59
+ }
68
60
 
69
- this.httpClient.defaults.validateStatus = (status) =>
70
- status >= 200 && status < 305
61
+ public setConfig(baseUrl: string, allowInsecure = false) {
62
+ this.createHttpClient(baseUrl, allowInsecure)
71
63
  }
72
64
 
73
65
  public getCsrfToken(type: 'general' | 'file' = 'general') {
@@ -83,6 +75,66 @@ export class RequestClient implements HttpClient {
83
75
  return this.httpClient.defaults.baseURL || ''
84
76
  }
85
77
 
78
+ /**
79
+ * this method returns all requests, an array of SASjsRequest type
80
+ * @returns SASjsRequest[]
81
+ */
82
+ public getRequests = () => this.requests
83
+
84
+ /**
85
+ * this method clears the requests array, i.e set to empty
86
+ */
87
+ public clearRequests = () => {
88
+ this.requests = []
89
+ }
90
+
91
+ /**
92
+ * this method appends the response from sasjs request to requests array
93
+ * @param response - response from sasjs request
94
+ * @param program - name of program
95
+ * @param debug - a boolean that indicates whether debug was enabled or not
96
+ */
97
+ public appendRequest(response: any, program: string, debug: boolean) {
98
+ let sourceCode = ''
99
+ let generatedCode = ''
100
+ let sasWork = null
101
+
102
+ if (debug) {
103
+ if (response?.log) {
104
+ sourceCode = parseSourceCode(response.log)
105
+ generatedCode = parseGeneratedCode(response.log)
106
+
107
+ if (response?.result) {
108
+ sasWork = response.result.WORK
109
+ } else {
110
+ sasWork = response.log
111
+ }
112
+ } else if (response?.result) {
113
+ sourceCode = parseSourceCode(response.result)
114
+ generatedCode = parseGeneratedCode(response.result)
115
+ sasWork = response.result.WORK
116
+ }
117
+ }
118
+
119
+ const stringifiedResult =
120
+ typeof response?.result === 'string'
121
+ ? response?.result
122
+ : JSON.stringify(response?.result, null, 2)
123
+
124
+ this.requests.push({
125
+ logFile: response?.log || stringifiedResult || response,
126
+ serviceLink: program,
127
+ timestamp: new Date(),
128
+ sourceCode,
129
+ generatedCode,
130
+ SASWORK: sasWork
131
+ })
132
+
133
+ if (this.requests.length > 20) {
134
+ this.requests.splice(0, 1)
135
+ }
136
+ }
137
+
86
138
  public async get<T>(
87
139
  url: string,
88
140
  accessToken: string | undefined,
@@ -130,7 +182,7 @@ export class RequestClient implements HttpClient {
130
182
  })
131
183
  }
132
184
 
133
- public post<T>(
185
+ public async post<T>(
134
186
  url: string,
135
187
  data: any,
136
188
  accessToken: string | undefined,
@@ -229,12 +281,16 @@ export class RequestClient implements HttpClient {
229
281
  }
230
282
 
231
283
  try {
232
- const response = await this.httpClient.post(url, content, { headers })
284
+ const response = await this.httpClient.post(url, content, {
285
+ headers,
286
+ transformRequest: (requestBody) => requestBody
287
+ })
288
+
233
289
  return {
234
290
  result: response.data,
235
291
  etag: response.headers['etag'] as string
236
292
  }
237
- } catch (e) {
293
+ } catch (e: any) {
238
294
  const response = e.response as AxiosResponse
239
295
  if (response?.status === 403 || response?.status === 449) {
240
296
  this.parseAndSetFileUploadCsrfToken(response)
@@ -454,6 +510,25 @@ export class RequestClient implements HttpClient {
454
510
 
455
511
  return responseToReturn
456
512
  }
513
+
514
+ private createHttpClient(baseUrl: string, allowInsecure = false) {
515
+ const https = require('https')
516
+ if (allowInsecure && https.Agent) {
517
+ this.httpClient = axios.create({
518
+ baseURL: baseUrl,
519
+ httpsAgent: new https.Agent({
520
+ rejectUnauthorized: !allowInsecure
521
+ })
522
+ })
523
+ } else {
524
+ this.httpClient = axios.create({
525
+ baseURL: baseUrl
526
+ })
527
+ }
528
+
529
+ this.httpClient.defaults.validateStatus = (status) =>
530
+ status >= 200 && status < 305
531
+ }
457
532
  }
458
533
 
459
534
  export const throwIfError = (response: AxiosResponse) => {
@@ -6,6 +6,8 @@ import { JsonParseArrayError, InvalidJsonError } from '../types/errors'
6
6
  */
7
7
  export const getValidJson = (str: string | object) => {
8
8
  try {
9
+ if (str === null || str === undefined) throw new InvalidJsonError()
10
+
9
11
  if (Array.isArray(str)) throw new JsonParseArrayError()
10
12
 
11
13
  if (typeof str === 'object') return str
@@ -3,6 +3,12 @@ enum domIDs {
3
3
  overlay = 'sasjsAdapterLoginPromptBG',
4
4
  dialog = 'sasjsAdapterLoginPrompt'
5
5
  }
6
+ const cssPrefix = 'sasjs-adapter'
7
+
8
+ const classes = {
9
+ popUp: `${cssPrefix}popUp`,
10
+ popUpBG: `${cssPrefix}popUpBG`
11
+ }
6
12
 
7
13
  export const openLoginPrompt = (): Promise<boolean> => {
8
14
  return new Promise(async (resolve) => {
@@ -12,11 +18,11 @@ export const openLoginPrompt = (): Promise<boolean> => {
12
18
 
13
19
  const loginPromptBG = document.createElement('div')
14
20
  loginPromptBG.id = domIDs.overlay
15
- loginPromptBG.classList.add('popUpBG')
21
+ loginPromptBG.classList.add(classes.popUpBG)
16
22
 
17
23
  const loginPrompt = document.createElement('div')
18
24
  loginPrompt.id = domIDs.dialog
19
- loginPrompt.classList.add('popUp')
25
+ loginPrompt.classList.add(classes.popUp)
20
26
 
21
27
  const title = document.createElement('h1')
22
28
  title.innerText = 'Session Expired!'
@@ -63,7 +69,11 @@ const closeLoginPrompt = () => {
63
69
  }
64
70
 
65
71
  const cssContent = `
66
- .popUp {
72
+ .${classes.popUpBG} ,
73
+ .${classes.popUp} {
74
+ z-index: 10000;
75
+ }
76
+ .${classes.popUp} {
67
77
  box-sizing: border-box;
68
78
  -webkit-box-sizing: border-box;
69
79
  -moz-box-sizing: border-box;
@@ -86,7 +96,7 @@ const cssContent = `
86
96
  max-height: 300px;
87
97
  transform: translate(-50%, -50%);
88
98
  }
89
- .popUp > h1 {
99
+ .${classes.popUp} > h1 {
90
100
  box-sizing: border-box;
91
101
  -webkit-box-sizing: border-box;
92
102
  -moz-box-sizing: border-box;
@@ -101,7 +111,7 @@ const cssContent = `
101
111
  border-width: 5px;
102
112
  border-color: black;
103
113
  }
104
- .popUp > div {
114
+ .${classes.popUp} > div {
105
115
  width: 100%;
106
116
  height: calc(100% -108px);
107
117
  margin: 0;
@@ -116,7 +126,7 @@ const cssContent = `
116
126
  border-style: none none solid none;
117
127
  overflow: auto;
118
128
  }
119
- .popUp > div > span {
129
+ .${classes.popUp} > div > span {
120
130
  display: table-cell;
121
131
  box-sizing: border-box;
122
132
  -webkit-box-sizing: border-box;
@@ -128,13 +138,13 @@ const cssContent = `
128
138
  vertical-align: middle;
129
139
  border-style: none;
130
140
  }
131
- .popUp .cancel {
141
+ .${classes.popUp} .cancel {
132
142
  float: left;
133
143
  }
134
- .popUp .confirm {
144
+ .${classes.popUp} .confirm {
135
145
  float: right;
136
146
  }
137
- .popUp > button {
147
+ .${classes.popUp} > button {
138
148
  box-sizing: border-box;
139
149
  -webkit-box-sizing: border-box;
140
150
  -moz-box-sizing: border-box;
@@ -148,10 +158,10 @@ const cssContent = `
148
158
  height: 50px;
149
159
  background: rgba(1, 1, 1, 0.2);
150
160
  }
151
- .popUp > button:hover {
161
+ .${classes.popUp} > button:hover {
152
162
  background: rgba(0, 0, 0, 0.2);
153
163
  }
154
- .popUpBG {
164
+ .${classes.popUpBG} {
155
165
  display: block;
156
166
  position: fixed;
157
167
  top: 0;
@@ -25,6 +25,6 @@ export const parseSasViyaDebugResponse = async (
25
25
  }
26
26
 
27
27
  return requestClient
28
- .get(serverUrl + jsonUrl, undefined)
28
+ .get(serverUrl + jsonUrl, undefined, 'text/plain')
29
29
  .then((res: any) => getValidJson(res.result))
30
30
  }
package/FileUploader.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import { UploadFile } from './types/UploadFile';
2
- import { RequestClient } from './request/RequestClient';
3
- import { SASjsConfig } from './types';
4
- export declare class FileUploader {
5
- private sasjsConfig;
6
- private jobsPath;
7
- private requestClient;
8
- constructor(sasjsConfig: SASjsConfig, jobsPath: string, requestClient: RequestClient);
9
- uploadFile(sasJob: string, files: UploadFile[], params: any): Promise<any>;
10
- }
@@ -1,10 +0,0 @@
1
- import { UploadFile } from './types/UploadFile';
2
- import { RequestClient } from './request/RequestClient';
3
- import { SASjsConfig } from './types';
4
- export declare class FileUploader {
5
- private sasjsConfig;
6
- private jobsPath;
7
- private requestClient;
8
- constructor(sasjsConfig: SASjsConfig, jobsPath: string, requestClient: RequestClient);
9
- uploadFile(sasJob: string, files: UploadFile[], params: any): Promise<any>;
10
- }
@@ -1,98 +0,0 @@
1
- import { isUrl, getValidJson, parseSasViyaDebugResponse } from './utils'
2
- import { UploadFile } from './types/UploadFile'
3
- import { ErrorResponse, LoginRequiredError } from './types/errors'
4
- import { RequestClient } from './request/RequestClient'
5
- import { ServerType } from '@sasjs/utils/types'
6
- import SASjs from './SASjs'
7
- import { Server } from 'https'
8
- import { SASjsConfig } from './types'
9
- import { config } from 'process'
10
-
11
- export class FileUploader {
12
- constructor(
13
- private sasjsConfig: SASjsConfig,
14
- private jobsPath: string,
15
- private requestClient: RequestClient
16
- ) {
17
- if (this.sasjsConfig.serverUrl) isUrl(this.sasjsConfig.serverUrl)
18
- }
19
-
20
- public uploadFile(sasJob: string, files: UploadFile[], params: any) {
21
- if (files?.length < 1)
22
- return Promise.reject(
23
- new ErrorResponse('At least one file must be provided.')
24
- )
25
- if (!sasJob || sasJob === '')
26
- return Promise.reject(new ErrorResponse('sasJob must be provided.'))
27
-
28
- let paramsString = ''
29
-
30
- for (let param in params) {
31
- if (params.hasOwnProperty(param)) {
32
- paramsString += `&${param}=${params[param]}`
33
- }
34
- }
35
-
36
- const program = this.sasjsConfig.appLoc
37
- ? this.sasjsConfig.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
38
- : sasJob
39
- const uploadUrl = `${this.jobsPath}/?${
40
- '_program=' + program
41
- }${paramsString}`
42
-
43
- const formData = new FormData()
44
-
45
- for (let file of files) {
46
- formData.append('file', file.file, file.fileName)
47
- }
48
-
49
- const csrfToken = this.requestClient.getCsrfToken('file')
50
- if (csrfToken) formData.append('_csrf', csrfToken.value)
51
- if (this.sasjsConfig.debug) formData.append('_debug', '131')
52
- if (
53
- this.sasjsConfig.serverType === ServerType.SasViya &&
54
- this.sasjsConfig.contextName
55
- )
56
- formData.append('_contextname', this.sasjsConfig.contextName)
57
-
58
- const headers = {
59
- 'cache-control': 'no-cache',
60
- Accept: '*/*',
61
- 'Content-Type': 'text/plain'
62
- }
63
-
64
- // currently only web approach is supported for file upload
65
- // therefore log is part of response with debug enabled and must be parsed
66
- return this.requestClient
67
- .post(uploadUrl, formData, undefined, 'application/json', headers)
68
- .then(async (res) => {
69
- if (
70
- this.sasjsConfig.serverType === ServerType.SasViya &&
71
- this.sasjsConfig.debug
72
- ) {
73
- const jsonResponse = await parseSasViyaDebugResponse(
74
- res.result as string,
75
- this.requestClient,
76
- this.sasjsConfig.serverUrl
77
- )
78
- return jsonResponse
79
- }
80
-
81
- return typeof res.result === 'string'
82
- ? getValidJson(res.result)
83
- : res.result
84
-
85
- //TODO: append to SASjs requests
86
- })
87
- .catch((err: Error) => {
88
- if (err instanceof LoginRequiredError) {
89
- return Promise.reject(
90
- new ErrorResponse('You must be logged in to upload a file.', err)
91
- )
92
- }
93
- return Promise.reject(
94
- new ErrorResponse('File upload request failed.', err)
95
- )
96
- })
97
- }
98
- }