Error handling#95
Conversation
✅ Deploy Preview for biongff-vizarr ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new error/warning handling pathway for the viewer by moving Zarr opening into a dedicated HTTP service layer, adding a consumer-provided logger, and surfacing metadata-derived warnings via snackbars; it also adds a new Vitest suite intended to cover common HTTP/metadata failures.
Changes:
- Add
viewer/src/services/http.tswith custom error classes andopenZarrRoot()to provide more structured HTTP/metadata failure handling. - Add warning detection (
getSourceDataWarnings) and notistack-based warning display inVizarrViewer, plus expose aloggerprop for consumers. - Add a new
viewer/tests/error-handling.test.tsand local fixture scaffolding for error-condition testing.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| viewer/tests/metadata.js | Updates test utility to use openZarrRoot() instead of removed utils.open(). |
| viewer/tests/error-handling.test.ts | Adds new test suite + local HTTP server for validating common HTTP/metadata error handling. |
| viewer/src/utils.ts | Removes utils.open() helper and keeps store normalization unchanged functionally. |
| viewer/src/state.ts | Removes sourceWarningAtom (warnings no longer stored in global state). |
| viewer/src/services/http.ts | New HTTP/Zarr open wrapper with custom error types and special-casing. |
| viewer/src/io.ts | Switches source opening to openZarrRoot() and tweaks an assertion string formatting. |
| viewer/src/error.ts | Simplifies user error message behavior; adds warning detection + centralized error side effects. |
| viewer/src/components/VizarrViewer.tsx | Integrates logger prop, warning snackbars, and new error handling flow. |
| viewer/src/components/Snackbar.tsx | Removes old snackbar component implementation. |
| viewer/src/components/LayerController/ChannelOptions.tsx | Removes channel-name warning emission from per-channel UI. |
| viewer/src/api.tsx | Introduces Logger interface type for the viewer API surface. |
| sites/app/src/App.tsx | Adds a console-backed logger and passes it into the Viewer component. |
| fixtures/local/only_zarr_json.zarr/zarr.json | Adds minimal local fixture intended for metadata edge-case testing. |
| fixtures/generic/c4904df5-0005-4a80-953e-62b1ede447b4.yaml | Removes an unused generic fixture entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (status === 403) { | ||
| throw new HttpError( | ||
| `403: Unauthorized to access resource at ${source}. Please check the specified URL is correct and that permission to access it is not restricted.`, | ||
| statusText, | ||
| status, | ||
| ); | ||
| } |
| if (error instanceof TypeError) { | ||
| if (error.message === MAYBE_CORS_ERROR_MESSAGE) { | ||
| throw new HttpError( | ||
| `An unknown error occurred while trying to fetch the resource ${source} from the server - this is most likely a CORs issue.`, | ||
| error.message, | ||
| ); |
| export interface VizarrViewerProps { | ||
| sources?: string[]; | ||
| viewState?: ViewState; | ||
| onViewStateChange?: (viewState: ViewState) => void; | ||
| onViewerStateChange?: (info: ViewerInfo) => void; | ||
| additionalLayers?: Layer[]; | ||
| pluginCursor?: string; | ||
| onPluginClick?: (coordinate: [number, number]) => boolean; | ||
| onPluginHover?: (coordinate: [number, number] | null) => void; | ||
| children?: React.ReactNode; | ||
| logger: Logger; | ||
| } |
| if (!sourceDataValid(results)) { | ||
| setSourceError(writeUserErrorMessage(getSourceDataError(results))); | ||
| const error = getSourceDataError(results); | ||
| setSourceError(writeUserErrorMessage(error)); | ||
| handleError(error, logger); | ||
| } |
| const warnings = getSourceDataWarnings(res.value); | ||
| warnings.map((warning: string) => { | ||
| handleWarning(warning); | ||
| }); |
| } | ||
| } | ||
|
|
||
| export async function openZarrRoot(source: string | zarr.Readable): Promise<zarr.Group<zarr.Readable<unknown>>> { |
| const store = await normalizeStore(source); | ||
| const location = await zarr.open(store, { kind: "group" }); | ||
| return location; |
| server.close(); | ||
| }); | ||
|
|
||
| beforeEach(() => { |
| server.listen(() => {}); | ||
| port = server.address().port; | ||
| server_url = `http://localhost:${port}`; | ||
| }); |
|
@davehorsfall, conflicts resolved, waiting for review/merge. |
Description
Overview of architecture/design decisions
Errors thrown from within the createSourceData function, which encompasses the majority of the internal logic of the viewer, are handled: taking the source URL and creating a source data object with all of the information necessary for deck.gl to render it. Errors occurring during the rendering of the image, e.g. from deck.gl still need to be handled.
createSourceData returns a promise, any errors raised during execution of the function are returned and then handled via the error.ts module. Currently, errors are thrown and logged. The VizarrViewer component renders the message. Where possible, custom error handlers are used to catch common errors and produce user-friendly error messages, and react-component-consumer-friendly error classes which can be used for downstream error handling (e.g. if instanceof MetadataError...)
If there are no errors, the source data is analysed for any potential warnings. These warnings are then displayed as stackable, dismiss-able toast notifications. Warnings generated in this way are also logged.
Warnings can be tested here
https://deploy-preview-95--biongff-vizarr.netlify.app/?source=https://s3.janelia.org/ahrens-lab/ruettenv/ExM/250531_foxj1a_eGFP_NPVF_mCherry_7dpf/dorsal/250531_foxj1a_egfp_npvf_mcherry_7dpf_dorsal.zarr
The design of the error handling/warning system allows plenty of flexibility in how warnings and errors are handled. The raising of errors and warnings is decoupled from the handling. Any additional side effects that need to be implemented when errors or warnings are raised can easily be added, e.g. via the handleError() function in the error.ts module.
Further work will:
Fixes # (issue)
#29
#46
#19
#78
#89
Type of change