From 85b554754147ea20e7238e12166ebb4ce55271ba Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 25 Nov 2022 11:37:45 +0300 Subject: [PATCH] Make the server proxy's properties visible to static analysis (#5345) Currently, all properties of the server proxy object are created using `Object.defineProperties` in the constructor, which means that IDEs like VS Code can't analyze the file's static structure to determine what properties there are and what types they have. Consequently, things like autocomplete and go-to-definition don't work. Fix that by removing the `ServerProxy` class altogether and exporting an anonymous object with all properties defined statically. --- cvat-core/src/server-proxy.ts | 3749 ++++++++++++++++----------------- 1 file changed, 1847 insertions(+), 1902 deletions(-) diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index 4cafdb8e..6deb7b73 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -205,1596 +205,1487 @@ class WorkerWrappedAxios { } } -class ServerProxy { - constructor() { - Axios.defaults.withCredentials = true; - Axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'; - Axios.defaults.xsrfCookieName = 'csrftoken'; - const workerAxios = new WorkerWrappedAxios(); - Axios.interceptors.request.use((reqConfig) => { - if ('params' in reqConfig && 'org' in reqConfig.params) { - return reqConfig; - } - - const organization = enableOrganization(); - // for users when organization is unset - // we are interested in getting all the users, - // not only those who are not in any organization - if (reqConfig.url.endsWith('/users') && !organization.org) { - return reqConfig; - } - - reqConfig.params = { ...organization, ...(reqConfig.params || {}) }; - return reqConfig; - }); - - let token = store.get('token'); - if (token) { - Axios.defaults.headers.common.Authorization = `Token ${token}`; - } +Axios.defaults.withCredentials = true; +Axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'; +Axios.defaults.xsrfCookieName = 'csrftoken'; +const workerAxios = new WorkerWrappedAxios(); +Axios.interceptors.request.use((reqConfig) => { + if ('params' in reqConfig && 'org' in reqConfig.params) { + return reqConfig; + } - async function about() { - const { backendAPI } = config; + const organization = enableOrganization(); + // for users when organization is unset + // we are interested in getting all the users, + // not only those who are not in any organization + if (reqConfig.url.endsWith('/users') && !organization.org) { + return reqConfig; + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/server/about`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + reqConfig.params = { ...organization, ...(reqConfig.params || {}) }; + return reqConfig; +}); - return response.data; - } +let token = store.get('token'); +if (token) { + Axios.defaults.headers.common.Authorization = `Token ${token}`; +} - async function share(directoryArg) { - const { backendAPI } = config; +async function about() { + const { backendAPI } = config; - let response = null; - try { - response = await Axios.get(`${backendAPI}/server/share`, { - proxy: config.proxy, - params: { directory: directoryArg }, - }); - } catch (errorData) { - throw generateError(errorData); - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/about`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - return response.data; - } + return response.data; +} - async function exception(exceptionObject) { - const { backendAPI } = config; +async function share(directoryArg) { + const { backendAPI } = config; - try { - await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/share`, { + proxy: config.proxy, + params: { directory: directoryArg }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function formats() { - const { backendAPI } = config; + return response.data; +} - let response = null; - try { - response = await Axios.get(`${backendAPI}/server/annotation/formats`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function exception(exceptionObject) { + const { backendAPI } = config; - return response.data; - } + try { + await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function userAgreements() { - const { backendAPI } = config; - let response = null; - try { - response = await Axios.get(`${backendAPI}/user-agreements`, { - proxy: config.proxy, - validateStatus: (status) => status === 200 || status === 404, - }); +async function formats() { + const { backendAPI } = config; - if (response.status === 200) { - return response.data; - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/annotation/formats`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - return []; - } catch (errorData) { - throw generateError(errorData); - } - } + return response.data; +} - async function register(username, firstName, lastName, email, password, confirmations) { - let response = null; - try { - const data = JSON.stringify({ - username, - first_name: firstName, - last_name: lastName, - email, - password1: password, - password2: password, - confirmations, - }); - response = await Axios.post(`${config.backendAPI}/auth/register`, data, { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function userAgreements() { + const { backendAPI } = config; + let response = null; + try { + response = await Axios.get(`${backendAPI}/user-agreements`, { + proxy: config.proxy, + validateStatus: (status) => status === 200 || status === 404, + }); + if (response.status === 200) { return response.data; } - async function login(credential, password) { - const authenticationData = [ - `${encodeURIComponent(isEmail(credential) ? 'email' : 'username')}=${encodeURIComponent(credential)}`, - `${encodeURIComponent('password')}=${encodeURIComponent(password)}`, - ] - .join('&') - .replace(/%20/g, '+'); + return []; + } catch (errorData) { + throw generateError(errorData); + } +} - removeToken(); - let authenticationResponse = null; - try { - authenticationResponse = await Axios.post(`${config.backendAPI}/auth/login`, authenticationData, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function register(username, firstName, lastName, email, password, confirmations) { + let response = null; + try { + const data = JSON.stringify({ + username, + first_name: firstName, + last_name: lastName, + email, + password1: password, + password2: password, + confirmations, + }); + response = await Axios.post(`${config.backendAPI}/auth/register`, data, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - if (authenticationResponse.headers['set-cookie']) { - // Browser itself setup cookie and header is none - // In NodeJS we need do it manually - const cookies = authenticationResponse.headers['set-cookie'].join(';'); - Axios.defaults.headers.common.Cookie = cookies; - } + return response.data; +} - token = authenticationResponse.data.key; - store.set('token', token); - Axios.defaults.headers.common.Authorization = `Token ${token}`; - } +async function login(credential, password) { + const authenticationData = [ + `${encodeURIComponent(isEmail(credential) ? 'email' : 'username')}=${encodeURIComponent(credential)}`, + `${encodeURIComponent('password')}=${encodeURIComponent(password)}`, + ] + .join('&') + .replace(/%20/g, '+'); + + removeToken(); + let authenticationResponse = null; + try { + authenticationResponse = await Axios.post(`${config.backendAPI}/auth/login`, authenticationData, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function logout() { - try { - await Axios.post(`${config.backendAPI}/auth/logout`, { - proxy: config.proxy, - }); - removeToken(); - } catch (errorData) { - throw generateError(errorData); - } - } + if (authenticationResponse.headers['set-cookie']) { + // Browser itself setup cookie and header is none + // In NodeJS we need do it manually + const cookies = authenticationResponse.headers['set-cookie'].join(';'); + Axios.defaults.headers.common.Cookie = cookies; + } - async function changePassword(oldPassword, newPassword1, newPassword2) { - try { - const data = JSON.stringify({ - old_password: oldPassword, - new_password1: newPassword1, - new_password2: newPassword2, - }); - await Axios.post(`${config.backendAPI}/auth/password/change`, data, { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } + token = authenticationResponse.data.key; + store.set('token', token); + Axios.defaults.headers.common.Authorization = `Token ${token}`; +} - async function requestPasswordReset(email) { - try { - const data = JSON.stringify({ - email, - }); - await Axios.post(`${config.backendAPI}/auth/password/reset`, data, { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function logout() { + try { + await Axios.post(`${config.backendAPI}/auth/logout`, { + proxy: config.proxy, + }); + removeToken(); + } catch (errorData) { + throw generateError(errorData); + } +} - async function resetPassword(newPassword1, newPassword2, uid, _token) { - try { - const data = JSON.stringify({ - new_password1: newPassword1, - new_password2: newPassword2, - uid, - token: _token, - }); - await Axios.post(`${config.backendAPI}/auth/password/reset/confirm`, data, { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function changePassword(oldPassword, newPassword1, newPassword2) { + try { + const data = JSON.stringify({ + old_password: oldPassword, + new_password1: newPassword1, + new_password2: newPassword2, + }); + await Axios.post(`${config.backendAPI}/auth/password/change`, data, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function getSelf() { - const { backendAPI } = config; +async function requestPasswordReset(email) { + try { + const data = JSON.stringify({ + email, + }); + await Axios.post(`${config.backendAPI}/auth/password/reset`, data, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - let response = null; - try { - response = await Axios.get(`${backendAPI}/users/self`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function resetPassword(newPassword1, newPassword2, uid, _token) { + try { + const data = JSON.stringify({ + new_password1: newPassword1, + new_password2: newPassword2, + uid, + token: _token, + }); + await Axios.post(`${config.backendAPI}/auth/password/reset/confirm`, data, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - return response.data; - } +async function getSelf() { + const { backendAPI } = config; - async function authorized() { - try { - const response = await getSelf(); - if (!store.get('token')) { - store.set('token', response.key); - Axios.defaults.headers.common.Authorization = `Token ${response.key}`; - } - } catch (serverError) { - if (serverError.code === 401) { - // In CVAT app we use two types of authentication, - // So here we are forcing user have both credential types - // First request will fail if session is expired, then we check - // for precense of token - await logout(); - return false; - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/users/self`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - throw serverError; - } + return response.data; +} - return true; +async function authorized() { + try { + const response = await getSelf(); + if (!store.get('token')) { + store.set('token', response.key); + Axios.defaults.headers.common.Authorization = `Token ${response.key}`; } - - async function serverRequest(url, data) { - try { - return ( - await Axios({ - url, - ...data, - }) - ).data; - } catch (errorData) { - throw generateError(errorData); - } + } catch (serverError) { + if (serverError.code === 401) { + // In CVAT app we use two types of authentication, + // So here we are forcing user have both credential types + // First request will fail if session is expired, then we check + // for precense of token + await logout(); + return false; } - async function searchProjectNames(search, limit) { - const { backendAPI, proxy } = config; + throw serverError; + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/projects`, { - proxy, - params: { - names_only: true, - page: 1, - page_size: limit, - search, - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + return true; +} - response.data.results.count = response.data.count; - return response.data.results; - } +async function serverRequest(url, data) { + try { + return ( + await Axios({ + url, + ...data, + }) + ).data; + } catch (errorData) { + throw generateError(errorData); + } +} - async function getProjects(filter = {}) { - const { backendAPI, proxy } = config; +async function searchProjectNames(search, limit) { + const { backendAPI, proxy } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/projects`, { + proxy, + params: { + names_only: true, + page: 1, + page_size: limit, + search, + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - if ('id' in filter) { - response = await Axios.get(`${backendAPI}/projects/${filter.id}`, { - proxy, - }); - const results = [response.data]; - results.count = 1; - return results; - } + response.data.results.count = response.data.count; + return response.data.results; +} - response = await Axios.get(`${backendAPI}/projects`, { - params: { - ...filter, - page_size: 12, - }, - proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function getProjects(filter = {}) { + const { backendAPI, proxy } = config; - response.data.results.count = response.data.count; - return response.data.results; + let response = null; + try { + if ('id' in filter) { + response = await Axios.get(`${backendAPI}/projects/${filter.id}`, { + proxy, + }); + const results = [response.data]; + results.count = 1; + return results; } - async function saveProject(id, projectData) { - const { backendAPI } = config; + response = await Axios.get(`${backendAPI}/projects`, { + params: { + ...filter, + page_size: 12, + }, + proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - try { - await Axios.patch(`${backendAPI}/projects/${id}`, JSON.stringify(projectData), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } + response.data.results.count = response.data.count; + return response.data.results; +} - async function deleteProject(id) { - const { backendAPI } = config; +async function saveProject(id, projectData) { + const { backendAPI } = config; - try { - await Axios.delete(`${backendAPI}/projects/${id}`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } + try { + await Axios.patch(`${backendAPI}/projects/${id}`, JSON.stringify(projectData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function createProject(projectSpec) { - const { backendAPI } = config; +async function deleteProject(id) { + const { backendAPI } = config; - try { - const response = await Axios.post(`${backendAPI}/projects`, JSON.stringify(projectSpec), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + try { + await Axios.delete(`${backendAPI}/projects/${id}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function getTasks(filter = {}) { - const { backendAPI } = config; +async function createProject(projectSpec) { + const { backendAPI } = config; - let response = null; - try { - if ('id' in filter) { - response = await Axios.get(`${backendAPI}/tasks/${filter.id}`, { - proxy: config.proxy, - }); - const results = [response.data]; - results.count = 1; - return results; - } + try { + const response = await Axios.post(`${backendAPI}/projects`, JSON.stringify(projectSpec), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - response = await Axios.get(`${backendAPI}/tasks`, { - params: { - ...filter, - page_size: 10, - }, - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function getTasks(filter = {}) { + const { backendAPI } = config; - response.data.results.count = response.data.count; - return response.data.results; + let response = null; + try { + if ('id' in filter) { + response = await Axios.get(`${backendAPI}/tasks/${filter.id}`, { + proxy: config.proxy, + }); + const results = [response.data]; + results.count = 1; + return results; } - async function saveTask(id, taskData) { - const { backendAPI } = config; + response = await Axios.get(`${backendAPI}/tasks`, { + params: { + ...filter, + page_size: 10, + }, + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - response = await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - - return response.data; - } - - async function deleteTask(id, organizationID = null) { - const { backendAPI } = config; + response.data.results.count = response.data.count; + return response.data.results; +} - try { - await Axios.delete(`${backendAPI}/tasks/${id}`, { - ...(organizationID ? { org: organizationID } : {}), - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function saveTask(id, taskData) { + const { backendAPI } = config; - function exportDataset(instanceType) { - return async function ( - id: number, - format: string, - saveImages: boolean, - useDefaultSettings: boolean, - targetStorage: Storage, - name?: string, - ) { - const { backendAPI } = config; - const baseURL = `${backendAPI}/${instanceType}/${id}/${saveImages ? 'dataset' : 'annotations'}`; - const params: Params = { - ...enableOrganization(), - ...configureStorage(targetStorage, useDefaultSettings), - ...(name ? { filename: name.replace(/\//g, '_') } : {}), - format, - }; + let response = null; + try { + response = await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - return new Promise((resolve, reject) => { - async function request() { - Axios.get(baseURL, { - proxy: config.proxy, - params, - }) - .then((response) => { - const isCloudStorage = targetStorage.location === StorageLocation.CLOUD_STORAGE; - const { status } = response; - if (status === 201) params.action = 'download'; - if (status === 202 || (isCloudStorage && status === 201)) { - setTimeout(request, 3000); - } else if (status === 201) { - resolve(`${baseURL}?${new URLSearchParams(params).toString()}`); - } else if (isCloudStorage && status === 200) { - resolve(); - } - }) - .catch((errorData) => { - reject(generateError(errorData)); - }); - } + return response.data; +} - setTimeout(request); - }); - }; - } +async function deleteTask(id, organizationID = null) { + const { backendAPI } = config; - async function importDataset( - id: number, - format: string, - useDefaultLocation: boolean, - sourceStorage: Storage, - file: File | string, - options: { - convMaskToPoly: boolean, - updateStatusCallback: (s: string, n: number) => void, + try { + await Axios.delete(`${backendAPI}/tasks/${id}`, { + ...(organizationID ? { org: organizationID } : {}), + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', }, - ): Promise { - const { backendAPI, origin } = config; - const params: Params & { conv_mask_to_poly: boolean } = { - ...enableOrganization(), - ...configureStorage(sourceStorage, useDefaultLocation), - format, - filename: typeof file === 'string' ? file : file.name, - conv_mask_to_poly: options.convMaskToPoly, - }; - - const url = `${backendAPI}/projects/${id}/dataset`; - - async function wait() { - return new Promise((resolve, reject) => { - async function requestStatus() { - try { - const response = await Axios.get(url, { - params: { ...params, action: 'import_status' }, - proxy: config.proxy, - }); - if (response.status === 202) { - if (response.data.message) { - options.updateStatusCallback(response.data.message, response.data.progress || 0); - } - setTimeout(requestStatus, 3000); - } else if (response.status === 201) { - resolve(); - } else { - reject(generateError(response)); - } - } catch (error) { - reject(generateError(error)); - } - } - setTimeout(requestStatus, 2000); - }); - } - const isCloudStorage = sourceStorage.location === StorageLocation.CLOUD_STORAGE; - - if (isCloudStorage) { - try { - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } else { - const uploadConfig = { - chunkSize: config.uploadChunkSize * 1024 * 1024, - endpoint: `${origin}${backendAPI}/projects/${id}/dataset/`, - totalSentSize: 0, - totalSize: (file as File).size, - onUpdate: (percentage) => { - options.updateStatusCallback('The dataset is being uploaded to the server', percentage); - }, - }; + }); + } catch (errorData) { + throw generateError(errorData); + } +} - try { - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - headers: { 'Upload-Start': true }, - }); - await chunkUpload(file, uploadConfig); - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - headers: { 'Upload-Finish': true }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } - try { - return await wait(); - } catch (errorData) { - throw generateError(errorData); - } - } +function exportDataset(instanceType) { + return async function ( + id: number, + format: string, + saveImages: boolean, + useDefaultSettings: boolean, + targetStorage: Storage, + name?: string, + ) { + const { backendAPI } = config; + const baseURL = `${backendAPI}/${instanceType}/${id}/${saveImages ? 'dataset' : 'annotations'}`; + const params: Params = { + ...enableOrganization(), + ...configureStorage(targetStorage, useDefaultSettings), + ...(name ? { filename: name.replace(/\//g, '_') } : {}), + format, + }; - async function backupTask(id: number, targetStorage: Storage, useDefaultSettings: boolean, fileName?: string) { - const { backendAPI } = config; - const params: Params = { - ...enableOrganization(), - ...configureStorage(targetStorage, useDefaultSettings), - ...(fileName ? { filename: fileName } : {}), - }; - const url = `${backendAPI}/tasks/${id}/backup`; - - return new Promise((resolve, reject) => { - async function request() { - try { - const response = await Axios.get(url, { - proxy: config.proxy, - params, - }); + return new Promise((resolve, reject) => { + async function request() { + Axios.get(baseURL, { + proxy: config.proxy, + params, + }) + .then((response) => { const isCloudStorage = targetStorage.location === StorageLocation.CLOUD_STORAGE; const { status } = response; if (status === 201) params.action = 'download'; if (status === 202 || (isCloudStorage && status === 201)) { setTimeout(request, 3000); } else if (status === 201) { - resolve(`${url}?${new URLSearchParams(params).toString()}`); + resolve(`${baseURL}?${new URLSearchParams(params).toString()}`); } else if (isCloudStorage && status === 200) { resolve(); } - } catch (errorData) { + }) + .catch((errorData) => { reject(generateError(errorData)); - } - } - - setTimeout(request); - }); - } - - async function restoreTask(storage: Storage, file: File | string) { - const { backendAPI } = config; - // keep current default params to 'freeze" them during this request - const params: Params = { - ...enableOrganization(), - ...configureStorage(storage), - }; - - const url = `${backendAPI}/tasks/backup`; - const taskData = new FormData(); - let response; - - async function wait() { - return new Promise((resolve, reject) => { - async function checkStatus() { - try { - taskData.set('rq_id', response.data.rq_id); - response = await Axios.post(url, taskData, { - proxy: config.proxy, - params, - }); - if (response.status === 202) { - setTimeout(checkStatus, 3000); - } else { - // to be able to get the task after it was created, pass frozen params - const importedTask = await getTasks({ id: response.data.id, ...params }); - resolve(importedTask[0]); - } - } catch (errorData) { - reject(generateError(errorData)); - } - } - setTimeout(checkStatus); - }); - } - const isCloudStorage = storage.location === StorageLocation.CLOUD_STORAGE; - - if (isCloudStorage) { - params.filename = file as string; - response = await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - }); - } else { - const uploadConfig = { - chunkSize: config.uploadChunkSize * 1024 * 1024, - endpoint: `${origin}${backendAPI}/tasks/backup/`, - totalSentSize: 0, - totalSize: (file as File).size, - }; - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - headers: { 'Upload-Start': true }, - }); - const { filename } = await chunkUpload(file, uploadConfig); - response = await Axios.post(url, - new FormData(), { - params: { ...params, filename }, - proxy: config.proxy, - headers: { 'Upload-Finish': true }, }); } - return wait(); - } - async function backupProject( - id: number, - targetStorage: Storage, - useDefaultSettings: boolean, - fileName?: string, - ) { - const { backendAPI } = config; - // keep current default params to 'freeze" them during this request - const params: Params = { - ...enableOrganization(), - ...configureStorage(targetStorage, useDefaultSettings), - ...(fileName ? { filename: fileName } : {}), - }; + setTimeout(request); + }); + }; +} - const url = `${backendAPI}/projects/${id}/backup`; +async function importDataset( + id: number, + format: string, + useDefaultLocation: boolean, + sourceStorage: Storage, + file: File | string, + options: { + convMaskToPoly: boolean, + updateStatusCallback: (s: string, n: number) => void, + }, +): Promise { + const { backendAPI, origin } = config; + const params: Params & { conv_mask_to_poly: boolean } = { + ...enableOrganization(), + ...configureStorage(sourceStorage, useDefaultLocation), + format, + filename: typeof file === 'string' ? file : file.name, + conv_mask_to_poly: options.convMaskToPoly, + }; - return new Promise((resolve, reject) => { - async function request() { - try { - const response = await Axios.get(url, { - proxy: config.proxy, - params, - }); - const isCloudStorage = targetStorage.location === StorageLocation.CLOUD_STORAGE; - const { status } = response; - if (status === 201) params.action = 'download'; - if (status === 202 || (isCloudStorage && status === 201)) { - setTimeout(request, 3000); - } else if (status === 201) { - resolve(`${url}?${new URLSearchParams(params).toString()}`); - } else if (isCloudStorage && status === 200) { - resolve(); + const url = `${backendAPI}/projects/${id}/dataset`; + + async function wait() { + return new Promise((resolve, reject) => { + async function requestStatus() { + try { + const response = await Axios.get(url, { + params: { ...params, action: 'import_status' }, + proxy: config.proxy, + }); + if (response.status === 202) { + if (response.data.message) { + options.updateStatusCallback(response.data.message, response.data.progress || 0); } - } catch (errorData) { - reject(generateError(errorData)); + setTimeout(requestStatus, 3000); + } else if (response.status === 201) { + resolve(); + } else { + reject(generateError(response)); } + } catch (error) { + reject(generateError(error)); } + } + setTimeout(requestStatus, 2000); + }); + } + const isCloudStorage = sourceStorage.location === StorageLocation.CLOUD_STORAGE; - setTimeout(request); - }); + if (isCloudStorage) { + try { + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } + } else { + const uploadConfig = { + chunkSize: config.uploadChunkSize * 1024 * 1024, + endpoint: `${origin}${backendAPI}/projects/${id}/dataset/`, + totalSentSize: 0, + totalSize: (file as File).size, + onUpdate: (percentage) => { + options.updateStatusCallback('The dataset is being uploaded to the server', percentage); + }, + }; + + try { + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + headers: { 'Upload-Start': true }, + }); + await chunkUpload(file, uploadConfig); + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + headers: { 'Upload-Finish': true }, + }); + } catch (errorData) { + throw generateError(errorData); } + } + try { + return await wait(); + } catch (errorData) { + throw generateError(errorData); + } +} - async function restoreProject(storage: Storage, file: File | string) { - const { backendAPI } = config; - // keep current default params to 'freeze" them during this request - const params: Params = { - ...enableOrganization(), - ...configureStorage(storage), - }; - - const url = `${backendAPI}/projects/backup`; - const projectData = new FormData(); - let response; - - async function wait() { - return new Promise((resolve, reject) => { - async function request() { - try { - projectData.set('rq_id', response.data.rq_id); - response = await Axios.post(`${backendAPI}/projects/backup`, projectData, { - proxy: config.proxy, - params, - }); - if (response.status === 202) { - setTimeout(request, 3000); - } else { - // to be able to get the task after it was created, pass frozen params - const restoredProject = await getProjects({ id: response.data.id, ...params }); - resolve(restoredProject[0]); - } - } catch (errorData) { - reject(generateError(errorData)); - } - } +async function backupTask(id: number, targetStorage: Storage, useDefaultSettings: boolean, fileName?: string) { + const { backendAPI } = config; + const params: Params = { + ...enableOrganization(), + ...configureStorage(targetStorage, useDefaultSettings), + ...(fileName ? { filename: fileName } : {}), + }; + const url = `${backendAPI}/tasks/${id}/backup`; - setTimeout(request); + return new Promise((resolve, reject) => { + async function request() { + try { + const response = await Axios.get(url, { + proxy: config.proxy, + params, }); + const isCloudStorage = targetStorage.location === StorageLocation.CLOUD_STORAGE; + const { status } = response; + if (status === 201) params.action = 'download'; + if (status === 202 || (isCloudStorage && status === 201)) { + setTimeout(request, 3000); + } else if (status === 201) { + resolve(`${url}?${new URLSearchParams(params).toString()}`); + } else if (isCloudStorage && status === 200) { + resolve(); + } + } catch (errorData) { + reject(generateError(errorData)); } + } - const isCloudStorage = storage.location === StorageLocation.CLOUD_STORAGE; + setTimeout(request); + }); +} - if (isCloudStorage) { - params.filename = file; - response = await Axios.post(url, - new FormData(), { - params, +async function restoreTask(storage: Storage, file: File | string) { + const { backendAPI } = config; + // keep current default params to 'freeze" them during this request + const params: Params = { + ...enableOrganization(), + ...configureStorage(storage), + }; + + const url = `${backendAPI}/tasks/backup`; + const taskData = new FormData(); + let response; + + async function wait() { + return new Promise((resolve, reject) => { + async function checkStatus() { + try { + taskData.set('rq_id', response.data.rq_id); + response = await Axios.post(url, taskData, { proxy: config.proxy, - }); - } else { - const uploadConfig = { - chunkSize: config.uploadChunkSize * 1024 * 1024, - endpoint: `${origin}${backendAPI}/projects/backup/`, - totalSentSize: 0, - totalSize: (file as File).size, - }; - await Axios.post(url, - new FormData(), { params, - proxy: config.proxy, - headers: { 'Upload-Start': true }, }); - const { filename } = await chunkUpload(file, uploadConfig); - response = await Axios.post(url, - new FormData(), { - params: { ...params, filename }, - proxy: config.proxy, - headers: { 'Upload-Finish': true }, - }); - } - return wait(); - } - - async function createTask(taskSpec, taskDataSpec, onUpdate) { - const { backendAPI, origin } = config; - // keep current default params to 'freeze" them during this request - const params = enableOrganization(); - - async function wait(id) { - return new Promise((resolve, reject) => { - async function checkStatus() { - try { - const response = await Axios.get(`${backendAPI}/tasks/${id}/status`, { params }); - if (['Queued', 'Started'].includes(response.data.state)) { - if (response.data.message !== '') { - onUpdate(response.data.message, response.data.progress || 0); - } - setTimeout(checkStatus, 1000); - } else if (response.data.state === 'Finished') { - resolve(); - } else if (response.data.state === 'Failed') { - // If request has been successful, but task hasn't been created - // Then passed data is wrong and we can pass code 400 - const message = ` - Could not create the task on the server. ${response.data.message}. - `; - reject(new ServerError(message, 400)); - } else { - // If server has another status, it is unexpected - // Therefore it is server error and we can pass code 500 - reject( - new ServerError( - `Unknown task state has been received: ${response.data.state}`, - 500, - ), - ); - } - } catch (errorData) { - reject(generateError(errorData)); - } + if (response.status === 202) { + setTimeout(checkStatus, 3000); + } else { + // to be able to get the task after it was created, pass frozen params + const importedTask = await getTasks({ id: response.data.id, ...params }); + resolve(importedTask[0]); } - - setTimeout(checkStatus, 1000); - }); - } - - const chunkSize = config.uploadChunkSize * 1024 * 1024; - const clientFiles = taskDataSpec.client_files; - const chunkFiles = []; - const bulkFiles = []; - let totalSize = 0; - let totalSentSize = 0; - for (const file of clientFiles) { - if (file.size > chunkSize) { - chunkFiles.push(file); - } else { - bulkFiles.push(file); + } catch (errorData) { + reject(generateError(errorData)); } - totalSize += file.size; } - delete taskDataSpec.client_files; + setTimeout(checkStatus); + }); + } + const isCloudStorage = storage.location === StorageLocation.CLOUD_STORAGE; + + if (isCloudStorage) { + params.filename = file as string; + response = await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + }); + } else { + const uploadConfig = { + chunkSize: config.uploadChunkSize * 1024 * 1024, + endpoint: `${origin}${backendAPI}/tasks/backup/`, + totalSentSize: 0, + totalSize: (file as File).size, + }; + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + headers: { 'Upload-Start': true }, + }); + const { filename } = await chunkUpload(file, uploadConfig); + response = await Axios.post(url, + new FormData(), { + params: { ...params, filename }, + proxy: config.proxy, + headers: { 'Upload-Finish': true }, + }); + } + return wait(); +} - const taskData = new FormData(); - for (const [key, value] of Object.entries(taskDataSpec)) { - if (Array.isArray(value)) { - value.forEach((element, idx) => { - taskData.append(`${key}[${idx}]`, element); - }); - } else { - taskData.set(key, value); - } - } +async function backupProject( + id: number, + targetStorage: Storage, + useDefaultSettings: boolean, + fileName?: string, +) { + const { backendAPI } = config; + // keep current default params to 'freeze" them during this request + const params: Params = { + ...enableOrganization(), + ...configureStorage(targetStorage, useDefaultSettings), + ...(fileName ? { filename: fileName } : {}), + }; - let response = null; + const url = `${backendAPI}/projects/${id}/backup`; - onUpdate('The task is being created on the server..', null); + return new Promise((resolve, reject) => { + async function request() { try { - response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskSpec), { + const response = await Axios.get(url, { proxy: config.proxy, params, - headers: { - 'Content-Type': 'application/json', - }, }); + const isCloudStorage = targetStorage.location === StorageLocation.CLOUD_STORAGE; + const { status } = response; + if (status === 201) params.action = 'download'; + if (status === 202 || (isCloudStorage && status === 201)) { + setTimeout(request, 3000); + } else if (status === 201) { + resolve(`${url}?${new URLSearchParams(params).toString()}`); + } else if (isCloudStorage && status === 200) { + resolve(); + } } catch (errorData) { - throw generateError(errorData); + reject(generateError(errorData)); } + } - onUpdate('The data are being uploaded to the server..', null); + setTimeout(request); + }); +} - async function bulkUpload(taskId, files) { - const fileBulks = files.reduce((fileGroups, file) => { - const lastBulk = fileGroups[fileGroups.length - 1]; - if (chunkSize - lastBulk.size >= file.size) { - lastBulk.files.push(file); - lastBulk.size += file.size; - } else { - fileGroups.push({ files: [file], size: file.size }); - } - return fileGroups; - }, [{ files: [], size: 0 }]); - const totalBulks = fileBulks.length; - let currentChunkNumber = 0; - while (currentChunkNumber < totalBulks) { - for (const [idx, element] of fileBulks[currentChunkNumber].files.entries()) { - taskData.append(`client_files[${idx}]`, element); - } - const percentage = totalSentSize / totalSize; - onUpdate('The data are being uploaded to the server', percentage); - await Axios.post(`${backendAPI}/tasks/${taskId}/data`, taskData, { - ...params, +async function restoreProject(storage: Storage, file: File | string) { + const { backendAPI } = config; + // keep current default params to 'freeze" them during this request + const params: Params = { + ...enableOrganization(), + ...configureStorage(storage), + }; + + const url = `${backendAPI}/projects/backup`; + const projectData = new FormData(); + let response; + + async function wait() { + return new Promise((resolve, reject) => { + async function request() { + try { + projectData.set('rq_id', response.data.rq_id); + response = await Axios.post(`${backendAPI}/projects/backup`, projectData, { proxy: config.proxy, - headers: { 'Upload-Multiple': true }, + params, }); - for (let i = 0; i < fileBulks[currentChunkNumber].files.length; i++) { - taskData.delete(`client_files[${i}]`); + if (response.status === 202) { + setTimeout(request, 3000); + } else { + // to be able to get the task after it was created, pass frozen params + const restoredProject = await getProjects({ id: response.data.id, ...params }); + resolve(restoredProject[0]); } - totalSentSize += fileBulks[currentChunkNumber].size; - currentChunkNumber++; + } catch (errorData) { + reject(generateError(errorData)); } } - try { - await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, - taskData, { - ...params, - proxy: config.proxy, - headers: { 'Upload-Start': true }, - }); - const uploadConfig = { - endpoint: `${origin}${backendAPI}/tasks/${response.data.id}/data/`, - onUpdate: (percentage) => { - onUpdate('The data are being uploaded to the server', percentage); - }, - chunkSize, - totalSize, - totalSentSize, - }; - for (const file of chunkFiles) { - uploadConfig.totalSentSize += await chunkUpload(file, uploadConfig); - } - if (bulkFiles.length > 0) { - await bulkUpload(response.data.id, bulkFiles); - } - await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, - taskData, { - ...params, - proxy: config.proxy, - headers: { 'Upload-Finish': true }, - }); - } catch (errorData) { - try { - await deleteTask(response.data.id, params.org || null); - } catch (_) { - // ignore - } - throw generateError(errorData); - } + setTimeout(request); + }); + } - try { - await wait(response.data.id); - } catch (createException) { - await deleteTask(response.data.id, params.org || null); - throw createException; - } + const isCloudStorage = storage.location === StorageLocation.CLOUD_STORAGE; - // to be able to get the task after it was created, pass frozen params - const createdTask = await getTasks({ id: response.data.id, ...params }); - return createdTask[0]; - } + if (isCloudStorage) { + params.filename = file; + response = await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + }); + } else { + const uploadConfig = { + chunkSize: config.uploadChunkSize * 1024 * 1024, + endpoint: `${origin}${backendAPI}/projects/backup/`, + totalSentSize: 0, + totalSize: (file as File).size, + }; + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + headers: { 'Upload-Start': true }, + }); + const { filename } = await chunkUpload(file, uploadConfig); + response = await Axios.post(url, + new FormData(), { + params: { ...params, filename }, + proxy: config.proxy, + headers: { 'Upload-Finish': true }, + }); + } + return wait(); +} - async function getJobs(filter = {}) { - const { backendAPI } = config; - const id = filter.id || null; +async function createTask(taskSpec, taskDataSpec, onUpdate) { + const { backendAPI, origin } = config; + // keep current default params to 'freeze" them during this request + const params = enableOrganization(); - let response = null; - try { - if (id !== null) { - response = await Axios.get(`${backendAPI}/jobs/${id}`, { - proxy: config.proxy, - }); - } else { - response = await Axios.get(`${backendAPI}/jobs`, { - proxy: config.proxy, - params: { - ...filter, - page_size: 12, - }, - }); + async function wait(id) { + return new Promise((resolve, reject) => { + async function checkStatus() { + try { + const response = await Axios.get(`${backendAPI}/tasks/${id}/status`, { params }); + if (['Queued', 'Started'].includes(response.data.state)) { + if (response.data.message !== '') { + onUpdate(response.data.message, response.data.progress || 0); + } + setTimeout(checkStatus, 1000); + } else if (response.data.state === 'Finished') { + resolve(); + } else if (response.data.state === 'Failed') { + // If request has been successful, but task hasn't been created + // Then passed data is wrong and we can pass code 400 + const message = ` + Could not create the task on the server. ${response.data.message}. + `; + reject(new ServerError(message, 400)); + } else { + // If server has another status, it is unexpected + // Therefore it is server error and we can pass code 500 + reject( + new ServerError( + `Unknown task state has been received: ${response.data.state}`, + 500, + ), + ); + } + } catch (errorData) { + reject(generateError(errorData)); } - } catch (errorData) { - throw generateError(errorData); } - return response.data; + setTimeout(checkStatus, 1000); + }); + } + + const chunkSize = config.uploadChunkSize * 1024 * 1024; + const clientFiles = taskDataSpec.client_files; + const chunkFiles = []; + const bulkFiles = []; + let totalSize = 0; + let totalSentSize = 0; + for (const file of clientFiles) { + if (file.size > chunkSize) { + chunkFiles.push(file); + } else { + bulkFiles.push(file); } + totalSize += file.size; + } + delete taskDataSpec.client_files; - async function getJobIssues(jobID) { - const { backendAPI } = config; + const taskData = new FormData(); + for (const [key, value] of Object.entries(taskDataSpec)) { + if (Array.isArray(value)) { + value.forEach((element, idx) => { + taskData.append(`${key}[${idx}]`, element); + }); + } else { + taskData.set(key, value); + } + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/jobs/${jobID}/issues`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + let response = null; - return response.data; - } + onUpdate('The task is being created on the server..', null); + try { + response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskSpec), { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function createComment(data) { - const { backendAPI } = config; + onUpdate('The data are being uploaded to the server..', null); - let response = null; - try { - response = await Axios.post(`${backendAPI}/comments`, JSON.stringify(data), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); + async function bulkUpload(taskId, files) { + const fileBulks = files.reduce((fileGroups, file) => { + const lastBulk = fileGroups[fileGroups.length - 1]; + if (chunkSize - lastBulk.size >= file.size) { + lastBulk.files.push(file); + lastBulk.size += file.size; + } else { + fileGroups.push({ files: [file], size: file.size }); + } + return fileGroups; + }, [{ files: [], size: 0 }]); + const totalBulks = fileBulks.length; + let currentChunkNumber = 0; + while (currentChunkNumber < totalBulks) { + for (const [idx, element] of fileBulks[currentChunkNumber].files.entries()) { + taskData.append(`client_files[${idx}]`, element); + } + const percentage = totalSentSize / totalSize; + onUpdate('The data are being uploaded to the server', percentage); + await Axios.post(`${backendAPI}/tasks/${taskId}/data`, taskData, { + ...params, + proxy: config.proxy, + headers: { 'Upload-Multiple': true }, + }); + for (let i = 0; i < fileBulks[currentChunkNumber].files.length; i++) { + taskData.delete(`client_files[${i}]`); } + totalSentSize += fileBulks[currentChunkNumber].size; + currentChunkNumber++; + } + } - return response.data; + try { + await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, + taskData, { + ...params, + proxy: config.proxy, + headers: { 'Upload-Start': true }, + }); + const uploadConfig = { + endpoint: `${origin}${backendAPI}/tasks/${response.data.id}/data/`, + onUpdate: (percentage) => { + onUpdate('The data are being uploaded to the server', percentage); + }, + chunkSize, + totalSize, + totalSentSize, + }; + for (const file of chunkFiles) { + uploadConfig.totalSentSize += await chunkUpload(file, uploadConfig); + } + if (bulkFiles.length > 0) { + await bulkUpload(response.data.id, bulkFiles); + } + await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, + taskData, { + ...params, + proxy: config.proxy, + headers: { 'Upload-Finish': true }, + }); + } catch (errorData) { + try { + await deleteTask(response.data.id, params.org || null); + } catch (_) { + // ignore } + throw generateError(errorData); + } - async function createIssue(data) { - const { backendAPI } = config; + try { + await wait(response.data.id); + } catch (createException) { + await deleteTask(response.data.id, params.org || null); + throw createException; + } - let response = null; - try { - response = await Axios.post(`${backendAPI}/issues`, JSON.stringify(data), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + // to be able to get the task after it was created, pass frozen params + const createdTask = await getTasks({ id: response.data.id, ...params }); + return createdTask[0]; +} - return response.data; +async function getJobs(filter = {}) { + const { backendAPI } = config; + const id = filter.id || null; + + let response = null; + try { + if (id !== null) { + response = await Axios.get(`${backendAPI}/jobs/${id}`, { + proxy: config.proxy, + }); + } else { + response = await Axios.get(`${backendAPI}/jobs`, { + proxy: config.proxy, + params: { + ...filter, + page_size: 12, + }, + }); } + } catch (errorData) { + throw generateError(errorData); + } - async function updateIssue(issueID, data) { - const { backendAPI } = config; + return response.data; +} - let response = null; - try { - response = await Axios.patch(`${backendAPI}/issues/${issueID}`, JSON.stringify(data), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function getJobIssues(jobID) { + const { backendAPI } = config; - return response.data; - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/jobs/${jobID}/issues`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function deleteIssue(issueID) { - const { backendAPI } = config; + return response.data; +} - try { - await Axios.delete(`${backendAPI}/issues/${issueID}`); - } catch (errorData) { - throw generateError(errorData); - } - } +async function createComment(data) { + const { backendAPI } = config; - async function saveJob(id, jobData) { - const { backendAPI } = config; + let response = null; + try { + response = await Axios.post(`${backendAPI}/comments`, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - response = await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + return response.data; +} - return response.data; - } +async function createIssue(data) { + const { backendAPI } = config; - async function getUsers(filter = { page_size: 'all' }) { - const { backendAPI } = config; + let response = null; + try { + response = await Axios.post(`${backendAPI}/issues`, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/users`, { - proxy: config.proxy, - params: { - ...filter, - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + return response.data; +} - return response.data.results; - } +async function updateIssue(issueID, data) { + const { backendAPI } = config; - async function getPreview(tid, jid) { - const { backendAPI } = config; + let response = null; + try { + response = await Axios.patch(`${backendAPI}/issues/${issueID}`, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - const url = `${backendAPI}/${jid !== null ? 'jobs' : 'tasks'}/${jid || tid}/data`; - response = await Axios.get(url, { - params: { - type: 'preview', - }, - proxy: config.proxy, - responseType: 'blob', - }); - } catch (errorData) { - const code = errorData.response ? errorData.response.status : errorData.code; - throw new ServerError(`Could not get preview frame for the task ${tid} from the server`, code); - } + return response.data; +} - return response.data; - } +async function deleteIssue(issueID) { + const { backendAPI } = config; - async function getImageContext(jid, frame) { - const { backendAPI } = config; + try { + await Axios.delete(`${backendAPI}/issues/${issueID}`); + } catch (errorData) { + throw generateError(errorData); + } +} - let response = null; - try { - response = await Axios.get(`${backendAPI}/jobs/${jid}/data`, { - params: { - quality: 'original', - type: 'context_image', - number: frame, - }, - proxy: config.proxy, - responseType: 'blob', - }); - } catch (errorData) { - throw generateError(errorData); - } +async function saveJob(id, jobData) { + const { backendAPI } = config; - return response.data; - } + let response = null; + try { + response = await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function getData(tid, jid, chunk) { - const { backendAPI } = config; + return response.data; +} - const url = jid === null ? `tasks/${tid}/data` : `jobs/${jid}/data`; +async function getUsers(filter = { page_size: 'all' }) { + const { backendAPI } = config; - let response = null; - try { - response = await workerAxios.get(`${backendAPI}/${url}`, { - params: { - ...enableOrganization(), - quality: 'compressed', - type: 'chunk', - number: chunk, - }, - proxy: config.proxy, - responseType: 'arraybuffer', - }); - } catch (errorData) { - throw generateError({ - message: '', - response: { - ...errorData.response, - data: String.fromCharCode.apply(null, new Uint8Array(errorData.response.data)), - }, - }); - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/users`, { + proxy: config.proxy, + params: { + ...filter, + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - return response; - } + return response.data.results; +} - async function getMeta(session, jid) { - const { backendAPI } = config; +async function getPreview(tid, jid) { + const { backendAPI } = config; - let response = null; - try { - response = await Axios.get(`${backendAPI}/${session}s/${jid}/data/meta`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + let response = null; + try { + const url = `${backendAPI}/${jid !== null ? 'jobs' : 'tasks'}/${jid || tid}/data`; + response = await Axios.get(url, { + params: { + type: 'preview', + }, + proxy: config.proxy, + responseType: 'blob', + }); + } catch (errorData) { + const code = errorData.response ? errorData.response.status : errorData.code; + throw new ServerError(`Could not get preview frame for the task ${tid} from the server`, code); + } - return response.data; - } + return response.data; +} - async function saveMeta(session, jid, meta) { - const { backendAPI } = config; +async function getImageContext(jid, frame) { + const { backendAPI } = config; - let response = null; - try { - response = await Axios.patch(`${backendAPI}/${session}s/${jid}/data/meta`, meta, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/jobs/${jid}/data`, { + params: { + quality: 'original', + type: 'context_image', + number: frame, + }, + proxy: config.proxy, + responseType: 'blob', + }); + } catch (errorData) { + throw generateError(errorData); + } - return response.data; - } + return response.data; +} - // Session is 'task' or 'job' - async function getAnnotations(session, id) { - const { backendAPI } = config; +async function getData(tid, jid, chunk) { + const { backendAPI } = config; - let response = null; - try { - response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + const url = jid === null ? `tasks/${tid}/data` : `jobs/${jid}/data`; - return response.data; - } + let response = null; + try { + response = await workerAxios.get(`${backendAPI}/${url}`, { + params: { + ...enableOrganization(), + quality: 'compressed', + type: 'chunk', + number: chunk, + }, + proxy: config.proxy, + responseType: 'arraybuffer', + }); + } catch (errorData) { + throw generateError({ + message: '', + response: { + ...errorData.response, + data: String.fromCharCode.apply(null, new Uint8Array(errorData.response.data)), + }, + }); + } - // Session is 'task' or 'job' - async function updateAnnotations(session, id, data, action) { - const { backendAPI } = config; - const url = `${backendAPI}/${session}s/${id}/annotations`; - const params = {}; - let requestFunc = null; + return response; +} - if (action.toUpperCase() === 'PUT') { - requestFunc = Axios.put.bind(Axios); - } else { - requestFunc = Axios.patch.bind(Axios); - params.action = action; - } +async function getMeta(session, jid) { + const { backendAPI } = config; - let response = null; - try { - response = await requestFunc(url, JSON.stringify(data), { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/${session}s/${jid}/data/meta`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - return response.data; - } + return response.data; +} - // Session is 'task' or 'job' - async function uploadAnnotations( - session, - id: number, - format: string, - useDefaultLocation: boolean, - sourceStorage: Storage, - file: File | string, - options: { convMaskToPoly: boolean }, - ): Promise { - const { backendAPI, origin } = config; - const params: Params & { conv_mask_to_poly: boolean } = { - ...enableOrganization(), - ...configureStorage(sourceStorage, useDefaultLocation), - format, - filename: typeof file === 'string' ? file : file.name, - conv_mask_to_poly: options.convMaskToPoly, - }; - - const url = `${backendAPI}/${session}s/${id}/annotations`; - - async function wait() { - return new Promise((resolve, reject) => { - async function requestStatus() { - try { - const response = await Axios.put( - url, - new FormData(), - { - params, - proxy: config.proxy, - }, - ); - if (response.status === 202) { - setTimeout(requestStatus, 3000); - } else { - resolve(); - } - } catch (errorData) { - reject(generateError(errorData)); - } - } - setTimeout(requestStatus); - }); - } - const isCloudStorage = sourceStorage.location === StorageLocation.CLOUD_STORAGE; +async function saveMeta(session, jid, meta) { + const { backendAPI } = config; - if (isCloudStorage) { - try { - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } else { - const chunkSize = config.uploadChunkSize * 1024 * 1024; - const uploadConfig = { - chunkSize, - endpoint: `${origin}${backendAPI}/${session}s/${id}/annotations/`, - }; + let response = null; + try { + response = await Axios.patch(`${backendAPI}/${session}s/${jid}/data/meta`, meta, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } + + return response.data; +} + +// Session is 'task' or 'job' +async function getAnnotations(session, id) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } + + return response.data; +} + +// Session is 'task' or 'job' +async function updateAnnotations(session, id, data, action) { + const { backendAPI } = config; + const url = `${backendAPI}/${session}s/${id}/annotations`; + const params = {}; + let requestFunc = null; + + if (action.toUpperCase() === 'PUT') { + requestFunc = Axios.put.bind(Axios); + } else { + requestFunc = Axios.patch.bind(Axios); + params.action = action; + } + + let response = null; + try { + response = await requestFunc(url, JSON.stringify(data), { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } + + return response.data; +} + +// Session is 'task' or 'job' +async function uploadAnnotations( + session, + id: number, + format: string, + useDefaultLocation: boolean, + sourceStorage: Storage, + file: File | string, + options: { convMaskToPoly: boolean }, +): Promise { + const { backendAPI, origin } = config; + const params: Params & { conv_mask_to_poly: boolean } = { + ...enableOrganization(), + ...configureStorage(sourceStorage, useDefaultLocation), + format, + filename: typeof file === 'string' ? file : file.name, + conv_mask_to_poly: options.convMaskToPoly, + }; + + const url = `${backendAPI}/${session}s/${id}/annotations`; + async function wait() { + return new Promise((resolve, reject) => { + async function requestStatus() { try { - await Axios.post(url, - new FormData(), { - params, - proxy: config.proxy, - headers: { 'Upload-Start': true }, - }); - await chunkUpload(file, uploadConfig); - await Axios.post(url, - new FormData(), { + const response = await Axios.put( + url, + new FormData(), + { params, proxy: config.proxy, - headers: { 'Upload-Finish': true }, - }); + }, + ); + if (response.status === 202) { + setTimeout(requestStatus, 3000); + } else { + resolve(); + } } catch (errorData) { - throw generateError(errorData); + reject(generateError(errorData)); } } + setTimeout(requestStatus); + }); + } + const isCloudStorage = sourceStorage.location === StorageLocation.CLOUD_STORAGE; - try { - return await wait(); - } catch (errorData) { - throw generateError(errorData); - } - } - - // Session is 'task' or 'job' - async function dumpAnnotations(id, name, format) { - const { backendAPI } = config; - const baseURL = `${backendAPI}/tasks/${id}/annotations`; - const params = enableOrganization(); - params.format = encodeURIComponent(format); - if (name) { - const filename = name.replace(/\//g, '_'); - params.filename = encodeURIComponent(filename); - } - - return new Promise((resolve, reject) => { - async function request() { - Axios.get(baseURL, { - proxy: config.proxy, - params, - }) - .then((response) => { - if (response.status === 202) { - setTimeout(request, 3000); - } else { - params.action = 'download'; - resolve(`${baseURL}?${new URLSearchParams(params).toString()}`); - } - }) - .catch((errorData) => { - reject(generateError(errorData)); - }); - } - - setTimeout(request); - }); + if (isCloudStorage) { + try { + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); } + } else { + const chunkSize = config.uploadChunkSize * 1024 * 1024; + const uploadConfig = { + chunkSize, + endpoint: `${origin}${backendAPI}/${session}s/${id}/annotations/`, + }; - async function saveLogs(logs) { - const { backendAPI } = config; - - try { - await Axios.post(`${backendAPI}/server/logs`, JSON.stringify(logs), { + try { + await Axios.post(url, + new FormData(), { + params, proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Upload-Start': true }, }); - } catch (errorData) { - throw generateError(errorData); - } + await chunkUpload(file, uploadConfig); + await Axios.post(url, + new FormData(), { + params, + proxy: config.proxy, + headers: { 'Upload-Finish': true }, + }); + } catch (errorData) { + throw generateError(errorData); } + } - async function getLambdaFunctions() { - const { backendAPI } = config; + try { + return await wait(); + } catch (errorData) { + throw generateError(errorData); + } +} - try { - const response = await Axios.get(`${backendAPI}/lambda/functions`, { - proxy: config.proxy, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } - - async function runLambdaRequest(body) { - const { backendAPI } = config; +// Session is 'task' or 'job' +async function dumpAnnotations(id, name, format) { + const { backendAPI } = config; + const baseURL = `${backendAPI}/tasks/${id}/annotations`; + const params = enableOrganization(); + params.format = encodeURIComponent(format); + if (name) { + const filename = name.replace(/\//g, '_'); + params.filename = encodeURIComponent(filename); + } - try { - const response = await Axios.post(`${backendAPI}/lambda/requests`, JSON.stringify(body), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, + return new Promise((resolve, reject) => { + async function request() { + Axios.get(baseURL, { + proxy: config.proxy, + params, + }) + .then((response) => { + if (response.status === 202) { + setTimeout(request, 3000); + } else { + params.action = 'download'; + resolve(`${baseURL}?${new URLSearchParams(params).toString()}`); + } + }) + .catch((errorData) => { + reject(generateError(errorData)); }); - - return response.data; - } catch (errorData) { - throw generateError(errorData); - } } - async function callLambdaFunction(funId, body) { - const { backendAPI } = config; + setTimeout(request); + }); +} - try { - const response = await Axios.post(`${backendAPI}/lambda/functions/${funId}`, JSON.stringify(body), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); +async function saveLogs(logs) { + const { backendAPI } = config; - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + try { + await Axios.post(`${backendAPI}/server/logs`, JSON.stringify(logs), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function getLambdaRequests() { - const { backendAPI } = config; +async function getLambdaFunctions() { + const { backendAPI } = config; - try { - const response = await Axios.get(`${backendAPI}/lambda/requests`, { - proxy: config.proxy, - }); - - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + try { + const response = await Axios.get(`${backendAPI}/lambda/functions`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - async function getRequestStatus(requestID) { - const { backendAPI } = config; +async function runLambdaRequest(body) { + const { backendAPI } = config; - try { - const response = await Axios.get(`${backendAPI}/lambda/requests/${requestID}`, { - proxy: config.proxy, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + try { + const response = await Axios.post(`${backendAPI}/lambda/requests`, JSON.stringify(body), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); - async function cancelLambdaRequest(requestId) { - const { backendAPI } = config; + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - try { - await Axios.delete(`${backendAPI}/lambda/requests/${requestId}`, { - method: 'DELETE', - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function callLambdaFunction(funId, body) { + const { backendAPI } = config; - function predictorStatus(projectId) { - const { backendAPI } = config; + try { + const response = await Axios.post(`${backendAPI}/lambda/functions/${funId}`, JSON.stringify(body), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); - return new Promise((resolve, reject) => { - async function request() { - try { - const response = await Axios.get(`${backendAPI}/predict/status`, { - params: { - project: projectId, - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - const timeoutCallback = async () => { - let data = null; - try { - data = await request(); - if (data.status === 'queued') { - setTimeout(timeoutCallback, 1000); - } else if (data.status === 'done') { - resolve(data); - } else { - throw new Error(`Unknown status was received "${data.status}"`); - } - } catch (error) { - reject(error); - } - }; +async function getLambdaRequests() { + const { backendAPI } = config; - setTimeout(timeoutCallback); - }); - } + try { + const response = await Axios.get(`${backendAPI}/lambda/requests`, { + proxy: config.proxy, + }); - function predictAnnotations(taskId, frame) { - return new Promise((resolve, reject) => { - const { backendAPI } = config; - - async function request() { - try { - const response = await Axios.get(`${backendAPI}/predict/frame`, { - params: { - task: taskId, - frame, - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - const timeoutCallback = async () => { - let data = null; - try { - data = await request(); - if (data.status === 'queued') { - setTimeout(timeoutCallback, 1000); - } else if (data.status === 'done') { - predictAnnotations.latestRequest.fetching = false; - resolve(data.annotation); - } else { - throw new Error(`Unknown status was received "${data.status}"`); - } - } catch (error) { - predictAnnotations.latestRequest.fetching = false; - reject(error); - } - }; +async function getRequestStatus(requestID) { + const { backendAPI } = config; - const closureId = Date.now(); - predictAnnotations.latestRequest.id = closureId; - const predicate = () => !predictAnnotations.latestRequest.fetching || - predictAnnotations.latestRequest.id !== closureId; - if (predictAnnotations.latestRequest.fetching) { - waitFor(5, predicate).then(() => { - if (predictAnnotations.latestRequest.id !== closureId) { - resolve(null); - } else { - predictAnnotations.latestRequest.fetching = true; - setTimeout(timeoutCallback); - } - }); - } else { - predictAnnotations.latestRequest.fetching = true; - setTimeout(timeoutCallback); - } - }); - } + try { + const response = await Axios.get(`${backendAPI}/lambda/requests/${requestID}`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - predictAnnotations.latestRequest = { - fetching: false, - id: null, - }; +async function cancelLambdaRequest(requestId) { + const { backendAPI } = config; - async function installedApps() { - const { backendAPI } = config; - try { - const response = await Axios.get(`${backendAPI}/server/plugins`, { - proxy: config.proxy, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + try { + await Axios.delete(`${backendAPI}/lambda/requests/${requestId}`, { + method: 'DELETE', + }); + } catch (errorData) { + throw generateError(errorData); + } +} - async function createCloudStorage(storageDetail) { - const { backendAPI } = config; +function predictorStatus(projectId) { + const { backendAPI } = config; - const storageDetailData = prepareData(storageDetail); + return new Promise((resolve, reject) => { + async function request() { try { - const response = await Axios.post(`${backendAPI}/cloudstorages`, storageDetailData, { - proxy: config.proxy, + const response = await Axios.get(`${backendAPI}/predict/status`, { + params: { + project: projectId, + }, }); return response.data; } catch (errorData) { @@ -1802,597 +1693,651 @@ class ServerProxy { } } - async function updateCloudStorage(id, storageDetail) { - const { backendAPI } = config; - - const storageDetailData = prepareData(storageDetail); - try { - await Axios.patch(`${backendAPI}/cloudstorages/${id}`, storageDetailData, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } - - async function getCloudStorages(filter = {}) { - const { backendAPI } = config; - - let response = null; + const timeoutCallback = async () => { + let data = null; try { - response = await Axios.get(`${backendAPI}/cloudstorages`, { - proxy: config.proxy, - params: filter, - page_size: 12, - }); - } catch (errorData) { - throw generateError(errorData); + data = await request(); + if (data.status === 'queued') { + setTimeout(timeoutCallback, 1000); + } else if (data.status === 'done') { + resolve(data); + } else { + throw new Error(`Unknown status was received "${data.status}"`); + } + } catch (error) { + reject(error); } + }; - response.data.results.count = response.data.count; - return response.data.results; - } + setTimeout(timeoutCallback); + }); +} - async function getCloudStorageContent(id, manifestPath) { - const { backendAPI } = config; +function predictAnnotations(taskId, frame) { + return new Promise((resolve, reject) => { + const { backendAPI } = config; - let response = null; + async function request() { try { - const url = `${backendAPI}/cloudstorages/${id}/content${ - manifestPath ? `?manifest_path=${manifestPath}` : '' - }`; - response = await Axios.get(url, { - proxy: config.proxy, + const response = await Axios.get(`${backendAPI}/predict/frame`, { + params: { + task: taskId, + frame, + }, }); + return response.data; } catch (errorData) { throw generateError(errorData); } - - return response.data; } - async function getCloudStoragePreview(id) { - const { backendAPI } = config; - - let response = null; + const timeoutCallback = async () => { + let data = null; try { - const url = `${backendAPI}/cloudstorages/${id}/preview`; - response = await workerAxios.get(url, { - params: enableOrganization(), - proxy: config.proxy, - responseType: 'arraybuffer', - }); - } catch (errorData) { - throw generateError({ - message: '', - response: { - ...errorData.response, - data: String.fromCharCode.apply(null, new Uint8Array(errorData.response.data)), - }, - }); - } - - return new Blob([new Uint8Array(response)]); - } - - async function getCloudStorageStatus(id) { - const { backendAPI } = config; - - let response = null; - try { - const url = `${backendAPI}/cloudstorages/${id}/status`; - response = await Axios.get(url, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); + data = await request(); + if (data.status === 'queued') { + setTimeout(timeoutCallback, 1000); + } else if (data.status === 'done') { + predictAnnotations.latestRequest.fetching = false; + resolve(data.annotation); + } else { + throw new Error(`Unknown status was received "${data.status}"`); + } + } catch (error) { + predictAnnotations.latestRequest.fetching = false; + reject(error); } + }; - return response.data; + const closureId = Date.now(); + predictAnnotations.latestRequest.id = closureId; + const predicate = () => !predictAnnotations.latestRequest.fetching || + predictAnnotations.latestRequest.id !== closureId; + if (predictAnnotations.latestRequest.fetching) { + waitFor(5, predicate).then(() => { + if (predictAnnotations.latestRequest.id !== closureId) { + resolve(null); + } else { + predictAnnotations.latestRequest.fetching = true; + setTimeout(timeoutCallback); + } + }); + } else { + predictAnnotations.latestRequest.fetching = true; + setTimeout(timeoutCallback); } + }); +} - async function deleteCloudStorage(id) { - const { backendAPI } = config; +predictAnnotations.latestRequest = { + fetching: false, + id: null, +}; - try { - await Axios.delete(`${backendAPI}/cloudstorages/${id}`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function installedApps() { + const { backendAPI } = config; + try { + const response = await Axios.get(`${backendAPI}/server/plugins`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - async function getOrganizations() { - const { backendAPI } = config; +async function createCloudStorage(storageDetail) { + const { backendAPI } = config; - let response = null; - try { - response = await Axios.get(`${backendAPI}/organizations`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } + const storageDetailData = prepareData(storageDetail); + try { + const response = await Axios.post(`${backendAPI}/cloudstorages`, storageDetailData, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - return response.data; - } +async function updateCloudStorage(id, storageDetail) { + const { backendAPI } = config; - async function createOrganization(data) { - const { backendAPI } = config; + const storageDetailData = prepareData(storageDetail); + try { + await Axios.patch(`${backendAPI}/cloudstorages/${id}`, storageDetailData, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - let response = null; - try { - response = await Axios.post(`${backendAPI}/organizations`, JSON.stringify(data), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function getCloudStorages(filter = {}) { + const { backendAPI } = config; - return response.data; - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/cloudstorages`, { + proxy: config.proxy, + params: filter, + page_size: 12, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function updateOrganization(id, data) { - const { backendAPI } = config; + response.data.results.count = response.data.count; + return response.data.results; +} - let response = null; - try { - response = await Axios.patch(`${backendAPI}/organizations/${id}`, JSON.stringify(data), { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } +async function getCloudStorageContent(id, manifestPath) { + const { backendAPI } = config; - return response.data; - } + let response = null; + try { + const url = `${backendAPI}/cloudstorages/${id}/content${ + manifestPath ? `?manifest_path=${manifestPath}` : '' + }`; + response = await Axios.get(url, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function deleteOrganization(id) { - const { backendAPI } = config; + return response.data; +} - try { - await Axios.delete(`${backendAPI}/organizations/${id}`, { - proxy: config.proxy, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function getCloudStoragePreview(id) { + const { backendAPI } = config; - async function getOrganizationMembers(orgSlug, page, pageSize, filters = {}) { - const { backendAPI } = config; + let response = null; + try { + const url = `${backendAPI}/cloudstorages/${id}/preview`; + response = await workerAxios.get(url, { + params: enableOrganization(), + proxy: config.proxy, + responseType: 'arraybuffer', + }); + } catch (errorData) { + throw generateError({ + message: '', + response: { + ...errorData.response, + data: String.fromCharCode.apply(null, new Uint8Array(errorData.response.data)), + }, + }); + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/memberships`, { - proxy: config.proxy, - params: { - ...filters, - org: orgSlug, - page, - page_size: pageSize, - }, - }); - } catch (errorData) { - throw generateError(errorData); - } + return new Blob([new Uint8Array(response)]); +} - return response.data; - } +async function getCloudStorageStatus(id) { + const { backendAPI } = config; - async function inviteOrganizationMembers(orgId, data) { - const { backendAPI } = config; - try { - await Axios.post( - `${backendAPI}/invitations`, - { - ...data, - organization: orgId, - }, - { - proxy: config.proxy, - }, - ); - } catch (errorData) { - throw generateError(errorData); - } - } + let response = null; + try { + const url = `${backendAPI}/cloudstorages/${id}/status`; + response = await Axios.get(url, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function updateOrganizationMembership(membershipId, data) { - const { backendAPI } = config; - let response = null; - try { - response = await Axios.patch( - `${backendAPI}/memberships/${membershipId}`, - { - ...data, - }, - { - proxy: config.proxy, - }, - ); - } catch (errorData) { - throw generateError(errorData); - } + return response.data; +} - return response.data; - } +async function deleteCloudStorage(id) { + const { backendAPI } = config; - async function deleteOrganizationMembership(membershipId) { - const { backendAPI } = config; + try { + await Axios.delete(`${backendAPI}/cloudstorages/${id}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - try { - await Axios.delete(`${backendAPI}/memberships/${membershipId}`, { - proxy: config.proxy, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function getOrganizations() { + const { backendAPI } = config; - async function getMembershipInvitation(id) { - const { backendAPI } = config; + let response = null; + try { + response = await Axios.get(`${backendAPI}/organizations`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } - let response = null; - try { - response = await Axios.get(`${backendAPI}/invitations/${id}`, { - proxy: config.proxy, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + return response.data; +} - async function getWebhookDelivery(webhookID: number, deliveryID: number): Promise { - const params = enableOrganization(); - const { backendAPI } = config; +async function createOrganization(data) { + const { backendAPI } = config; - try { - const response = await Axios.get(`${backendAPI}/webhooks/${webhookID}/deliveries/${deliveryID}`, { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + let response = null; + try { + response = await Axios.post(`${backendAPI}/organizations`, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function getWebhooks(filter, pageSize = 10): Promise { - const params = enableOrganization(); - const { backendAPI } = config; + return response.data; +} - try { - const response = await Axios.get(`${backendAPI}/webhooks`, { - proxy: config.proxy, - params: { - ...params, - ...filter, - page_size: pageSize, - }, - headers: { - 'Content-Type': 'application/json', - }, - }); +async function updateOrganization(id, data) { + const { backendAPI } = config; - response.data.results.count = response.data.count; - return response.data.results; - } catch (errorData) { - throw generateError(errorData); - } - } + let response = null; + try { + response = await Axios.patch(`${backendAPI}/organizations/${id}`, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function createWebhook(webhookData: any): Promise { - const params = enableOrganization(); - const { backendAPI } = config; + return response.data; +} - try { - const response = await Axios.post(`${backendAPI}/webhooks`, JSON.stringify(webhookData), { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } +async function deleteOrganization(id) { + const { backendAPI } = config; - async function updateWebhook(webhookID: number, webhookData: any): Promise { - const params = enableOrganization(); - const { backendAPI } = config; + try { + await Axios.delete(`${backendAPI}/organizations/${id}`, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - try { - const response = await Axios - .patch(`${backendAPI}/webhooks/${webhookID}`, JSON.stringify(webhookData), { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } +async function getOrganizationMembers(orgSlug, page, pageSize, filters = {}) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/memberships`, { + proxy: config.proxy, + params: { + ...filters, + org: orgSlug, + page, + page_size: pageSize, + }, + }); + } catch (errorData) { + throw generateError(errorData); + } - async function deleteWebhook(webhookID: number): Promise { - const params = enableOrganization(); - const { backendAPI } = config; + return response.data; +} - try { - await Axios.delete(`${backendAPI}/webhooks/${webhookID}`, { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (errorData) { - throw generateError(errorData); - } - } +async function inviteOrganizationMembers(orgId, data) { + const { backendAPI } = config; + try { + await Axios.post( + `${backendAPI}/invitations`, + { + ...data, + organization: orgId, + }, + { + proxy: config.proxy, + }, + ); + } catch (errorData) { + throw generateError(errorData); + } +} - async function pingWebhook(webhookID: number): Promise { - const params = enableOrganization(); - const { backendAPI } = config; - - async function waitPingDelivery(deliveryID: number): Promise { - return new Promise((resolve) => { - async function checkStatus(): Promise { - const delivery = await getWebhookDelivery(webhookID, deliveryID); - if (delivery.status_code) { - resolve(delivery); - } else { - setTimeout(checkStatus, 1000); - } - } - setTimeout(checkStatus, 1000); - }); - } +async function updateOrganizationMembership(membershipId, data) { + const { backendAPI } = config; + let response = null; + try { + response = await Axios.patch( + `${backendAPI}/memberships/${membershipId}`, + { + ...data, + }, + { + proxy: config.proxy, + }, + ); + } catch (errorData) { + throw generateError(errorData); + } - try { - const response = await Axios.post(`${backendAPI}/webhooks/${webhookID}/ping`, { - proxy: config.proxy, - params, - headers: { - 'Content-Type': 'application/json', - }, - }); + return response.data; +} - const deliveryID = response.data.id; - const delivery = await waitPingDelivery(deliveryID); - return delivery; - } catch (errorData) { - throw generateError(errorData); - } - } +async function deleteOrganizationMembership(membershipId) { + const { backendAPI } = config; - async function receiveWebhookEvents(type: WebhookSourceType): Promise { - const { backendAPI } = config; + try { + await Axios.delete(`${backendAPI}/memberships/${membershipId}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - try { - const response = await Axios.get(`${backendAPI}/webhooks/events`, { - proxy: config.proxy, - params: { - type, - }, - headers: { - 'Content-Type': 'application/json', - }, - }); - return response.data.events; - } catch (errorData) { - throw generateError(errorData); - } - } +async function getMembershipInvitation(id) { + const { backendAPI } = config; - async function advancedAuthentication(): Promise { - const { backendAPI } = config; - try { - const response = await Axios.get(`${backendAPI}/server/advanced-auth`, { - proxy: config.proxy, - }); - return response.data; - } catch (errorData) { - throw generateError(errorData); - } - } + let response = null; + try { + response = await Axios.get(`${backendAPI}/invitations/${id}`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - Object.defineProperties( - this, - Object.freeze({ - server: { - value: Object.freeze({ - about, - share, - formats, - exception, - login, - logout, - advancedAuthentication, - changePassword, - requestPasswordReset, - resetPassword, - authorized, - register, - request: serverRequest, - userAgreements, - installedApps, - }), - writable: false, - }, +async function getWebhookDelivery(webhookID: number, deliveryID: number): Promise { + const params = enableOrganization(); + const { backendAPI } = config; - projects: { - value: Object.freeze({ - get: getProjects, - searchNames: searchProjectNames, - save: saveProject, - create: createProject, - delete: deleteProject, - exportDataset: exportDataset('projects'), - backup: backupProject, - restore: restoreProject, - importDataset, - }), - writable: false, - }, + try { + const response = await Axios.get(`${backendAPI}/webhooks/${webhookID}/deliveries/${deliveryID}`, { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - tasks: { - value: Object.freeze({ - get: getTasks, - save: saveTask, - create: createTask, - delete: deleteTask, - exportDataset: exportDataset('tasks'), - backup: backupTask, - restore: restoreTask, - }), - writable: false, - }, +async function getWebhooks(filter, pageSize = 10): Promise { + const params = enableOrganization(); + const { backendAPI } = config; + + try { + const response = await Axios.get(`${backendAPI}/webhooks`, { + proxy: config.proxy, + params: { + ...params, + ...filter, + page_size: pageSize, + }, + headers: { + 'Content-Type': 'application/json', + }, + }); - jobs: { - value: Object.freeze({ - get: getJobs, - save: saveJob, - exportDataset: exportDataset('jobs'), - }), - writable: false, - }, + response.data.results.count = response.data.count; + return response.data.results; + } catch (errorData) { + throw generateError(errorData); + } +} - users: { - value: Object.freeze({ - get: getUsers, - self: getSelf, - }), - writable: false, - }, +async function createWebhook(webhookData: any): Promise { + const params = enableOrganization(); + const { backendAPI } = config; - frames: { - value: Object.freeze({ - getData, - getMeta, - saveMeta, - getPreview, - getImageContext, - }), - writable: false, - }, + try { + const response = await Axios.post(`${backendAPI}/webhooks`, JSON.stringify(webhookData), { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - annotations: { - value: Object.freeze({ - updateAnnotations, - getAnnotations, - dumpAnnotations, - uploadAnnotations, - }), - writable: false, +async function updateWebhook(webhookID: number, webhookData: any): Promise { + const params = enableOrganization(); + const { backendAPI } = config; + + try { + const response = await Axios + .patch(`${backendAPI}/webhooks/${webhookID}`, JSON.stringify(webhookData), { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', }, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} - logs: { - value: Object.freeze({ - save: saveLogs, - }), - writable: false, - }, +async function deleteWebhook(webhookID: number): Promise { + const params = enableOrganization(); + const { backendAPI } = config; - lambda: { - value: Object.freeze({ - list: getLambdaFunctions, - status: getRequestStatus, - requests: getLambdaRequests, - run: runLambdaRequest, - call: callLambdaFunction, - cancel: cancelLambdaRequest, - }), - writable: false, - }, + try { + await Axios.delete(`${backendAPI}/webhooks/${webhookID}`, { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData); + } +} - issues: { - value: Object.freeze({ - create: createIssue, - update: updateIssue, - get: getJobIssues, - delete: deleteIssue, - }), - writable: false, - }, +async function pingWebhook(webhookID: number): Promise { + const params = enableOrganization(); + const { backendAPI } = config; + + async function waitPingDelivery(deliveryID: number): Promise { + return new Promise((resolve) => { + async function checkStatus(): Promise { + const delivery = await getWebhookDelivery(webhookID, deliveryID); + if (delivery.status_code) { + resolve(delivery); + } else { + setTimeout(checkStatus, 1000); + } + } + setTimeout(checkStatus, 1000); + }); + } - comments: { - value: Object.freeze({ - create: createComment, - }), - writable: false, - }, + try { + const response = await Axios.post(`${backendAPI}/webhooks/${webhookID}/ping`, { + proxy: config.proxy, + params, + headers: { + 'Content-Type': 'application/json', + }, + }); - predictor: { - value: Object.freeze({ - status: predictorStatus, - predict: predictAnnotations, - }), - writable: false, - }, + const deliveryID = response.data.id; + const delivery = await waitPingDelivery(deliveryID); + return delivery; + } catch (errorData) { + throw generateError(errorData); + } +} - cloudStorages: { - value: Object.freeze({ - get: getCloudStorages, - getContent: getCloudStorageContent, - getPreview: getCloudStoragePreview, - getStatus: getCloudStorageStatus, - create: createCloudStorage, - delete: deleteCloudStorage, - update: updateCloudStorage, - }), - writable: false, - }, +async function receiveWebhookEvents(type: WebhookSourceType): Promise { + const { backendAPI } = config; - organizations: { - value: Object.freeze({ - get: getOrganizations, - create: createOrganization, - update: updateOrganization, - members: getOrganizationMembers, - invitation: getMembershipInvitation, - delete: deleteOrganization, - invite: inviteOrganizationMembers, - updateMembership: updateOrganizationMembership, - deleteMembership: deleteOrganizationMembership, - }), - writable: false, - }, + try { + const response = await Axios.get(`${backendAPI}/webhooks/events`, { + proxy: config.proxy, + params: { + type, + }, + headers: { + 'Content-Type': 'application/json', + }, + }); + return response.data.events; + } catch (errorData) { + throw generateError(errorData); + } +} - webhooks: { - value: Object.freeze({ - get: getWebhooks, - create: createWebhook, - update: updateWebhook, - delete: deleteWebhook, - ping: pingWebhook, - events: receiveWebhookEvents, - }), - writable: false, - }, - }), - ); +async function advancedAuthentication(): Promise { + const { backendAPI } = config; + try { + const response = await Axios.get(`${backendAPI}/server/advanced-auth`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); } } -const serverProxy = new ServerProxy(); -export default serverProxy; +export default Object.freeze({ + server: Object.freeze({ + about, + share, + formats, + exception, + login, + logout, + advancedAuthentication, + changePassword, + requestPasswordReset, + resetPassword, + authorized, + register, + request: serverRequest, + userAgreements, + installedApps, + }), + + projects: Object.freeze({ + get: getProjects, + searchNames: searchProjectNames, + save: saveProject, + create: createProject, + delete: deleteProject, + exportDataset: exportDataset('projects'), + backup: backupProject, + restore: restoreProject, + importDataset, + }), + + tasks: Object.freeze({ + get: getTasks, + save: saveTask, + create: createTask, + delete: deleteTask, + exportDataset: exportDataset('tasks'), + backup: backupTask, + restore: restoreTask, + }), + + jobs: Object.freeze({ + get: getJobs, + save: saveJob, + exportDataset: exportDataset('jobs'), + }), + + users: Object.freeze({ + get: getUsers, + self: getSelf, + }), + + frames: Object.freeze({ + getData, + getMeta, + saveMeta, + getPreview, + getImageContext, + }), + + annotations: Object.freeze({ + updateAnnotations, + getAnnotations, + dumpAnnotations, + uploadAnnotations, + }), + + logs: Object.freeze({ + save: saveLogs, + }), + + lambda: Object.freeze({ + list: getLambdaFunctions, + status: getRequestStatus, + requests: getLambdaRequests, + run: runLambdaRequest, + call: callLambdaFunction, + cancel: cancelLambdaRequest, + }), + + issues: Object.freeze({ + create: createIssue, + update: updateIssue, + get: getJobIssues, + delete: deleteIssue, + }), + + comments: Object.freeze({ + create: createComment, + }), + + predictor: Object.freeze({ + status: predictorStatus, + predict: predictAnnotations, + }), + + cloudStorages: Object.freeze({ + get: getCloudStorages, + getContent: getCloudStorageContent, + getPreview: getCloudStoragePreview, + getStatus: getCloudStorageStatus, + create: createCloudStorage, + delete: deleteCloudStorage, + update: updateCloudStorage, + }), + + organizations: Object.freeze({ + get: getOrganizations, + create: createOrganization, + update: updateOrganization, + members: getOrganizationMembers, + invitation: getMembershipInvitation, + delete: deleteOrganization, + invite: inviteOrganizationMembers, + updateMembership: updateOrganizationMembership, + deleteMembership: deleteOrganizationMembership, + }), + + webhooks: Object.freeze({ + get: getWebhooks, + create: createWebhook, + update: updateWebhook, + delete: deleteWebhook, + ping: pingWebhook, + events: receiveWebhookEvents, + }), +});