Last active
September 6, 2024 09:00
-
-
Save acron0/d55a32cb88e1d87007c2450738938a0f to your computer and use it in GitHub Desktop.
Effect retry based on status code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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