Skip to content

Instantly share code, notes, and snippets.

@acron0
Last active September 6, 2024 09:00
Show Gist options
  • Save acron0/d55a32cb88e1d87007c2450738938a0f to your computer and use it in GitHub Desktop.
Save acron0/d55a32cb88e1d87007c2450738938a0f to your computer and use it in GitHub Desktop.
Effect retry based on status code
import { Effect, Ref, Either } from 'effect'
import { NodeRuntime } from '@effect/platform-node'
import {
HttpClient,
HttpClientRequest,
} from '@effect/platform'
const MAX_RETRIES = 3
const BAD_TOKEN = 'bad_token'
const isBadToken = (e: Either.Either<unknown, unknown>) =>
Either.isLeft(e) && e.left == BAD_TOKEN
const testRequest = ({
message,
password
}: {
message: string
password: string
}): Effect.Effect<unknown, unknown> =>
HttpClientRequest.get(`https://postman-echo.com/basic-auth`).pipe(
HttpClientRequest.appendUrlParams({ message }),
HttpClientRequest.basicAuth('postman', password),
HttpClient.filterStatus(
HttpClient.fetch,
(status) => (status >= 200 && status < 300) || status == 401
),
Effect.andThen((res) => {
if (res.status == 401) {
return Effect.fail(BAD_TOKEN)
} else return Effect.succeed(res)
}),
Effect.tapError((e) => Effect.logError(`Request failed: ${e}`)),
Effect.andThen((res) => res.json),
Effect.scoped
)
const setPasswordRef = (ref: Ref.Ref<string>, password: string) =>
Effect.logInfo('Setting password to "password"...').pipe(
Effect.tap(() => Ref.set(ref, password)),
Effect.andThen(() => Effect.sleep(1000))
)
const incrementTriesRef = (
ref: Ref.Ref<number>,
res: Either.Either<unknown, unknown>
) =>
Ref.updateAndGet(ref, (t) => t + 1).pipe(
Effect.andThen((t) =>
Effect.gen(function* () {
if (t > MAX_RETRIES) {
yield* Effect.fail('Max retries exceeded')
}
return res
})
)
)
const testProgram = Effect.gen(function* () {
yield* Effect.logInfo('Test application running')
//
const password = yield* Ref.make('foobar')
const tries = yield* Ref.make(0)
const result = yield* Ref.get(password).pipe(
Effect.tap((p) => Effect.logInfo(`Attempting auth. Password is "${p}"`)),
Effect.andThen((password) => testRequest({ message: 'Hello', password })),
Effect.either,
Effect.andThen((e) => incrementTriesRef(tries, e)),
Effect.tap((e) =>
Effect.if(isBadToken(e), {
onTrue: () => setPasswordRef(password, 'passwordx'),
onFalse: () => Effect.void
})
),
Effect.repeat({
while: isBadToken
})
)
yield* Effect.logInfo({ result })
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment