From b723c8beac3b3dae89f3e22a9c3ece37cd5465d7 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Mon, 21 Oct 2024 17:23:30 +0300 Subject: [PATCH 1/5] initial implementation --- cvat-core/src/request.ts | 13 ++++++-- cvat-core/src/requests-manager.ts | 12 +++++-- cvat-core/src/server-response-types.ts | 5 +-- cvat-ui/src/actions/export-actions.ts | 9 ++++-- cvat-ui/src/actions/requests-actions.ts | 43 ++++++++++++++++++++++--- cvat-ui/src/cvat-core-wrapper.ts | 4 ++- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/cvat-core/src/request.ts b/cvat-core/src/request.ts index 66ae49b4c96b..524b21610dc2 100644 --- a/cvat-core/src/request.ts +++ b/cvat-core/src/request.ts @@ -6,16 +6,22 @@ import { RQStatus } from './enums'; import User from './user'; import { SerializedRequest } from './server-response-types'; -type Operation = { +export type RequestOperation = { target: string; type: string; - format: string; + format: string | null; jobID: number | null; taskID: number | null; projectID: number | null; functionID: string | null; }; +export type RequestInitialData = { + operation: RequestOperation; + createdDate: string; + owner: any; +}; + export class Request { #id: string; #status: RQStatus; @@ -44,6 +50,7 @@ export class Request { this.#finishedDate = initialData.finished_date; this.#expiryDate = initialData.expiry_date; + this.#owner = null; if (initialData.owner) { this.#owner = new User(initialData.owner); } @@ -65,7 +72,7 @@ export class Request { return this.#message; } - get operation(): Operation { + get operation(): RequestOperation { return { target: this.#operation.target, type: this.#operation.type, diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index 429c42dba2f3..3281ec3ceed3 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -4,9 +4,11 @@ import serverProxy from './server-proxy'; import { RQStatus } from './enums'; -import { Request } from './request'; +import { Request, RequestInitialData } from './request'; +import { SerializedRequest } from './server-response-types'; import { RequestError } from './exceptions'; import { PaginatedResource } from './core-types'; +import { fieldsToSnakeCase } from './common'; import config from './config'; const REQUESTS_COUNT = 5; @@ -56,6 +58,7 @@ class RequestsManager { requestID: string, options: { callback: (request: Request) => void, + initialData: RequestInitialData, initialRequest?: Request, }, ): Promise { @@ -91,6 +94,7 @@ class RequestsManager { try { const serializedRequest = await serverProxy.requests.status(requestID); + throw Error('network error'); if (requestID in this.listening) { const request = new Request({ ...serializedRequest }); const { status } = request; @@ -123,12 +127,16 @@ class RequestsManager { } catch (error) { if (requestID in this.listening) { const { onUpdate } = this.listening[requestID]; - + const { initialData } = options; onUpdate .forEach((update) => update(new Request({ id: requestID, status: RQStatus.FAILED, message: `Could not get a status of the request ${requestID}. ${error.toString()}`, + ...{ + ...initialData, + operation: fieldsToSnakeCase(initialData.operation) as SerializedRequest['operation'], + }, }))); reject(error); } diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index 5dd8cc3d54d2..eefdd5ab09b2 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -505,13 +505,14 @@ export interface SerializedAPISchema { export interface SerializedRequest { id?: string; status: string; - operation?: { + operation: { target: string; type: string; - format: string; + format: string | null; job_id: number | null; task_id: number | null; project_id: number | null; + function_id: string | null; }; progress?: number; message: string; diff --git a/cvat-ui/src/actions/export-actions.ts b/cvat-ui/src/actions/export-actions.ts index db59a6315c6a..65ac4c62e234 100644 --- a/cvat-ui/src/actions/export-actions.ts +++ b/cvat-ui/src/actions/export-actions.ts @@ -8,7 +8,7 @@ import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { Storage, ProjectOrTaskOrJob, Job } from 'cvat-core-wrapper'; import { getInstanceType, RequestInstanceType, listen, RequestsActions, - shouldListenForProgress, + shouldListenForProgress, generateInitialRequestData, } from './requests-actions'; export enum ExportActionTypes { @@ -141,7 +141,12 @@ export async function listenExportBackupAsync( const instanceType = getInstanceType(instance) as 'project' | 'task'; try { - const result = await listen(rqID, dispatch); + const result = await listen(rqID, dispatch, { + initialData: generateInitialRequestData({ + target: instanceType, + type: 'export:backup', + }), + }); const target = !result?.url ? 'cloudstorage' : 'local'; dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); } catch (error) { diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index 6a62e7cecf7c..9984a33144a9 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -2,13 +2,22 @@ // // SPDX-License-Identifier: MIT +import { Store } from 'redux'; import { ActionUnion, createAction } from 'utils/redux'; -import { RequestsQuery, RequestsState } from 'reducers'; +import { CombinedState, RequestsQuery, RequestsState } from 'reducers'; import { - Request, ProjectOrTaskOrJob, getCore, RQStatus, + Request, ProjectOrTaskOrJob, getCore, RQStatus, RequestOperation, RequestInitialData, } from 'cvat-core-wrapper'; +import { getCVATStore } from 'cvat-store'; const core = getCore(); +let store: null | Store = null; +function getStore(): Store { + if (store === null) { + store = getCVATStore(); + } + return store; +} export enum RequestsActionsTypes { GET_REQUESTS = 'GET_REQUESTS', @@ -86,16 +95,42 @@ export function shouldListenForProgress(rqID: string | undefined, state: Request ); } +export function generateInitialRequestData( + operation: Partial & Pick, +): RequestInitialData { + const { + target, type, format, jobID, taskID, projectID, functionID, + } = operation; + const { user } = getStore().getState().auth; + const requestOperation = { + target, + type, + format: format ?? null, + jobID: jobID ?? null, + taskID: taskID ?? null, + projectID: projectID ?? null, + functionID: functionID ?? null, + }; + return { + operation: requestOperation, + createdDate: new Date().toISOString(), + owner: user, + }; +} + export function listen( requestID: string, dispatch: (action: RequestsActions) => void, - initialRequest?: Request, + options: { + initialRequest?: Request, + initialData: RequestInitialData, + }, ) : Promise { return core.requests .listen(requestID, { callback: (updatedRequest) => { updateRequestProgress(updatedRequest, dispatch); }, - initialRequest, + ...options, }); } diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index dac86011953a..0762b8ac7fab 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -41,7 +41,7 @@ import { Dumper } from 'cvat-core/src/annotation-formats'; import { Event } from 'cvat-core/src/event'; import { APIWrapperEnterOptions } from 'cvat-core/src/plugins'; import BaseSingleFrameAction, { ActionParameterType, FrameSelectionType } from 'cvat-core/src/annotations-actions'; -import { Request } from 'cvat-core/src/request'; +import { Request, RequestOperation, RequestInitialData } from 'cvat-core/src/request'; const cvat: CVATCore = _cvat; @@ -119,4 +119,6 @@ export type { CVATCore, SerializedAPISchema, ProjectOrTaskOrJob, + RequestOperation, + RequestInitialData, }; From daa90301d96929f9a932d15722882cb855885543 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Tue, 22 Oct 2024 12:00:22 +0300 Subject: [PATCH 2/5] improved fix --- cvat-core/src/requests-manager.ts | 17 ++++----- cvat-core/src/session-implementation.ts | 1 + cvat-core/src/session.ts | 5 ++- cvat-ui/src/actions/export-actions.ts | 13 +++++-- cvat-ui/src/actions/import-actions.ts | 30 ++++++++++++--- cvat-ui/src/actions/requests-actions.ts | 37 ++++++++++--------- cvat-ui/src/actions/requests-async-actions.ts | 11 +++++- cvat-ui/src/actions/tasks-actions.ts | 9 ++++- .../components/requests-page/request-card.tsx | 8 +++- cvat-ui/src/cvat-core-wrapper.ts | 8 ++-- 10 files changed, 95 insertions(+), 44 deletions(-) diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index 3281ec3ceed3..77936570a4fc 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -4,7 +4,7 @@ import serverProxy from './server-proxy'; import { RQStatus } from './enums'; -import { Request, RequestInitialData } from './request'; +import { Request } from './request'; import { SerializedRequest } from './server-response-types'; import { RequestError } from './exceptions'; import { PaginatedResource } from './core-types'; @@ -58,15 +58,14 @@ class RequestsManager { requestID: string, options: { callback: (request: Request) => void, - initialData: RequestInitialData, - initialRequest?: Request, + initialRequest: Request, }, ): Promise { if (!requestID) { throw new Error('Request id is not provided'); } const callback = options?.callback; - const initialRequest = options?.initialRequest; + const { initialRequest } = options; if (requestID in this.listening) { if (callback) { @@ -94,7 +93,6 @@ class RequestsManager { try { const serializedRequest = await serverProxy.requests.status(requestID); - throw Error('network error'); if (requestID in this.listening) { const request = new Request({ ...serializedRequest }); const { status } = request; @@ -127,15 +125,16 @@ class RequestsManager { } catch (error) { if (requestID in this.listening) { const { onUpdate } = this.listening[requestID]; - const { initialData } = options; onUpdate .forEach((update) => update(new Request({ id: requestID, status: RQStatus.FAILED, message: `Could not get a status of the request ${requestID}. ${error.toString()}`, - ...{ - ...initialData, - operation: fieldsToSnakeCase(initialData.operation) as SerializedRequest['operation'], + operation: fieldsToSnakeCase(initialRequest.operation) as SerializedRequest['operation'], + created_date: initialRequest.createdDate, + owner: { + id: initialRequest.owner.id, + username: initialRequest.owner.username, }, }))); reject(error); diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index 38728a409448..2b008f8783b1 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -763,6 +763,7 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { serverProxy.tasks.delete(taskID, config.organization.organizationSlug || null); } }, + initialRequest: options?.getInitialRequest(taskID), }); const [task] = await serverProxy.tasks.get({ id: taskID }); diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index cf82aa9a050c..5a9f71024d4f 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -1141,7 +1141,10 @@ export class Task extends Session { async save( fields: Record = {}, - options?: { requestStatusCallback?: (request: Request) => void }, + options?: { + requestStatusCallback?: (request: Request) => void, + getInitialRequest?: (taskID: number) => Request, + }, ): Promise { const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.save, fields, options); return result; diff --git a/cvat-ui/src/actions/export-actions.ts b/cvat-ui/src/actions/export-actions.ts index 65ac4c62e234..90d6d6dd96e2 100644 --- a/cvat-ui/src/actions/export-actions.ts +++ b/cvat-ui/src/actions/export-actions.ts @@ -8,7 +8,7 @@ import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { Storage, ProjectOrTaskOrJob, Job } from 'cvat-core-wrapper'; import { getInstanceType, RequestInstanceType, listen, RequestsActions, - shouldListenForProgress, generateInitialRequestData, + shouldListenForProgress, generateInitialRequest, } from './requests-actions'; export enum ExportActionTypes { @@ -89,7 +89,13 @@ export async function listenExportDatasetAsync( const instanceType = getInstanceType(instance); try { - const result = await listen(rqID, dispatch); + const result = await listen(rqID, dispatch, { + initialRequest: generateInitialRequest({ + target: instanceType, + type: `export:${resource}`, + instance, + }), + }); const target = !result?.url ? 'cloudstorage' : 'local'; dispatch(exportActions.exportDatasetSuccess( instance, instanceType, format, resource, target, @@ -142,9 +148,10 @@ export async function listenExportBackupAsync( try { const result = await listen(rqID, dispatch, { - initialData: generateInitialRequestData({ + initialRequest: generateInitialRequest({ target: instanceType, type: 'export:backup', + instance, }), }); const target = !result?.url ? 'cloudstorage' : 'local'; diff --git a/cvat-ui/src/actions/import-actions.ts b/cvat-ui/src/actions/import-actions.ts index e47db0b47818..ca69af335b5e 100644 --- a/cvat-ui/src/actions/import-actions.ts +++ b/cvat-ui/src/actions/import-actions.ts @@ -12,7 +12,7 @@ import { getProjectsAsync } from './projects-actions'; import { AnnotationActionTypes, fetchAnnotationsAsync } from './annotation-actions'; import { getInstanceType, listen, RequestInstanceType, RequestsActions, - shouldListenForProgress, + shouldListenForProgress, generateInitialRequest, } from './requests-actions'; const core = getCore(); @@ -81,7 +81,13 @@ export async function listenImportDatasetAsync( const instanceType = getInstanceType(instance); const resource = instanceType === 'project' ? 'dataset' : 'annotation'; try { - await listen(rqID, dispatch); + await listen(rqID, dispatch, { + initialRequest: generateInitialRequest({ + target: instanceType, + type: `import:${resource}`, + instance, + }), + }); dispatch(importActions.importDatasetSuccess(instance, resource)); } catch (error) { dispatch(importActions.importDatasetFailed(instance, resource, error)); @@ -102,6 +108,13 @@ export const importDatasetAsync = ( try { const state: CombinedState = getState(); + const listenForImport = (rqID: string) => listen(rqID, dispatch, { + initialRequest: generateInitialRequest({ + target: instanceType, + type: `import:${resource}`, + instance, + }), + }); if (instanceType === 'project') { dispatch(importActions.importDataset(instance, format)); @@ -115,7 +128,7 @@ export const importDatasetAsync = ( ), }); if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); + await listenForImport(rqID); } } else if (instanceType === 'task') { dispatch(importActions.importDataset(instance, format)); @@ -124,7 +137,7 @@ export const importDatasetAsync = ( convMaskToPoly, }); if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); + await listenForImport(rqID); } } else { // job dispatch(importActions.importDataset(instance, format)); @@ -133,7 +146,7 @@ export const importDatasetAsync = ( convMaskToPoly, }); if (shouldListenForProgress(rqID, state.requests)) { - await listen(rqID, dispatch); + await listenForImport(rqID); await (instance as Job).annotations.clear({ reload: true }); await (instance as Job).actions.clear(); @@ -173,7 +186,12 @@ export async function listenImportBackupAsync( const { instanceType } = params; try { - const result = await listen(rqID, dispatch); + const result = await listen(rqID, dispatch, { + initialRequest: generateInitialRequest({ + target: instanceType, + type: 'import:backup', + }), + }); dispatch(importActions.importBackupSuccess(result?.resultID, instanceType)); } catch (error) { diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index 9984a33144a9..39e87f338179 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -6,7 +6,7 @@ import { Store } from 'redux'; import { ActionUnion, createAction } from 'utils/redux'; import { CombinedState, RequestsQuery, RequestsState } from 'reducers'; import { - Request, ProjectOrTaskOrJob, getCore, RQStatus, RequestOperation, RequestInitialData, + Request, ProjectOrTaskOrJob, getCore, RQStatus, RequestOperation, fieldsToSnakeCase, } from 'cvat-core-wrapper'; import { getCVATStore } from 'cvat-store'; @@ -88,42 +88,45 @@ export function updateRequestProgress(request: Request, dispatch: (action: Reque ); } -export function shouldListenForProgress(rqID: string | undefined, state: RequestsState): boolean { +export function shouldListenForProgress(rqID: string | void, state: RequestsState): boolean { return ( typeof rqID === 'string' && (!state.requests[rqID] || [RQStatus.FINISHED, RQStatus.FAILED].includes(state.requests[rqID]?.status)) ); } -export function generateInitialRequestData( - operation: Partial & Pick, -): RequestInitialData { +export function generateInitialRequest( + initialData: Partial & + Pick & + { instance?: ProjectOrTaskOrJob | RequestInstanceType }, +): Request { const { - target, type, format, jobID, taskID, projectID, functionID, - } = operation; + target, type, format, instance, + } = initialData; const { user } = getStore().getState().auth; const requestOperation = { target, type, format: format ?? null, - jobID: jobID ?? null, - taskID: taskID ?? null, - projectID: projectID ?? null, - functionID: functionID ?? null, + jobID: instance && target === 'job' ? instance.id : null, + taskID: instance && target === 'task' ? instance.id : null, + projectID: instance && target === 'project' ? instance.id : null, + functionID: null, }; - return { - operation: requestOperation, - createdDate: new Date().toISOString(), + return new Request({ + status: RQStatus.QUEUED, + operation: fieldsToSnakeCase(requestOperation) as any, + created_date: new Date().toISOString(), + message: 'Status request sent', owner: user, - }; + }); } export function listen( requestID: string, dispatch: (action: RequestsActions) => void, options: { - initialRequest?: Request, - initialData: RequestInitialData, + initialRequest: Request, }, ) : Promise { return core.requests diff --git a/cvat-ui/src/actions/requests-async-actions.ts b/cvat-ui/src/actions/requests-async-actions.ts index 04a5ffd0a5c5..317fd7559301 100644 --- a/cvat-ui/src/actions/requests-async-actions.ts +++ b/cvat-ui/src/actions/requests-async-actions.ts @@ -8,7 +8,9 @@ import { getCore, RQStatus, Request, Project, Task, Job, } from 'cvat-core-wrapper'; import { listenExportBackupAsync, listenExportDatasetAsync } from './export-actions'; -import { RequestInstanceType, listen, requestsActions } from './requests-actions'; +import { + RequestInstanceType, generateInitialRequest, listen, requestsActions, +} from './requests-actions'; import { listenImportBackupAsync, listenImportDatasetAsync } from './import-actions'; const core = getCore(); @@ -76,7 +78,12 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { } } else if (operationType === 'create') { if (operationTarget === 'task') { - listen(rqID, dispatch); + listen(rqID, dispatch, { + initialRequest: generateInitialRequest({ + target: operationTarget, + type: 'create:task', + }), + }); } } }); diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 60e4da022ef2..872a48b87879 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -13,7 +13,7 @@ import { ThunkDispatch, ThunkAction } from 'utils/redux'; import { ValidationMode } from 'components/create-task-page/quality-configuration-form'; import { getInferenceStatusAsync } from './models-actions'; -import { updateRequestProgress } from './requests-actions'; +import { generateInitialRequest, updateRequestProgress } from './requests-actions'; const cvat = getCore(); @@ -293,6 +293,13 @@ ThunkAction { onProgress?.(`${message} ${progress ? `${Math.floor(progress * 100)}%` : ''}. ${helperMessage}`); if (request.id) updateRequestProgress(request, dispatch); }, + getInitialRequest(taskID) { + return generateInitialRequest({ + target: 'task', + type: 'create:task', + instance: { id: taskID, type: 'task' }, + }); + }, }); dispatch(updateTaskInState(savedTask)); diff --git a/cvat-ui/src/components/requests-page/request-card.tsx b/cvat-ui/src/components/requests-page/request-card.tsx index 980af114563a..52c109e3822c 100644 --- a/cvat-ui/src/components/requests-page/request-card.tsx +++ b/cvat-ui/src/components/requests-page/request-card.tsx @@ -100,11 +100,15 @@ function constructTimestamps(request: Request): JSX.Element { ); } case RQStatus.FAILED: { - return ( + return (request.startedDate ? ( {`Started by ${request.owner.username} on ${started}`} - ); + ) : ( + + {`Enqueued by ${request.owner.username} on ${created}`} + + )); } case RQStatus.STARTED: { return ( diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index 0762b8ac7fab..c109b3fd3472 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -15,7 +15,7 @@ import { Label, Attribute, } from 'cvat-core/src/labels'; import { - SerializedAttribute, SerializedLabel, SerializedAPISchema, + SerializedAttribute, SerializedLabel, SerializedAPISchema, SerializedRequest, } from 'cvat-core/src/server-response-types'; import { Job, Task } from 'cvat-core/src/session'; import Project from 'cvat-core/src/project'; @@ -29,6 +29,7 @@ import { ModelReturnType, DimensionType, JobType, JobStage, JobState, RQStatus, } from 'cvat-core/src/enums'; +import { fieldsToSnakeCase } from 'cvat-core/src/common'; import { Storage, StorageData } from 'cvat-core/src/storage'; import Issue from 'cvat-core/src/issue'; import Comment from 'cvat-core/src/comment'; @@ -41,7 +42,7 @@ import { Dumper } from 'cvat-core/src/annotation-formats'; import { Event } from 'cvat-core/src/event'; import { APIWrapperEnterOptions } from 'cvat-core/src/plugins'; import BaseSingleFrameAction, { ActionParameterType, FrameSelectionType } from 'cvat-core/src/annotations-actions'; -import { Request, RequestOperation, RequestInitialData } from 'cvat-core/src/request'; +import { Request, RequestOperation } from 'cvat-core/src/request'; const cvat: CVATCore = _cvat; @@ -106,6 +107,7 @@ export { ActionParameterType, FrameSelectionType, Request, + fieldsToSnakeCase, ValidationLayout, }; @@ -120,5 +122,5 @@ export type { SerializedAPISchema, ProjectOrTaskOrJob, RequestOperation, - RequestInitialData, + SerializedRequest, }; From c3b468cfb3b9be96c3ec808f264401ef7d8e9db5 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Tue, 22 Oct 2024 12:20:35 +0300 Subject: [PATCH 3/5] changelog & package ver --- .../20241022_121638_klakhov_fix_request_status_crush.md | 4 ++++ cvat-core/package.json | 2 +- cvat-core/src/request.ts | 6 ------ cvat-ui/package.json | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 changelog.d/20241022_121638_klakhov_fix_request_status_crush.md diff --git a/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md b/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md new file mode 100644 index 000000000000..082b59a70d4f --- /dev/null +++ b/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md @@ -0,0 +1,4 @@ +### Fixed + +- Requests page crush with `Cannot read property 'target' of undefined` error + () diff --git a/cvat-core/package.json b/cvat-core/package.json index 782d74c15b65..a769b74bf78c 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "15.2.0", + "version": "15.2.1", "type": "module", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "src/api.ts", diff --git a/cvat-core/src/request.ts b/cvat-core/src/request.ts index 524b21610dc2..eebcec756e30 100644 --- a/cvat-core/src/request.ts +++ b/cvat-core/src/request.ts @@ -16,12 +16,6 @@ export type RequestOperation = { functionID: string | null; }; -export type RequestInitialData = { - operation: RequestOperation; - createdDate: string; - owner: any; -}; - export class Request { #id: string; #status: RQStatus; diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 81b392eb7e54..2c43904a3fb9 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.66.1", + "version": "1.66.2", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From 58c5354949e498869e26da2fd4b49402250c2be1 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Fri, 25 Oct 2024 09:43:20 +0300 Subject: [PATCH 4/5] applied comments --- cvat-core/src/core-types.ts | 10 +++- cvat-core/src/request.ts | 47 +++++++++++++++---- cvat-core/src/requests-manager.ts | 28 +++++------ cvat-core/src/server-proxy.ts | 46 ++++++++++++------ cvat-core/src/server-response-types.ts | 8 ++-- cvat-core/src/session-implementation.ts | 5 +- cvat-core/src/session.ts | 6 +-- cvat-ui/src/actions/export-actions.ts | 18 ++----- cvat-ui/src/actions/import-actions.ts | 30 +++--------- cvat-ui/src/actions/requests-actions.ts | 37 ++------------- cvat-ui/src/actions/requests-async-actions.ts | 11 ++--- cvat-ui/src/actions/tasks-actions.ts | 19 +++----- cvat-ui/src/cvat-core-wrapper.ts | 2 + 13 files changed, 120 insertions(+), 147 deletions(-) diff --git a/cvat-core/src/core-types.ts b/cvat-core/src/core-types.ts index e44a354cb5bd..c05b7b6ba4a5 100644 --- a/cvat-core/src/core-types.ts +++ b/cvat-core/src/core-types.ts @@ -2,7 +2,9 @@ // // SPDX-License-Identifier: MIT -import { ModelKind, ModelReturnType, ShapeType } from './enums'; +import { + ModelKind, ModelReturnType, RQStatus, ShapeType, +} from './enums'; export interface ModelAttribute { name: string; @@ -54,4 +56,10 @@ export interface SerializedModel { updated_date?: string; } +export interface UpdateStatusData { + status: RQStatus; + progress: number; + message: string; +} + export type PaginatedResource = T[] & { count: number }; diff --git a/cvat-core/src/request.ts b/cvat-core/src/request.ts index eebcec756e30..1935f78b2f0c 100644 --- a/cvat-core/src/request.ts +++ b/cvat-core/src/request.ts @@ -44,10 +44,7 @@ export class Request { this.#finishedDate = initialData.finished_date; this.#expiryDate = initialData.expiry_date; - this.#owner = null; - if (initialData.owner) { - this.#owner = new User(initialData.owner); - } + this.#owner = new User(initialData.owner); } get id(): string { @@ -58,7 +55,7 @@ export class Request { return this.#status.toLowerCase() as RQStatus; } - get progress(): number { + get progress(): number | undefined { return this.#progress; } @@ -78,11 +75,11 @@ export class Request { }; } - get url(): string { + get url(): string | undefined { return this.#resultUrl; } - get resultID(): number { + get resultID(): number | undefined { return this.#resultID; } @@ -90,19 +87,49 @@ export class Request { return this.#createdDate; } - get startedDate(): string { + get startedDate(): string | undefined { return this.#startedDate; } - get finishedDate(): string { + get finishedDate(): string | undefined { return this.#finishedDate; } - get expiryDate(): string { + get expiryDate(): string | undefined { return this.#expiryDate; } get owner(): User { return this.#owner; } + + public toJSON(): SerializedRequest { + const result: SerializedRequest = { + id: this.#id, + status: this.#status, + operation: { + target: this.#operation.target, + type: this.#operation.type, + format: this.#operation.format, + job_id: this.#operation.job_id, + task_id: this.#operation.task_id, + project_id: this.#operation.project_id, + function_id: this.#operation.function_id, + }, + progress: this.#progress, + message: this.#message, + result_url: this.#resultUrl, + result_id: this.#resultID, + created_date: this.#createdDate, + started_date: this.#startedDate, + finished_date: this.#finishedDate, + expiry_date: this.#expiryDate, + owner: { + id: this.#owner.id, + username: this.#owner.username, + }, + }; + + return result; + } } diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index 77936570a4fc..97a7c90ecb80 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -5,10 +5,8 @@ import serverProxy from './server-proxy'; import { RQStatus } from './enums'; import { Request } from './request'; -import { SerializedRequest } from './server-response-types'; import { RequestError } from './exceptions'; import { PaginatedResource } from './core-types'; -import { fieldsToSnakeCase } from './common'; import config from './config'; const REQUESTS_COUNT = 5; @@ -58,14 +56,14 @@ class RequestsManager { requestID: string, options: { callback: (request: Request) => void, - initialRequest: Request, + initialRequest?: Request, }, ): Promise { if (!requestID) { throw new Error('Request id is not provided'); } const callback = options?.callback; - const { initialRequest } = options; + const initialRequest = options?.initialRequest; if (requestID in this.listening) { if (callback) { @@ -124,19 +122,15 @@ class RequestsManager { } } catch (error) { if (requestID in this.listening) { - const { onUpdate } = this.listening[requestID]; - onUpdate - .forEach((update) => update(new Request({ - id: requestID, - status: RQStatus.FAILED, - message: `Could not get a status of the request ${requestID}. ${error.toString()}`, - operation: fieldsToSnakeCase(initialRequest.operation) as SerializedRequest['operation'], - created_date: initialRequest.createdDate, - owner: { - id: initialRequest.owner.id, - username: initialRequest.owner.username, - }, - }))); + const { onUpdate, request } = this.listening[requestID]; + if (request) { + onUpdate + .forEach((update) => update(new Request({ + ...request.toJSON(), + status: RQStatus.FAILED, + message: `Could not get a status of the request ${requestID}. ${error.toString()}`, + }))); + } reject(error); } } diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index be8d3d4fb636..e9b94c0b0e7d 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -21,7 +21,7 @@ import { SerializedQualityReportData, APIQualityReportsFilter, SerializedAnalyticsReport, APIAnalyticsReportFilter, SerializedRequest, SerializedJobValidationLayout, SerializedTaskValidationLayout, } from './server-response-types'; -import { PaginatedResource } from './core-types'; +import { PaginatedResource, UpdateStatusData } from './core-types'; import { Request } from './request'; import { Storage } from './storage'; import { SerializedEvent } from './event'; @@ -1069,7 +1069,7 @@ type LongProcessListener = Record, taskDataSpec: any, - onUpdate: (request: Request) => void, + onUpdate: (request: Request | UpdateStatusData) => void, ): Promise<{ taskID: number, rqID: string }> { const { backendAPI, origin } = config; // keep current default params to 'freeze" them during this request @@ -1104,11 +1104,11 @@ async function createTask( let response = null; - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: 0, message: 'CVAT is creating your task', - })); + }); try { response = await Axios.post(`${backendAPI}/tasks`, taskSpec, { @@ -1118,11 +1118,11 @@ async function createTask( throw generateError(errorData); } - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: 0, message: 'CVAT is uploading task data to the server', - })); + }); async function bulkUpload(taskId, files) { const fileBulks = files.reduce((fileGroups, file) => { @@ -1142,11 +1142,11 @@ async function createTask( taskData.append(`client_files[${idx}]`, element); } const percentage = totalSentSize / totalSize; - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: percentage, message: 'CVAT is uploading task data to the server', - })); + }); await Axios.post(`${backendAPI}/tasks/${taskId}/data`, taskData, { ...params, headers: { 'Upload-Multiple': true }, @@ -1170,11 +1170,11 @@ async function createTask( const uploadConfig = { endpoint: `${origin}${backendAPI}/tasks/${response.data.id}/data/`, onUpdate: (percentage) => { - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: percentage, message: 'CVAT is uploading task data to the server', - })); + }); }, chunkSize, totalSize, @@ -2250,16 +2250,32 @@ async function getRequestsList(): Promise> } } +// Temporary solution for server availability problems +const retryTimeouts = [5000, 10000, 15000]; async function getRequestStatus(rqID: string): Promise { const { backendAPI } = config; + let retryCount = 0; + let lastError = null; - try { - const response = await Axios.get(`${backendAPI}/requests/${rqID}`); + while (retryCount < 3) { + try { + const response = await Axios.get(`${backendAPI}/requests/${rqID}`); - return response.data; - } catch (errorData) { - throw generateError(errorData); + return response.data; + } catch (errorData) { + lastError = generateError(errorData); + const { response } = errorData; + if (response && [502, 503, 504].includes(response.status)) { + const timeout = retryTimeouts[retryCount]; + await new Promise((resolve) => { setTimeout(resolve, timeout); }); + retryCount++; + } else { + throw generateError(errorData); + } + } } + + throw lastError; } async function cancelRequest(requestID): Promise { diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index 28b049745e4f..9365dd8a79db 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -503,7 +503,8 @@ export interface SerializedAPISchema { } export interface SerializedRequest { - id?: string; + id: string; + message: string; status: string; operation: { target: string; @@ -515,14 +516,13 @@ export interface SerializedRequest { function_id: string | null; }; progress?: number; - message: string; result_url?: string; result_id?: number; - created_date?: string; + created_date: string; started_date?: string; finished_date?: string; expiry_date?: string; - owner?: any; + owner: any; } export interface SerializedJobValidationLayout { diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index 842b91d81bb3..cb1f595d9a56 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -756,17 +756,16 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { const { taskID, rqID } = await serverProxy.tasks.create( taskSpec, taskDataSpec, - options?.requestStatusCallback || (() => {}), + options?.updateStatusCallback || (() => {}), ); await requestsManager.listen(rqID, { callback: (request: Request) => { - options?.requestStatusCallback(request); + options?.updateStatusCallback(request); if (request.status === RQStatus.FAILED) { serverProxy.tasks.delete(taskID, config.organization.organizationSlug || null); } }, - initialRequest: options?.getInitialRequest(taskID), }); const [task] = await serverProxy.tasks.get({ id: taskID }); diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index db0d6eb490be..1164ae0c07de 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -29,6 +29,7 @@ import logger from './logger'; import Issue from './issue'; import ObjectState from './object-state'; import { JobValidationLayout, TaskValidationLayout } from './validation-layout'; +import { UpdateStatusData } from './core-types'; function buildDuplicatedAPI(prototype) { Object.defineProperties(prototype, { @@ -1141,10 +1142,7 @@ export class Task extends Session { async save( fields: Record = {}, - options?: { - requestStatusCallback?: (request: Request) => void, - getInitialRequest?: (taskID: number) => Request, - }, + options?: { updateStatusCallback?: (updateData: Request | UpdateStatusData) => void }, ): Promise { const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.save, fields, options); return result; diff --git a/cvat-ui/src/actions/export-actions.ts b/cvat-ui/src/actions/export-actions.ts index 90d6d6dd96e2..db59a6315c6a 100644 --- a/cvat-ui/src/actions/export-actions.ts +++ b/cvat-ui/src/actions/export-actions.ts @@ -8,7 +8,7 @@ import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { Storage, ProjectOrTaskOrJob, Job } from 'cvat-core-wrapper'; import { getInstanceType, RequestInstanceType, listen, RequestsActions, - shouldListenForProgress, generateInitialRequest, + shouldListenForProgress, } from './requests-actions'; export enum ExportActionTypes { @@ -89,13 +89,7 @@ export async function listenExportDatasetAsync( const instanceType = getInstanceType(instance); try { - const result = await listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: instanceType, - type: `export:${resource}`, - instance, - }), - }); + const result = await listen(rqID, dispatch); const target = !result?.url ? 'cloudstorage' : 'local'; dispatch(exportActions.exportDatasetSuccess( instance, instanceType, format, resource, target, @@ -147,13 +141,7 @@ export async function listenExportBackupAsync( const instanceType = getInstanceType(instance) as 'project' | 'task'; try { - const result = await listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: instanceType, - type: 'export:backup', - instance, - }), - }); + const result = await listen(rqID, dispatch); const target = !result?.url ? 'cloudstorage' : 'local'; dispatch(exportActions.exportBackupSuccess(instance, instanceType, target)); } catch (error) { diff --git a/cvat-ui/src/actions/import-actions.ts b/cvat-ui/src/actions/import-actions.ts index ca69af335b5e..e47db0b47818 100644 --- a/cvat-ui/src/actions/import-actions.ts +++ b/cvat-ui/src/actions/import-actions.ts @@ -12,7 +12,7 @@ import { getProjectsAsync } from './projects-actions'; import { AnnotationActionTypes, fetchAnnotationsAsync } from './annotation-actions'; import { getInstanceType, listen, RequestInstanceType, RequestsActions, - shouldListenForProgress, generateInitialRequest, + shouldListenForProgress, } from './requests-actions'; const core = getCore(); @@ -81,13 +81,7 @@ export async function listenImportDatasetAsync( const instanceType = getInstanceType(instance); const resource = instanceType === 'project' ? 'dataset' : 'annotation'; try { - await listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: instanceType, - type: `import:${resource}`, - instance, - }), - }); + await listen(rqID, dispatch); dispatch(importActions.importDatasetSuccess(instance, resource)); } catch (error) { dispatch(importActions.importDatasetFailed(instance, resource, error)); @@ -108,13 +102,6 @@ export const importDatasetAsync = ( try { const state: CombinedState = getState(); - const listenForImport = (rqID: string) => listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: instanceType, - type: `import:${resource}`, - instance, - }), - }); if (instanceType === 'project') { dispatch(importActions.importDataset(instance, format)); @@ -128,7 +115,7 @@ export const importDatasetAsync = ( ), }); if (shouldListenForProgress(rqID, state.requests)) { - await listenForImport(rqID); + await listen(rqID, dispatch); } } else if (instanceType === 'task') { dispatch(importActions.importDataset(instance, format)); @@ -137,7 +124,7 @@ export const importDatasetAsync = ( convMaskToPoly, }); if (shouldListenForProgress(rqID, state.requests)) { - await listenForImport(rqID); + await listen(rqID, dispatch); } } else { // job dispatch(importActions.importDataset(instance, format)); @@ -146,7 +133,7 @@ export const importDatasetAsync = ( convMaskToPoly, }); if (shouldListenForProgress(rqID, state.requests)) { - await listenForImport(rqID); + await listen(rqID, dispatch); await (instance as Job).annotations.clear({ reload: true }); await (instance as Job).actions.clear(); @@ -186,12 +173,7 @@ export async function listenImportBackupAsync( const { instanceType } = params; try { - const result = await listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: instanceType, - type: 'import:backup', - }), - }); + const result = await listen(rqID, dispatch); dispatch(importActions.importBackupSuccess(result?.resultID, instanceType)); } catch (error) { diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index 39e87f338179..1f3972746e7e 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: MIT -import { Store } from 'redux'; import { ActionUnion, createAction } from 'utils/redux'; import { CombinedState, RequestsQuery, RequestsState } from 'reducers'; import { - Request, ProjectOrTaskOrJob, getCore, RQStatus, RequestOperation, fieldsToSnakeCase, + Request, ProjectOrTaskOrJob, getCore, RQStatus, } from 'cvat-core-wrapper'; +import { Store } from 'redux'; import { getCVATStore } from 'cvat-store'; const core = getCore(); @@ -95,45 +95,16 @@ export function shouldListenForProgress(rqID: string | void, state: RequestsStat ); } -export function generateInitialRequest( - initialData: Partial & - Pick & - { instance?: ProjectOrTaskOrJob | RequestInstanceType }, -): Request { - const { - target, type, format, instance, - } = initialData; - const { user } = getStore().getState().auth; - const requestOperation = { - target, - type, - format: format ?? null, - jobID: instance && target === 'job' ? instance.id : null, - taskID: instance && target === 'task' ? instance.id : null, - projectID: instance && target === 'project' ? instance.id : null, - functionID: null, - }; - return new Request({ - status: RQStatus.QUEUED, - operation: fieldsToSnakeCase(requestOperation) as any, - created_date: new Date().toISOString(), - message: 'Status request sent', - owner: user, - }); -} - export function listen( requestID: string, dispatch: (action: RequestsActions) => void, - options: { - initialRequest: Request, - }, ) : Promise { + const { requests } = getStore().getState().requests; return core.requests .listen(requestID, { callback: (updatedRequest) => { updateRequestProgress(updatedRequest, dispatch); }, - ...options, + initialRequest: requests[requestID], }); } diff --git a/cvat-ui/src/actions/requests-async-actions.ts b/cvat-ui/src/actions/requests-async-actions.ts index 317fd7559301..06a137eafd28 100644 --- a/cvat-ui/src/actions/requests-async-actions.ts +++ b/cvat-ui/src/actions/requests-async-actions.ts @@ -9,7 +9,7 @@ import { } from 'cvat-core-wrapper'; import { listenExportBackupAsync, listenExportDatasetAsync } from './export-actions'; import { - RequestInstanceType, generateInitialRequest, listen, requestsActions, + RequestInstanceType, listen, requestsActions, } from './requests-actions'; import { listenImportBackupAsync, listenImportDatasetAsync } from './import-actions'; @@ -30,6 +30,7 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { try { const requests = await core.requests.list(); + dispatch(requestsActions.getRequestsSuccess(requests)); requests .filter((request: Request) => [RQStatus.STARTED, RQStatus.QUEUED].includes(request.status)) @@ -78,16 +79,10 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { } } else if (operationType === 'create') { if (operationTarget === 'task') { - listen(rqID, dispatch, { - initialRequest: generateInitialRequest({ - target: operationTarget, - type: 'create:task', - }), - }); + listen(rqID, dispatch); } } }); - dispatch(requestsActions.getRequestsSuccess(requests)); } catch (error) { dispatch(requestsActions.getRequestsFailed(error)); } diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 872a48b87879..d15f033f1e6f 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -6,14 +6,14 @@ import { AnyAction } from 'redux'; import { TasksQuery, StorageLocation } from 'reducers'; import { - getCore, RQStatus, Storage, Task, + getCore, RQStatus, Storage, Task, UpdateStatusData, Request, } from 'cvat-core-wrapper'; import { filterNull } from 'utils/filter-null'; import { ThunkDispatch, ThunkAction } from 'utils/redux'; import { ValidationMode } from 'components/create-task-page/quality-configuration-form'; import { getInferenceStatusAsync } from './models-actions'; -import { generateInitialRequest, updateRequestProgress } from './requests-actions'; +import { updateRequestProgress } from './requests-actions'; const cvat = getCore(); @@ -274,10 +274,10 @@ ThunkAction { taskInstance.remoteFiles = data.files.remote; try { const savedTask = await taskInstance.save(extras, { - requestStatusCallback(request) { - let { message } = request; + updateStatusCallback(updateData: Request | UpdateStatusData) { + let { message } = updateData; + const { status, progress } = updateData; let helperMessage = ''; - const { status, progress } = request; if (!message) { if ([RQStatus.QUEUED, RQStatus.STARTED].includes(status)) { message = 'CVAT queued the task to import'; @@ -291,14 +291,7 @@ ThunkAction { } } onProgress?.(`${message} ${progress ? `${Math.floor(progress * 100)}%` : ''}. ${helperMessage}`); - if (request.id) updateRequestProgress(request, dispatch); - }, - getInitialRequest(taskID) { - return generateInitialRequest({ - target: 'task', - type: 'create:task', - instance: { id: taskID, type: 'task' }, - }); + if (updateData instanceof Request) updateRequestProgress(updateData, dispatch); }, }); diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index 58ba35510939..f99b58cfd3bb 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -17,6 +17,7 @@ import { import { SerializedAttribute, SerializedLabel, SerializedAPISchema, SerializedRequest, } from 'cvat-core/src/server-response-types'; +import { UpdateStatusData } from 'cvat-core/src/core-types'; import { Job, Task } from 'cvat-core/src/session'; import Project from 'cvat-core/src/project'; import QualityReport, { QualitySummary } from 'cvat-core/src/quality-report'; @@ -124,4 +125,5 @@ export type { ProjectOrTaskOrJob, RequestOperation, SerializedRequest, + UpdateStatusData, }; From da489947766b81e261eaa46b3cc1b75ec6023c71 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Mon, 28 Oct 2024 11:03:08 +0300 Subject: [PATCH 5/5] removed unused import --- cvat-ui/src/cvat-core-wrapper.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index f99b58cfd3bb..94b70373a1c7 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -15,7 +15,7 @@ import { Label, Attribute, } from 'cvat-core/src/labels'; import { - SerializedAttribute, SerializedLabel, SerializedAPISchema, SerializedRequest, + SerializedAttribute, SerializedLabel, SerializedAPISchema, } from 'cvat-core/src/server-response-types'; import { UpdateStatusData } from 'cvat-core/src/core-types'; import { Job, Task } from 'cvat-core/src/session'; @@ -30,7 +30,6 @@ import { ModelReturnType, DimensionType, JobType, JobStage, JobState, RQStatus, } from 'cvat-core/src/enums'; -import { fieldsToSnakeCase } from 'cvat-core/src/common'; import { Storage, StorageData } from 'cvat-core/src/storage'; import Issue from 'cvat-core/src/issue'; import Comment from 'cvat-core/src/comment'; @@ -108,7 +107,6 @@ export { ActionParameterType, FrameSelectionType, Request, - fieldsToSnakeCase, JobValidationLayout, TaskValidationLayout, }; @@ -124,6 +122,5 @@ export type { SerializedAPISchema, ProjectOrTaskOrJob, RequestOperation, - SerializedRequest, UpdateStatusData, };