mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-07 12:30:06 +00:00
feat(poll-strategy): added subsequentStrategies to PollStrategy
This commit is contained in:
@@ -24,20 +24,19 @@ export enum JobState {
|
|||||||
* @param authConfig - an access token, refresh token, client and secret for an authorized user.
|
* @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.
|
* @param pollStrategy - an object containing maxPollCount, pollInterval, streamLog and logFolderPath. It will override the first default poll strategy if provided.
|
||||||
* Example:
|
* Example:
|
||||||
* { maxPollCount: 200, pollInterval: 300, streamLog: false }
|
* {
|
||||||
* @param pollStrategies - an array of poll strategies. It will override default poll strategies if provided.
|
* maxPollCount: 200,
|
||||||
* Example:
|
* 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: 200, pollInterval: 300 }, // approximately ~2 mins (including time to get response (~300ms))
|
||||||
* { maxPollCount: 300, pollInterval: 3000, streamLog: false },
|
* { maxPollCount: 300, pollInterval: 3000 }, // approximately ~5.5 mins (including time to get response (~300ms))
|
||||||
* { maxPollCount: 500, pollInterval: 30000, streamLog: false }
|
* { 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))
|
||||||
* 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))
|
|
||||||
* ]
|
* ]
|
||||||
* @returns - a promise which resolves with a job state
|
* @returns - a promise which resolves with a job state
|
||||||
*/
|
*/
|
||||||
@@ -47,20 +46,42 @@ export async function pollJobState(
|
|||||||
debug: boolean,
|
debug: boolean,
|
||||||
authConfig?: AuthConfig,
|
authConfig?: AuthConfig,
|
||||||
pollStrategy?: PollStrategy,
|
pollStrategy?: PollStrategy,
|
||||||
pollStrategies?: PollStrategies
|
streamLog?: boolean
|
||||||
): Promise<JobState> {
|
): Promise<JobState> {
|
||||||
const logger = process.logger || console
|
const logger = process.logger || console
|
||||||
|
|
||||||
const defaultStreamLog = pollStrategy ? pollStrategy.streamLog : false
|
streamLog = streamLog || false
|
||||||
|
|
||||||
const defaultPollStrategies: PollStrategies = [
|
const defaultPollStrategies: PollStrategies = [
|
||||||
{ maxPollCount: 200, pollInterval: 300, streamLog: defaultStreamLog },
|
{ maxPollCount: 200, pollInterval: 300 },
|
||||||
{ maxPollCount: 300, pollInterval: 3000, streamLog: defaultStreamLog },
|
{ maxPollCount: 400, pollInterval: 30000 },
|
||||||
{ maxPollCount: 400, pollInterval: 30000, streamLog: defaultStreamLog },
|
{ maxPollCount: 300, pollInterval: 3000 },
|
||||||
{ maxPollCount: 3400, pollInterval: 60000, streamLog: defaultStreamLog }
|
{ maxPollCount: 3400, pollInterval: 60000 }
|
||||||
]
|
]
|
||||||
|
|
||||||
if (pollStrategies === undefined) pollStrategies = defaultPollStrategies
|
let pollStrategies: PollStrategies
|
||||||
else validatePollStrategies(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]
|
let defaultPollStrategy: PollStrategy = pollStrategies.splice(0, 1)[0]
|
||||||
|
|
||||||
@@ -93,8 +114,9 @@ export async function pollJobState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let logFileStream
|
let logFileStream
|
||||||
if (pollStrategy.streamLog && isNode()) {
|
if (streamLog && isNode()) {
|
||||||
const { getFileStream } = require('./getFileStream')
|
const { getFileStream } = require('./getFileStream')
|
||||||
|
|
||||||
logFileStream = await getFileStream(postedJob, pollStrategy.logFolderPath)
|
logFileStream = await getFileStream(postedJob, pollStrategy.logFolderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +128,7 @@ export async function pollJobState(
|
|||||||
pollCount,
|
pollCount,
|
||||||
pollStrategy,
|
pollStrategy,
|
||||||
authConfig,
|
authConfig,
|
||||||
|
streamLog,
|
||||||
logFileStream
|
logFileStream
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -125,7 +148,6 @@ export async function pollJobState(
|
|||||||
defaultPollStrategy = pollStrategies.splice(0, 1)[0]
|
defaultPollStrategy = pollStrategies.splice(0, 1)[0]
|
||||||
|
|
||||||
if (pollStrategy) {
|
if (pollStrategy) {
|
||||||
defaultPollStrategy.streamLog = pollStrategy.streamLog
|
|
||||||
defaultPollStrategy.logFolderPath = pollStrategy.logFolderPath
|
defaultPollStrategy.logFolderPath = pollStrategy.logFolderPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +159,7 @@ export async function pollJobState(
|
|||||||
pollCount,
|
pollCount,
|
||||||
defaultPollStrategy,
|
defaultPollStrategy,
|
||||||
authConfig,
|
authConfig,
|
||||||
|
streamLog,
|
||||||
logFileStream
|
logFileStream
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,6 +218,7 @@ const doPoll = async (
|
|||||||
pollCount: number,
|
pollCount: number,
|
||||||
pollStrategy: PollStrategy,
|
pollStrategy: PollStrategy,
|
||||||
authConfig?: AuthConfig,
|
authConfig?: AuthConfig,
|
||||||
|
streamLog?: boolean,
|
||||||
logStream?: WriteStream
|
logStream?: WriteStream
|
||||||
): Promise<{ state: JobState; pollCount: number }> => {
|
): Promise<{ state: JobState; pollCount: number }> => {
|
||||||
const { maxPollCount, pollInterval } = pollStrategy
|
const { maxPollCount, pollInterval } = pollStrategy
|
||||||
@@ -232,7 +256,7 @@ const doPoll = async (
|
|||||||
|
|
||||||
const jobHref = postedJob.links.find((l: Link) => l.rel === 'self')!.href
|
const jobHref = postedJob.links.find((l: Link) => l.rel === 'self')!.href
|
||||||
|
|
||||||
if (pollStrategy?.streamLog) {
|
if (streamLog) {
|
||||||
const { result: job } = await requestClient.get<Job>(
|
const { result: job } = await requestClient.get<Job>(
|
||||||
jobHref,
|
jobHref,
|
||||||
authConfig?.access_token
|
authConfig?.access_token
|
||||||
@@ -285,8 +309,6 @@ const validatePollStrategies = (strategies: PollStrategies) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strategies.length) throwError('No strategies provided.')
|
|
||||||
|
|
||||||
strategies.forEach((strategy: PollStrategy, i: number) => {
|
strategies.forEach((strategy: PollStrategy, i: number) => {
|
||||||
const { maxPollCount, pollInterval } = strategy
|
const { maxPollCount, pollInterval } = strategy
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ const sessionManager = new (<jest.Mock<SessionManager>>SessionManager)()
|
|||||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||||
const defaultPollStrategy: PollStrategy = {
|
const defaultPollStrategy: PollStrategy = {
|
||||||
maxPollCount: 100,
|
maxPollCount: 100,
|
||||||
pollInterval: 500,
|
pollInterval: 500
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('executeScript', () => {
|
describe('executeScript', () => {
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ const baseUrl = 'http://localhost'
|
|||||||
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
|
||||||
requestClient['httpClient'].defaults.baseURL = baseUrl
|
requestClient['httpClient'].defaults.baseURL = baseUrl
|
||||||
|
|
||||||
|
const defaultStreamLog = false
|
||||||
const defaultPollStrategy: PollStrategy = {
|
const defaultPollStrategy: PollStrategy = {
|
||||||
maxPollCount: 100,
|
maxPollCount: 100,
|
||||||
pollInterval: 500,
|
pollInterval: 500
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('pollJobState', () => {
|
describe('pollJobState', () => {
|
||||||
@@ -32,7 +32,8 @@ describe('pollJobState', () => {
|
|||||||
mockJob,
|
mockJob,
|
||||||
false,
|
false,
|
||||||
mockAuthConfig,
|
mockAuthConfig,
|
||||||
defaultPollStrategy
|
defaultPollStrategy,
|
||||||
|
defaultStreamLog
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(getTokensModule.getTokens).toHaveBeenCalledWith(
|
expect(getTokensModule.getTokens).toHaveBeenCalledWith(
|
||||||
@@ -83,10 +84,14 @@ describe('pollJobState', () => {
|
|||||||
mockSimplePoll()
|
mockSimplePoll()
|
||||||
const { saveLog } = require('../saveLog')
|
const { saveLog } = require('../saveLog')
|
||||||
|
|
||||||
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
|
await pollJobState(
|
||||||
...defaultPollStrategy,
|
requestClient,
|
||||||
streamLog: true
|
mockJob,
|
||||||
})
|
false,
|
||||||
|
mockAuthConfig,
|
||||||
|
defaultPollStrategy,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
expect(saveLog).toHaveBeenCalledTimes(2)
|
expect(saveLog).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
@@ -96,10 +101,14 @@ describe('pollJobState', () => {
|
|||||||
const { getFileStream } = require('../getFileStream')
|
const { getFileStream } = require('../getFileStream')
|
||||||
const { saveLog } = require('../saveLog')
|
const { saveLog } = require('../saveLog')
|
||||||
|
|
||||||
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
|
await pollJobState(
|
||||||
...defaultPollStrategy,
|
requestClient,
|
||||||
streamLog: true
|
mockJob,
|
||||||
})
|
false,
|
||||||
|
mockAuthConfig,
|
||||||
|
defaultPollStrategy,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
expect(getFileStream).toHaveBeenCalled()
|
expect(getFileStream).toHaveBeenCalled()
|
||||||
expect(saveLog).toHaveBeenCalledTimes(2)
|
expect(saveLog).toHaveBeenCalledTimes(2)
|
||||||
@@ -111,10 +120,14 @@ describe('pollJobState', () => {
|
|||||||
const { saveLog } = require('../saveLog')
|
const { saveLog } = require('../saveLog')
|
||||||
const { getFileStream } = require('../getFileStream')
|
const { getFileStream } = require('../getFileStream')
|
||||||
|
|
||||||
await pollJobState(requestClient, mockJob, false, mockAuthConfig, {
|
await pollJobState(
|
||||||
...defaultPollStrategy,
|
requestClient,
|
||||||
streamLog: true
|
mockJob,
|
||||||
})
|
false,
|
||||||
|
mockAuthConfig,
|
||||||
|
defaultPollStrategy,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
expect(getFileStream).not.toHaveBeenCalled()
|
expect(getFileStream).not.toHaveBeenCalled()
|
||||||
expect(saveLog).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 () => {
|
it('should return the current status when the max poll count is reached', async () => {
|
||||||
mockRunningPoll()
|
mockRunningPoll()
|
||||||
|
|
||||||
const pollStrategy = {
|
const pollStrategy: PollStrategy = {
|
||||||
...defaultPollStrategy,
|
...defaultPollStrategy,
|
||||||
maxPollCount: 1
|
maxPollCount: 1,
|
||||||
|
subsequentStrategies: []
|
||||||
}
|
}
|
||||||
const pollStrategies = [pollStrategy]
|
|
||||||
|
|
||||||
const state = await pollJobState(
|
const state = await pollJobState(
|
||||||
requestClient,
|
requestClient,
|
||||||
mockJob,
|
mockJob,
|
||||||
false,
|
false,
|
||||||
mockAuthConfig,
|
mockAuthConfig,
|
||||||
pollStrategy,
|
pollStrategy
|
||||||
pollStrategies
|
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(state).toEqual('running')
|
expect(state).toEqual('running')
|
||||||
@@ -264,53 +276,32 @@ describe('pollJobState', () => {
|
|||||||
const pollIntervals = [3, 4, 5, 6]
|
const pollIntervals = [3, 4, 5, 6]
|
||||||
|
|
||||||
const pollStrategies = [
|
const pollStrategies = [
|
||||||
{ maxPollCount: 1, pollInterval: pollIntervals[0], streamLog: false },
|
{ maxPollCount: 2, pollInterval: pollIntervals[1] },
|
||||||
{ maxPollCount: 2, pollInterval: pollIntervals[1], streamLog: false },
|
{ maxPollCount: 3, pollInterval: pollIntervals[2] },
|
||||||
{ maxPollCount: 3, pollInterval: pollIntervals[2], streamLog: false },
|
{ maxPollCount: 4, pollInterval: pollIntervals[3] }
|
||||||
{ maxPollCount: 4, pollInterval: pollIntervals[3], streamLog: false }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
await pollJobState(
|
const pollStrategy: PollStrategy = {
|
||||||
requestClient,
|
maxPollCount: 1,
|
||||||
mockJob,
|
pollInterval: pollIntervals[0],
|
||||||
false,
|
subsequentStrategies: pollStrategies
|
||||||
undefined,
|
}
|
||||||
undefined,
|
|
||||||
pollStrategies
|
await pollJobState(requestClient, mockJob, false, undefined, pollStrategy)
|
||||||
)
|
|
||||||
|
|
||||||
expect(delays).toEqual([pollIntervals[0], ...pollIntervals])
|
expect(delays).toEqual([pollIntervals[0], ...pollIntervals])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw an error if not valid poll strategies provided', async () => {
|
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
|
// INFO: 'maxPollCount' has to be > 0
|
||||||
let invalidPollStrategy = {
|
let invalidPollStrategy = {
|
||||||
maxPollCount: 0,
|
maxPollCount: 0,
|
||||||
pollInterval: 3,
|
pollInterval: 3
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
`Poll strategies are not valid. 'maxPollCount' has to be greater than 0. Invalid poll strategy: \n${JSON.stringify(
|
||||||
invalidPollStrategy,
|
invalidPollStrategy,
|
||||||
null,
|
null,
|
||||||
@@ -319,27 +310,21 @@ describe('pollJobState', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
pollJobState(
|
pollJobState(requestClient, mockJob, false, undefined, {
|
||||||
requestClient,
|
...defaultPollStrategy,
|
||||||
mockJob,
|
subsequentStrategies: pollStrategies
|
||||||
false,
|
})
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
pollStrategies
|
|
||||||
)
|
|
||||||
).rejects.toThrow(expectedError)
|
).rejects.toThrow(expectedError)
|
||||||
|
|
||||||
// INFO: 'maxPollCount' has to be > than 'maxPollCount' of the previous strategy
|
// INFO: 'maxPollCount' has to be > than 'maxPollCount' of the previous strategy
|
||||||
const validPollStrategy = {
|
const validPollStrategy = {
|
||||||
maxPollCount: 5,
|
maxPollCount: 5,
|
||||||
pollInterval: 2,
|
pollInterval: 2
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidPollStrategy = {
|
invalidPollStrategy = {
|
||||||
maxPollCount: validPollStrategy.maxPollCount,
|
maxPollCount: validPollStrategy.maxPollCount,
|
||||||
pollInterval: 3,
|
pollInterval: 3
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pollStrategies = [validPollStrategy, invalidPollStrategy]
|
pollStrategies = [validPollStrategy, invalidPollStrategy]
|
||||||
@@ -353,21 +338,16 @@ describe('pollJobState', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
pollJobState(
|
pollJobState(requestClient, mockJob, false, undefined, {
|
||||||
requestClient,
|
...defaultPollStrategy,
|
||||||
mockJob,
|
subsequentStrategies: pollStrategies
|
||||||
false,
|
})
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
pollStrategies
|
|
||||||
)
|
|
||||||
).rejects.toThrow(expectedError)
|
).rejects.toThrow(expectedError)
|
||||||
|
|
||||||
// INFO: invalid 'pollInterval'
|
// INFO: invalid 'pollInterval'
|
||||||
invalidPollStrategy = {
|
invalidPollStrategy = {
|
||||||
maxPollCount: 1,
|
maxPollCount: 1,
|
||||||
pollInterval: 0,
|
pollInterval: 0
|
||||||
streamLog: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pollStrategies = [invalidPollStrategy]
|
pollStrategies = [invalidPollStrategy]
|
||||||
@@ -381,14 +361,10 @@ describe('pollJobState', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
pollJobState(
|
pollJobState(requestClient, mockJob, false, undefined, {
|
||||||
requestClient,
|
...defaultPollStrategy,
|
||||||
mockJob,
|
subsequentStrategies: pollStrategies
|
||||||
false,
|
})
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
pollStrategies
|
|
||||||
)
|
|
||||||
).rejects.toThrow(expectedError)
|
).rejects.toThrow(expectedError)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
export interface PollStrategy {
|
export interface PollStrategy {
|
||||||
maxPollCount: number
|
maxPollCount: number
|
||||||
pollInterval: number // milliseconds
|
pollInterval: number // milliseconds
|
||||||
streamLog: boolean
|
subsequentStrategies?: PollStrategy[]
|
||||||
|
streamLog?: boolean
|
||||||
logFolderPath?: string
|
logFolderPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user