From 0835f26e2541e90cd965de08ae80f01993f247ee Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 26 Nov 2020 14:04:52 +0300 Subject: [PATCH] Added handling of the case when a shared files are no longer available --- cvat-canvas/README.md | 1 + cvat-canvas/src/typescript/canvasModel.ts | 9 ++++++++ cvat-canvas/src/typescript/canvasView.ts | 8 +++++++ cvat-core/src/download.worker.js | 4 +++- cvat-core/src/server-proxy.js | 4 +++- cvat-ui/src/actions/annotation-actions.ts | 21 ++++++++++++++++++- .../standard-workspace/canvas-wrapper.tsx | 9 ++++++++ .../standard-workspace/canvas-wrapper.tsx | 5 +++++ cvat-ui/src/reducers/annotation-reducer.ts | 12 +++++++++++ cvat-ui/src/reducers/notifications-reducer.ts | 15 +++++++++++++ 10 files changed, 85 insertions(+), 3 deletions(-) diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index e12a764f..b4a74285 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -180,6 +180,7 @@ Standard JS events are used. - canvas.dragshape => {id: number} - canvas.resizeshape => {id: number} - canvas.contextmenu => { mouseEvent: MouseEvent, objectState: ObjectState, pointID: number } + - canvas.error => { exception: Error } ``` ### WEB diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 4521c570..ad6a7af2 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -130,6 +130,7 @@ export enum UpdateReasons { DRAG_CANVAS = 'drag_canvas', ZOOM_CANVAS = 'zoom_canvas', CONFIG_UPDATED = 'config_updated', + DATA_FAILED = 'data_failed', } export enum Mode { @@ -163,6 +164,7 @@ export interface CanvasModel { readonly selected: any; geometry: Geometry; mode: Mode; + exception: Error | null; zoom(x: number, y: number, direction: number): void; move(topOffset: number, leftOffset: number): void; @@ -216,6 +218,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { splitData: SplitData; selected: any; mode: Mode; + exception: Error | null; }; public constructor() { @@ -275,6 +278,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { }, selected: null, mode: Mode.IDLE, + exception: null, }; } @@ -389,6 +393,8 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { this.notify(UpdateReasons.OBJECTS_UPDATED); }) .catch((exception: any): void => { + this.data.exception = exception; + this.notify(UpdateReasons.DATA_FAILED); throw exception; }); } @@ -709,4 +715,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { public get mode(): Mode { return this.data.mode; } + public get exception(): Error { + return this.data.exception; + } } diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 8d8e4177..9235dbab 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1172,6 +1172,14 @@ export class CanvasViewImpl implements CanvasView, Listener { this.mode = Mode.IDLE; this.canvas.style.cursor = ''; } + else if (reason === UpdateReasons.DATA_FAILED) { + const event: CustomEvent = new CustomEvent('canvas.error', { + detail: { + exception: model.exception, + }, + }); + this.canvas.dispatchEvent(event); + } if (model.imageBitmap && [UpdateReasons.IMAGE_CHANGED, UpdateReasons.OBJECTS_UPDATED].includes(reason)) { this.redrawBitmap(); diff --git a/cvat-core/src/download.worker.js b/cvat-core/src/download.worker.js index 027ba87a..35d899d2 100644 --- a/cvat-core/src/download.worker.js +++ b/cvat-core/src/download.worker.js @@ -20,7 +20,9 @@ onmessage = (e) => { .catch((error) => { postMessage({ id: e.data.id, - error, + error: error, + status: error.response.status, + responseData: error.response.data, isSuccess: false, }); }); diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js index aaea4dce..2caa4e37 100644 --- a/cvat-core/src/server-proxy.js +++ b/cvat-core/src/server-proxy.js @@ -31,7 +31,7 @@ if (e.data.isSuccess) { requests[e.data.id].resolve(e.data.responseData); } else { - requests[e.data.id].reject(e.data.error); + requests[e.data.id].reject({ error: e.data.error, response: { status: e.data.status, data: e.data.responseData } }); } delete requests[e.data.id]; @@ -566,6 +566,8 @@ }, ); } catch (errorData) { + errorData.message = ""; + errorData.response.data = String.fromCharCode.apply(null, new Uint8Array(errorData.response.data)); throw generateError(errorData); } diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index f864bcac..db22e006 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -185,6 +185,7 @@ export enum AnnotationActionTypes { SAVE_LOGS_FAILED = 'SAVE_LOGS_FAILED', INTERACT_WITH_CANVAS = 'INTERACT_WITH_CANVAS', SET_AI_TOOLS_REF = 'SET_AI_TOOLS_REF', + GET_DATA_FAILED = 'GET_DATA_FAILED', } export function saveLogsAsync(): ThunkAction { @@ -215,6 +216,15 @@ export function changeWorkspace(workspace: Workspace): AnyAction { }; } +export function getDataFailed(error: any): AnyAction { + return { + type: AnnotationActionTypes.GET_DATA_FAILED, + payload: { + error: error, + }, + }; +} + export function addZLayer(): AnyAction { return { type: AnnotationActionTypes.ADD_Z_LAYER, @@ -930,7 +940,16 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init const frameData = await job.frames.get(frameNumber); // call first getting of frame data before rendering interface // to load and decode first chunk - await frameData.data(); + try{ + await frameData.data(); + } catch(error){ + dispatch({ + type: AnnotationActionTypes.GET_DATA_FAILED, + payload: { + error, + }, + }); + } const states = await job.annotations.get(frameNumber, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); const colors = [...cvat.enums.colors]; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx index d760411a..69a5b530 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -87,6 +87,7 @@ interface Props { onSwitchGrid(enabled: boolean): void; onSwitchAutomaticBordering(enabled: boolean): void; onFetchAnnotation(): void; + onGetDataFailed(error: any): void; } export default class CanvasWrapperComponent extends React.PureComponent { @@ -299,10 +300,17 @@ export default class CanvasWrapperComponent extends React.PureComponent { canvasInstance.html().removeEventListener('canvas.splitted', this.onCanvasTrackSplitted); canvasInstance.html().removeEventListener('canvas.contextmenu', this.onCanvasPointContextMenu); + canvasInstance.html().removeEventListener('canvas.error', this.onCanvasErrorOccurrence); window.removeEventListener('resize', this.fitCanvas); } + private onCanvasErrorOccurrence = (event: any): void => { + const { exception } = event.detail; + const { onGetDataFailed } = this.props; + onGetDataFailed(exception); + } + private onCanvasShapeDrawn = (event: any): void => { const { jobInstance, activeLabelID, activeObjectType, frame, onShapeDrawn, onCreateAnnotations } = this.props; @@ -682,6 +690,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { canvasInstance.html().addEventListener('canvas.splitted', this.onCanvasTrackSplitted); canvasInstance.html().addEventListener('canvas.contextmenu', this.onCanvasPointContextMenu); + canvasInstance.html().addEventListener('canvas.error', this.onCanvasErrorOccurrence); } public render(): JSX.Element { diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx index 817ef4be..ba0436aa 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -27,6 +27,7 @@ import { addZLayer, switchZLayer, fetchAnnotationsAsync, + getDataFailed, } from 'actions/annotation-actions'; import { switchGrid, @@ -119,6 +120,7 @@ interface DispatchToProps { onSwitchGrid(enabled: boolean): void; onSwitchAutomaticBordering(enabled: boolean): void; onFetchAnnotation(): void; + onGetDataFailed(error: any): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -298,6 +300,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { onFetchAnnotation(): void { dispatch(fetchAnnotationsAsync()); }, + onGetDataFailed(error: any): void { + dispatch(getDataFailed(error)); + }, }; } diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index 310767bf..626797b0 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -177,6 +177,18 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }, }; } + case AnnotationActionTypes.GET_DATA_FAILED: { + return { + ...state, + player: { + ...state.player, + frame: { + ...state.player.frame, + fetching: false, + }, + }, + } + } case AnnotationActionTypes.CHANGE_FRAME: { return { ...state, diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 54e931db..f0649d59 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -936,6 +936,21 @@ export default function (state = defaultState, action: AnyAction): Notifications }, }; } + case AnnotationActionTypes.GET_DATA_FAILED: { + return { + ...state, + errors: { + ...state.errors, + annotation: { + ...state.errors.annotation, + jobFetching: { + message: 'Could not fetch frame data from the server', + reason: action.payload.error, + }, + }, + }, + }; + } case BoundariesActionTypes.RESET_AFTER_ERROR: case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState };