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:
@@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
src/SASjs.ts
95
src/SASjs.ts
@@ -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" });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
)))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user