Skip to content

Commit 16b5b32

Browse files
authored
Display API message on not found errors (#1935)
* display API message on not found errors * add unit test, don't fall back because API error always has message
1 parent 2aa720d commit 16b5b32

6 files changed

Lines changed: 36 additions & 7 deletions

File tree

app/test/e2e/instance-create.e2e.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,11 @@ test('add ssh key from instance create form', async ({ page }) => {
217217
await page.getByRole('link', { name: 'SSH Keys' }).click()
218218
await expectRowVisible(page.getByRole('table'), { Name: newKey, Description: 'hi' })
219219
})
220+
221+
test('shows object not found error on no default pool', async ({ page }) => {
222+
await page.goto('/projects/mock-project/instances-new')
223+
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('no-default-pool')
224+
await page.getByRole('button', { name: 'Create instance' }).click()
225+
226+
await expect(page.getByText('Not found: default IP pool')).toBeVisible()
227+
})

libs/api-mocks/msw/db.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import { internalError, json } from './util'
1919

2020
const notFoundBody = { error_code: 'ObjectNotFound' } as const
2121
export type NotFound = typeof notFoundBody
22-
export const notFoundErr = () =>
23-
json({ error_code: 'ObjectNotFound' } as const, { status: 404 })
22+
export const notFoundErr = (msg?: string) => {
23+
const message = msg ? `not found: ${msg}` : 'not found'
24+
return json({ error_code: 'ObjectNotFound', message } as const, { status: 404 })
25+
}
2426

2527
export const lookupById = <T extends { id: string }>(table: T[], id: string) => {
2628
const item = table.find((i) => i.id === id)

libs/api-mocks/msw/handlers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ export const handlers = makeHandlers({
292292
instanceCreate({ body, query }) {
293293
const project = lookup.project(query)
294294

295+
if (body.name === 'no-default-pool') {
296+
throw notFoundErr('default IP pool for current silo')
297+
}
298+
295299
errIfExists(db.instances, { name: body.name, project_id: project.id }, 'instance')
296300

297301
const instanceId = uuid()

libs/api/__tests__/errors.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,21 @@ describe('processServerError', () => {
8383
})
8484
})
8585

86+
describe('ObjectNotFound', () => {
87+
it('passes through the API error', () => {
88+
const error = makeError({
89+
errorCode: 'ObjectNotFound',
90+
message: 'not found: whatever',
91+
statusCode: 404,
92+
})
93+
expect(processServerError('fakeThingCreate', error)).toEqual({
94+
errorCode: 'ObjectNotFound',
95+
message: 'Not found: whatever',
96+
statusCode: 404,
97+
})
98+
})
99+
})
100+
86101
it('falls back to server error message if code not found', () => {
87102
const error = makeError({ errorCode: 'WeirdError', message: 'whatever' })
88103
expect(processServerError('womp', error)).toEqual({
@@ -100,6 +115,6 @@ it.each([
100115
['instanceNetworkInterfaceCreate', '', 'interface'],
101116
['instanceNetworkInterfaceCreate', 'already exists: something else', 'something else'],
102117
['doesNotContainC-reate', '', null],
103-
])('getResourceName', (method, message, resource) => {
118+
])('getResourceName: %s', (method, message, resource) => {
104119
expect(getResourceName(method, message)).toEqual(resource)
105120
})

libs/api/__tests__/hooks.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ describe('useApiQuery', () => {
130130
const error = onError.mock.lastCall?.[0]
131131
expect(error).toEqual({
132132
errorCode: 'ObjectNotFound',
133-
message: 'Object not found',
133+
message: 'Not found',
134134
statusCode: 404,
135135
})
136136
})
@@ -208,7 +208,7 @@ describe('useApiMutation', () => {
208208

209209
expect(result.current.error).toEqual({
210210
errorCode: 'ObjectNotFound',
211-
message: 'Object not found',
211+
message: 'Not found',
212212
statusCode: 404,
213213
})
214214
})

libs/api/errors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
import { camelCaseToWords, capitalize } from '@oxide/util'
99

10-
import type { ErrorResult } from '.'
10+
import type { ErrorResult } from './__generated__/Api'
1111

1212
/**
1313
* Processed API error ready for display in the console. Note that it's possible
@@ -56,7 +56,7 @@ export function processServerError(method: string, resp: ErrorResult): ApiError
5656
if (code === 'Forbidden') {
5757
message = 'Action not authorized'
5858
} else if (code === 'ObjectNotFound') {
59-
message = 'Object not found'
59+
message = capitalize(resp.data.message)
6060
} else if (code === 'ObjectAlreadyExists') {
6161
const resource = getResourceName(method, resp.data.message)
6262
if (resource) {

0 commit comments

Comments
 (0)