Skip to content

Commit

Permalink
feat(errors): retryFailures and prependErrorMessageWith
Browse files Browse the repository at this point in the history
  • Loading branch information
Vehmloewff committed Oct 26, 2023
1 parent e3ab304 commit 7df69c2
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
1 change: 1 addition & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * as colors from 'https://deno.land/[email protected]/fmt/colors.ts'
export * as base64 from 'https://deno.land/[email protected]/encoding/base64.ts'
export * as streamUtils from 'https://deno.land/[email protected]/streams/mod.ts'
export * as hexEncodingUtils from 'https://deno.land/[email protected]/encoding/hex.ts'
export * as asyncUtils from 'https://deno.land/[email protected]/async/mod.ts'

// Third party
export * as jwtCore from 'https://deno.land/x/[email protected]/mod.ts'
Expand Down
50 changes: 49 additions & 1 deletion errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { asserts } from './deps.ts'
import { bindErrorRecovery, withAsyncErrorRecovery, withErrorRecovery } from './errors.ts'
import { bindErrorRecovery, prependErrorMessageWith, retryFailures, withAsyncErrorRecovery, withErrorRecovery } from './errors.ts'

Deno.test('bindErrorRecovery works', () => {
function foo(error: boolean) {
Expand Down Expand Up @@ -60,3 +60,51 @@ Deno.test('withAsyncErrorRecovery works', async () => {
13,
)
})

Deno.test('retryFailures retries correct number of times', async () => {
let didRun = 0

await retryFailures(
() => {
didRun++

if (didRun < 10) throw new Error('Test error')
},
5,
10,
)

asserts.assertEquals(didRun, 10)
})

Deno.test('retryFailures stops retrying on success', async () => {
let didRun = 0

const result = await retryFailures(
() => {
didRun++

if (didRun < 5) throw new Error('Test error')

return 45
},
5,
10,
)

asserts.assertEquals(result, 45)
asserts.assertEquals(didRun, 5)
})

Deno.test('prependErrorMessageWith prepends error messages with', () => {
asserts.assertThrows(
() => {
const error = new Error('Test error')
prependErrorMessageWith(error, 'prepend stuff')

throw error
},
Error,
'prepend stuff Test error',
)
})
29 changes: 29 additions & 0 deletions errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { asyncUtils } from './deps.ts'

export class UserError extends Error {
code = 'USER_FAULT'
}
Expand Down Expand Up @@ -110,3 +112,30 @@ export async function withAsyncErrorRecovery<T, O>(fn: () => Promise<T>, recover
return recoverWith
}
}

/** Call `fn`. If it throws, delay for `delayTime`, then call it again, up to `retryCount` times. */
export async function retryFailures<T>(fn: () => Promise<T> | T, delayTime = 3000, retryCount = 3): Promise<T> {
let triedTime = 0
let lastError: unknown

while (triedTime <= retryCount) {
triedTime++

try {
return await fn()
} catch (error) {
lastError = error
await asyncUtils.delay(delayTime)
}
}

prependErrorMessageWith(lastError, `After ${retryCount} retries, the error remains:`)

throw lastError
}

/** Modifies the `message` field of `error` to be prepended with `prepend` */
export function prependErrorMessageWith(error: unknown, prepend: string): void {
// @ts-ignore we want to append some info to the error message if it exists
if (error.message) error.message = `${prepend} ${error.message}`
}

0 comments on commit 7df69c2

Please sign in to comment.