1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-11 01:14:36 +00:00

Compare commits

..

26 Commits

Author SHA1 Message Date
Allan Bowe
b4c7868fb6 Merge pull request #36 from sasjs/issue34
fix: job definition debug log parse
2020-08-11 18:44:39 +02:00
Mihajlo Medjedovic
2266578013 Merge branch 'master' into issue34 2020-08-11 18:29:41 +02:00
Mihajlo Medjedovic
f2ebe1a5b0 fix: job definition debug log parse 2020-08-10 18:13:11 +02:00
Allan Bowe
6a52bbe560 Merge pull request #35 from sasjs/issue33
fix: makeRequest inconsistent response structure
2020-08-10 17:19:05 +02:00
Mihajlo Medjedovic
a5c725e677 fix: makeRequest incositent response structure 2020-08-10 17:05:10 +02:00
Krishna Acondy
f5e1907e28 feat(clear-requests): add function to clear debug requests 2020-08-08 14:27:40 +01:00
Krishna Acondy
f7a9b0cbb6 fix(compute-api): ignore 404s when requesting webout content 2020-08-08 14:26:09 +01:00
Krishna Acondy
1258a1a180 fix(login-callback): fix request failure when login is required 2020-08-08 12:56:31 +01:00
Krishna Acondy
0bb343a1de chore(deps): bump adapter and test framework 2020-08-08 11:42:16 +01:00
Krishna Acondy
929c89b70f Merge pull request #32 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.9
chore(deps-dev): bump @types/jest from 26.0.8 to 26.0.9
2020-08-08 11:04:25 +01:00
dependabot-preview[bot]
169ca35238 chore(deps-dev): bump @types/jest from 26.0.8 to 26.0.9
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.8 to 26.0.9.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-06 12:59:52 +00:00
Allan Bowe
60be28f149 Merge pull request #31 from sasjs/session-cleanup
feat(session-cleanup): delete a session after it has been used
2020-08-05 23:17:42 +02:00
Krishna Acondy
14daa55184 feat(session-cleanup): delete a session after it has been used 2020-08-05 21:52:23 +01:00
Krishna Acondy
f763f05b5e Merge pull request #30 from sasjs/deployFix
fix: makeRequest retry
2020-08-05 20:02:44 +01:00
Mihajlo Medjedovic
b6aced5bad fix: makeRequest retry 2020-08-05 20:58:10 +02:00
Allan Bowe
7bb7db0f27 Merge pull request #24 from sasjs/dependabot/npm_and_yarn/webpack-4.44.1
chore(deps-dev): bump webpack from 4.43.0 to 4.44.1
2020-08-04 20:57:22 +02:00
Allan Bowe
36ea148446 Merge branch 'master' into dependabot/npm_and_yarn/webpack-4.44.1 2020-08-04 20:56:51 +02:00
Allan Bowe
762254d8c4 Merge pull request #29 from sasjs/deployFixing
fix: makeRequest retry, getFolderUri error catching
2020-08-04 20:56:20 +02:00
Mihajlo Medjedovic
8474b222ea fix: makeRequest retry, getFolderUri error catching 2020-08-04 20:39:09 +02:00
dependabot-preview[bot]
c1750c014e chore(deps-dev): bump webpack from 4.43.0 to 4.44.1
Bumps [webpack](https://github.com/webpack/webpack) from 4.43.0 to 4.44.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.43.0...v4.44.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 20:41:21 +00:00
Krishna Acondy
d7a7909529 Merge pull request #26 from sasjs/dependabot/npm_and_yarn/tslint-6.1.3
chore(deps-dev): bump tslint from 6.1.2 to 6.1.3
2020-08-03 21:39:07 +01:00
dependabot-preview[bot]
31b60a985e chore(deps-dev): bump tslint from 6.1.2 to 6.1.3
Bumps [tslint](https://github.com/palantir/tslint) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/palantir/tslint/releases)
- [Changelog](https://github.com/palantir/tslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/palantir/tslint/compare/6.1.2...6.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 20:38:05 +00:00
Krishna Acondy
a6b13d9cb9 Merge pull request #27 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.8
chore(deps-dev): bump @types/jest from 26.0.5 to 26.0.8
2020-08-03 21:35:56 +01:00
dependabot-preview[bot]
55fcbf2e36 chore(deps-dev): bump @types/jest from 26.0.5 to 26.0.8
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.5 to 26.0.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 19:34:31 +00:00
Krishna Acondy
fad8549d92 Merge pull request #28 from sasjs/dependabot/npm_and_yarn/ts-loader-8.0.2
chore(deps-dev): bump ts-loader from 8.0.1 to 8.0.2
2020-08-03 20:32:10 +01:00
dependabot-preview[bot]
95c03e5d07 chore(deps-dev): bump ts-loader from 8.0.1 to 8.0.2
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.1...v8.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 18:57:46 +00:00
12 changed files with 715 additions and 959 deletions

1281
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@
"license": "ISC",
"devDependencies": {
"@types/isomorphic-fetch": "0.0.35",
"@types/jest": "^26.0.5",
"@types/jest": "^26.0.9",
"cp": "^0.2.0",
"jest": "^25.5.4",
"path": "^0.12.7",
@@ -45,15 +45,15 @@
"rimraf": "^3.0.2",
"semantic-release": "^17.1.1",
"ts-jest": "^25.5.1",
"ts-loader": "^8.0.1",
"tslint": "^6.1.2",
"ts-loader": "^8.0.2",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.17.8",
"typedoc-neo-theme": "^1.0.9",
"typedoc-plugin-external-module-name": "^4.0.3",
"typescript": "^3.9.7",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.43.0",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"main": "index.js",

View File

@@ -1357,9 +1357,9 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
},
"@sasjs/adapter": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.0.5.tgz",
"integrity": "sha512-54gQZD7QdNmQu77axOqr0vMS7hUVXO5hPbUtwXXocMIi3kRQDbROYjC3kuiFM9FrxqiZWbLRcyOqmFv3W/N36w==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.2.0.tgz",
"integrity": "sha512-PcQcmb7TsfPJ94tzFnvycm+tMYD3wKx2a6niwHfsV9+g6XHtmwReVV3EPZZ5XB4s565vU6Qc+ZnFbMIAeik8QA==",
"requires": {
"es6-promise": "^4.2.8",
"form-data": "^3.0.0",
@@ -1379,15 +1379,23 @@
}
},
"@sasjs/test-framework": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.3.0.tgz",
"integrity": "sha512-vrbRFUhNUShLlNFZO+XwVwFLXDLApQG9zOPx00xhQ8IUA0cSDFFmf2mP/KBdFCxa1REaR6GHvMctUj+xRZo9QQ==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.3.3.tgz",
"integrity": "sha512-Ou4UXlxBAVR8jv7boVvJ/eKLHRTQvDi9LouPAasLCO2EC4AD0wX1hLMwVhmydCvsdgVEeXs6InvX3ROHiKSADg==",
"requires": {
"@types/react-highlight.js": "^1.0.0",
"immer": "^7.0.7",
"moment": "^2.27.0",
"react-highlight.js": "^1.0.7",
"semantic-ui-css": "^2.4.1",
"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": {
@@ -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": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -3829,6 +3832,11 @@
"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": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -12226,21 +12234,36 @@
}
},
"semantic-ui-react": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.0.0.tgz",
"integrity": "sha512-85mYHYuDBNa6la1BgKwuOSD1vcIPsFQEXRxGsZ9pUtE4iHlEcylF+x46NYHIGbBjlys63SpNH3PtK6VyZj9LBw==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.1.1.tgz",
"integrity": "sha512-QtzLNkK4MUe1HQo4S7/tIkSp4NFtxSGDzTMKxmvztMJ6jt+nKGmMyjpyxJsrm3ohU8Z3sTyBUyiBsDYW4jNtjw==",
"requires": {
"@babel/runtime": "^7.1.2",
"@babel/runtime": "^7.10.5",
"@semantic-ui-react/event-stack": "^3.1.0",
"@stardust-ui/react-component-event-listener": "~0.38.0",
"@stardust-ui/react-component-ref": "~0.38.0",
"classnames": "^2.2.6",
"keyboard-key": "^1.0.4",
"lodash": "^4.17.15",
"clsx": "^1.1.1",
"keyboard-key": "^1.1.0",
"lodash": "^4.17.19",
"prop-types": "^15.7.2",
"react-is": "^16.8.6",
"react-popper": "^1.3.4",
"react-popper": "^1.3.7",
"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.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
}
}
},
"semver": {

View File

@@ -4,8 +4,8 @@
"homepage": ".",
"private": true,
"dependencies": {
"@sasjs/adapter": "^1.0.5",
"@sasjs/test-framework": "^1.3.0",
"@sasjs/adapter": "^1.2.0",
"@sasjs/test-framework": "^1.3.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",

View File

@@ -12,7 +12,7 @@ const Login = (): ReactElement<{}> => {
(e) => {
e.preventDefault();
appContext.adapter.logIn(username, password).then((res) => {
appContext.setIsLoggedIn(true);
appContext.setIsLoggedIn(res.isLoggedIn);
});
},
[username, password, appContext]

View File

@@ -29,7 +29,11 @@ export class SASViyaApiClient {
}
private csrfToken: CsrfToken | 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.
@@ -113,9 +117,7 @@ export class SASViyaApiClient {
`test-${context.name}`,
linesOfCode,
context.name,
accessToken,
undefined,
true
accessToken
).catch(() => null);
});
const results = await Promise.all(promises);
@@ -201,17 +203,20 @@ export class SASViyaApiClient {
linesOfCode: string[],
contextName: string,
accessToken?: string,
sessionId = "",
silent = false,
data = null,
debug = false
) {
silent = !debug;
const headers: any = {
"Content-Type": "application/json",
};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
}
let executionSessionId: string;
const session = await this.sessionManager.getSession(accessToken);
executionSessionId = session!.id;
@@ -239,7 +244,9 @@ export class SASViyaApiClient {
SYS_JES_JOB_URI: "",
_program: this.rootFolderName + "/" + jobName,
};
let files: any[] = [];
if (data) {
if (JSON.stringify(data).includes(";")) {
files = await this.uploadTables(data, accessToken);
@@ -267,10 +274,12 @@ export class SASViyaApiClient {
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(
@@ -286,34 +295,41 @@ export class SASViyaApiClient {
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"
);
).catch((e) => ({
result: JSON.stringify(e),
}));
}
if (true && logLink) {
log = await this.request<any>(
`${this.serverUrl}${logLink.href}/content`,
`${this.serverUrl}${logLink.href}/content?limit=10000`,
{
headers,
}
).then((res: any) => res.result.items.map((i: any) => i.line).join("\n"));
}
await this.sessionManager.clearSession(executionSessionId, accessToken);
return { result: jobResult?.result, log };
// } else {
// console.error(
@@ -623,12 +639,16 @@ export class SASViyaApiClient {
await this.populateRootFolder(accessToken);
}
if (!this.rootFolder) {
console.error("Root folder was not found");
throw new Error("Root folder was not found");
}
if (!this.rootFolderMap.size) {
await this.populateRootFolderMap(accessToken);
}
if (!this.rootFolderMap.size) {
console.error(
`The job ${sasJob} was not found in ${this.rootFolderName}`
);
throw new Error(
`The job ${sasJob} was not found in ${this.rootFolderName}`
);
@@ -647,6 +667,7 @@ export class SASViyaApiClient {
(l) => l.rel === "getResource"
);
if (!jobDefinitionLink) {
console.error("Job definition URI was not found.");
throw new Error("Job definition URI was not found.");
}
const { result: jobDefinition } = await this.request<JobDefinition>(
@@ -661,7 +682,6 @@ export class SASViyaApiClient {
linesToExecute,
contextName,
accessToken,
"",
true,
data,
debug
@@ -698,11 +718,12 @@ export class SASViyaApiClient {
`The job ${sasJob} was not found in ${this.rootFolderName}`
);
}
let files: any[] = [];
if (data && Object.keys(data).length) {
files = await this.uploadTables(data, accessToken);
}
const jobName = path.basename(sasJob);
const jobFolder = sasJob.replace(`/${jobName}`, "");
const allJobsInFolder = this.rootFolderMap.get(jobFolder.replace("/", ""));
@@ -997,12 +1018,12 @@ export class SASViyaApiClient {
headers,
};
const { result: file } = await this.request<any>(
const uploadResponse = await this.request<any>(
`${this.serverUrl}/files/files#rawUpload`,
createFileRequest
);
uploadedFiles.push({ tableName, file });
uploadedFiles.push({ tableName, file: uploadResponse.result });
}
return uploadedFiles;
}
@@ -1018,7 +1039,10 @@ export class SASViyaApiClient {
const { result: folder } = await this.request<Folder>(
`${this.serverUrl}${url}`,
requestInfo
);
).catch((err) => {
return { result: null };
});
if (!folder) return undefined;
return `/folders/folders/${folder.id}`;
}
@@ -1039,6 +1063,11 @@ export class SASViyaApiClient {
[this.csrfToken.headerName]: this.csrfToken.value,
};
}
return await makeRequest<T>(url, options, this.setCsrfTokenLocal, contentType);
return await makeRequest<T>(
url,
options,
this.setCsrfTokenLocal,
contentType
);
}
}

View File

@@ -12,6 +12,7 @@ import {
isLogInSuccess,
parseSourceCode,
parseGeneratedCode,
parseWeboutResponse,
needsRetry,
asyncForEach,
} from "./utils";
@@ -21,7 +22,7 @@ import {
SASjsWaitingRequest,
ServerType,
CsrfToken,
UploadFile
UploadFile,
} from "./types";
import { SASViyaApiClient } from "./SASViyaApiClient";
import { SAS9ApiClient } from "./SAS9ApiClient";
@@ -122,7 +123,6 @@ export default class SASjs {
linesOfCode,
contextName,
accessToken,
sessionId,
silent
);
}
@@ -546,52 +546,60 @@ export default class SASjs {
sasjsWaitingRequest.requestPromise.promise = new Promise(
async (resolve, reject) => {
this.sasViyaApiClient
?.executeComputeJob(
sasJob,
config.contextName,
config.debug,
data,
accessToken
)
.then((response) => {
if (!config.debug) {
this.appendSasjsRequest(null, sasJob, null);
?.executeComputeJob(
sasJob,
config.contextName,
config.debug,
data,
accessToken
)
.then((response) => {
if (!config.debug) {
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 (e) => {
if (needsRetry(JSON.stringify(e))) {
if (this.retryCountComputeApi < requestRetryLimit) {
let retryResponse = await this.executeJobViaComputeApi(
sasJob,
data,
config,
loginRequiredCallback,
accessToken
);
this.retryCountComputeApi++;
resolve(retryResponse);
} else {
this.appendSasjsRequest(response, sasJob, null);
this.retryCountComputeApi = 0;
reject({ MESSAGE: "Compute API retry requests limit reached" });
}
}
resolve(JSON.parse(response!.result));
})
.catch(async (e) => {
if (needsRetry(JSON.stringify(e))) {
if (this.retryCountComputeApi < requestRetryLimit) {
let retryResponse = await this.executeJobViaComputeApi(
sasJob,
data,
config,
loginRequiredCallback,
accessToken
);
this.retryCountComputeApi++;
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" });
}
})
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;
@@ -640,7 +648,16 @@ export default class SASjs {
} else {
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) => {
if (needsRetry(JSON.stringify(e))) {
@@ -652,9 +669,9 @@ export default class SASjs {
loginRequiredCallback,
accessToken
);
this.retryCountJeseApi++;
resolve(retryResponse);
} else {
this.retryCountJeseApi = 0;
@@ -662,7 +679,7 @@ export default class SASjs {
}
}
reject({ MESSAGE: (e && e.message) || "Job execution failed" })
reject({ MESSAGE: (e && e.message) || "Job execution failed" });
})
);
}
@@ -835,7 +852,7 @@ export default class SASjs {
} else {
if (config.serverType === ServerType.SAS9 && config.debug) {
this.updateUsername(responseText);
const jsonResponseText = this.parseSAS9Response(responseText);
const jsonResponseText = parseWeboutResponse(responseText);
if (jsonResponseText !== "") {
resolve(JSON.parse(jsonResponseText));
@@ -976,23 +993,6 @@ export default class SASjs {
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) {
const logLines = response.split("\n");
const parsedLines: string[] = [];
@@ -1050,7 +1050,12 @@ export default class SASjs {
if (response && response.result && response.log) {
sourceCode = parseSourceCode(response.log);
generatedCode = parseGeneratedCode(response.log);
sasWork = JSON.parse(response.result).WORK;
if (this.sasjsConfig.debug) {
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK;
} else {
sasWork = JSON.parse(response.result).WORK;
}
} else {
if (response) {
sourceCode = parseSourceCode(response);
@@ -1079,7 +1084,7 @@ export default class SASjs {
if (this.sasjsConfig.serverType === ServerType.SAS9) {
try {
jsonResponse = JSON.parse(this.parseSAS9Response(response));
jsonResponse = JSON.parse(parseWeboutResponse(response));
} catch (e) {
console.error(e);
}
@@ -1110,6 +1115,10 @@ export default class SASjs {
return sortedRequests;
}
public clearSasRequests() {
this.sasjsRequests = [];
}
private setupConfiguration() {
if (
this.sasjsConfig.serverUrl === undefined ||

View File

@@ -16,7 +16,21 @@ export class SessionManager {
async getSession(accessToken?: string) {
await this.createSessions(accessToken);
this.createAndWaitForSession(accessToken);
return this.sessions.pop();
const session = this.sessions.pop();
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) {

View File

@@ -12,3 +12,4 @@ export * from "./parseSourceCode";
export * from "./parseSasViyaLog";
export * from "./serialize";
export * from "./splitChunks";
export * from "./parseWeboutResponse";

View File

@@ -1,4 +1,8 @@
import { CsrfToken } from "../types";
import { needsRetry } from "./needsRetry";
let retryCount: number = 0;
let retryLimit: number = 5;
export async function makeRequest<T>(
url: string,
@@ -6,12 +10,17 @@ export async function makeRequest<T>(
callback: (value: CsrfToken) => any,
contentType: "text" | "json" = "json"
): Promise<{ result: T; etag: string | null }> {
let retryRequest: any = null;
const responseTransform =
contentType === "json"
? (res: Response) => res.json()
: (res: Response) => res.text();
let etag = null;
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.status === 403) {
const tokenHeader = response.headers.get("X-CSRF-HEADER");
@@ -23,7 +32,7 @@ export async function makeRequest<T>(
value: token || "",
});
const retryRequest = {
retryRequest = {
...request,
headers: { ...request.headers, [tokenHeader]: token },
};
@@ -34,15 +43,64 @@ export async function makeRequest<T>(
}
} else {
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 });
}
} else {
if (response.redirected && response.url.includes("SASLogon/login")) {
const body = await response.text();
return Promise.reject({ status: 401, body });
if (response.status === 204) {
return Promise.resolve();
}
const responseTransformed = await responseTransform(response);
let responseText = "";
if (typeof responseTransformed === "string") {
responseText = responseTransformed;
} else {
responseText = JSON.stringify(responseTransformed);
}
if (needsRetry(responseText)) {
if (retryCount < retryLimit) {
retryCount++;
const 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");
}
}
etag = response.headers.get("ETag");
return responseTransform(response);
return responseTransformed;
}
});
return { result, etag };

View File

@@ -1,11 +1,14 @@
export const needsRetry = (responseText: string): boolean => {
return (
(responseText.includes('"errorCode":403') &&
!!responseText &&
((responseText.includes('"errorCode":403') &&
responseText.includes("_csrf") &&
responseText.includes("X-CSRF-TOKEN")) ||
(responseText.includes('"status":403') &&
responseText.includes('"error":"Forbidden"')) ||
(responseText.includes('"status":449') &&
responseText.includes("Authentication success, retry original request"))
(responseText.includes('"status":403') &&
responseText.includes('"error":"Forbidden"')) ||
(responseText.includes('"status":449') &&
responseText.includes(
"Authentication success, retry original request"
)))
);
};

View 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;
}