@sasjs/adapter 4.19.0 → 4.19.1

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.
@@ -104,7 +104,7 @@ export declare class RequestClient implements HttpClient {
104
104
  * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
105
105
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
106
106
  */
107
- enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any, {}>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any>) => void;
107
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any, {}, any>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any, any>) => void;
108
108
  /**
109
109
  * Turns off verbose mode to log every HTTP response.
110
110
  */
@@ -0,0 +1,13 @@
1
+ /**
2
+ * True when the response inlines the webout in a script-constructed Blob,
3
+ * which is the shape the compute task debug path returns. The iframe file URL
4
+ * path does not, so callers use this to pick the matching parser.
5
+ */
6
+ export declare const hasWeboutBlob: (response: string) => boolean;
7
+ /**
8
+ * Extracts and parses the webout JSON from a JES web app debug response that
9
+ * inlines it via a script-constructed Blob - see blobPattern for the shapes
10
+ * handled. Returns null (rather than throwing) if no such blob is present, so
11
+ * callers can fall back to another extraction strategy first.
12
+ */
13
+ export declare const extractWeboutBlob: (response: string) => object | null;
@@ -4,6 +4,7 @@ export * from './compareTimestamps';
4
4
  export * from './convertToCsv';
5
5
  export * from './createAxiosInstance';
6
6
  export * from './delay';
7
+ export * from './extractWeboutBlob';
7
8
  export * from './fetchLogByChunks';
8
9
  export * from './getValidJson';
9
10
  export * from './isNode';
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * When querying a Viya job using the Web approach with _DEBUG=128 (used when
3
- * runAsTask is true), the webout JSON is inlined into the response via:
4
- * var blob = new Blob([`{...}`], {type: 'application/json'});
5
- * On abort/error paths the same shape is used but with text/plain and
6
- * weboutBEGIN/END markers around the JSON:
7
- * var blob = new Blob([`>>weboutBEGIN<<\n{...}\n>>weboutEND<<\n`], {type: 'text/plain'});
8
- * No follow-up request is needed — extract and parse the JSON directly.
3
+ * runAsTask is true), the webout JSON is inlined into the response via a
4
+ * script-constructed Blob - see extractWeboutBlob for the exact shapes
5
+ * handled. No follow-up request is needed — extract and parse the JSON
6
+ * directly.
9
7
  */
10
8
  export declare const parseSasViyaLogDebugResponse: (response: any) => Promise<any>;
package/package.json CHANGED
@@ -93,11 +93,11 @@
93
93
  "main": "index.js",
94
94
  "dependencies": {
95
95
  "@sasjs/utils": "^3.6.0",
96
- "axios": "1.18.1",
96
+ "axios": "1.20.0",
97
97
  "axios-cookiejar-support": "5.0.5",
98
98
  "form-data": "4.0.6",
99
99
  "https": "1.0.0",
100
100
  "tough-cookie": "4.1.3"
101
101
  },
102
- "version": "4.19.0"
102
+ "version": "4.19.1"
103
103
  }
@@ -104,7 +104,7 @@ export declare class RequestClient implements HttpClient {
104
104
  * @param successCallBack - function that should be triggered on every HTTP response with the status 2**.
105
105
  * @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
106
106
  */
107
- enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any, {}>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any>) => void;
107
+ enableVerboseMode: (successCallBack?: (response: AxiosResponse) => AxiosResponse<any, any, {}, any>, errorCallBack?: (error: AxiosError) => AxiosError<unknown, any, any>) => void;
108
108
  /**
109
109
  * Turns off verbose mode to log every HTTP response.
110
110
  */
@@ -20,6 +20,7 @@ import {
20
20
  appendExtraResponseAttributes,
21
21
  parseWeboutResponse,
22
22
  getFormData,
23
+ hasWeboutBlob,
23
24
  isNode
24
25
  } from '../utils'
25
26
  import { BaseJobExecutor } from './JobExecutor'
@@ -59,7 +60,7 @@ export class WebJobExecutor extends BaseJobExecutor {
59
60
  if (config.serverType === ServerType.SasViya) {
60
61
  let jobUri
61
62
  try {
62
- jobUri = await this.getJobUri(sasJob)
63
+ jobUri = await this.getJobUri(program)
63
64
  } catch (e: any) {
64
65
  return new Promise(async (resolve, reject) => {
65
66
  if (e instanceof LoginRequiredError) {
@@ -88,8 +89,6 @@ export class WebJobExecutor extends BaseJobExecutor {
88
89
  })
89
90
  }
90
91
 
91
- apiUrl += jobUri.length > 0 ? '&_job=' + jobUri : ''
92
-
93
92
  if (jobUri.length > 0) {
94
93
  apiUrl += '&_job=' + jobUri
95
94
  /**
@@ -205,8 +204,17 @@ export class WebJobExecutor extends BaseJobExecutor {
205
204
  if (config.debug) {
206
205
  switch (this.serverType) {
207
206
  case ServerType.SasViya:
207
+ // Route on the shape of the response itself. The JES web app
208
+ // returns the webout inline in a script-constructed Blob when
209
+ // it runs the job as a compute task, and as an iframe file URL
210
+ // otherwise - but the _debug value sent does not determine
211
+ // which: a _debug=128 request without _EXECUTIONTASKS gets the
212
+ // iframe form back. A result that is already an object has
213
+ // been parsed upstream, and parseSasViyaDebugResponse also
214
+ // falls back to blob extraction, so either parser copes with
215
+ // a response that mixes the two.
208
216
  jsonResponse =
209
- config.useComputeApi === null && config.runAsTask === true
217
+ typeof res.result !== 'string' || hasWeboutBlob(res.result)
210
218
  ? await parseSasViyaLogDebugResponse(res.result)
211
219
  : await parseSasViyaDebugResponse(
212
220
  res.result,
@@ -292,17 +300,13 @@ export class WebJobExecutor extends BaseJobExecutor {
292
300
  if (!this.sasViyaApiClient) return ''
293
301
  let uri = ''
294
302
 
295
- let folderPath
296
- let jobName: string
297
- if (isRelativePath(sasJob)) {
298
- const folderPathParts = sasJob.split('/')
299
- folderPath = folderPathParts.length > 1 ? folderPathParts[0] : ''
300
- jobName = folderPathParts.length > 1 ? folderPathParts[1] : ''
301
- } else {
302
- const folderPathParts = sasJob.split('/')
303
- jobName = folderPathParts.pop() || ''
304
- folderPath = folderPathParts.join('/')
305
- }
303
+ // sasJob arrives with the appLoc already applied, so the folder is
304
+ // everything before the last segment. Reading the first two segments only
305
+ // would look up folder 'services' and job 'common' for a job like
306
+ // services/common/configure.
307
+ const folderPathParts = sasJob.split('/')
308
+ const jobName = folderPathParts.pop() || ''
309
+ const folderPath = folderPathParts.join('/')
306
310
 
307
311
  if (!jobName) {
308
312
  throw new Error('Job name is empty, null or undefined.')
@@ -0,0 +1,36 @@
1
+ import { getValidJson } from './getValidJson'
2
+ import { parseWeboutResponse } from './parseWeboutResponse'
3
+
4
+ // Matches the script-constructed Blob that the JES web app uses to inline the
5
+ // webout: `new Blob([`{...}`], {type: 'application/json'})`. The type is
6
+ // text/plain on abort and error paths, where the JSON is wrapped in
7
+ // weboutBEGIN/END markers. Quote style and whitespace vary between Viya
8
+ // releases, so both are tolerated here rather than in a second pattern.
9
+ const blobPattern =
10
+ /new Blob\(\[`([\s\S]*?)`\],\s*\{\s*type:\s*['"](?:application\/json|text\/plain)['"]\s*\}/
11
+
12
+ /**
13
+ * True when the response inlines the webout in a script-constructed Blob,
14
+ * which is the shape the compute task debug path returns. The iframe file URL
15
+ * path does not, so callers use this to pick the matching parser.
16
+ */
17
+ export const hasWeboutBlob = (response: string): boolean =>
18
+ blobPattern.test(response)
19
+
20
+ /**
21
+ * Extracts and parses the webout JSON from a JES web app debug response that
22
+ * inlines it via a script-constructed Blob - see blobPattern for the shapes
23
+ * handled. Returns null (rather than throwing) if no such blob is present, so
24
+ * callers can fall back to another extraction strategy first.
25
+ */
26
+ export const extractWeboutBlob = (response: string): object | null => {
27
+ const blobMatch = response.match(blobPattern)
28
+ if (!blobMatch) return null
29
+
30
+ const blobContent = blobMatch[1]
31
+ const stripped = blobContent.includes('>>weboutBEGIN<<')
32
+ ? parseWeboutResponse(blobContent)
33
+ : blobContent
34
+
35
+ return getValidJson(stripped)
36
+ }
@@ -4,6 +4,7 @@ export * from './compareTimestamps'
4
4
  export * from './convertToCsv'
5
5
  export * from './createAxiosInstance'
6
6
  export * from './delay'
7
+ export * from './extractWeboutBlob'
7
8
  export * from './fetchLogByChunks'
8
9
  export * from './getValidJson'
9
10
  export * from './isNode'
@@ -1,5 +1,6 @@
1
1
  import { RequestClient } from '../request/RequestClient'
2
2
  import { getValidJson } from '../utils'
3
+ import { extractWeboutBlob } from './extractWeboutBlob'
3
4
 
4
5
  /**
5
6
  * When querying a Viya job using the Web approach (as opposed to using the APIs) with _DEBUG enabled,
@@ -25,11 +26,21 @@ export const parseSasViyaDebugResponse = async (
25
26
  const jsonUrl = iframeStart
26
27
  ? iframeStart.split(/"><\/iframe>|><\/iframe>/)[0]
27
28
  : null
28
- if (!jsonUrl) {
29
- throw new Error('Unable to find webout file URL.')
29
+
30
+ if (jsonUrl) {
31
+ return requestClient
32
+ .get(serverUrl + jsonUrl, undefined, 'text/plain')
33
+ .then((res: any) => getValidJson(res.result))
30
34
  }
31
35
 
32
- return requestClient
33
- .get(serverUrl + jsonUrl, undefined, 'text/plain')
34
- .then((res: any) => getValidJson(res.result))
36
+ // Fallback: some Viya configurations inline the webout via a
37
+ // script-constructed Blob instead of an iframe src URL, e.g. when the
38
+ // _debug=128 format is served on a path expecting the older _debug=131
39
+ // iframe-URL format.
40
+ const blobResult = extractWeboutBlob(response)
41
+ if (blobResult !== null) return blobResult
42
+
43
+ throw new Error(
44
+ 'Unable to find webout content - no iframe URL and no inline blob.'
45
+ )
35
46
  }
@@ -1,14 +1,11 @@
1
- import { getValidJson } from './getValidJson'
2
- import { parseWeboutResponse } from './parseWeboutResponse'
1
+ import { extractWeboutBlob } from './extractWeboutBlob'
3
2
 
4
3
  /**
5
4
  * When querying a Viya job using the Web approach with _DEBUG=128 (used when
6
- * runAsTask is true), the webout JSON is inlined into the response via:
7
- * var blob = new Blob([`{...}`], {type: 'application/json'});
8
- * On abort/error paths the same shape is used but with text/plain and
9
- * weboutBEGIN/END markers around the JSON:
10
- * var blob = new Blob([`>>weboutBEGIN<<\n{...}\n>>weboutEND<<\n`], {type: 'text/plain'});
11
- * No follow-up request is needed — extract and parse the JSON directly.
5
+ * runAsTask is true), the webout JSON is inlined into the response via a
6
+ * script-constructed Blob - see extractWeboutBlob for the exact shapes
7
+ * handled. No follow-up request is needed — extract and parse the JSON
8
+ * directly.
12
9
  */
13
10
  export const parseSasViyaLogDebugResponse = async (response: any) => {
14
11
  // If upstream already parsed the response as JSON (object), pass through.
@@ -16,17 +13,10 @@ export const parseSasViyaLogDebugResponse = async (response: any) => {
16
13
  return response
17
14
  }
18
15
 
19
- const blobMatch = response.match(
20
- /new Blob\(\[`([\s\S]*?)`\],\s*\{type:\s*'(?:application\/json|text\/plain)'\}\)/
21
- )
22
- if (!blobMatch) {
16
+ const result = extractWeboutBlob(response)
17
+ if (result === null) {
23
18
  throw new Error('Unable to find webout blob in debug log response.')
24
19
  }
25
20
 
26
- const blobContent = blobMatch[1]
27
- const stripped = blobContent.includes('>>weboutBEGIN<<')
28
- ? parseWeboutResponse(blobContent)
29
- : blobContent
30
-
31
- return getValidJson(stripped)
21
+ return result
32
22
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * True when the response inlines the webout in a script-constructed Blob,
3
+ * which is the shape the compute task debug path returns. The iframe file URL
4
+ * path does not, so callers use this to pick the matching parser.
5
+ */
6
+ export declare const hasWeboutBlob: (response: string) => boolean;
7
+ /**
8
+ * Extracts and parses the webout JSON from a JES web app debug response that
9
+ * inlines it via a script-constructed Blob - see blobPattern for the shapes
10
+ * handled. Returns null (rather than throwing) if no such blob is present, so
11
+ * callers can fall back to another extraction strategy first.
12
+ */
13
+ export declare const extractWeboutBlob: (response: string) => object | null;
package/utils/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './compareTimestamps';
4
4
  export * from './convertToCsv';
5
5
  export * from './createAxiosInstance';
6
6
  export * from './delay';
7
+ export * from './extractWeboutBlob';
7
8
  export * from './fetchLogByChunks';
8
9
  export * from './getValidJson';
9
10
  export * from './isNode';
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * When querying a Viya job using the Web approach with _DEBUG=128 (used when
3
- * runAsTask is true), the webout JSON is inlined into the response via:
4
- * var blob = new Blob([`{...}`], {type: 'application/json'});
5
- * On abort/error paths the same shape is used but with text/plain and
6
- * weboutBEGIN/END markers around the JSON:
7
- * var blob = new Blob([`>>weboutBEGIN<<\n{...}\n>>weboutEND<<\n`], {type: 'text/plain'});
8
- * No follow-up request is needed — extract and parse the JSON directly.
3
+ * runAsTask is true), the webout JSON is inlined into the response via a
4
+ * script-constructed Blob - see extractWeboutBlob for the exact shapes
5
+ * handled. No follow-up request is needed — extract and parse the JSON
6
+ * directly.
9
7
  */
10
8
  export declare const parseSasViyaLogDebugResponse: (response: any) => Promise<any>;