@sasjs/adapter 4.11.2 → 4.12.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.
- package/SASViyaApiClient.d.ts +25 -2
- package/SASjs.d.ts +20 -2
- package/api/viya/writeStream.d.ts +1 -1
- package/auth/getTokenRequestErrorPrefix.d.ts +2 -2
- package/file/generateFileUploadForm.d.ts +1 -1
- package/file/generateTableUploadForm.d.ts +1 -1
- package/index-dev.js +77153 -3
- package/index.js +77153 -2
- package/job-execution/JobExecutor.d.ts +1 -1
- package/minified_sas9.js +16558 -2
- package/node/SASViyaApiClient.d.ts +25 -2
- package/node/SASjs.d.ts +20 -2
- package/node/api/viya/writeStream.d.ts +1 -1
- package/node/auth/getTokenRequestErrorPrefix.d.ts +2 -2
- package/node/file/generateFileUploadForm.d.ts +1 -1
- package/node/file/generateTableUploadForm.d.ts +1 -1
- package/node/index.js +28247 -2
- package/node/job-execution/JobExecutor.d.ts +1 -1
- package/node/request/RequestClient.d.ts +11 -2
- package/node/types/FileResource.d.ts +32 -0
- package/node/types/PollOptions.d.ts +1 -1
- package/node/types/RequestClient.d.ts +1 -1
- package/node/types/WriteStream.d.ts +6 -3
- package/node/utils/getFormData.d.ts +1 -1
- package/package.json +12 -9
- package/request/RequestClient.d.ts +11 -2
- package/spec/SAS9ApiClient.spec.d.ts +1 -0
- package/spec/SASjsApiClient.spec.d.ts +1 -0
- package/src/SAS9ApiClient.ts +1 -1
- package/src/SASViyaApiClient.ts +124 -6
- package/src/SASjs.ts +54 -4
- package/src/SASjsApiClient.ts +1 -1
- package/src/api/viya/writeStream.ts +10 -5
- package/src/auth/refreshTokensForViya.ts +1 -1
- package/src/file/generateFileUploadForm.ts +1 -1
- package/src/file/generateTableUploadForm.ts +1 -1
- package/src/job-execution/Sas9JobExecutor.ts +1 -1
- package/src/job-execution/SasjsJobExecutor.ts +5 -3
- package/src/job-execution/WebJobExecutor.ts +5 -3
- package/src/request/RequestClient.ts +73 -81
- package/src/request/Sas9RequestClient.ts +5 -5
- package/src/test/SAS_server_app.ts +10 -2
- package/src/types/FileResource.ts +33 -0
- package/src/types/WriteStream.ts +9 -3
- package/src/utils/convertToCsv.ts +5 -1
- package/src/utils/getFormData.ts +2 -2
- package/src/utils/validateInput.ts +16 -13
- package/types/FileResource.d.ts +32 -0
- package/types/PollOptions.d.ts +1 -1
- package/types/RequestClient.d.ts +1 -1
- package/types/WriteStream.d.ts +6 -3
- package/types/errors/spec/SAS9AuthError.spec.d.ts +1 -0
- package/utils/getFormData.d.ts +1 -1
- package/utils/spec/parseSasViyaLog.spec.d.ts +1 -0
- package/utils/spec/parseViyaDebugResponse.spec.d.ts +1 -0
- package/index-dev.js.LICENSE.txt +0 -110
- package/index.js.LICENSE.txt +0 -110
- package/minified_sas9.js.LICENSE.txt +0 -15
- 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
|
|
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
|
|
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?: (
|
|
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,32 @@
|
|
|
1
|
+
export interface FileResource {
|
|
2
|
+
creationTimeStamp: string;
|
|
3
|
+
modifiedTimeStamp: string;
|
|
4
|
+
createdBy: string;
|
|
5
|
+
modifiedBy: string;
|
|
6
|
+
id: string;
|
|
7
|
+
properties: Properties;
|
|
8
|
+
contentDisposition: string;
|
|
9
|
+
contentType: string;
|
|
10
|
+
encoding: string;
|
|
11
|
+
links: Link[];
|
|
12
|
+
name: string;
|
|
13
|
+
size: number;
|
|
14
|
+
searchable: boolean;
|
|
15
|
+
fileStatus: string;
|
|
16
|
+
fileVersion: number;
|
|
17
|
+
typeDefName: string;
|
|
18
|
+
version: number;
|
|
19
|
+
virusDetected: boolean;
|
|
20
|
+
urlDetected: boolean;
|
|
21
|
+
quarantine: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Link {
|
|
24
|
+
method: string;
|
|
25
|
+
rel: string;
|
|
26
|
+
href: string;
|
|
27
|
+
uri: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
responseType?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface Properties {
|
|
32
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
1
|
+
import NodeFormData from 'form-data';
|
|
2
2
|
export declare const getFormData: () => NodeFormData | FormData;
|
package/package.json
CHANGED
|
@@ -45,18 +45,21 @@
|
|
|
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": "
|
|
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": "
|
|
59
|
-
"jest-
|
|
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",
|
|
@@ -64,24 +67,24 @@
|
|
|
64
67
|
"process": "0.11.10",
|
|
65
68
|
"semantic-release": "19.0.3",
|
|
66
69
|
"terser-webpack-plugin": "5.3.6",
|
|
67
|
-
"ts-jest": "
|
|
70
|
+
"ts-jest": "29.2.6",
|
|
68
71
|
"ts-loader": "9.4.0",
|
|
69
72
|
"tslint": "6.1.3",
|
|
70
73
|
"tslint-config-prettier": "1.18.0",
|
|
71
74
|
"typedoc": "0.23.24",
|
|
72
75
|
"typedoc-plugin-rename-defaults": "0.6.4",
|
|
73
|
-
"typescript": "4.
|
|
76
|
+
"typescript": "4.9.5",
|
|
74
77
|
"webpack": "5.76.2",
|
|
75
78
|
"webpack-cli": "4.9.2"
|
|
76
79
|
},
|
|
77
80
|
"main": "index.js",
|
|
78
81
|
"dependencies": {
|
|
79
|
-
"@sasjs/utils": "
|
|
80
|
-
"axios": "
|
|
81
|
-
"axios-cookiejar-support": "
|
|
82
|
+
"@sasjs/utils": "3.5.2",
|
|
83
|
+
"axios": "1.8.2",
|
|
84
|
+
"axios-cookiejar-support": "5.0.5",
|
|
82
85
|
"form-data": "4.0.0",
|
|
83
86
|
"https": "1.0.0",
|
|
84
87
|
"tough-cookie": "4.1.3"
|
|
85
88
|
},
|
|
86
|
-
"version": "4.
|
|
89
|
+
"version": "4.12.0"
|
|
87
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
|
|
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?: (
|
|
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 {};
|
package/src/SAS9ApiClient.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as https from 'https'
|
|
2
2
|
import { generateTimestamp } from '@sasjs/utils/time'
|
|
3
|
-
import
|
|
3
|
+
import NodeFormData from 'form-data'
|
|
4
4
|
import { Sas9RequestClient } from './request/Sas9RequestClient'
|
|
5
5
|
import { isUrl } from './utils'
|
|
6
6
|
|
package/src/SASViyaApiClient.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRelativePath, isUri, isUrl } from './utils'
|
|
2
|
-
import
|
|
2
|
+
import NodeFormData from 'form-data'
|
|
3
3
|
import {
|
|
4
4
|
Job,
|
|
5
5
|
Session,
|
|
@@ -28,6 +28,7 @@ import { uploadTables } from './api/viya/uploadTables'
|
|
|
28
28
|
import { executeOnComputeApi } from './api/viya/executeOnComputeApi'
|
|
29
29
|
import { getAccessTokenForViya } from './auth/getAccessTokenForViya'
|
|
30
30
|
import { refreshTokensForViya } from './auth/refreshTokensForViya'
|
|
31
|
+
import { FileResource } from './types/FileResource'
|
|
31
32
|
|
|
32
33
|
interface JobExecutionResult {
|
|
33
34
|
result?: { result: object }
|
|
@@ -311,6 +312,84 @@ export class SASViyaApiClient {
|
|
|
311
312
|
)
|
|
312
313
|
}
|
|
313
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Fetches the file content for a file in the specified folder.
|
|
317
|
+
*
|
|
318
|
+
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
|
319
|
+
* @param fileName - the name of the file in the `folderPath`
|
|
320
|
+
* @param accessToken - an access token for authorizing the request
|
|
321
|
+
*/
|
|
322
|
+
public async getFileContent(
|
|
323
|
+
folderPath: string,
|
|
324
|
+
fileName: string,
|
|
325
|
+
accessToken?: string
|
|
326
|
+
) {
|
|
327
|
+
const fileUri = await this.getFileUri(
|
|
328
|
+
folderPath,
|
|
329
|
+
fileName,
|
|
330
|
+
accessToken
|
|
331
|
+
).catch((err) => {
|
|
332
|
+
throw prefixMessage(
|
|
333
|
+
err,
|
|
334
|
+
`Error while getting file URI for: ${fileName} in folder: ${folderPath}. `
|
|
335
|
+
)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
return await this.requestClient
|
|
339
|
+
.get<string>(`${this.serverUrl}${fileUri}/content`, accessToken)
|
|
340
|
+
.then((res) => res.result)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Updates the file content for a file in the specified folder.
|
|
345
|
+
*
|
|
346
|
+
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
|
347
|
+
* @param fileName - the name of the file in the `folderPath`
|
|
348
|
+
* @param content - the new content to be written to the file
|
|
349
|
+
* @param accessToken - an access token for authorizing the request
|
|
350
|
+
*/
|
|
351
|
+
public async updateFileContent(
|
|
352
|
+
folderPath: string,
|
|
353
|
+
fileName: string,
|
|
354
|
+
content: string,
|
|
355
|
+
accessToken?: string
|
|
356
|
+
) {
|
|
357
|
+
const fileUri = await this.getFileUri(
|
|
358
|
+
folderPath,
|
|
359
|
+
fileName,
|
|
360
|
+
accessToken
|
|
361
|
+
).catch((err) => {
|
|
362
|
+
throw prefixMessage(
|
|
363
|
+
err,
|
|
364
|
+
`Error while getting file URI for: ${fileName} in folder: ${folderPath}. `
|
|
365
|
+
)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// Fetch the file resource details to get the Etag and content type
|
|
369
|
+
const { result: originalFileResource, etag } =
|
|
370
|
+
await this.requestClient.get<FileResource>(
|
|
371
|
+
`${this.serverUrl}${fileUri}`,
|
|
372
|
+
accessToken
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
if (!originalFileResource || !etag)
|
|
376
|
+
throw new Error(
|
|
377
|
+
`File ${fileName} does not have an ETag, or request failed.`
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return await this.requestClient
|
|
381
|
+
.put<FileResource>(
|
|
382
|
+
`${this.serverUrl}${fileUri}/content`,
|
|
383
|
+
content,
|
|
384
|
+
accessToken,
|
|
385
|
+
{
|
|
386
|
+
'If-Match': etag,
|
|
387
|
+
'Content-Type': originalFileResource.contentType
|
|
388
|
+
}
|
|
389
|
+
)
|
|
390
|
+
.then((res) => res.result)
|
|
391
|
+
}
|
|
392
|
+
|
|
314
393
|
/**
|
|
315
394
|
* Fetches a folder. Path to the folder is required.
|
|
316
395
|
* @param folderPath - the absolute path to the folder.
|
|
@@ -941,6 +1020,7 @@ export class SASViyaApiClient {
|
|
|
941
1020
|
})
|
|
942
1021
|
|
|
943
1022
|
if (!folder) return undefined
|
|
1023
|
+
|
|
944
1024
|
return folder
|
|
945
1025
|
}
|
|
946
1026
|
|
|
@@ -952,6 +1032,30 @@ export class SASViyaApiClient {
|
|
|
952
1032
|
return `/folders/folders/${folderDetails.id}`
|
|
953
1033
|
}
|
|
954
1034
|
|
|
1035
|
+
private async getFileUri(
|
|
1036
|
+
folderPath: string,
|
|
1037
|
+
fileName: string,
|
|
1038
|
+
accessToken?: string
|
|
1039
|
+
): Promise<string> {
|
|
1040
|
+
const folderMembers = await this.listFolder(folderPath, accessToken, 1000, {
|
|
1041
|
+
returnDetails: true
|
|
1042
|
+
}).catch((err) => {
|
|
1043
|
+
throw prefixMessage(err, `Error while listing folder: ${folderPath}. `)
|
|
1044
|
+
})
|
|
1045
|
+
|
|
1046
|
+
if (!folderMembers || !folderMembers.length)
|
|
1047
|
+
throw new Error(`No members found in folder: ${folderPath}`)
|
|
1048
|
+
|
|
1049
|
+
const fileUri = folderMembers.find(
|
|
1050
|
+
(member) => member.name === fileName
|
|
1051
|
+
)?.uri
|
|
1052
|
+
|
|
1053
|
+
if (!fileUri)
|
|
1054
|
+
throw new Error(`File ${fileName} not found in folder: ${folderPath}`)
|
|
1055
|
+
|
|
1056
|
+
return fileUri
|
|
1057
|
+
}
|
|
1058
|
+
|
|
955
1059
|
private async getRecycleBinUri(accessToken?: string) {
|
|
956
1060
|
const url = '/folders/folders/@myRecycleBin'
|
|
957
1061
|
|
|
@@ -999,14 +1103,19 @@ export class SASViyaApiClient {
|
|
|
999
1103
|
}
|
|
1000
1104
|
|
|
1001
1105
|
/**
|
|
1002
|
-
* Lists children folders for given Viya folder.
|
|
1106
|
+
* Lists children folders/files for given Viya folder.
|
|
1003
1107
|
* @param sourceFolder - the full path (eg `/Public/example/myFolder`) or URI of the source folder listed. Providing URI instead of path will save one extra request.
|
|
1004
1108
|
* @param accessToken - an access token for authorizing the request.
|
|
1109
|
+
* @param {Object} [options] - Additional options.
|
|
1110
|
+
* @param {boolean} [options.returnDetails=false] - when set to true, the function will return an array of objects with member details, otherwise it will return an array of member names.
|
|
1005
1111
|
*/
|
|
1006
1112
|
public async listFolder(
|
|
1007
1113
|
sourceFolder: string,
|
|
1008
1114
|
accessToken?: string,
|
|
1009
|
-
limit: number = 20
|
|
1115
|
+
limit: number = 20,
|
|
1116
|
+
options?: {
|
|
1117
|
+
returnDetails?: boolean
|
|
1118
|
+
}
|
|
1010
1119
|
) {
|
|
1011
1120
|
// checks if 'sourceFolder' is already a URI
|
|
1012
1121
|
const sourceFolderUri = isUri(sourceFolder)
|
|
@@ -1018,11 +1127,20 @@ export class SASViyaApiClient {
|
|
|
1018
1127
|
accessToken
|
|
1019
1128
|
)
|
|
1020
1129
|
|
|
1130
|
+
let membersToReturn = []
|
|
1131
|
+
|
|
1021
1132
|
if (members && members.items) {
|
|
1022
|
-
return
|
|
1023
|
-
|
|
1024
|
-
|
|
1133
|
+
// If returnDetails is true, return full member details
|
|
1134
|
+
if (options?.returnDetails) {
|
|
1135
|
+
membersToReturn = members.items
|
|
1136
|
+
} else {
|
|
1137
|
+
// If returnDetails is false, return only member names
|
|
1138
|
+
membersToReturn = members.items.map((item: any) => item.name)
|
|
1139
|
+
}
|
|
1025
1140
|
}
|
|
1141
|
+
|
|
1142
|
+
// Return members without Etag
|
|
1143
|
+
return membersToReturn
|
|
1026
1144
|
}
|
|
1027
1145
|
|
|
1028
1146
|
/**
|
package/src/SASjs.ts
CHANGED
|
@@ -411,6 +411,51 @@ export default class SASjs {
|
|
|
411
411
|
)
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Fetches the file content for a file in the specified folder.
|
|
416
|
+
*
|
|
417
|
+
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
|
418
|
+
* @param fileName - the name of the file in the `folderPath`
|
|
419
|
+
* @param accessToken - an access token for authorizing the request
|
|
420
|
+
*/
|
|
421
|
+
public async getFileContent(
|
|
422
|
+
folderPath: string,
|
|
423
|
+
fileName: string,
|
|
424
|
+
accessToken?: string
|
|
425
|
+
) {
|
|
426
|
+
this.isMethodSupported('getFileContent', [ServerType.SasViya])
|
|
427
|
+
|
|
428
|
+
return await this.sasViyaApiClient!.getFileContent(
|
|
429
|
+
folderPath,
|
|
430
|
+
fileName,
|
|
431
|
+
accessToken
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Updates the file content for a file in the specified folder.
|
|
437
|
+
*
|
|
438
|
+
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
|
439
|
+
* @param fileName - the name of the file in the `folderPath`
|
|
440
|
+
* @param content - the new content to be written to the file
|
|
441
|
+
* @param accessToken - an access token for authorizing the request
|
|
442
|
+
*/
|
|
443
|
+
public async updateFileContent(
|
|
444
|
+
folderPath: string,
|
|
445
|
+
fileName: string,
|
|
446
|
+
content: string,
|
|
447
|
+
accessToken?: string
|
|
448
|
+
) {
|
|
449
|
+
this.isMethodSupported('updateFileContent', [ServerType.SasViya])
|
|
450
|
+
|
|
451
|
+
return await this.sasViyaApiClient!.updateFileContent(
|
|
452
|
+
folderPath,
|
|
453
|
+
fileName,
|
|
454
|
+
content,
|
|
455
|
+
accessToken
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
|
|
414
459
|
/**
|
|
415
460
|
* Fetches a folder from the SAS file system.
|
|
416
461
|
* @param folderPath - path of the folder to be fetched.
|
|
@@ -436,18 +481,23 @@ export default class SASjs {
|
|
|
436
481
|
* Lists children folders for given Viya folder.
|
|
437
482
|
* @param sourceFolder - the full path (eg `/Public/example/myFolder`) or URI of the source folder listed. Providing URI instead of path will save one extra request.
|
|
438
483
|
* @param accessToken - an access token for authorizing the request.
|
|
484
|
+
* @param returnDetails - when set to true, the function will return an array of objects with member details, otherwise it will return an array of member names.
|
|
439
485
|
*/
|
|
440
486
|
public async listFolder(
|
|
441
487
|
sourceFolder: string,
|
|
442
488
|
accessToken?: string,
|
|
443
|
-
limit?: number
|
|
489
|
+
limit?: number,
|
|
490
|
+
returnDetails = false
|
|
444
491
|
) {
|
|
445
492
|
this.isMethodSupported('listFolder', [ServerType.SasViya])
|
|
446
493
|
|
|
447
494
|
return await this.sasViyaApiClient?.listFolder(
|
|
448
495
|
sourceFolder,
|
|
449
496
|
accessToken,
|
|
450
|
-
limit
|
|
497
|
+
limit,
|
|
498
|
+
{
|
|
499
|
+
returnDetails
|
|
500
|
+
}
|
|
451
501
|
)
|
|
452
502
|
}
|
|
453
503
|
|
|
@@ -1170,8 +1220,8 @@ export default class SASjs {
|
|
|
1170
1220
|
* @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
|
|
1171
1221
|
*/
|
|
1172
1222
|
public enableVerboseMode(
|
|
1173
|
-
successCallBack?: (response: AxiosResponse
|
|
1174
|
-
errorCallBack?: (response:
|
|
1223
|
+
successCallBack?: (response: AxiosResponse) => AxiosResponse,
|
|
1224
|
+
errorCallBack?: (response: AxiosError) => AxiosError
|
|
1175
1225
|
) {
|
|
1176
1226
|
this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
|
|
1177
1227
|
}
|
package/src/SASjsApiClient.ts
CHANGED
|
@@ -3,9 +3,14 @@ import { WriteStream } from '../../types'
|
|
|
3
3
|
export const writeStream = async (
|
|
4
4
|
stream: WriteStream,
|
|
5
5
|
content: string
|
|
6
|
-
): Promise<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import * as https from 'https'
|
|
2
2
|
import { ServerType } from '@sasjs/utils/types'
|
|
3
|
-
import
|
|
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
|
|
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=${
|
|
77
|
-
|
|
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
|
|
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=${
|
|
154
|
-
|
|
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(
|