mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-09 05:20:05 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a006ead205 | ||
|
|
422c2a1fd5 | ||
|
|
0c6409e402 | ||
|
|
68b864cf75 | ||
|
|
75a11cdff4 | ||
|
|
4e2b6d32cc | ||
|
|
cd9757b383 | ||
|
|
fb727788d0 | ||
|
|
35eb6c4935 | ||
|
|
ea0f338b90 | ||
|
|
b6a17b39b9 | ||
|
|
9ed64e5a2c | ||
|
|
0479a5d651 | ||
|
|
005f10bb47 | ||
|
|
98c9cb78ff | ||
|
|
8192f69f67 | ||
|
|
c28a8ebf15 | ||
|
|
a409d8cdb6 | ||
|
|
618a20eaba | ||
|
|
c9b1273c31 | ||
|
|
59674744be | ||
|
|
870cc0055b | ||
|
|
0ffa62fab4 | ||
|
|
b4c7868fb6 | ||
|
|
2266578013 | ||
|
|
f2ebe1a5b0 | ||
|
|
6a52bbe560 | ||
|
|
a5c725e677 | ||
|
|
f5e1907e28 | ||
|
|
f7a9b0cbb6 | ||
|
|
1258a1a180 | ||
|
|
0bb343a1de | ||
|
|
929c89b70f | ||
|
|
169ca35238 | ||
|
|
60be28f149 | ||
|
|
14daa55184 | ||
|
|
f763f05b5e | ||
|
|
b6aced5bad | ||
|
|
7bb7db0f27 | ||
|
|
36ea148446 | ||
|
|
c1750c014e |
@@ -70,6 +70,14 @@ parmcards4;
|
|||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mp_createwebservice(path=/Public/app/common,name=sendArr)
|
%mp_createwebservice(path=/Public/app/common,name=sendArr)
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
If you can keep your head when all about you
|
||||||
|
Are losing theirs and blaming it on you,
|
||||||
|
If you can trust yourself when all men doubt you,
|
||||||
|
But make allowance for their doubting too;
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=makeErr)
|
||||||
```
|
```
|
||||||
|
|
||||||
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
|
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
|
||||||
|
|||||||
1250
package-lock.json
generated
1250
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/isomorphic-fetch": "0.0.35",
|
"@types/isomorphic-fetch": "0.0.35",
|
||||||
"@types/jest": "^26.0.8",
|
"@types/jest": "^26.0.10",
|
||||||
"cp": "^0.2.0",
|
"cp": "^0.2.0",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"typedoc-plugin-external-module-name": "^4.0.3",
|
"typedoc-plugin-external-module-name": "^4.0.3",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.44.1",
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
61
sasjs-tests/package-lock.json
generated
61
sasjs-tests/package-lock.json
generated
@@ -1357,9 +1357,9 @@
|
|||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
"@sasjs/adapter": {
|
"@sasjs/adapter": {
|
||||||
"version": "1.0.5",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.6.tgz",
|
||||||
"integrity": "sha512-54gQZD7QdNmQu77axOqr0vMS7hUVXO5hPbUtwXXocMIi3kRQDbROYjC3kuiFM9FrxqiZWbLRcyOqmFv3W/N36w==",
|
"integrity": "sha512-d2B+cTII+vabKCU8mJy90mEz3tCWw2pEp4qIBGsDamJiTS0Rx69dgXGHuRUm8KtjLDHHrzwXATsqviU3dnU0QQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-promise": "^4.2.8",
|
"es6-promise": "^4.2.8",
|
||||||
"form-data": "^3.0.0",
|
"form-data": "^3.0.0",
|
||||||
@@ -1379,15 +1379,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/test-framework": {
|
"@sasjs/test-framework": {
|
||||||
"version": "1.3.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.4.0.tgz",
|
||||||
"integrity": "sha512-vrbRFUhNUShLlNFZO+XwVwFLXDLApQG9zOPx00xhQ8IUA0cSDFFmf2mP/KBdFCxa1REaR6GHvMctUj+xRZo9QQ==",
|
"integrity": "sha512-Pd8PUH5B5RO6q4w3OQXX7aWicvA/CJMXA/FCf2xp332ZTKBb/5uV+HphAOFKpCh58y+ykYYVSV0ZaDO/4t1h3A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/react-highlight.js": "^1.0.0",
|
"@types/react-highlight.js": "^1.0.0",
|
||||||
|
"immer": "^7.0.7",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.27.0",
|
||||||
"react-highlight.js": "^1.0.7",
|
"react-highlight.js": "^1.0.7",
|
||||||
"semantic-ui-css": "^2.4.1",
|
"semantic-ui-css": "^2.4.1",
|
||||||
"semantic-ui-react": "^1.0.0"
|
"semantic-ui-react": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"immer": {
|
||||||
|
"version": "7.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.7.tgz",
|
||||||
|
"integrity": "sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@semantic-ui-react/event-stack": {
|
"@semantic-ui-react/event-stack": {
|
||||||
@@ -3747,11 +3755,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"classnames": {
|
|
||||||
"version": "2.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
|
|
||||||
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
|
|
||||||
},
|
|
||||||
"clean-css": {
|
"clean-css": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
|
||||||
@@ -3829,6 +3832,11 @@
|
|||||||
"shallow-clone": "^0.1.2"
|
"shallow-clone": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clsx": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
|
||||||
|
},
|
||||||
"co": {
|
"co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -12226,21 +12234,36 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semantic-ui-react": {
|
"semantic-ui-react": {
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.2.0.tgz",
|
||||||
"integrity": "sha512-85mYHYuDBNa6la1BgKwuOSD1vcIPsFQEXRxGsZ9pUtE4iHlEcylF+x46NYHIGbBjlys63SpNH3PtK6VyZj9LBw==",
|
"integrity": "sha512-9tNL94nEy16RdupTQNiURyemWUIxtTpQgFimCbOOHRBOe1ApsFz3FWFsrGjv9zFtE7dQMslLYov9BQOelTCVwA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"@babel/runtime": "^7.10.5",
|
||||||
"@semantic-ui-react/event-stack": "^3.1.0",
|
"@semantic-ui-react/event-stack": "^3.1.0",
|
||||||
"@stardust-ui/react-component-event-listener": "~0.38.0",
|
"@stardust-ui/react-component-event-listener": "~0.38.0",
|
||||||
"@stardust-ui/react-component-ref": "~0.38.0",
|
"@stardust-ui/react-component-ref": "~0.38.0",
|
||||||
"classnames": "^2.2.6",
|
"clsx": "^1.1.1",
|
||||||
"keyboard-key": "^1.0.4",
|
"keyboard-key": "^1.1.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.19",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-is": "^16.8.6",
|
"react-is": "^16.8.6",
|
||||||
"react-popper": "^1.3.4",
|
"react-popper": "^1.3.7",
|
||||||
"shallowequal": "^1.1.0"
|
"shallowequal": "^1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": {
|
||||||
|
"version": "7.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
|
||||||
|
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
|
||||||
|
"requires": {
|
||||||
|
"regenerator-runtime": "^0.13.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||||
|
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
"homepage": ".",
|
"homepage": ".",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "^1.0.5",
|
"@sasjs/adapter": "^1.3.6",
|
||||||
"@sasjs/test-framework": "^1.3.0",
|
"@sasjs/test-framework": "^1.4.0",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.5.0",
|
"@testing-library/react": "^9.5.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const Login = (): ReactElement<{}> => {
|
|||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
appContext.adapter.logIn(username, password).then((res) => {
|
appContext.adapter.logIn(username, password).then((res) => {
|
||||||
appContext.setIsLoggedIn(true);
|
appContext.setIsLoggedIn(res.isLoggedIn);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[username, password, appContext]
|
[username, password, appContext]
|
||||||
|
|||||||
@@ -21,5 +21,26 @@ export const sasjsRequestTests = (adapter: SASjs): TestSuite => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Make error and capture log",
|
||||||
|
description: "Should make an error and capture log",
|
||||||
|
test: async () => {
|
||||||
|
return new Promise( async (resolve, reject) => {
|
||||||
|
adapter.request("common/makeErr", data)
|
||||||
|
.then((res) => {
|
||||||
|
//no action here, this request must throw error
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
let sasRequests = adapter.getSasRequests();
|
||||||
|
let makeErrRequest = sasRequests.find(req => req.serviceLink.includes('makeErr')) || null;
|
||||||
|
|
||||||
|
resolve(!!makeErrRequest);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
assertion: (response) => {
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export class FileUploader {
|
|||||||
private appLoc: string,
|
private appLoc: string,
|
||||||
private serverUrl: string,
|
private serverUrl: string,
|
||||||
private jobsPath: string,
|
private jobsPath: string,
|
||||||
private csrfToken: CsrfToken | null = null
|
private setCsrfTokenWeb: any,
|
||||||
|
private csrfToken: CsrfToken | null = null,
|
||||||
) {}
|
) {}
|
||||||
private retryCount = 0;
|
private retryCount = 0;
|
||||||
|
|
||||||
@@ -61,6 +62,8 @@ export class FileUploader {
|
|||||||
headerName: tokenHeader,
|
headerName: tokenHeader,
|
||||||
value: token || "",
|
value: token || "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.setCsrfTokenWeb(this.csrfToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ export class SASViyaApiClient {
|
|||||||
}
|
}
|
||||||
private csrfToken: CsrfToken | null = null;
|
private csrfToken: CsrfToken | null = null;
|
||||||
private rootFolder: Folder | null = null;
|
private rootFolder: Folder | null = null;
|
||||||
private sessionManager = new SessionManager(this.serverUrl, this.contextName, this.setCsrfToken);
|
private sessionManager = new SessionManager(
|
||||||
|
this.serverUrl,
|
||||||
|
this.contextName,
|
||||||
|
this.setCsrfToken
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a map containing the directory structure in the currently set root folder.
|
* Returns a map containing the directory structure in the currently set root folder.
|
||||||
@@ -113,9 +117,7 @@ export class SASViyaApiClient {
|
|||||||
`test-${context.name}`,
|
`test-${context.name}`,
|
||||||
linesOfCode,
|
linesOfCode,
|
||||||
context.name,
|
context.name,
|
||||||
accessToken,
|
accessToken
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
).catch(() => null);
|
).catch(() => null);
|
||||||
});
|
});
|
||||||
const results = await Promise.all(promises);
|
const results = await Promise.all(promises);
|
||||||
@@ -201,126 +203,152 @@ export class SASViyaApiClient {
|
|||||||
linesOfCode: string[],
|
linesOfCode: string[],
|
||||||
contextName: string,
|
contextName: string,
|
||||||
accessToken?: string,
|
accessToken?: string,
|
||||||
sessionId = "",
|
|
||||||
silent = false,
|
silent = false,
|
||||||
data = null,
|
data = null,
|
||||||
debug = false
|
debug = false
|
||||||
) {
|
): Promise<any> {
|
||||||
const headers: any = {
|
silent = !debug;
|
||||||
"Content-Type": "application/json",
|
try {
|
||||||
};
|
const headers: any = {
|
||||||
if (accessToken) {
|
"Content-Type": "application/json",
|
||||||
headers.Authorization = `Bearer ${accessToken}`;
|
};
|
||||||
}
|
|
||||||
let executionSessionId: string;
|
|
||||||
const session = await this.sessionManager.getSession(accessToken);
|
|
||||||
executionSessionId = session!.id;
|
|
||||||
|
|
||||||
const jobArguments: { [key: string]: any } = {
|
if (accessToken) {
|
||||||
_contextName: contextName,
|
headers.Authorization = `Bearer ${accessToken}`;
|
||||||
_OMITJSONLISTING: true,
|
}
|
||||||
_OMITJSONLOG: true,
|
|
||||||
_OMITSESSIONRESULTS: true,
|
|
||||||
_OMITTEXTLISTING: true,
|
|
||||||
_OMITTEXTLOG: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (debug) {
|
let executionSessionId: string;
|
||||||
jobArguments["_OMITTEXTLOG"] = false;
|
const session = await this.sessionManager.getSession(accessToken);
|
||||||
jobArguments["_OMITSESSIONRESULTS"] = false;
|
executionSessionId = session!.id;
|
||||||
jobArguments["_DEBUG"] = 131;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = `exec-${
|
const jobArguments: { [key: string]: any } = {
|
||||||
jobName.includes("/") ? jobName.split("/")[1] : jobName
|
_contextName: contextName,
|
||||||
}`;
|
_OMITJSONLISTING: true,
|
||||||
|
_OMITJSONLOG: true,
|
||||||
|
_OMITSESSIONRESULTS: true,
|
||||||
|
_OMITTEXTLISTING: true,
|
||||||
|
_OMITTEXTLOG: true,
|
||||||
|
};
|
||||||
|
|
||||||
let jobVariables: any = {
|
if (debug) {
|
||||||
SYS_JES_JOB_URI: "",
|
jobArguments["_OMITTEXTLOG"] = false;
|
||||||
_program: this.rootFolderName + "/" + jobName,
|
jobArguments["_OMITSESSIONRESULTS"] = false;
|
||||||
};
|
jobArguments["_DEBUG"] = 131;
|
||||||
let files: any[] = [];
|
}
|
||||||
if (data) {
|
|
||||||
if (JSON.stringify(data).includes(";")) {
|
const fileName = `exec-${
|
||||||
files = await this.uploadTables(data, accessToken);
|
jobName.includes("/") ? jobName.split("/")[1] : jobName
|
||||||
jobVariables["_webin_file_count"] = files.length;
|
}`;
|
||||||
files.forEach((fileInfo, index) => {
|
|
||||||
jobVariables[
|
let jobVariables: any = {
|
||||||
`_webin_fileuri${index + 1}`
|
SYS_JES_JOB_URI: "",
|
||||||
] = `/files/files/${fileInfo.file.id}`;
|
_program: this.rootFolderName + "/" + jobName,
|
||||||
jobVariables[`_webin_name${index + 1}`] = fileInfo.tableName;
|
};
|
||||||
});
|
|
||||||
|
let files: any[] = [];
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
if (JSON.stringify(data).includes(";")) {
|
||||||
|
files = await this.uploadTables(data, accessToken);
|
||||||
|
jobVariables["_webin_file_count"] = files.length;
|
||||||
|
files.forEach((fileInfo, index) => {
|
||||||
|
jobVariables[
|
||||||
|
`_webin_fileuri${index + 1}`
|
||||||
|
] = `/files/files/${fileInfo.file.id}`;
|
||||||
|
jobVariables[`_webin_name${index + 1}`] = fileInfo.tableName;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
jobVariables = { ...jobVariables, ...formatDataForRequest(data) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute job in session
|
||||||
|
const postJobRequest = {
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: fileName,
|
||||||
|
description: "Powered by SASjs",
|
||||||
|
code: linesOfCode,
|
||||||
|
variables: jobVariables,
|
||||||
|
arguments: jobArguments,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { result: postedJob, etag } = await this.request<Job>(
|
||||||
|
`${this.serverUrl}/compute/sessions/${executionSessionId}/jobs`,
|
||||||
|
postJobRequest
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`Job has been submitted for ${fileName}`);
|
||||||
|
console.log(
|
||||||
|
`You can monitor the job progress at ${this.serverUrl}${
|
||||||
|
postedJob.links.find((l: any) => l.rel === "state")!.href
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobStatus = await this.pollJobState(
|
||||||
|
postedJob,
|
||||||
|
etag,
|
||||||
|
accessToken,
|
||||||
|
silent
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result: currentJob } = await this.request<Job>(
|
||||||
|
`${this.serverUrl}/compute/sessions/${executionSessionId}/jobs/${postedJob.id}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
let jobResult, log;
|
||||||
|
|
||||||
|
const logLink = currentJob.links.find((l) => l.rel === "log");
|
||||||
|
|
||||||
|
if (true && logLink) {
|
||||||
|
log = await this.request<any>(
|
||||||
|
`${this.serverUrl}${logLink.href}/content?limit=10000`,
|
||||||
|
{
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
).then((res: any) =>
|
||||||
|
res.result.items.map((i: any) => i.line).join("\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jobStatus === "failed" || jobStatus === "error") {
|
||||||
|
return Promise.reject({ error: currentJob.error, log: log });
|
||||||
|
}
|
||||||
|
const resultLink = `/compute/sessions/${executionSessionId}/filerefs/_webout/content`;
|
||||||
|
|
||||||
|
if (resultLink) {
|
||||||
|
jobResult = await this.request<any>(
|
||||||
|
`${this.serverUrl}${resultLink}`,
|
||||||
|
{ headers },
|
||||||
|
"text"
|
||||||
|
).catch((e) => ({
|
||||||
|
result: JSON.stringify(e),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.sessionManager.clearSession(executionSessionId, accessToken);
|
||||||
|
|
||||||
|
return { result: jobResult?.result, log };
|
||||||
|
} catch (e) {
|
||||||
|
if (e && e.status === 404) {
|
||||||
|
return this.executeScript(
|
||||||
|
jobName,
|
||||||
|
linesOfCode,
|
||||||
|
contextName,
|
||||||
|
accessToken,
|
||||||
|
silent,
|
||||||
|
data,
|
||||||
|
debug
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
jobVariables = { ...jobVariables, ...formatDataForRequest(data) };
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute job in session
|
|
||||||
const postJobRequest = {
|
|
||||||
method: "POST",
|
|
||||||
headers,
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: fileName,
|
|
||||||
description: "Powered by SASjs",
|
|
||||||
code: linesOfCode,
|
|
||||||
variables: jobVariables,
|
|
||||||
arguments: jobArguments,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
const { result: postedJob, etag } = await this.request<Job>(
|
|
||||||
`${this.serverUrl}/compute/sessions/${executionSessionId}/jobs`,
|
|
||||||
postJobRequest
|
|
||||||
);
|
|
||||||
if (!silent) {
|
|
||||||
console.log(`Job has been submitted for ${fileName}`);
|
|
||||||
console.log(
|
|
||||||
`You can monitor the job progress at ${this.serverUrl}${
|
|
||||||
postedJob.links.find((l: any) => l.rel === "state")!.href
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const jobStatus = await this.pollJobState(
|
|
||||||
postedJob,
|
|
||||||
etag,
|
|
||||||
accessToken,
|
|
||||||
silent
|
|
||||||
);
|
|
||||||
const { result: currentJob } = await this.request<Job>(
|
|
||||||
`${this.serverUrl}/compute/sessions/${executionSessionId}/jobs/${postedJob.id}`,
|
|
||||||
{ headers }
|
|
||||||
);
|
|
||||||
|
|
||||||
let jobResult, log;
|
|
||||||
if (jobStatus === "failed" || jobStatus === "error") {
|
|
||||||
return Promise.reject(currentJob.error);
|
|
||||||
}
|
|
||||||
const resultLink = `/compute/sessions/${executionSessionId}/filerefs/_webout/content`;
|
|
||||||
const logLink = currentJob.links.find((l) => l.rel === "log");
|
|
||||||
if (resultLink) {
|
|
||||||
jobResult = await this.request<any>(
|
|
||||||
`${this.serverUrl}${resultLink}`,
|
|
||||||
{ headers },
|
|
||||||
"text"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true && logLink) {
|
|
||||||
log = await this.request<any>(
|
|
||||||
`${this.serverUrl}${logLink.href}/content`,
|
|
||||||
{
|
|
||||||
headers,
|
|
||||||
}
|
|
||||||
).then((res: any) => res.result.items.map((i: any) => i.line).join("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { result: jobResult?.result, log };
|
|
||||||
// } else {
|
|
||||||
// console.error(
|
|
||||||
// `Unable to find execution context ${contextName}.\nPlease check the contextName in the tgtDeployVars and try again.`
|
|
||||||
// );
|
|
||||||
// console.error("Response from server: ", JSON.stringify(this.contexts));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -623,12 +651,16 @@ export class SASViyaApiClient {
|
|||||||
await this.populateRootFolder(accessToken);
|
await this.populateRootFolder(accessToken);
|
||||||
}
|
}
|
||||||
if (!this.rootFolder) {
|
if (!this.rootFolder) {
|
||||||
|
console.error("Root folder was not found");
|
||||||
throw new Error("Root folder was not found");
|
throw new Error("Root folder was not found");
|
||||||
}
|
}
|
||||||
if (!this.rootFolderMap.size) {
|
if (!this.rootFolderMap.size) {
|
||||||
await this.populateRootFolderMap(accessToken);
|
await this.populateRootFolderMap(accessToken);
|
||||||
}
|
}
|
||||||
if (!this.rootFolderMap.size) {
|
if (!this.rootFolderMap.size) {
|
||||||
|
console.error(
|
||||||
|
`The job ${sasJob} was not found in ${this.rootFolderName}`
|
||||||
|
);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The job ${sasJob} was not found in ${this.rootFolderName}`
|
`The job ${sasJob} was not found in ${this.rootFolderName}`
|
||||||
);
|
);
|
||||||
@@ -647,6 +679,7 @@ export class SASViyaApiClient {
|
|||||||
(l) => l.rel === "getResource"
|
(l) => l.rel === "getResource"
|
||||||
);
|
);
|
||||||
if (!jobDefinitionLink) {
|
if (!jobDefinitionLink) {
|
||||||
|
console.error("Job definition URI was not found.");
|
||||||
throw new Error("Job definition URI was not found.");
|
throw new Error("Job definition URI was not found.");
|
||||||
}
|
}
|
||||||
const { result: jobDefinition } = await this.request<JobDefinition>(
|
const { result: jobDefinition } = await this.request<JobDefinition>(
|
||||||
@@ -661,7 +694,6 @@ export class SASViyaApiClient {
|
|||||||
linesToExecute,
|
linesToExecute,
|
||||||
contextName,
|
contextName,
|
||||||
accessToken,
|
accessToken,
|
||||||
"",
|
|
||||||
true,
|
true,
|
||||||
data,
|
data,
|
||||||
debug
|
debug
|
||||||
@@ -703,6 +735,7 @@ export class SASViyaApiClient {
|
|||||||
if (data && Object.keys(data).length) {
|
if (data && Object.keys(data).length) {
|
||||||
files = await this.uploadTables(data, accessToken);
|
files = await this.uploadTables(data, accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobName = path.basename(sasJob);
|
const jobName = path.basename(sasJob);
|
||||||
const jobFolder = sasJob.replace(`/${jobName}`, "");
|
const jobFolder = sasJob.replace(`/${jobName}`, "");
|
||||||
const allJobsInFolder = this.rootFolderMap.get(jobFolder.replace("/", ""));
|
const allJobsInFolder = this.rootFolderMap.get(jobFolder.replace("/", ""));
|
||||||
@@ -997,12 +1030,12 @@ export class SASViyaApiClient {
|
|||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { result: file } = await this.request<any>(
|
const uploadResponse = await this.request<any>(
|
||||||
`${this.serverUrl}/files/files#rawUpload`,
|
`${this.serverUrl}/files/files#rawUpload`,
|
||||||
createFileRequest
|
createFileRequest
|
||||||
);
|
);
|
||||||
|
|
||||||
uploadedFiles.push({ tableName, file });
|
uploadedFiles.push({ tableName, file: uploadResponse.result });
|
||||||
}
|
}
|
||||||
return uploadedFiles;
|
return uploadedFiles;
|
||||||
}
|
}
|
||||||
@@ -1019,9 +1052,9 @@ export class SASViyaApiClient {
|
|||||||
`${this.serverUrl}${url}`,
|
`${this.serverUrl}${url}`,
|
||||||
requestInfo
|
requestInfo
|
||||||
).catch((err) => {
|
).catch((err) => {
|
||||||
return {result: null};
|
return { result: null };
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!folder) return undefined;
|
if (!folder) return undefined;
|
||||||
return `/folders/folders/${folder.id}`;
|
return `/folders/folders/${folder.id}`;
|
||||||
}
|
}
|
||||||
@@ -1042,6 +1075,11 @@ export class SASViyaApiClient {
|
|||||||
[this.csrfToken.headerName]: this.csrfToken.value,
|
[this.csrfToken.headerName]: this.csrfToken.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return await makeRequest<T>(url, options, this.setCsrfTokenLocal, contentType);
|
return await makeRequest<T>(
|
||||||
|
url,
|
||||||
|
options,
|
||||||
|
this.setCsrfTokenLocal,
|
||||||
|
contentType
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
179
src/SASjs.ts
179
src/SASjs.ts
@@ -1,6 +1,13 @@
|
|||||||
import "isomorphic-fetch";
|
import { isIEorEdgeOrOldFirefox } from "./utils/isIeOrEdge";
|
||||||
import * as e6p from "es6-promise";
|
import * as e6p from "es6-promise";
|
||||||
(e6p as any).polyfill();
|
(e6p as any).polyfill();
|
||||||
|
if (isIEorEdgeOrOldFirefox()) {
|
||||||
|
if (window) {
|
||||||
|
window.fetch = undefined as any; // ensure the polyfill runs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line
|
||||||
|
require("isomorphic-fetch");
|
||||||
import {
|
import {
|
||||||
convertToCSV,
|
convertToCSV,
|
||||||
compareTimestamps,
|
compareTimestamps,
|
||||||
@@ -12,6 +19,7 @@ import {
|
|||||||
isLogInSuccess,
|
isLogInSuccess,
|
||||||
parseSourceCode,
|
parseSourceCode,
|
||||||
parseGeneratedCode,
|
parseGeneratedCode,
|
||||||
|
parseWeboutResponse,
|
||||||
needsRetry,
|
needsRetry,
|
||||||
asyncForEach,
|
asyncForEach,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -21,7 +29,7 @@ import {
|
|||||||
SASjsWaitingRequest,
|
SASjsWaitingRequest,
|
||||||
ServerType,
|
ServerType,
|
||||||
CsrfToken,
|
CsrfToken,
|
||||||
UploadFile
|
UploadFile,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { SASViyaApiClient } from "./SASViyaApiClient";
|
import { SASViyaApiClient } from "./SASViyaApiClient";
|
||||||
import { SAS9ApiClient } from "./SAS9ApiClient";
|
import { SAS9ApiClient } from "./SAS9ApiClient";
|
||||||
@@ -122,7 +130,6 @@ export default class SASjs {
|
|||||||
linesOfCode,
|
linesOfCode,
|
||||||
contextName,
|
contextName,
|
||||||
accessToken,
|
accessToken,
|
||||||
sessionId,
|
|
||||||
silent
|
silent
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -392,8 +399,10 @@ export default class SASjs {
|
|||||||
this.sasjsConfig.appLoc,
|
this.sasjsConfig.appLoc,
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.jobsPath,
|
this.jobsPath,
|
||||||
|
this.setCsrfTokenWeb,
|
||||||
this.csrfTokenWeb
|
this.csrfTokenWeb
|
||||||
);
|
);
|
||||||
|
|
||||||
return fileUploader.uploadFile(sasJob, files, params);
|
return fileUploader.uploadFile(sasJob, files, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,52 +555,64 @@ export default class SASjs {
|
|||||||
sasjsWaitingRequest.requestPromise.promise = new Promise(
|
sasjsWaitingRequest.requestPromise.promise = new Promise(
|
||||||
async (resolve, reject) => {
|
async (resolve, reject) => {
|
||||||
this.sasViyaApiClient
|
this.sasViyaApiClient
|
||||||
?.executeComputeJob(
|
?.executeComputeJob(
|
||||||
sasJob,
|
sasJob,
|
||||||
config.contextName,
|
config.contextName,
|
||||||
config.debug,
|
config.debug,
|
||||||
data,
|
data,
|
||||||
accessToken
|
accessToken
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!config.debug) {
|
if (!config.debug) {
|
||||||
this.appendSasjsRequest(null, sasJob, null);
|
this.appendSasjsRequest(null, sasJob, null);
|
||||||
|
} else {
|
||||||
|
this.appendSasjsRequest(response, sasJob, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseJson;
|
||||||
|
|
||||||
|
try {
|
||||||
|
responseJson = JSON.parse(response!.result);
|
||||||
|
} catch {
|
||||||
|
responseJson = JSON.parse(parseWeboutResponse(response!.result));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(responseJson);
|
||||||
|
})
|
||||||
|
.catch(async (response) => {
|
||||||
|
let error = response.error || response;
|
||||||
|
|
||||||
|
if (needsRetry(JSON.stringify(error))) {
|
||||||
|
if (this.retryCountComputeApi < requestRetryLimit) {
|
||||||
|
let retryResponse = await this.executeJobViaComputeApi(
|
||||||
|
sasJob,
|
||||||
|
data,
|
||||||
|
config,
|
||||||
|
loginRequiredCallback,
|
||||||
|
accessToken
|
||||||
|
);
|
||||||
|
|
||||||
|
this.retryCountComputeApi++;
|
||||||
|
|
||||||
|
resolve(retryResponse);
|
||||||
} else {
|
} else {
|
||||||
this.appendSasjsRequest(response, sasJob, null);
|
this.retryCountComputeApi = 0;
|
||||||
|
reject({ MESSAGE: "Compute API retry requests limit reached" });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolve(JSON.parse(response!.result));
|
if (error && error.status === 401) {
|
||||||
})
|
if (loginRequiredCallback) loginRequiredCallback(true);
|
||||||
.catch(async (e) => {
|
sasjsWaitingRequest.requestPromise.resolve = resolve;
|
||||||
if (needsRetry(JSON.stringify(e))) {
|
sasjsWaitingRequest.requestPromise.reject = reject;
|
||||||
if (this.retryCountComputeApi < requestRetryLimit) {
|
sasjsWaitingRequest.config = config;
|
||||||
let retryResponse = await this.executeJobViaComputeApi(
|
this.sasjsWaitingRequests.push(sasjsWaitingRequest);
|
||||||
sasJob,
|
} else {
|
||||||
data,
|
reject({ MESSAGE: error || "Job execution failed" });
|
||||||
config,
|
}
|
||||||
loginRequiredCallback,
|
|
||||||
accessToken
|
|
||||||
);
|
|
||||||
|
|
||||||
this.retryCountComputeApi++;
|
this.appendSasjsRequest(response.log, sasJob, null);
|
||||||
|
});
|
||||||
resolve(retryResponse);
|
|
||||||
} else {
|
|
||||||
this.retryCountComputeApi = 0;
|
|
||||||
reject({ MESSAGE: "Compute API retry requests limit reached" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e && e.status === 401) {
|
|
||||||
if (loginRequiredCallback) loginRequiredCallback(true);
|
|
||||||
sasjsWaitingRequest.requestPromise.resolve = resolve;
|
|
||||||
sasjsWaitingRequest.requestPromise.reject = reject;
|
|
||||||
sasjsWaitingRequest.config = config;
|
|
||||||
this.sasjsWaitingRequests.push(sasjsWaitingRequest);
|
|
||||||
} else {
|
|
||||||
reject({ MESSAGE: e || "Job execution failed" });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return sasjsWaitingRequest.requestPromise.promise;
|
return sasjsWaitingRequest.requestPromise.promise;
|
||||||
@@ -640,7 +661,18 @@ export default class SASjs {
|
|||||||
} else {
|
} else {
|
||||||
this.appendSasjsRequest(response, sasJob, null);
|
this.appendSasjsRequest(response, sasJob, null);
|
||||||
}
|
}
|
||||||
return JSON.parse(response!.result);
|
|
||||||
|
let responseJson;
|
||||||
|
|
||||||
|
try {
|
||||||
|
responseJson = JSON.parse(response!.result);
|
||||||
|
} catch {
|
||||||
|
responseJson = JSON.parse(
|
||||||
|
parseWeboutResponse(response!.result)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseJson;
|
||||||
})
|
})
|
||||||
.catch(async (e) => {
|
.catch(async (e) => {
|
||||||
if (needsRetry(JSON.stringify(e))) {
|
if (needsRetry(JSON.stringify(e))) {
|
||||||
@@ -652,9 +684,9 @@ export default class SASjs {
|
|||||||
loginRequiredCallback,
|
loginRequiredCallback,
|
||||||
accessToken
|
accessToken
|
||||||
);
|
);
|
||||||
|
|
||||||
this.retryCountJeseApi++;
|
this.retryCountJeseApi++;
|
||||||
|
|
||||||
resolve(retryResponse);
|
resolve(retryResponse);
|
||||||
} else {
|
} else {
|
||||||
this.retryCountJeseApi = 0;
|
this.retryCountJeseApi = 0;
|
||||||
@@ -662,7 +694,7 @@ export default class SASjs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reject({ MESSAGE: (e && e.message) || "Job execution failed" })
|
reject({ MESSAGE: (e && e.message) || "Job execution failed" });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -698,7 +730,7 @@ export default class SASjs {
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
const requestParams = {
|
const requestParams = {
|
||||||
...this.getRequestParamsWeb(),
|
...this.getRequestParamsWeb(config),
|
||||||
};
|
};
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -835,7 +867,7 @@ export default class SASjs {
|
|||||||
} else {
|
} else {
|
||||||
if (config.serverType === ServerType.SAS9 && config.debug) {
|
if (config.serverType === ServerType.SAS9 && config.debug) {
|
||||||
this.updateUsername(responseText);
|
this.updateUsername(responseText);
|
||||||
const jsonResponseText = this.parseSAS9Response(responseText);
|
const jsonResponseText = parseWeboutResponse(responseText);
|
||||||
|
|
||||||
if (jsonResponseText !== "") {
|
if (jsonResponseText !== "") {
|
||||||
resolve(JSON.parse(jsonResponseText));
|
resolve(JSON.parse(jsonResponseText));
|
||||||
@@ -885,6 +917,10 @@ export default class SASjs {
|
|||||||
|
|
||||||
return sasjsWaitingRequest.requestPromise.promise;
|
return sasjsWaitingRequest.requestPromise.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setCsrfTokenWeb = (csrfToken: CsrfToken) => {
|
||||||
|
this.csrfTokenWeb = csrfToken;
|
||||||
|
};
|
||||||
|
|
||||||
private setCsrfTokenApi = (csrfToken: CsrfToken) => {
|
private setCsrfTokenApi = (csrfToken: CsrfToken) => {
|
||||||
this.csrfTokenApi = csrfToken;
|
this.csrfTokenApi = csrfToken;
|
||||||
@@ -905,14 +941,14 @@ export default class SASjs {
|
|||||||
this.sasjsWaitingRequests = [];
|
this.sasjsWaitingRequests = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRequestParamsWeb(): any {
|
private getRequestParamsWeb(config: any): any {
|
||||||
const requestParams: any = {};
|
const requestParams: any = {};
|
||||||
|
|
||||||
if (this.csrfTokenWeb) {
|
if (this.csrfTokenWeb) {
|
||||||
requestParams["_csrf"] = this.csrfTokenWeb.value;
|
requestParams["_csrf"] = this.csrfTokenWeb.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sasjsConfig.debug) {
|
if (config.debug) {
|
||||||
requestParams["_omittextlog"] = "false";
|
requestParams["_omittextlog"] = "false";
|
||||||
requestParams["_omitsessionresults"] = "false";
|
requestParams["_omitsessionresults"] = "false";
|
||||||
|
|
||||||
@@ -976,23 +1012,6 @@ export default class SASjs {
|
|||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseSAS9Response(response: string) {
|
|
||||||
let sas9Response = "";
|
|
||||||
|
|
||||||
if (response.includes(">>weboutBEGIN<<")) {
|
|
||||||
try {
|
|
||||||
sas9Response = response
|
|
||||||
.split(">>weboutBEGIN<<")[1]
|
|
||||||
.split(">>weboutEND<<")[0];
|
|
||||||
} catch (e) {
|
|
||||||
sas9Response = "";
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sas9Response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseSAS9ErrorResponse(response: string) {
|
private parseSAS9ErrorResponse(response: string) {
|
||||||
const logLines = response.split("\n");
|
const logLines = response.split("\n");
|
||||||
const parsedLines: string[] = [];
|
const parsedLines: string[] = [];
|
||||||
@@ -1050,7 +1069,16 @@ export default class SASjs {
|
|||||||
if (response && response.result && response.log) {
|
if (response && response.result && response.log) {
|
||||||
sourceCode = parseSourceCode(response.log);
|
sourceCode = parseSourceCode(response.log);
|
||||||
generatedCode = parseGeneratedCode(response.log);
|
generatedCode = parseGeneratedCode(response.log);
|
||||||
sasWork = JSON.parse(response.result).WORK;
|
|
||||||
|
if (this.sasjsConfig.debug) {
|
||||||
|
if (response.log) {
|
||||||
|
sasWork = response.log;
|
||||||
|
} else {
|
||||||
|
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sasWork = JSON.parse(response.result).WORK;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (response) {
|
if (response) {
|
||||||
sourceCode = parseSourceCode(response);
|
sourceCode = parseSourceCode(response);
|
||||||
@@ -1079,7 +1107,7 @@ export default class SASjs {
|
|||||||
|
|
||||||
if (this.sasjsConfig.serverType === ServerType.SAS9) {
|
if (this.sasjsConfig.serverType === ServerType.SAS9) {
|
||||||
try {
|
try {
|
||||||
jsonResponse = JSON.parse(this.parseSAS9Response(response));
|
jsonResponse = JSON.parse(parseWeboutResponse(response));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -1110,6 +1138,10 @@ export default class SASjs {
|
|||||||
return sortedRequests;
|
return sortedRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearSasRequests() {
|
||||||
|
this.sasjsRequests = [];
|
||||||
|
}
|
||||||
|
|
||||||
private setupConfiguration() {
|
private setupConfiguration() {
|
||||||
if (
|
if (
|
||||||
this.sasjsConfig.serverUrl === undefined ||
|
this.sasjsConfig.serverUrl === undefined ||
|
||||||
@@ -1159,7 +1191,8 @@ export default class SASjs {
|
|||||||
this.fileUploader = new FileUploader(
|
this.fileUploader = new FileUploader(
|
||||||
this.sasjsConfig.appLoc,
|
this.sasjsConfig.appLoc,
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.jobsPath
|
this.jobsPath,
|
||||||
|
this.setCsrfTokenWeb
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,31 @@ export class SessionManager {
|
|||||||
async getSession(accessToken?: string) {
|
async getSession(accessToken?: string) {
|
||||||
await this.createSessions(accessToken);
|
await this.createSessions(accessToken);
|
||||||
this.createAndWaitForSession(accessToken);
|
this.createAndWaitForSession(accessToken);
|
||||||
return this.sessions.pop();
|
const session = this.sessions.pop();
|
||||||
|
const secondsSinceSessionCreation =
|
||||||
|
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
||||||
|
1000;
|
||||||
|
if (
|
||||||
|
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
||||||
|
) {
|
||||||
|
await this.createSessions(accessToken);
|
||||||
|
const freshSession = this.sessions.pop();
|
||||||
|
return freshSession;
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearSession(id: string, accessToken?: string) {
|
||||||
|
const deleteSessionRequest = {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: this.getHeaders(accessToken),
|
||||||
|
};
|
||||||
|
return await this.request<Session>(
|
||||||
|
`${this.serverUrl}/compute/sessions/${id}`,
|
||||||
|
deleteSessionRequest
|
||||||
|
).then(() => {
|
||||||
|
this.sessions = this.sessions.filter((s) => s.id !== id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSessions(accessToken?: string) {
|
private async createSessions(accessToken?: string) {
|
||||||
|
|||||||
@@ -4,4 +4,8 @@ export interface Session {
|
|||||||
id: string;
|
id: string;
|
||||||
state: string;
|
state: string;
|
||||||
links: Link[];
|
links: Link[];
|
||||||
|
attributes: {
|
||||||
|
sessionInactiveTimeout: number;
|
||||||
|
};
|
||||||
|
creationTimeStamp: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ export * from "./parseSourceCode";
|
|||||||
export * from "./parseSasViyaLog";
|
export * from "./parseSasViyaLog";
|
||||||
export * from "./serialize";
|
export * from "./serialize";
|
||||||
export * from "./splitChunks";
|
export * from "./splitChunks";
|
||||||
|
export * from "./parseWeboutResponse";
|
||||||
|
|||||||
34
src/utils/isIeOrEdge.ts
Normal file
34
src/utils/isIeOrEdge.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export function isIEorEdgeOrOldFirefox() {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const ua = window.navigator.userAgent;
|
||||||
|
|
||||||
|
if (ua.indexOf("Firefox") > 0) {
|
||||||
|
const version = parseInt(
|
||||||
|
ua.substring(ua.lastIndexOf("Firefox/") + 8, ua.length),
|
||||||
|
10
|
||||||
|
);
|
||||||
|
return version <= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msie = ua.indexOf("MSIE ");
|
||||||
|
if (msie > 0) {
|
||||||
|
// IE 10 or older => return version number
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trident = ua.indexOf("Trident/");
|
||||||
|
if (trident > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edge = ua.indexOf("Edge/");
|
||||||
|
if (edge > 0) {
|
||||||
|
// Edge (IE 12+) => return version number
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other browser
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ export async function makeRequest<T>(
|
|||||||
: (res: Response) => res.text();
|
: (res: Response) => res.text();
|
||||||
let etag = null;
|
let etag = null;
|
||||||
const result = await fetch(url, request).then(async (response) => {
|
const result = await fetch(url, request).then(async (response) => {
|
||||||
|
if (response.redirected && response.url.includes("SASLogon/login")) {
|
||||||
|
return Promise.reject({ status: 401 });
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
||||||
@@ -40,33 +43,59 @@ export async function makeRequest<T>(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const body = await response.text();
|
const body = await response.text();
|
||||||
|
|
||||||
|
if (needsRetry(body)) {
|
||||||
|
if (retryCount < retryLimit) {
|
||||||
|
retryCount++;
|
||||||
|
let retryResponse = await makeRequest(
|
||||||
|
url,
|
||||||
|
retryRequest || request,
|
||||||
|
callback,
|
||||||
|
contentType
|
||||||
|
);
|
||||||
|
retryCount = 0;
|
||||||
|
|
||||||
|
etag = retryResponse.etag;
|
||||||
|
return retryResponse.result;
|
||||||
|
} else {
|
||||||
|
retryCount = 0;
|
||||||
|
|
||||||
|
throw new Error("Request retry limit exceeded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.reject({ status: response.status, body });
|
return Promise.reject({ status: response.status, body });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const responseTransformed = responseTransform(response);
|
if (response.status === 204) {
|
||||||
let responseText = '';
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const responseTransformed = await responseTransform(response);
|
||||||
|
let responseText = "";
|
||||||
|
|
||||||
if (typeof responseTransformed === 'string') {
|
if (typeof responseTransformed === "string") {
|
||||||
responseText = responseTransformed;
|
responseText = responseTransformed;
|
||||||
} else {
|
} else {
|
||||||
responseText = JSON.stringify(responseTransformed);
|
responseText = JSON.stringify(responseTransformed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.redirected && response.url.includes("SASLogon/login")) {
|
|
||||||
return Promise.reject({ status: 401, responseTransformed });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsRetry(responseText)) {
|
if (needsRetry(responseText)) {
|
||||||
if (retryCount < retryLimit) {
|
if (retryCount < retryLimit) {
|
||||||
retryCount++;
|
retryCount++;
|
||||||
let retryResponse = await makeRequest(url, retryRequest || request, callback, contentType);
|
const retryResponse = await makeRequest(
|
||||||
|
url,
|
||||||
|
retryRequest || request,
|
||||||
|
callback,
|
||||||
|
contentType
|
||||||
|
);
|
||||||
retryCount = 0;
|
retryCount = 0;
|
||||||
|
|
||||||
return retryResponse;
|
etag = retryResponse.etag;
|
||||||
|
return retryResponse.result;
|
||||||
} else {
|
} else {
|
||||||
retryCount = 0;
|
retryCount = 0;
|
||||||
|
|
||||||
throw new Error('Request retry limit exceeded');
|
throw new Error("Request retry limit exceeded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
export const needsRetry = (responseText: string): boolean => {
|
export const needsRetry = (responseText: string): boolean => {
|
||||||
return (
|
return (
|
||||||
(responseText.includes('"errorCode":403') &&
|
!!responseText &&
|
||||||
|
((responseText.includes('"errorCode":403') &&
|
||||||
responseText.includes("_csrf") &&
|
responseText.includes("_csrf") &&
|
||||||
responseText.includes("X-CSRF-TOKEN")) ||
|
responseText.includes("X-CSRF-TOKEN")) ||
|
||||||
(responseText.includes('"status":403') &&
|
(responseText.includes('"status":403') &&
|
||||||
responseText.includes('"error":"Forbidden"')) ||
|
responseText.includes('"error":"Forbidden"')) ||
|
||||||
(responseText.includes('"status":449') &&
|
(responseText.includes('"status":449') &&
|
||||||
responseText.includes("Authentication success, retry original request"))
|
responseText.includes(
|
||||||
|
"Authentication success, retry original request"
|
||||||
|
)))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
16
src/utils/parseWeboutResponse.ts
Normal file
16
src/utils/parseWeboutResponse.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const parseWeboutResponse = (response: string) => {
|
||||||
|
let sasResponse = "";
|
||||||
|
|
||||||
|
if (response.includes(">>weboutBEGIN<<")) {
|
||||||
|
try {
|
||||||
|
sasResponse = response
|
||||||
|
.split(">>weboutBEGIN<<")[1]
|
||||||
|
.split(">>weboutEND<<")[0];
|
||||||
|
} catch (e) {
|
||||||
|
sasResponse = "";
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sasResponse;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user