mirror of https://github.com/grafana/grafana
AzureMonitor: display errors from requests for the dropdowns (#31921)
* AzureMonitor: display errors from requests for the dropdowns * switch to array of errors, using just the last one * unify error object * move files into utils * TESTS * fix testspull/32290/head
parent
2179a2658e
commit
9a7c10cffe
@ -0,0 +1,22 @@ |
||||
export function invalidNamespaceError() { |
||||
return { |
||||
status: 404, |
||||
statusText: 'Not Found', |
||||
data: { |
||||
error: { |
||||
code: 'InvalidResourceNamespace', |
||||
message: "The resource namespace 'grafanadev' is invalid.", |
||||
}, |
||||
}, |
||||
config: { |
||||
url: |
||||
'api/datasources/proxy/31/azuremonitor/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/grafanadev/providers/grafanadev/select/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=select', |
||||
method: 'GET', |
||||
retry: 0, |
||||
headers: { |
||||
'X-Grafana-Org-Id': 1, |
||||
}, |
||||
hideFromInspector: false, |
||||
}, |
||||
}; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
import { invalidNamespaceError } from '../__mocks__/errors'; |
||||
import messageFromError from './messageFromError'; |
||||
|
||||
describe('AzureMonitor: messageFromError', () => { |
||||
it('returns message from Error exception', () => { |
||||
const err = new Error('wowee an error'); |
||||
expect(messageFromError(err)).toBe('wowee an error'); |
||||
}); |
||||
|
||||
it('returns message from Azure API error', () => { |
||||
const err = invalidNamespaceError(); |
||||
expect(messageFromError(err)).toBe("The resource namespace 'grafanadev' is invalid."); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,33 @@ |
||||
export default function messageFromError(error: any): string | undefined { |
||||
if (!error || typeof error !== 'object') { |
||||
return undefined; |
||||
} |
||||
|
||||
if (typeof error.message === 'string') { |
||||
return error.message; |
||||
} |
||||
|
||||
if (typeof error.data?.error?.message === 'string') { |
||||
return error.data.error.message; |
||||
} |
||||
|
||||
// Copied from the old Angular code - this might be checking for errors in places
|
||||
// that the new code just doesnt use.
|
||||
// As new error objects are discovered they should be added to the above code, rather
|
||||
// than below
|
||||
const maybeAMessage = |
||||
error.error?.data?.error?.innererror?.innererror?.message || |
||||
error.error?.data?.error?.innererror?.message || |
||||
error.error?.data?.error?.message || |
||||
error.error?.data?.message || |
||||
error.data?.message || |
||||
error; |
||||
|
||||
if (typeof maybeAMessage === 'string') { |
||||
return maybeAMessage; |
||||
} else if (maybeAMessage && maybeAMessage.toString) { |
||||
return maybeAMessage.toString(); |
||||
} |
||||
|
||||
return undefined; |
||||
} |
||||
@ -0,0 +1,26 @@ |
||||
import { renderHook, act } from '@testing-library/react-hooks'; |
||||
import useLastError from './useLastError'; |
||||
|
||||
describe('AzureMonitor: useLastError', () => { |
||||
it('returns the set error', () => { |
||||
const { result } = renderHook(() => useLastError()); |
||||
|
||||
act(() => { |
||||
result.current[1]('component-a', new Error('an error')); |
||||
}); |
||||
|
||||
expect(result.current[0]).toBe('an error'); |
||||
}); |
||||
|
||||
it('returns the most recent error', () => { |
||||
const { result } = renderHook(() => useLastError()); |
||||
|
||||
act(() => { |
||||
result.current[1]('component-a', new Error('component a error')); |
||||
result.current[1]('component-b', new Error('component b error')); |
||||
result.current[1]('component-a', new Error('second component a error')); |
||||
}); |
||||
|
||||
expect(result.current[0]).toBe('second component a error'); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,38 @@ |
||||
import { useState, useCallback, useMemo } from 'react'; |
||||
import { AzureMonitorErrorish } from '../types'; |
||||
import messageFromError from './messageFromError'; |
||||
|
||||
type SourcedError = [string, AzureMonitorErrorish]; |
||||
|
||||
export default function useLastError() { |
||||
const [errors, setErrors] = useState<SourcedError[]>([]); |
||||
|
||||
// Handles errors from any child components that request data to display their options
|
||||
const addError = useCallback((errorSource: string, error: AzureMonitorErrorish | undefined) => { |
||||
setErrors((errors) => { |
||||
const errorsCopy = [...errors]; |
||||
const index = errors.findIndex(([vSource]) => vSource === errorSource); |
||||
|
||||
// If there's already an error, remove it. If we're setting a new error
|
||||
// below, we'll move it to the front
|
||||
if (index > -1) { |
||||
errorsCopy.splice(index, 1); |
||||
} |
||||
|
||||
// And then add the new error to the top of the array. If error is defined, it was already
|
||||
// removed above.
|
||||
if (error) { |
||||
errorsCopy.unshift([errorSource, error]); |
||||
} |
||||
|
||||
return errorsCopy; |
||||
}); |
||||
}, []); |
||||
|
||||
const errorMessage = useMemo(() => { |
||||
const recentError = errors[0]; |
||||
return recentError && messageFromError(recentError[1]); |
||||
}, [errors]); |
||||
|
||||
return [errorMessage, addError] as const; |
||||
} |
||||
Loading…
Reference in new issue