fix(settings): revalidate queued credentials and handle status failures

This commit is contained in:
Danila Poyarkov 2026-09-06 16:41:50 +03:00
parent 2b4e5240e7
commit cc4dc933e8
3 changed files with 54 additions and 4 deletions

View file

@ -101,6 +101,10 @@ export function useMCPConnectionSettings(
const connection = services.save({ ...target, enabled: false })
if (target.authenticationType === 'none') await services.setCredential(connection.id, '')
else if (token.trim()) await services.setCredential(connection.id, token)
else if (target.enabled && (await services.status(connection.id)) !== 'configured') {
throw new Error(automation.value.bearerTokenRequired)
}
services.save({ ...target, id: connection.id })
})
if (!current(request)) return false

View file

@ -42,10 +42,18 @@ export function useStorageSettings(credentialDrafts: Ref<Record<string, string>>
async function refreshStatuses(): Promise<void> {
const request = ++statusRequest
const id = provider.value.id
const statuses = await storageCredentialStatuses(id)
if (!disposed && statusRequest === request && provider.value.id === id)
credentialStatuses.value = statuses
const target = provider.value
try {
const statuses = await storageCredentialStatuses(target.id)
if (!disposed && statusRequest === request && provider.value.id === target.id)
credentialStatuses.value = statuses
} catch {
if (!disposed && statusRequest === request && provider.value.id === target.id) {
credentialStatuses.value = Object.fromEntries(
target.credentialFields.map((field) => [field.id, 'unavailable' as const])
)
}
}
}
function savePreferences(): void {

View file

@ -280,3 +280,41 @@ test('reopened editors serialize writes to the same connection while other conne
secondScope.stop()
}
})
test('blank-token save rechecks status after a different editor clears the credential', async () => {
const scope = effectScope()
let configured = true
const enabled: boolean[] = []
const services = {
status: async () => (configured ? ('configured' as const) : ('missing' as const)),
setCredential: async (_id: string, value: string) => {
configured = Boolean(value)
},
save: (draft: { enabled: boolean }) => {
enabled.push(draft.enabled)
return connection
},
remove: async () => undefined
}
try {
const first = scope.run(() =>
useMCPConnectionSettings(ref(''), ref({ bearerTokenRequired: 'Required' }), services)
)
const second = scope.run(() =>
useMCPConnectionSettings(ref(''), ref({ bearerTokenRequired: 'Required' }), services)
)
if (!first || !second) throw new Error('Missing scope')
await first.startEdit(connection.id)
await second.startEdit(connection.id)
second.draft.value.authenticationType = 'bearer'
second.draft.value.enabled = true
const clearing = first.clearCredential()
const saving = second.save()
await clearing
expect(await saving).toBe(false)
expect(second.error.value).toBe('Required')
expect(enabled).toEqual([false, false])
} finally {
scope.stop()
}
})