1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-11 09:24:35 +00:00

Merge pull request #31 from sasjs/session-cleanup

feat(session-cleanup): delete a session after it has been used
This commit is contained in:
Allan Bowe
2020-08-05 23:17:42 +02:00
committed by GitHub
5 changed files with 118 additions and 76 deletions

View File

@@ -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,11 +203,11 @@ 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
) { ) {
silent = !debug;
const headers: any = { const headers: any = {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };
@@ -313,7 +315,9 @@ export class SASViyaApiClient {
} }
).then((res: any) => res.result.items.map((i: any) => i.line).join("\n")); ).then((res: any) => res.result.items.map((i: any) => i.line).join("\n"));
} }
await this.sessionManager.clearSession(executionSessionId, accessToken);
return { result: jobResult?.result, log }; return { result: jobResult?.result, log };
// } else { // } else {
// console.error( // console.error(
@@ -623,12 +627,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 +655,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 +670,6 @@ export class SASViyaApiClient {
linesToExecute, linesToExecute,
contextName, contextName,
accessToken, accessToken,
"",
true, true,
data, data,
debug debug
@@ -1019,9 +1027,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 +1050,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
);
} }
} }

View File

@@ -21,7 +21,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 +122,6 @@ export default class SASjs {
linesOfCode, linesOfCode,
contextName, contextName,
accessToken, accessToken,
sessionId,
silent silent
); );
} }
@@ -546,52 +545,52 @@ 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);
}
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 { } else {
this.appendSasjsRequest(response, sasJob, null); this.retryCountComputeApi = 0;
reject({ MESSAGE: "Compute API retry requests limit reached" });
} }
}
resolve(JSON.parse(response!.result)); if (e && e.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: e || "Job execution failed" });
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" });
}
})
} }
); );
return sasjsWaitingRequest.requestPromise.promise; return sasjsWaitingRequest.requestPromise.promise;
@@ -652,9 +651,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 +661,7 @@ export default class SASjs {
} }
} }
reject({ MESSAGE: (e && e.message) || "Job execution failed" }) reject({ MESSAGE: (e && e.message) || "Job execution failed" });
}) })
); );
} }

View File

@@ -16,7 +16,21 @@ 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();
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) {

View File

@@ -44,24 +44,32 @@ export async function makeRequest<T>(
if (needsRetry(body)) { if (needsRetry(body)) {
if (retryCount < retryLimit) { if (retryCount < retryLimit) {
retryCount++; retryCount++;
let retryResponse = await makeRequest(url, retryRequest || request, callback, contentType); let retryResponse = await makeRequest(
url,
retryRequest || request,
callback,
contentType
);
retryCount = 0; retryCount = 0;
return retryResponse; return retryResponse;
} else { } else {
retryCount = 0; retryCount = 0;
throw new Error('Request retry limit exceeded'); throw new Error("Request retry limit exceeded");
} }
} }
return Promise.reject({ status: response.status, body }); return Promise.reject({ status: response.status, body });
} }
} else { } else {
if (response.status === 204) {
return Promise.resolve();
}
const responseTransformed = await responseTransform(response); const responseTransformed = await responseTransform(response);
let responseText = ''; let responseText = "";
if (typeof responseTransformed === 'string') { if (typeof responseTransformed === "string") {
responseText = responseTransformed; responseText = responseTransformed;
} else { } else {
responseText = JSON.stringify(responseTransformed); responseText = JSON.stringify(responseTransformed);
@@ -70,18 +78,23 @@ export async function makeRequest<T>(
if (response.redirected && response.url.includes("SASLogon/login")) { if (response.redirected && response.url.includes("SASLogon/login")) {
return Promise.reject({ status: 401, responseTransformed }); 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; return retryResponse;
} else { } else {
retryCount = 0; retryCount = 0;
throw new Error('Request retry limit exceeded'); throw new Error("Request retry limit exceeded");
} }
} }

View File

@@ -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"
)))
); );
}; };