Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,35 @@ describe('AdditionalInitializers', () => {

expect(defaultProps.onAdd).toHaveBeenCalledWith('load_default_datasets', null)
})

it('should show a server error inside the add dialog when onAdd fails', async () => {
const user = userEvent.setup()

const props = {
...defaultProps,
registeredInitializers: [refreshInitializer],
onAdd: jest.fn().mockRejectedValue(new Error('Invalid days value.')),
}

render(
<TestWrapper>
<AdditionalInitializers {...props} />
</TestWrapper>,
)

fireEvent.change(screen.getByRole('combobox', { name: 'Initializer to add' }), {
target: { value: 'refresh_datasets' },
})
await user.click(screen.getByRole('button', { name: 'Add initializer' }))

const dialog = await screen.findByRole('dialog', {}, { timeout: 3000 })
await within(dialog).findByText('Add refresh_datasets initializer')
fireEvent.change(within(dialog).getByTestId('param-days'), { target: { value: '12' } })
await user.click(await within(dialog).findByRole('button', { name: 'Add', hidden: true }))

expect(await within(dialog).findByRole('alert', { hidden: true })).toHaveTextContent(
'Invalid days value.',
)
expect(dialog).toBeInTheDocument()
})
})
21 changes: 17 additions & 4 deletions frontend/src/components/Initializers/AdditionalInitializers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
UpdateAdditionalInitializerRequest,
} from '@/types'

import { toApiError } from '@/services/errors'
import { useAdditionalInitializersStyles } from './AdditionalInitializers.styles'
import { formatInitializerParameters, formatSupportedParameterSummary } from './initializerFormatting'
import { resolveRegisteredInitializer } from './initializerLookup'
Expand Down Expand Up @@ -153,6 +154,7 @@ export default function AdditionalInitializers({
const listStyles = useAdditionalInitializersStyles()
const [selectedInitializerName, setSelectedInitializerName] = useState('')
const [addDialogOpen, setAddDialogOpen] = useState(false)
const [addError, setAddError] = useState<string | null>(null)
const initializerName = selectedInitializerName || registeredInitializers[0]?.initializer_name || ''
const selectedInitializer = registeredInitializers.find(
(initializer) => initializer.initializer_name === initializerName,
Expand All @@ -162,9 +164,14 @@ export default function AdditionalInitializers({
if (!initializerName) {
return
}
const added = await onAdd(initializerName, parameters)
if (added) {
setAddDialogOpen(false)
setAddError(null)
try {
const added = await onAdd(initializerName, parameters)
if (added) {
setAddDialogOpen(false)
}
} catch (e) {
setAddError(toApiError(e).detail)
}
}

Expand Down Expand Up @@ -231,8 +238,14 @@ export default function AdditionalInitializers({
initializer={selectedInitializer}
initialParameters={null}
submitting={creating}
externalError={addError}
onSubmit={handleAdd}
onOpenChange={setAddDialogOpen}
onOpenChange={(open) => {
setAddDialogOpen(open)
if (!open) {
setAddError(null)
}
}}
/>
)}
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,18 @@ describe('InitializerParametersDialog', () => {
expect(screen.getByRole('button', { name: 'Add...' })).toBeDisabled()
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled()
})

it('displays an external error passed via externalError prop', () => {
render(
<TestWrapper>
<InitializerParametersDialog
{...baseProps}
initializer={numericInitializer}
externalError="Server rejected the request."
/>
</TestWrapper>,
)

expect(screen.getByRole('alert')).toHaveTextContent('Server rejected the request.')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface InitializerParametersDialogProps {
initializer: RegisteredInitializer | null
initialParameters?: Record<string, unknown> | null
submitting?: boolean
externalError?: string | null
onSubmit: (parameters: Record<string, unknown> | null) => void | Promise<void>
onOpenChange: (open: boolean) => void
}
Expand All @@ -41,6 +42,7 @@ export default function InitializerParametersDialog({
initializer,
initialParameters = null,
submitting = false,
externalError = null,
onSubmit,
onOpenChange,
}: InitializerParametersDialogProps) {
Expand Down Expand Up @@ -112,9 +114,9 @@ export default function InitializerParametersDialog({
This initializer takes no parameters.
</Text>
)}
{error && (
{(error || externalError) && (
<Text role="alert" className={styles.errorText}>
{error}
{error || externalError}
</Text>
)}
</DialogContent>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/Initializers/Initializers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ export default function Initializers() {
await refetchSettingsOnly()
return true
} catch (error) {
setStatusMessage({ intent: 'error', text: toApiError(error).detail })
return false
const detail = toApiError(error).detail
setStatusMessage({ intent: 'error', text: detail })
throw error
} finally {
setCreating(false)
}
Expand Down