Handle transient errors during PDS detection
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
import {getMain, XrpcResponseError} from '@atproto/lex'
|
||||||
|
import {beforeEach, describe, expect, it, jest} from '@jest/globals'
|
||||||
|
|
||||||
|
import {com} from '#/lexicons'
|
||||||
|
|
||||||
|
const mockCall = jest.fn<(...args: unknown[]) => Promise<unknown>>()
|
||||||
|
|
||||||
|
jest.mock('#/state/session/clients', () => ({
|
||||||
|
getPublicAppviewClient: () => ({call: mockCall}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
import {resolvePdsForIdentifier} from '../pds-detection'
|
||||||
|
|
||||||
|
function responseError(status: number) {
|
||||||
|
return new XrpcResponseError(
|
||||||
|
getMain(com.atproto.identity.resolveHandle),
|
||||||
|
new Response(null, {status}),
|
||||||
|
undefined,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('resolvePdsForIdentifier', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockCall.mockReset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it.each([429, 500, 502, 503])(
|
||||||
|
'surfaces a transient resolveHandle %s response as a network failure',
|
||||||
|
async status => {
|
||||||
|
mockCall.mockRejectedValueOnce(responseError(status))
|
||||||
|
|
||||||
|
await expect(resolvePdsForIdentifier('samuel.fm')).rejects.toThrow(
|
||||||
|
`Network request failed: resolveHandle returned ${status}`,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
it('treats a permanent resolveHandle 4xx response as unresolved', async () => {
|
||||||
|
mockCall.mockRejectedValueOnce(responseError(400))
|
||||||
|
|
||||||
|
await expect(resolvePdsForIdentifier('missing.test')).resolves.toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -3,12 +3,13 @@ import {type DidDocument, getPdsEndpoint} from '@atproto/common-web'
|
|||||||
import {type HandleString} from '@atproto/syntax'
|
import {type HandleString} from '@atproto/syntax'
|
||||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {DEFAULT_SERVICE, PUBLIC_BSKY_SERVICE} from '#/lib/constants'
|
import {DEFAULT_SERVICE} from '#/lib/constants'
|
||||||
import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
|
import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
|
||||||
import {createServiceClient} from '#/lib/lexClient'
|
|
||||||
import {isNetworkError} from '#/lib/strings/errors'
|
import {isNetworkError} from '#/lib/strings/errors'
|
||||||
|
import {getErrorStatus} from '#/lib/xrpc-error'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
|
import {getPublicAppviewClient} from '#/state/session/clients'
|
||||||
import {com} from '#/lexicons'
|
import {com} from '#/lexicons'
|
||||||
|
|
||||||
const RQKEY_ROOT = 'pds-detection'
|
const RQKEY_ROOT = 'pds-detection'
|
||||||
@@ -149,14 +150,7 @@ export async function resolvePdsForIdentifier(
|
|||||||
identifier: string,
|
identifier: string,
|
||||||
): Promise<{did: string; pdsUrl: string | null} | null> {
|
): Promise<{did: string; pdsUrl: string | null} | null> {
|
||||||
const norm = normalizeIdentifier(identifier)
|
const norm = normalizeIdentifier(identifier)
|
||||||
/*
|
const client = getPublicAppviewClient()
|
||||||
* This is a pre-auth request. Use an isolated service client rather than the
|
|
||||||
* shared public appview client, whose global moderation configuration is
|
|
||||||
* intended for appview content reads and can change independently of this
|
|
||||||
* login flow. This also matches the pre-auth handle resolution used during
|
|
||||||
* signup.
|
|
||||||
*/
|
|
||||||
const client = createServiceClient(PUBLIC_BSKY_SERVICE)
|
|
||||||
try {
|
try {
|
||||||
let did: string
|
let did: string
|
||||||
if (norm.startsWith('did:')) {
|
if (norm.startsWith('did:')) {
|
||||||
@@ -194,6 +188,13 @@ export async function resolvePdsForIdentifier(
|
|||||||
isNetworkError: isNetworkError(err),
|
isNetworkError: isNetworkError(err),
|
||||||
})
|
})
|
||||||
if (isNetworkError(err)) throw err
|
if (isNetworkError(err)) throw err
|
||||||
|
const status = getErrorStatus(err)
|
||||||
|
if (status !== undefined && isTransientHttpStatus(status)) {
|
||||||
|
throw new Error(
|
||||||
|
`Network request failed: resolveHandle returned ${status}`,
|
||||||
|
{cause: err},
|
||||||
|
)
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user