1
0
mirror of https://github.com/sasjs/adapter.git synced 2026-01-10 22:00:05 +00:00

feat(session-manager): refactored and covered with tests

This commit is contained in:
Yury Shkoda
2023-03-24 11:39:59 +03:00
parent 891cc13362
commit c1704fff78
2 changed files with 438 additions and 132 deletions

View File

@@ -14,12 +14,12 @@ export class SessionManager {
private contextName: string,
private requestClient: RequestClient
) {
console.log(`🤖[SessionManager constructor]🤖`)
if (serverUrl) isUrl(serverUrl)
}
private sessions: Session[] = []
private currentContext: Context | null = null
private settingContext: boolean = false
private _debug: boolean = false
private printedSessionState = {
printed: false,
@@ -34,164 +34,206 @@ export class SessionManager {
this._debug = value
}
async getSession(accessToken?: string) {
console.log(`🤖[]🤖`)
console.log(`🤖[---- SessionManager getSession start]🤖`)
console.log(
`🤖[this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
private isSessionValid(session: Session) {
if (!session) return false
const secondsSinceSessionCreation =
(new Date().getTime() - new Date(session.creationTimeStamp).getTime()) /
1000
if (
!session!.attributes ||
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
) {
return false
} else {
return true
}
}
private removeSessionFromPull(session: Session) {
this.sessions = this.sessions.filter((ses) => ses.id !== session.id)
}
private removeExpiredSessions() {
this.sessions = this.sessions.filter((session) =>
this.isSessionValid(session)
)
}
private throwErrors(errors: (Error | string)[], prefix?: string) {
throw prefix
? prefixMessage(new Error(errors.join('. ')), prefix)
: new Error(
errors
.map((err) =>
(err as Error).message ? (err as Error).message : err
)
.join('. ')
)
}
async getSession(accessToken?: string) {
const errors: (Error | string)[] = []
let isErrorThrown = false
const throwIfError = () => {
if (errors.length && !isErrorThrown) {
isErrorThrown = true
this.throwErrors(errors)
}
}
this.removeExpiredSessions()
if (this.sessions.length) {
const session = this.sessions[0]
this.createSessions(accessToken)
this.createAndWaitForSession(accessToken)
this.removeSessionFromPull(session)
// TODO: check secondsSinceSessionCreation
this.createSessions(accessToken).catch((err) => {
errors.push(err)
})
this.createAndWaitForSession(accessToken).catch((err) => {
errors.push(err)
})
throwIfError()
return session
} else {
await this.createSessions(accessToken)
console.log(
`🤖[ 45 this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
)
await this.createAndWaitForSession(accessToken)
console.log(
`🤖[ 50 this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
)
this.createSessions(accessToken).catch((err) => {
errors.push(err)
})
const session = this.sessions.pop()
console.log(`🤖[session]🤖`, session!.id)
await this.createAndWaitForSession(accessToken).catch((err) => {
errors.push(err)
})
console.log(
`🤖[59 this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
)
this.removeExpiredSessions()
const secondsSinceSessionCreation =
(new Date().getTime() -
new Date(session!.creationTimeStamp).getTime()) /
1000
console.log(
`🤖[secondsSinceSessionCreation]🤖`,
secondsSinceSessionCreation
)
const session = this.sessions.pop()!
this.removeSessionFromPull(session)
throwIfError()
if (
!session!.attributes ||
secondsSinceSessionCreation >=
session!.attributes.sessionInactiveTimeout
) {
console.log(`🤖[54]🤖`, 54)
await this.createSessions(accessToken)
const freshSession = this.sessions.pop()
console.log(`🤖[freshSession]🤖`, freshSession!.id)
return freshSession
}
console.log(`🤖[60]🤖`, 60)
console.log(`🤖[---- SessionManager getSession end]🤖`)
console.log(`🤖[]🤖`)
return session
}
}
async clearSession(id: string, accessToken?: string) {
console.log(
`🤖[clearSession this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
private getErrorMessage(
err: any,
url: string,
method: 'GET' | 'POST' | 'DELETE'
) {
return (
`${method} request to ${url} failed with status code ${
err?.response?.status || 'unknown'
}. ` + err?.response?.data?.message || ''
)
console.log(`🤖[SessionManager clearSession id]🤖`, id)
}
async clearSession(id: string, accessToken?: string) {
const url = `/compute/sessions/${id}`
return await this.requestClient
.delete<Session>(`/compute/sessions/${id}`, accessToken)
.delete<Session>(url, accessToken)
.then(() => {
this.sessions = this.sessions.filter((s) => s.id !== id)
})
.catch((err) => {
throw prefixMessage(err, 'Error while deleting session. ')
throw prefixMessage(
this.getErrorMessage(err, url, 'DELETE'),
'Error while deleting session. '
)
})
}
private async createSessions(accessToken?: string) {
console.log(`🤖[SessionManager createSessions]🤖`)
const errors: (Error | string)[] = []
if (!this.sessions.length) {
if (!this.currentContext) {
await this.setCurrentContext(accessToken).catch((err) => {
throw err
})
}
console.log(
`🤖[createSessions start this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
)
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
const createdSession = await this.createAndWaitForSession(
accessToken
).catch((err) => {
throw err
await this.createAndWaitForSession(accessToken).catch((err) => {
errors.push(err)
})
// console.log(`🤖[createSessions new session id]🤖`, createdSession.id)
// this.sessions.push(createdSession)
}).catch((err) => {
throw err
})
}
console.log(
`🤖[createSessions end this.sessions]🤖`,
this.sessions.map((session: any) => session.id)
)
if (errors.length) {
this.throwErrors(errors, 'Error while creating session. ')
}
}
private async waitForCurrentContext(): Promise<void> {
return new Promise((resolve) => {
const timer = setInterval(() => {
if (this.currentContext) {
this.settingContext = false
clearInterval(timer)
resolve()
}
}, 100)
})
}
private async createAndWaitForSession(accessToken?: string) {
console.log(`🤖[SessionManager createAndWaitForSession]🤖`)
if (!this.currentContext) {
if (!this.settingContext) {
await this.setCurrentContext(accessToken)
} else {
await this.waitForCurrentContext()
}
}
const url = `${this.serverUrl}/compute/contexts/${
this.currentContext!.id
}/sessions`
const { result: createdSession, etag } = await this.requestClient
.post<Session>(
`${this.serverUrl}/compute/contexts/${
this.currentContext!.id
}/sessions`,
{},
accessToken
)
.post<Session>(url, {}, accessToken)
.catch((err) => {
throw err
throw prefixMessage(
err,
`Error while creating session. ${this.getErrorMessage(
err,
url,
'POST'
)}`
)
})
await this.waitForSession(createdSession, etag, accessToken)
console.log(
`🤖[createAndWaitForSession this.sessions.map((session: any) => session.id)]🤖`,
this.sessions.map((session: any) => session.id)
)
console.log(
`🤖[createAndWaitForSession adding createdSession.id]🤖`,
createdSession.id
)
this.sessions.push(createdSession)
return createdSession
}
private async setCurrentContext(accessToken?: string) {
console.log(`🤖[SessionManager setCurrentContext]🤖`)
if (!this.currentContext) {
const url = `${this.serverUrl}/compute/contexts?limit=10000`
this.settingContext = true
const { result: contexts } = await this.requestClient
.get<{
items: Context[]
}>(`${this.serverUrl}/compute/contexts?limit=10000`, accessToken)
}>(url, accessToken)
.catch((err) => {
throw err
throw prefixMessage(
err,
`Error while getting list of contexts. ${this.getErrorMessage(
err,
url,
'GET'
)}`
)
})
const contextsList =
@@ -215,26 +257,11 @@ export class SessionManager {
}
}
// DEPRECATE
private getHeaders(accessToken?: string) {
const headers: any = {
'Content-Type': 'application/json'
}
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`
}
return headers
}
private async waitForSession(
session: Session,
etag: string | null,
accessToken?: string
): Promise<string> {
console.log(`🤖[SessionManager waitForSession]🤖`)
const logger = process.logger || console
let sessionState = session.state
@@ -253,13 +280,14 @@ export class SessionManager {
this.printedSessionState.printed = true
}
const url = `${this.serverUrl}${stateLink.href}?wait=30`
const { result: state, responseStatus: responseStatus } =
await this.getSessionState(
`${this.serverUrl}${stateLink.href}?wait=30`,
etag!,
accessToken
).catch((err) => {
throw prefixMessage(err, 'Error while getting session state.')
await this.getSessionState(url, etag!, accessToken).catch((err) => {
throw prefixMessage(
this.getErrorMessage(err, url, 'GET'),
'Error while getting session state. '
)
})
sessionState = state.trim()
@@ -296,7 +324,7 @@ export class SessionManager {
return sessionState
} else {
throw 'Error while getting session state link.'
throw 'Error while getting session state link. '
}
} else {
this.loggedErrors = []
@@ -310,8 +338,6 @@ export class SessionManager {
etag: string,
accessToken?: string
) {
console.log(`🤖[SessionManager getSessionState]🤖`)
return await this.requestClient
.get(url, accessToken, 'text/plain', { 'If-None-Match': etag })
.then((res) => ({
@@ -319,13 +345,14 @@ export class SessionManager {
responseStatus: res.status
}))
.catch((err) => {
throw err
throw prefixMessage(
this.getErrorMessage(err, url, 'GET'),
'Error while getting session state. '
)
})
}
async getVariable(sessionId: string, variable: string, accessToken?: string) {
console.log(`🤖[SessionManager getVariable]🤖`)
return await this.requestClient
.get<SessionVariable>(
`${this.serverUrl}/compute/sessions/${sessionId}/variables/${variable}`,