mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-09 05:20:05 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde28046be | ||
|
|
eab61a80bf | ||
|
|
9149f932c3 | ||
|
|
fb30ff8876 | ||
|
|
afff422333 | ||
|
|
b49010cfe5 | ||
|
|
fd6fad9b07 | ||
|
|
8a10c229d6 | ||
|
|
66462fcc50 | ||
|
|
7e23b5db9d | ||
| 78f117812e | |||
|
|
55af8c3f50 | ||
| 1185c2f1bf | |||
|
|
2842636c4a | ||
| 8c7f614509 | |||
| 943f60ea11 | |||
| 3de343f135 | |||
| e11c97ec5d | |||
| 49fba07824 | |||
| b1c0e26c23 |
11
.github/workflows/build-unit-tests.yml
vendored
11
.github/workflows/build-unit-tests.yml
vendored
@@ -20,7 +20,16 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
|
||||||
|
# 2. Restore npm cache manually
|
||||||
|
- name: Restore npm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: npm-cache
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-
|
||||||
|
|
||||||
- name: Check npm audit
|
- name: Check npm audit
|
||||||
run: npm audit --production --audit-level=low
|
run: npm audit --production --audit-level=low
|
||||||
|
|||||||
11
.github/workflows/generateDocs.yml
vendored
11
.github/workflows/generateDocs.yml
vendored
@@ -21,7 +21,16 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
|
||||||
|
# 2. Restore npm cache manually
|
||||||
|
- name: Restore npm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: npm-cache
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
11
.github/workflows/npmpublish.yml
vendored
11
.github/workflows/npmpublish.yml
vendored
@@ -22,7 +22,16 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
|
||||||
|
# 2. Restore npm cache manually
|
||||||
|
- name: Restore npm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: npm-cache
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
11
.github/workflows/server-tests.yml
vendored
11
.github/workflows/server-tests.yml
vendored
@@ -20,7 +20,16 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
|
||||||
|
# 2. Restore npm cache manually
|
||||||
|
- name: Restore npm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: npm-cache
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
46
package-lock.json
generated
46
package-lock.json
generated
@@ -9,9 +9,9 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "3.5.2",
|
"@sasjs/utils": "3.5.2",
|
||||||
"axios": "1.8.2",
|
"axios": "1.12.2",
|
||||||
"axios-cookiejar-support": "5.0.5",
|
"axios-cookiejar-support": "5.0.5",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.4",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "4.1.3"
|
"tough-cookie": "4.1.3"
|
||||||
},
|
},
|
||||||
@@ -3510,13 +3510,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.8.2",
|
"version": "1.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||||
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.4",
|
||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4096,7 +4096,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -5204,7 +5203,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
@@ -5459,7 +5457,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/es-define-property": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -5467,7 +5464,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/es-errors": {
|
"node_modules/es-errors": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -5480,7 +5476,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/es-object-atoms": {
|
"node_modules/es-object-atoms": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@@ -5489,6 +5484,21 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-set-tostringtag": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es6-promisify": {
|
"node_modules/es6-promisify": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -6046,11 +6056,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -6147,7 +6161,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -6171,7 +6184,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -6204,7 +6216,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/get-proto": {
|
"node_modules/get-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dunder-proto": "^1.0.1",
|
"dunder-proto": "^1.0.1",
|
||||||
@@ -6343,7 +6354,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/gopd": {
|
"node_modules/gopd": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -6404,7 +6414,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/has-symbols": {
|
"node_modules/has-symbols": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -6415,7 +6424,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/has-tostringtag": {
|
"node_modules/has-tostringtag": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
@@ -6450,7 +6458,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -8639,7 +8646,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
|||||||
@@ -80,9 +80,9 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "3.5.2",
|
"@sasjs/utils": "3.5.2",
|
||||||
"axios": "1.8.2",
|
"axios": "1.12.2",
|
||||||
"axios-cookiejar-support": "5.0.5",
|
"axios-cookiejar-support": "5.0.5",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.4",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "4.1.3"
|
"tough-cookie": "4.1.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { uploadTables } from './api/viya/uploadTables'
|
|||||||
import { executeOnComputeApi } from './api/viya/executeOnComputeApi'
|
import { executeOnComputeApi } from './api/viya/executeOnComputeApi'
|
||||||
import { getAccessTokenForViya } from './auth/getAccessTokenForViya'
|
import { getAccessTokenForViya } from './auth/getAccessTokenForViya'
|
||||||
import { refreshTokensForViya } from './auth/refreshTokensForViya'
|
import { refreshTokensForViya } from './auth/refreshTokensForViya'
|
||||||
|
import { FileResource } from './types/FileResource'
|
||||||
|
|
||||||
interface JobExecutionResult {
|
interface JobExecutionResult {
|
||||||
result?: { result: object }
|
result?: { result: object }
|
||||||
@@ -311,6 +312,84 @@ export class SASViyaApiClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the file content for a file in the specified folder.
|
||||||
|
*
|
||||||
|
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
||||||
|
* @param fileName - the name of the file in the `folderPath`
|
||||||
|
* @param accessToken - an access token for authorizing the request
|
||||||
|
*/
|
||||||
|
public async getFileContent(
|
||||||
|
folderPath: string,
|
||||||
|
fileName: string,
|
||||||
|
accessToken?: string
|
||||||
|
) {
|
||||||
|
const fileUri = await this.getFileUri(
|
||||||
|
folderPath,
|
||||||
|
fileName,
|
||||||
|
accessToken
|
||||||
|
).catch((err) => {
|
||||||
|
throw prefixMessage(
|
||||||
|
err,
|
||||||
|
`Error while getting file URI for: ${fileName} in folder: ${folderPath}. `
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return await this.requestClient
|
||||||
|
.get<string>(`${this.serverUrl}${fileUri}/content`, accessToken)
|
||||||
|
.then((res) => res.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the file content for a file in the specified folder.
|
||||||
|
*
|
||||||
|
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
||||||
|
* @param fileName - the name of the file in the `folderPath`
|
||||||
|
* @param content - the new content to be written to the file
|
||||||
|
* @param accessToken - an access token for authorizing the request
|
||||||
|
*/
|
||||||
|
public async updateFileContent(
|
||||||
|
folderPath: string,
|
||||||
|
fileName: string,
|
||||||
|
content: string,
|
||||||
|
accessToken?: string
|
||||||
|
) {
|
||||||
|
const fileUri = await this.getFileUri(
|
||||||
|
folderPath,
|
||||||
|
fileName,
|
||||||
|
accessToken
|
||||||
|
).catch((err) => {
|
||||||
|
throw prefixMessage(
|
||||||
|
err,
|
||||||
|
`Error while getting file URI for: ${fileName} in folder: ${folderPath}. `
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch the file resource details to get the Etag and content type
|
||||||
|
const { result: originalFileResource, etag } =
|
||||||
|
await this.requestClient.get<FileResource>(
|
||||||
|
`${this.serverUrl}${fileUri}`,
|
||||||
|
accessToken
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!originalFileResource || !etag)
|
||||||
|
throw new Error(
|
||||||
|
`File ${fileName} does not have an ETag, or request failed.`
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.requestClient
|
||||||
|
.put<FileResource>(
|
||||||
|
`${this.serverUrl}${fileUri}/content`,
|
||||||
|
content,
|
||||||
|
accessToken,
|
||||||
|
{
|
||||||
|
'If-Match': etag,
|
||||||
|
'Content-Type': originalFileResource.contentType
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => res.result)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a folder. Path to the folder is required.
|
* Fetches a folder. Path to the folder is required.
|
||||||
* @param folderPath - the absolute path to the folder.
|
* @param folderPath - the absolute path to the folder.
|
||||||
@@ -791,14 +870,14 @@ export class SASViyaApiClient {
|
|||||||
_webin_file_count: files.length,
|
_webin_file_count: files.length,
|
||||||
_OMITJSONLISTING: true,
|
_OMITJSONLISTING: true,
|
||||||
_OMITJSONLOG: true,
|
_OMITJSONLOG: true,
|
||||||
_OMITSESSIONRESULTS: true,
|
_omitSessionResults: false,
|
||||||
_OMITTEXTLISTING: true,
|
_OMITTEXTLISTING: true,
|
||||||
_OMITTEXTLOG: true
|
_OMITTEXTLOG: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
jobArguments['_OMITTEXTLOG'] = 'false'
|
jobArguments['_OMITTEXTLOG'] = 'false'
|
||||||
jobArguments['_OMITSESSIONRESULTS'] = 'false'
|
jobArguments['_omitSessionResults'] = 'false'
|
||||||
jobArguments['_DEBUG'] = 131
|
jobArguments['_DEBUG'] = 131
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,6 +1020,7 @@ export class SASViyaApiClient {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!folder) return undefined
|
if (!folder) return undefined
|
||||||
|
|
||||||
return folder
|
return folder
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -952,6 +1032,30 @@ export class SASViyaApiClient {
|
|||||||
return `/folders/folders/${folderDetails.id}`
|
return `/folders/folders/${folderDetails.id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getFileUri(
|
||||||
|
folderPath: string,
|
||||||
|
fileName: string,
|
||||||
|
accessToken?: string
|
||||||
|
): Promise<string> {
|
||||||
|
const folderMembers = await this.listFolder(folderPath, accessToken, 1000, {
|
||||||
|
returnDetails: true
|
||||||
|
}).catch((err) => {
|
||||||
|
throw prefixMessage(err, `Error while listing folder: ${folderPath}. `)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!folderMembers || !folderMembers.length)
|
||||||
|
throw new Error(`No members found in folder: ${folderPath}`)
|
||||||
|
|
||||||
|
const fileUri = folderMembers.find(
|
||||||
|
(member) => member.name === fileName
|
||||||
|
)?.uri
|
||||||
|
|
||||||
|
if (!fileUri)
|
||||||
|
throw new Error(`File ${fileName} not found in folder: ${folderPath}`)
|
||||||
|
|
||||||
|
return fileUri
|
||||||
|
}
|
||||||
|
|
||||||
private async getRecycleBinUri(accessToken?: string) {
|
private async getRecycleBinUri(accessToken?: string) {
|
||||||
const url = '/folders/folders/@myRecycleBin'
|
const url = '/folders/folders/@myRecycleBin'
|
||||||
|
|
||||||
@@ -999,14 +1103,19 @@ export class SASViyaApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists children folders for given Viya folder.
|
* Lists children folders/files for given Viya folder.
|
||||||
* @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.
|
* @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.
|
||||||
* @param accessToken - an access token for authorizing the request.
|
* @param accessToken - an access token for authorizing the request.
|
||||||
|
* @param {Object} [options] - Additional options.
|
||||||
|
* @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.
|
||||||
*/
|
*/
|
||||||
public async listFolder(
|
public async listFolder(
|
||||||
sourceFolder: string,
|
sourceFolder: string,
|
||||||
accessToken?: string,
|
accessToken?: string,
|
||||||
limit: number = 20
|
limit: number = 20,
|
||||||
|
options?: {
|
||||||
|
returnDetails?: boolean
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
// checks if 'sourceFolder' is already a URI
|
// checks if 'sourceFolder' is already a URI
|
||||||
const sourceFolderUri = isUri(sourceFolder)
|
const sourceFolderUri = isUri(sourceFolder)
|
||||||
@@ -1018,11 +1127,20 @@ export class SASViyaApiClient {
|
|||||||
accessToken
|
accessToken
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let membersToReturn = []
|
||||||
|
|
||||||
if (members && members.items) {
|
if (members && members.items) {
|
||||||
return members.items.map((item: any) => item.name)
|
// If returnDetails is true, return full member details
|
||||||
} else {
|
if (options?.returnDetails) {
|
||||||
return []
|
membersToReturn = members.items
|
||||||
|
} else {
|
||||||
|
// If returnDetails is false, return only member names
|
||||||
|
membersToReturn = members.items.map((item: any) => item.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return members without Etag
|
||||||
|
return membersToReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
68
src/SASjs.ts
68
src/SASjs.ts
@@ -9,7 +9,8 @@ import {
|
|||||||
ErrorResponse,
|
ErrorResponse,
|
||||||
LoginOptions,
|
LoginOptions,
|
||||||
LoginResult,
|
LoginResult,
|
||||||
ExecutionQuery
|
ExecutionQuery,
|
||||||
|
Tables
|
||||||
} from './types'
|
} from './types'
|
||||||
import { SASViyaApiClient } from './SASViyaApiClient'
|
import { SASViyaApiClient } from './SASViyaApiClient'
|
||||||
import { SAS9ApiClient } from './SAS9ApiClient'
|
import { SAS9ApiClient } from './SAS9ApiClient'
|
||||||
@@ -411,6 +412,51 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the file content for a file in the specified folder.
|
||||||
|
*
|
||||||
|
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
||||||
|
* @param fileName - the name of the file in the `folderPath`
|
||||||
|
* @param accessToken - an access token for authorizing the request
|
||||||
|
*/
|
||||||
|
public async getFileContent(
|
||||||
|
folderPath: string,
|
||||||
|
fileName: string,
|
||||||
|
accessToken?: string
|
||||||
|
) {
|
||||||
|
this.isMethodSupported('getFileContent', [ServerType.SasViya])
|
||||||
|
|
||||||
|
return await this.sasViyaApiClient!.getFileContent(
|
||||||
|
folderPath,
|
||||||
|
fileName,
|
||||||
|
accessToken
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the file content for a file in the specified folder.
|
||||||
|
*
|
||||||
|
* @param folderPath - the full path to the folder containing the file. eg: /Public/folder1/folder2
|
||||||
|
* @param fileName - the name of the file in the `folderPath`
|
||||||
|
* @param content - the new content to be written to the file
|
||||||
|
* @param accessToken - an access token for authorizing the request
|
||||||
|
*/
|
||||||
|
public async updateFileContent(
|
||||||
|
folderPath: string,
|
||||||
|
fileName: string,
|
||||||
|
content: string,
|
||||||
|
accessToken?: string
|
||||||
|
) {
|
||||||
|
this.isMethodSupported('updateFileContent', [ServerType.SasViya])
|
||||||
|
|
||||||
|
return await this.sasViyaApiClient!.updateFileContent(
|
||||||
|
folderPath,
|
||||||
|
fileName,
|
||||||
|
content,
|
||||||
|
accessToken
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a folder from the SAS file system.
|
* Fetches a folder from the SAS file system.
|
||||||
* @param folderPath - path of the folder to be fetched.
|
* @param folderPath - path of the folder to be fetched.
|
||||||
@@ -436,18 +482,23 @@ export default class SASjs {
|
|||||||
* Lists children folders for given Viya folder.
|
* Lists children folders for given Viya folder.
|
||||||
* @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.
|
* @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.
|
||||||
* @param accessToken - an access token for authorizing the request.
|
* @param accessToken - an access token for authorizing the request.
|
||||||
|
* @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.
|
||||||
*/
|
*/
|
||||||
public async listFolder(
|
public async listFolder(
|
||||||
sourceFolder: string,
|
sourceFolder: string,
|
||||||
accessToken?: string,
|
accessToken?: string,
|
||||||
limit?: number
|
limit?: number,
|
||||||
|
returnDetails = false
|
||||||
) {
|
) {
|
||||||
this.isMethodSupported('listFolder', [ServerType.SasViya])
|
this.isMethodSupported('listFolder', [ServerType.SasViya])
|
||||||
|
|
||||||
return await this.sasViyaApiClient?.listFolder(
|
return await this.sasViyaApiClient?.listFolder(
|
||||||
sourceFolder,
|
sourceFolder,
|
||||||
accessToken,
|
accessToken,
|
||||||
limit
|
limit,
|
||||||
|
{
|
||||||
|
returnDetails
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,4 +1241,15 @@ export default class SASjs {
|
|||||||
public setVerboseMode = (verboseMode: VerboseMode) => {
|
public setVerboseMode = (verboseMode: VerboseMode) => {
|
||||||
this.requestClient?.setVerboseMode(verboseMode)
|
this.requestClient?.setVerboseMode(verboseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tables class containing one or more tables to be sent to
|
||||||
|
* SAS.
|
||||||
|
* @param table - initial table data
|
||||||
|
* @param macroName - macro name
|
||||||
|
* @returns Tables class
|
||||||
|
*/
|
||||||
|
Tables(table: Record<string, any>, macroName: string) {
|
||||||
|
return new Tables(table, macroName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export abstract class BaseJobExecutor implements JobExecutor {
|
|||||||
|
|
||||||
if (config.debug) {
|
if (config.debug) {
|
||||||
requestParams['_omittextlog'] = 'false'
|
requestParams['_omittextlog'] = 'false'
|
||||||
requestParams['_omitsessionresults'] = 'false'
|
requestParams['_omitSessionResults'] = 'false'
|
||||||
|
|
||||||
requestParams['_debug'] = 131
|
requestParams['_debug'] = 131
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ Connection: close
|
|||||||
_contextName: 'SAS Job Execution compute context',
|
_contextName: 'SAS Job Execution compute context',
|
||||||
_OMITJSONLISTING: true,
|
_OMITJSONLISTING: true,
|
||||||
_OMITJSONLOG: true,
|
_OMITJSONLOG: true,
|
||||||
_OMITSESSIONRESULTS: true,
|
_omitSessionResults: true,
|
||||||
_OMITTEXTLISTING: true,
|
_OMITTEXTLISTING: true,
|
||||||
_OMITTEXTLOG: true
|
_OMITTEXTLOG: true
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/types/FileResource.ts
Normal file
33
src/types/FileResource.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export interface FileResource {
|
||||||
|
creationTimeStamp: string
|
||||||
|
modifiedTimeStamp: string
|
||||||
|
createdBy: string
|
||||||
|
modifiedBy: string
|
||||||
|
id: string
|
||||||
|
properties: Properties
|
||||||
|
contentDisposition: string
|
||||||
|
contentType: string
|
||||||
|
encoding: string
|
||||||
|
links: Link[]
|
||||||
|
name: string
|
||||||
|
size: number
|
||||||
|
searchable: boolean
|
||||||
|
fileStatus: string
|
||||||
|
fileVersion: number
|
||||||
|
typeDefName: string
|
||||||
|
version: number
|
||||||
|
virusDetected: boolean
|
||||||
|
urlDetected: boolean
|
||||||
|
quarantine: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Link {
|
||||||
|
method: string
|
||||||
|
rel: string
|
||||||
|
href: string
|
||||||
|
uri: string
|
||||||
|
type?: string
|
||||||
|
responseType?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Properties {}
|
||||||
28
src/types/Tables.spec.ts
Normal file
28
src/types/Tables.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import SASjs from '../SASjs'
|
||||||
|
|
||||||
|
describe('Tables - basic coverage', () => {
|
||||||
|
const adapter = new SASjs()
|
||||||
|
|
||||||
|
it('should throw an error if first argument is not an array', () => {
|
||||||
|
expect(() => adapter.Tables({}, 'test')).toThrow('First argument')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if second argument is not a string', () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => adapter.Tables([], 1234)).toThrow('Second argument')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if macro name ends with a number', () => {
|
||||||
|
expect(() => adapter.Tables([], 'test1')).toThrow('number at the end')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if no arguments are passed', () => {
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => adapter.Tables()).toThrow('Missing arguments')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create Tables class successfully with _tables property', () => {
|
||||||
|
const tables = adapter.Tables([], 'test')
|
||||||
|
expect(tables).toHaveProperty('_tables')
|
||||||
|
})
|
||||||
|
})
|
||||||
29
src/types/Tables.ts
Normal file
29
src/types/Tables.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { ArgumentError } from './errors'
|
||||||
|
|
||||||
|
export class Tables {
|
||||||
|
_tables: { [macroName: string]: Record<string, any> }
|
||||||
|
|
||||||
|
constructor(table: Record<string, any>, macroName: string) {
|
||||||
|
this._tables = {}
|
||||||
|
|
||||||
|
this.add(table, macroName)
|
||||||
|
}
|
||||||
|
|
||||||
|
add(table: Record<string, any> | null, macroName: string) {
|
||||||
|
if (table && macroName) {
|
||||||
|
if (!(table instanceof Array)) {
|
||||||
|
throw new ArgumentError('First argument must be array')
|
||||||
|
}
|
||||||
|
if (typeof macroName !== 'string') {
|
||||||
|
throw new ArgumentError('Second argument must be string')
|
||||||
|
}
|
||||||
|
if (!isNaN(Number(macroName[macroName.length - 1]))) {
|
||||||
|
throw new ArgumentError('Macro name cannot have number at the end')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Missing arguments')
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tables[macroName] = table
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/types/errors/ArgumentError.ts
Normal file
7
src/types/errors/ArgumentError.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class ArgumentError extends Error {
|
||||||
|
constructor(public message: string) {
|
||||||
|
super(message)
|
||||||
|
this.name = 'ArgumentError'
|
||||||
|
Object.setPrototypeOf(this, ArgumentError.prototype)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './ArgumentError'
|
||||||
export * from './AuthorizeError'
|
export * from './AuthorizeError'
|
||||||
export * from './CertificateError'
|
export * from './CertificateError'
|
||||||
export * from './ComputeJobExecutionError'
|
export * from './ComputeJobExecutionError'
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ export * from './PollOptions'
|
|||||||
export * from './WriteStream'
|
export * from './WriteStream'
|
||||||
export * from './ExecuteScript'
|
export * from './ExecuteScript'
|
||||||
export * from './errors'
|
export * from './errors'
|
||||||
|
export * from './Tables'
|
||||||
|
|||||||
Reference in New Issue
Block a user