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

feat(poll-strategy): added subsequentStrategies to PollStrategy

This commit is contained in:
Yury Shkoda
2023-05-16 17:48:04 +03:00
parent ea68c3dff3
commit 5c74186bab
4 changed files with 111 additions and 113 deletions

View File

@@ -24,20 +24,19 @@ export enum JobState {
* @param authConfig - an access token, refresh token, client and secret for an authorized user.
* @param pollStrategy - an object containing maxPollCount, pollInterval, streamLog and logFolderPath. It will override the first default poll strategy if provided.
* Example:
* { maxPollCount: 200, pollInterval: 300, streamLog: false }
* @param pollStrategies - an array of poll strategies. It will override default poll strategies if provided.
* Example:
* {
* maxPollCount: 200,
* pollInterval: 300,
* streamLog: true, // optional, equals to false by default.
* subsequentStrategies?: // optional array of the strategies that should be applied after 'maxPollCount' of the provided poll strategy is reached. If not provided the default (see example below) subsequent poll strategies will be used.
* }
* @param pollStrategies - optional array of poll strategies. It will override default poll strategies if provided.
* Example (default poll strategies):
* [
* { maxPollCount: 200, pollInterval: 300, streamLog: false },
* { maxPollCount: 300, pollInterval: 3000, streamLog: false },
* { maxPollCount: 500, pollInterval: 30000, streamLog: false }
* ]
* Default poll strategies:
* [
* { maxPollCount: 200, pollInterval: 300, streamLog: false }, // INFO: approximately ~2 mins (including time to get response (~300ms))
* { maxPollCount: 300, pollInterval: 3000, streamLog: false }, // INFO: approximately ~5.5 mins (including time to get response (~300ms))
* { maxPollCount: 400, pollInterval: 30000, streamLog: false }, // INFO: approximately ~50.5 mins (including time to get response (~300ms))
* { maxPollCount: 3400, pollInterval: 60000, streamLog: false } // INFO: approximately ~3015 mins (~125 hours) (including time to get response (~300ms))
* { maxPollCount: 200, pollInterval: 300 }, // approximately ~2 mins (including time to get response (~300ms))
* { maxPollCount: 300, pollInterval: 3000 }, // approximately ~5.5 mins (including time to get response (~300ms))
* { maxPollCount: 500, pollInterval: 30000 }, // approximately ~50.5 mins (including time to get response (~300ms))
* { maxPollCount: 3400, pollInterval: 60000 } // approximately ~3015 mins (~125 hours) (including time to get response (~300ms))
* ]
* @returns - a promise which resolves with a job state
*/
@@ -47,20 +46,42 @@ export async function pollJobState(
debug: boolean,
authConfig?: AuthConfig,
pollStrategy?: PollStrategy,
pollStrategies?: PollStrategies
streamLog?: boolean
): Promise<JobState> {
const logger = process.logger || console
const defaultStreamLog = pollStrategy ? pollStrategy.streamLog : false
streamLog = streamLog || false
const defaultPollStrategies: PollStrategies = [
{ maxPollCount: 200, pollInterval: 300, streamLog: defaultStreamLog },
{ maxPollCount: 300, pollInterval: 3000, streamLog: defaultStreamLog },
{ maxPollCount: 400, pollInterval: 30000, streamLog: defaultStreamLog },
{ maxPollCount: 3400, pollInterval: 60000, streamLog: defaultStreamLog }
{ maxPollCount: 200, pollInterval: 300 },
{ maxPollCount: 400, pollInterval: 30000 },
{ maxPollCount: 300, pollInterval: 3000 },
{ maxPollCount: 3400, pollInterval: 60000 }
]
if (pollStrategies === undefined) pollStrategies = defaultPollStrategies
else validatePollStrategies(pollStrategies)
let pollStrategies: PollStrategies
if (pollStrategy !== undefined) {
pollStrategies = [pollStrategy]
let { subsequentStrategies } = pollStrategy
if (subsequentStrategies !== undefined) {
validatePollStrategies(subsequentStrategies)
// INFO: sort by 'maxPollCount'
subsequentStrategies = subsequentStrategies.sort(
(strategyA: PollStrategy, strategyB: PollStrategy) =>
strategyA.maxPollCount - strategyB.maxPollCount
)
pollStrategies = [...pollStrategies, ...subsequentStrategies]
} else {
pollStrategies = [...pollStrategies, ...defaultPollStrategies]
}
} else {
pollStrategies = defaultPollStrategies
}
let defaultPollStrategy: PollStrategy = pollStrategies.splice(0, 1)[0]
@@ -93,8 +114,9 @@ export async function pollJobState(
}
let logFileStream
if (pollStrategy.streamLog && isNode()) {
if (streamLog && isNode()) {
const { getFileStream } = require('./getFileStream')
logFileStream = await getFileStream(postedJob, pollStrategy.logFolderPath)
}
@@ -106,6 +128,7 @@ export async function pollJobState(
pollCount,
pollStrategy,
authConfig,
streamLog,
logFileStream
)
@@ -125,7 +148,6 @@ export async function pollJobState(
defaultPollStrategy = pollStrategies.splice(0, 1)[0]
if (pollStrategy) {
defaultPollStrategy.streamLog = pollStrategy.streamLog
defaultPollStrategy.logFolderPath = pollStrategy.logFolderPath
}
@@ -137,6 +159,7 @@ export async function pollJobState(
pollCount,
defaultPollStrategy,
authConfig,
streamLog,
logFileStream
)
@@ -195,6 +218,7 @@ const doPoll = async (
pollCount: number,
pollStrategy: PollStrategy,
authConfig?: AuthConfig,
streamLog?: boolean,
logStream?: WriteStream
): Promise<{ state: JobState; pollCount: number }> => {
const { maxPollCount, pollInterval } = pollStrategy
@@ -232,7 +256,7 @@ const doPoll = async (
const jobHref = postedJob.links.find((l: Link) => l.rel === 'self')!.href
if (pollStrategy?.streamLog) {
if (streamLog) {
const { result: job } = await requestClient.get<Job>(
jobHref,
authConfig?.access_token
@@ -285,8 +309,6 @@ const validatePollStrategies = (strategies: PollStrategies) => {
)
}
if (!strategies.length) throwError('No strategies provided.')
strategies.forEach((strategy: PollStrategy, i: number) => {
const { maxPollCount, pollInterval } = strategy

View File

@@ -15,8 +15,7 @@ const sessionManager = new (<jest.Mock<SessionManager>>SessionManager)()
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
const defaultPollStrategy: PollStrategy = {
maxPollCount: 100,
pollInterval: 500,
streamLog: false
pollInterval: 500
}
describe('executeScript', () => {

View File

@@ -14,10 +14,10 @@ const baseUrl = 'http://localhost'
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
requestClient['httpClient'].defaults.baseURL = baseUrl
const defaultStreamLog = false
const defaultPollStrategy: PollStrategy = {
maxPollCount: 100,
pollInterval: 500,
streamLog: false
pollInterval: 500
}
describe('pollJobState', () => {
@@ -32,7 +32,8 @@ describe('pollJobState', () => {
mockJob,
false,
mockAuthConfig,
defaultPollStrategy
defaultPollStrategy,
defaultStreamLog
)
expect(getTokensModule.getTokens).toHaveBeenCalledWith(
@@ -83,10 +84,14 @@ describe('pollJobState', () => {
mockSimplePoll()
const { saveLog } = require('../saveLog')
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
...defaultPollStrategy,
streamLog: true
})
await pollJobState(
requestClient,
mockJob,
false,
mockAuthConfig,
defaultPollStrategy,
true
)
expect(saveLog).toHaveBeenCalledTimes(2)
})
@@ -96,10 +101,14 @@ describe('pollJobState', () => {
const { getFileStream } = require('../getFileStream')
const { saveLog } = require('../saveLog')
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
...defaultPollStrategy,
streamLog: true
})
await pollJobState(
requestClient,
mockJob,
false,
mockAuthConfig,
defaultPollStrategy,
true
)
expect(getFileStream).toHaveBeenCalled()
expect(saveLog).toHaveBeenCalledTimes(2)
@@ -111,10 +120,14 @@ describe('pollJobState', () => {
const { saveLog } = require('../saveLog')
const { getFileStream } = require('../getFileStream')
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
...defaultPollStrategy,
streamLog: true
})
await pollJobState(
requestClient,
mockJob,
false,
mockAuthConfig,
defaultPollStrategy,
true
)
expect(getFileStream).not.toHaveBeenCalled()
expect(saveLog).not.toHaveBeenCalled()
@@ -137,19 +150,18 @@ describe('pollJobState', () => {
it('should return the current status when the max poll count is reached', async () => {
mockRunningPoll()
const pollStrategy = {
const pollStrategy: PollStrategy = {
...defaultPollStrategy,
maxPollCount: 1
maxPollCount: 1,
subsequentStrategies: []
}
const pollStrategies = [pollStrategy]
const state = await pollJobState(
requestClient,
mockJob,
false,
mockAuthConfig,
pollStrategy,
pollStrategies
pollStrategy
)
expect(state).toEqual('running')
@@ -264,53 +276,32 @@ describe('pollJobState', () => {
const pollIntervals = [3, 4, 5, 6]
const pollStrategies = [
{ maxPollCount: 1, pollInterval: pollIntervals[0], streamLog: false },
{ maxPollCount: 2, pollInterval: pollIntervals[1], streamLog: false },
{ maxPollCount: 3, pollInterval: pollIntervals[2], streamLog: false },
{ maxPollCount: 4, pollInterval: pollIntervals[3], streamLog: false }
{ maxPollCount: 2, pollInterval: pollIntervals[1] },
{ maxPollCount: 3, pollInterval: pollIntervals[2] },
{ maxPollCount: 4, pollInterval: pollIntervals[3] }
]
await pollJobState(
requestClient,
mockJob,
false,
undefined,
undefined,
pollStrategies
)
const pollStrategy: PollStrategy = {
maxPollCount: 1,
pollInterval: pollIntervals[0],
subsequentStrategies: pollStrategies
}
await pollJobState(requestClient, mockJob, false, undefined, pollStrategy)
expect(delays).toEqual([pollIntervals[0], ...pollIntervals])
})
it('should throw an error if not valid poll strategies provided', async () => {
// INFO: No strategies provided.
let expectedError = new Error(
'Poll strategies are not valid. No strategies provided.'
)
let pollStrategies: PollStrategies = []
await expect(
pollJobState(
requestClient,
mockJob,
false,
undefined,
undefined,
pollStrategies
)
).rejects.toThrow(expectedError)
// INFO: 'maxPollCount' has to be > 0
let invalidPollStrategy = {
maxPollCount: 0,
pollInterval: 3,
streamLog: false
pollInterval: 3
}
pollStrategies.push(invalidPollStrategy)
let pollStrategies: PollStrategies = [invalidPollStrategy]
expectedError = new Error(
let expectedError = new Error(
`Poll strategies are not valid. 'maxPollCount' has to be greater than 0. Invalid poll strategy: \n${JSON.stringify(
invalidPollStrategy,
null,
@@ -319,27 +310,21 @@ describe('pollJobState', () => {
)
await expect(
pollJobState(
requestClient,
mockJob,
false,
undefined,
undefined,
pollStrategies
)
pollJobState(requestClient, mockJob, false, undefined, {
...defaultPollStrategy,
subsequentStrategies: pollStrategies
})
).rejects.toThrow(expectedError)
// INFO: 'maxPollCount' has to be > than 'maxPollCount' of the previous strategy
const validPollStrategy = {
maxPollCount: 5,
pollInterval: 2,
streamLog: false
pollInterval: 2
}
invalidPollStrategy = {
maxPollCount: validPollStrategy.maxPollCount,
pollInterval: 3,
streamLog: false
pollInterval: 3
}
pollStrategies = [validPollStrategy, invalidPollStrategy]
@@ -353,21 +338,16 @@ describe('pollJobState', () => {
)
await expect(
pollJobState(
requestClient,
mockJob,
false,
undefined,
undefined,
pollStrategies
)
pollJobState(requestClient, mockJob, false, undefined, {
...defaultPollStrategy,
subsequentStrategies: pollStrategies
})
).rejects.toThrow(expectedError)
// INFO: invalid 'pollInterval'
invalidPollStrategy = {
maxPollCount: 1,
pollInterval: 0,
streamLog: false
pollInterval: 0
}
pollStrategies = [invalidPollStrategy]
@@ -381,14 +361,10 @@ describe('pollJobState', () => {
)
await expect(
pollJobState(
requestClient,
mockJob,
false,
undefined,
undefined,
pollStrategies
)
pollJobState(requestClient, mockJob, false, undefined, {
...defaultPollStrategy,
subsequentStrategies: pollStrategies
})
).rejects.toThrow(expectedError)
})
})

View File

@@ -1,7 +1,8 @@
export interface PollStrategy {
maxPollCount: number
pollInterval: number // milliseconds
streamLog: boolean
subsequentStrategies?: PollStrategy[]
streamLog?: boolean
logFolderPath?: string
}