mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-19 18:10:06 +00:00
Merge pull request #491 from sasjs/session-state-fix
fix(session): remove retry limit if could not get state
This commit is contained in:
@@ -5,10 +5,10 @@ import { prefixMessage } from '@sasjs/utils/error'
|
|||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
|
|
||||||
const MAX_SESSION_COUNT = 1
|
const MAX_SESSION_COUNT = 1
|
||||||
const RETRY_LIMIT: number = 3
|
|
||||||
let RETRY_COUNT: number = 0
|
|
||||||
|
|
||||||
export class SessionManager {
|
export class SessionManager {
|
||||||
|
private loggedErrors: NoSessionStateError[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private serverUrl: string,
|
private serverUrl: string,
|
||||||
private contextName: string,
|
private contextName: string,
|
||||||
@@ -154,14 +154,13 @@ export class SessionManager {
|
|||||||
session: Session,
|
session: Session,
|
||||||
etag: string | null,
|
etag: string | null,
|
||||||
accessToken?: string
|
accessToken?: string
|
||||||
) {
|
): Promise<string> {
|
||||||
const logger = process.logger || console
|
const logger = process.logger || console
|
||||||
|
|
||||||
let sessionState = session.state
|
let sessionState = session.state
|
||||||
|
|
||||||
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
if (
|
if (
|
||||||
sessionState === 'pending' ||
|
sessionState === 'pending' ||
|
||||||
sessionState === 'running' ||
|
sessionState === 'running' ||
|
||||||
@@ -192,31 +191,38 @@ export class SessionManager {
|
|||||||
this.printedSessionState.printed = false
|
this.printedSessionState.printed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is an internal error present in SAS Viya 3.5
|
|
||||||
// Retry to wait for a session status in such case of SAS internal error
|
|
||||||
if (!sessionState) {
|
if (!sessionState) {
|
||||||
if (RETRY_COUNT < RETRY_LIMIT) {
|
const stateError = new NoSessionStateError(
|
||||||
RETRY_COUNT++
|
|
||||||
|
|
||||||
resolve(this.waitForSession(session, etag, accessToken))
|
|
||||||
} else {
|
|
||||||
reject(
|
|
||||||
new NoSessionStateError(
|
|
||||||
responseStatus,
|
responseStatus,
|
||||||
this.serverUrl + stateLink.href,
|
this.serverUrl + stateLink.href,
|
||||||
session.links.find((l: any) => l.rel === 'log')
|
session.links.find((l: any) => l.rel === 'log')?.href as string
|
||||||
?.href as string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.loggedErrors.find(
|
||||||
|
(err: NoSessionStateError) =>
|
||||||
|
err.serverResponseStatus === stateError.serverResponseStatus
|
||||||
)
|
)
|
||||||
}
|
) {
|
||||||
|
this.loggedErrors.push(stateError)
|
||||||
|
|
||||||
|
logger.info(stateError.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(sessionState)
|
return await this.waitForSession(session, etag, accessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loggedErrors = []
|
||||||
|
|
||||||
|
return sessionState
|
||||||
|
} else {
|
||||||
|
throw 'Error while getting session state link.'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(sessionState)
|
this.loggedErrors = []
|
||||||
|
|
||||||
|
return sessionState
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSessionState(
|
private async getSessionState(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { RequestClient } from '../request/RequestClient'
|
|||||||
import { NoSessionStateError } from '../types/errors'
|
import { NoSessionStateError } from '../types/errors'
|
||||||
import * as dotenv from 'dotenv'
|
import * as dotenv from 'dotenv'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { Logger, LogLevel } from '@sasjs/utils'
|
||||||
|
import { Session } from '../types'
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
const mockedAxios = axios as jest.Mocked<typeof axios>
|
const mockedAxios = axios as jest.Mocked<typeof axios>
|
||||||
@@ -47,36 +49,91 @@ describe('SessionManager', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('waitForSession', () => {
|
describe('waitForSession', () => {
|
||||||
it('should reject with NoSessionStateError if SAS server did not provide session state', async () => {
|
const session: Session = {
|
||||||
const responseStatus = 304
|
|
||||||
|
|
||||||
mockedAxios.get.mockImplementation(() =>
|
|
||||||
Promise.resolve({ data: '', status: responseStatus })
|
|
||||||
)
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
sessionManager['waitForSession'](
|
|
||||||
{
|
|
||||||
id: 'id',
|
id: 'id',
|
||||||
state: '',
|
state: '',
|
||||||
links: [
|
links: [{ rel: 'state', href: '', uri: '', type: '', method: 'GET' }],
|
||||||
{ rel: 'state', href: '', uri: '', type: '', method: 'GET' }
|
|
||||||
],
|
|
||||||
attributes: {
|
attributes: {
|
||||||
sessionInactiveTimeout: 0
|
sessionInactiveTimeout: 0
|
||||||
},
|
},
|
||||||
creationTimeStamp: ''
|
creationTimeStamp: ''
|
||||||
},
|
}
|
||||||
null,
|
|
||||||
'access_token'
|
beforeEach(() => {
|
||||||
|
;(process as any).logger = new Logger(LogLevel.Off)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject with NoSessionStateError if SAS server did not provide session state', async () => {
|
||||||
|
let requestAttempt = 0
|
||||||
|
const requestAttemptLimit = 10
|
||||||
|
const sessionState = 'idle'
|
||||||
|
|
||||||
|
mockedAxios.get.mockImplementation(() => {
|
||||||
|
requestAttempt += 1
|
||||||
|
|
||||||
|
if (requestAttempt >= requestAttemptLimit) {
|
||||||
|
return Promise.resolve({ data: sessionState, status: 200 })
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({ data: '', status: 304 })
|
||||||
|
})
|
||||||
|
|
||||||
|
jest.spyOn((process as any).logger, 'info')
|
||||||
|
|
||||||
|
sessionManager.debug = true
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sessionManager['waitForSession'](session, null, 'access_token')
|
||||||
|
).resolves.toEqual(sessionState)
|
||||||
|
|
||||||
|
expect(mockedAxios.get).toHaveBeenCalledTimes(requestAttemptLimit)
|
||||||
|
expect((process as any).logger.info).toHaveBeenCalledTimes(3)
|
||||||
|
expect((process as any).logger.info).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'Polling session status...'
|
||||||
)
|
)
|
||||||
).rejects.toEqual(
|
expect((process as any).logger.info).toHaveBeenNthCalledWith(
|
||||||
new NoSessionStateError(
|
2,
|
||||||
responseStatus,
|
`Could not get session state. Server responded with 304 whilst checking state: ${process.env.SERVER_URL}`
|
||||||
process.env.SERVER_URL as string,
|
|
||||||
'logUrl'
|
|
||||||
)
|
)
|
||||||
|
expect((process as any).logger.info).toHaveBeenNthCalledWith(
|
||||||
|
3,
|
||||||
|
`Current session state is '${sessionState}'`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should throw an error if there is no session link', async () => {
|
||||||
|
const customSession = JSON.parse(JSON.stringify(session))
|
||||||
|
customSession.links = []
|
||||||
|
|
||||||
|
mockedAxios.get.mockImplementation(() =>
|
||||||
|
Promise.resolve({ data: customSession.state, status: 200 })
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sessionManager['waitForSession'](customSession, null, 'access_token')
|
||||||
|
).rejects.toContain('Error while getting session state link.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if could not get session state', async () => {
|
||||||
|
mockedAxios.get.mockImplementation(() => Promise.reject('Mocked error'))
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sessionManager['waitForSession'](session, null, 'access_token')
|
||||||
|
).rejects.toContain('Error while getting session state.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return session state', async () => {
|
||||||
|
const customSession = JSON.parse(JSON.stringify(session))
|
||||||
|
customSession.state = 'completed'
|
||||||
|
|
||||||
|
mockedAxios.get.mockImplementation(() =>
|
||||||
|
Promise.resolve({ data: customSession.state, status: 200 })
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sessionManager['waitForSession'](customSession, null, 'access_token')
|
||||||
|
).resolves.toEqual(customSession.state)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user