Fix ESlint problems / typescript for cvat-core (#5027)

* typescripted some files

* typescripted cloud storage

* reverted api-implementation, fixed minor errors

* updated changelog

* fixed jest test

* updated file: to link:

Co-authored-by: Boris Sekachev <boris.sekachev@yandex.ru>
main
Kirill Lakhov 3 years ago committed by GitHub
parent e7c16064c4
commit f719f58df4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,6 +33,7 @@ non-ascii paths while adding files from "Connected file share" (issue #4428)
- IOG and f-BRS serverless function (<https://github.com/opencv/cvat/pulls>) - IOG and f-BRS serverless function (<https://github.com/opencv/cvat/pulls>)
- Invisible label item in label constructor when label color background is white, - Invisible label item in label constructor when label color background is white,
or close to it (<https://github.com/opencv/cvat/pull/5041>) or close to it (<https://github.com/opencv/cvat/pull/5041>)
- Fixed cvat-core ESlint problems (<https://github.com/opencv/cvat/pull/5027>)
### Security ### Security
- TDB - TDB

@ -29,7 +29,7 @@
"dependencies": { "dependencies": {
"axios": "^0.27.2", "axios": "^0.27.2",
"browser-or-node": "^2.0.0", "browser-or-node": "^2.0.0",
"cvat-data": "file:../cvat-data", "cvat-data": "link:./../cvat-data",
"detect-browser": "^5.2.1", "detect-browser": "^5.2.1",
"error-stack-parser": "^2.0.2", "error-stack-parser": "^2.0.2",
"form-data": "^4.0.0", "form-data": "^4.0.0",

@ -23,8 +23,8 @@ const config = require('./config');
const { ArgumentError } = require('./exceptions'); const { ArgumentError } = require('./exceptions');
const { Task, Job } = require('./session'); const { Task, Job } = require('./session');
const Project = require('./project').default; const Project = require('./project').default;
const { CloudStorage } = require('./cloud-storage'); const CloudStorage = require('./cloud-storage').default;
const Organization = require('./organization'); const Organization = require('./organization').default;
const Webhook = require('./webhook').default; const Webhook = require('./webhook').default;
function implementAPI(cvat) { function implementAPI(cvat) {

@ -10,20 +10,20 @@
function build() { function build() {
const PluginRegistry = require('./plugins').default; const PluginRegistry = require('./plugins').default;
const loggerStorage = require('./logger-storage'); const loggerStorage = require('./logger-storage').default;
const Log = require('./log'); const { Log } = require('./log');
const ObjectState = require('./object-state').default; const ObjectState = require('./object-state').default;
const Statistics = require('./statistics'); const Statistics = require('./statistics');
const Comment = require('./comment'); const Comment = require('./comment').default;
const Issue = require('./issue'); const Issue = require('./issue').default;
const { Job, Task } = require('./session'); const { Job, Task } = require('./session');
const Project = require('./project').default; const Project = require('./project').default;
const implementProject = require('./project-implementation').default; const implementProject = require('./project-implementation').default;
const { Attribute, Label } = require('./labels'); const { Attribute, Label } = require('./labels');
const MLModel = require('./ml-model'); const MLModel = require('./ml-model');
const { FrameData } = require('./frames'); const { FrameData } = require('./frames');
const { CloudStorage } = require('./cloud-storage'); const CloudStorage = require('./cloud-storage').default;
const Organization = require('./organization'); const Organization = require('./organization').default;
const Webhook = require('./webhook').default; const Webhook = require('./webhook').default;
const enums = require('./enums'); const enums = require('./enums');

@ -1,29 +1,64 @@
// Copyright (C) 2021-2022 Intel Corporation // Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
(() => { import { isBrowser, isNode } from 'browser-or-node';
const PluginRegistry = require('./plugins').default; import PluginRegistry from './plugins';
const serverProxy = require('./server-proxy').default; import serverProxy from './server-proxy';
const { isBrowser, isNode } = require('browser-or-node'); import { ArgumentError } from './exceptions';
const { ArgumentError } = require('./exceptions'); import { CloudStorageCredentialsType, CloudStorageProviderType, CloudStorageStatus } from './enums';
const { CloudStorageCredentialsType, CloudStorageProviderType } = require('./enums'); import User from './user';
function validateNotEmptyString(value) { function validateNotEmptyString(value: string): void {
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw new ArgumentError(`Value must be a string. ${typeof value} was found`); throw new ArgumentError(`Value must be a string. ${typeof value} was found`);
} else if (!value.trim().length) { } else if (!value.trim().length) {
throw new ArgumentError('Value mustn\'t be empty string'); throw new ArgumentError('Value mustn\'t be empty string');
} }
} }
interface RawCloudStorageData {
id?: number;
display_name?: string;
description?: string,
credentials_type?: CloudStorageCredentialsType,
provider_type?: CloudStorageProviderType,
resource?: string,
account_name?: string,
key?: string,
secret_key?: string,
session_token?: string,
key_file?: File,
specific_attributes?: string,
owner?: any,
created_date?: string,
updated_date?: string,
manifest_path?: string,
manifests?: string[],
}
export default class CloudStorage {
public readonly id: number;
public displayName: string;
public description: string;
public accountName: string;
public accessKey: string;
public secretKey: string;
public token: string;
public keyFile: File;
public resource: string;
public manifestPath: string;
public provider_type: CloudStorageProviderType;
public credentials_type: CloudStorageCredentialsType;
public specificAttributes: string;
public manifests: string[];
public readonly owner: User;
public readonly createdDate: string;
public readonly updatedDate: string;
/** constructor(initialData: RawCloudStorageData) {
* Class representing a cloud storage const data: RawCloudStorageData = {
* @memberof module:API.cvat.classes
*/
class CloudStorage {
constructor(initialData) {
const data = {
id: undefined, id: undefined,
display_name: undefined, display_name: undefined,
description: undefined, description: undefined,
@ -52,24 +87,9 @@
Object.defineProperties( Object.defineProperties(
this, this,
Object.freeze({ Object.freeze({
/**
* @name id
* @type {number}
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
*/
id: { id: {
get: () => data.id, get: () => data.id,
}, },
/**
* Storage name
* @name displayName
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
displayName: { displayName: {
get: () => data.display_name, get: () => data.display_name,
set: (value) => { set: (value) => {
@ -77,14 +97,6 @@
data.display_name = value; data.display_name = value;
}, },
}, },
/**
* Storage description
* @name description
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
description: { description: {
get: () => data.description, get: () => data.description,
set: (value) => { set: (value) => {
@ -94,14 +106,6 @@
data.description = value; data.description = value;
}, },
}, },
/**
* Azure account name
* @name accountName
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
accountName: { accountName: {
get: () => data.account_name, get: () => data.account_name,
set: (value) => { set: (value) => {
@ -109,14 +113,6 @@
data.account_name = value; data.account_name = value;
}, },
}, },
/**
* AWS access key id
* @name accessKey
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
accessKey: { accessKey: {
get: () => data.key, get: () => data.key,
set: (value) => { set: (value) => {
@ -124,14 +120,6 @@
data.key = value; data.key = value;
}, },
}, },
/**
* AWS secret key
* @name secretKey
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
secretKey: { secretKey: {
get: () => data.secret_key, get: () => data.secret_key,
set: (value) => { set: (value) => {
@ -139,14 +127,6 @@
data.secret_key = value; data.secret_key = value;
}, },
}, },
/**
* Session token
* @name token
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
token: { token: {
get: () => data.session_token, get: () => data.session_token,
set: (value) => { set: (value) => {
@ -154,14 +134,6 @@
data.session_token = value; data.session_token = value;
}, },
}, },
/**
* Key file
* @name keyFile
* @type {File}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
keyFile: { keyFile: {
get: () => data.key_file, get: () => data.key_file,
set: (file) => { set: (file) => {
@ -172,14 +144,6 @@
} }
}, },
}, },
/**
* Unique resource name
* @name resource
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
resource: { resource: {
get: () => data.resource, get: () => data.resource,
set: (value) => { set: (value) => {
@ -187,13 +151,6 @@
data.resource = value; data.resource = value;
}, },
}, },
/**
* @name manifestPath
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
manifestPath: { manifestPath: {
get: () => data.manifest_path, get: () => data.manifest_path,
set: (value) => { set: (value) => {
@ -201,13 +158,6 @@
data.manifest_path = value; data.manifest_path = value;
}, },
}, },
/**
* @name providerType
* @type {module:API.cvat.enums.ProviderType}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
providerType: { providerType: {
get: () => data.provider_type, get: () => data.provider_type,
set: (key) => { set: (key) => {
@ -218,13 +168,6 @@
} }
}, },
}, },
/**
* @name credentialsType
* @type {module:API.cvat.enums.CloudStorageCredentialsType}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
credentialsType: { credentialsType: {
get: () => data.credentials_type, get: () => data.credentials_type,
set: (key) => { set: (key) => {
@ -235,13 +178,6 @@
} }
}, },
}, },
/**
* @name specificAttributes
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
specificAttributes: { specificAttributes: {
get: () => data.specific_attributes, get: () => data.specific_attributes,
set: (attributesValue) => { set: (attributesValue) => {
@ -260,44 +196,15 @@
} }
}, },
}, },
/**
* Instance of a user who has created the cloud storage
* @name owner
* @type {module:API.cvat.classes.User}
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
*/
owner: { owner: {
get: () => data.owner, get: () => data.owner,
}, },
/**
* @name createdDate
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
*/
createdDate: { createdDate: {
get: () => data.created_date, get: () => data.created_date,
}, },
/**
* @name updatedDate
* @type {string}
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
*/
updatedDate: { updatedDate: {
get: () => data.updated_date, get: () => data.updated_date,
}, },
/**
* @name manifests
* @type {string[]}
* @memberof module:API.cvat.classes.CloudStorage
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
manifests: { manifests: {
get: () => data.manifests, get: () => data.manifests,
set: (manifests) => { set: (manifests) => {
@ -317,86 +224,40 @@
); );
} }
/** // Method updates data of a created cloud storage or creates new cloud storage
* Method updates data of a created cloud storage or creates new cloud storage public async save(): Promise<CloudStorage> {
* @method save
* @returns {module:API.cvat.classes.CloudStorage}
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async save() {
const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.save); const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.save);
return result; return result;
} }
/** public async delete(): Promise<void> {
* Method deletes a cloud storage from a server
* @method delete
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async delete() {
const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.delete); const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.delete);
return result; return result;
} }
/** public async getContent(): Promise<any> {
* Method returns cloud storage content
* @method getContent
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async getContent() {
const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getContent); const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getContent);
return result; return result;
} }
/** public async getPreview(): Promise<string | ArrayBuffer> {
* Method returns the cloud storage preview
* @method getPreview
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async getPreview() {
const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getPreview); const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getPreview);
return result; return result;
} }
/** public async getStatus(): Promise<CloudStorageStatus> {
* Method returns cloud storage status
* @method getStatus
* @memberof module:API.cvat.classes.CloudStorage
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async getStatus() {
const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getStatus); const result = await PluginRegistry.apiWrapper.call(this, CloudStorage.prototype.getStatus);
return result; return result;
} }
} }
CloudStorage.prototype.save.implementation = async function () { Object.defineProperties(CloudStorage.prototype.save, {
function prepareOptionalFields(cloudStorageInstance) { implementation: {
const data = {}; writable: false,
enumerable: false,
value: async function implementation(): Promise<CloudStorage> {
function prepareOptionalFields(cloudStorageInstance: CloudStorage): RawCloudStorageData {
const data: RawCloudStorageData = {};
if (cloudStorageInstance.description !== undefined) { if (cloudStorageInstance.description !== undefined) {
data.description = cloudStorageInstance.description; data.description = cloudStorageInstance.description;
} }
@ -430,7 +291,7 @@
if (typeof this.id !== 'undefined') { if (typeof this.id !== 'undefined') {
// provider_type and recource should not change; // provider_type and recource should not change;
// send to the server only the values that have changed // send to the server only the values that have changed
const initialData = {}; const initialData: RawCloudStorageData = {};
if (this.displayName) { if (this.displayName) {
initialData.display_name = this.displayName; initialData.display_name = this.displayName;
} }
@ -452,7 +313,7 @@
} }
// create // create
const initialData = { const initialData: RawCloudStorageData = {
display_name: this.displayName, display_name: this.displayName,
credentials_type: this.credentialsType, credentials_type: this.credentialsType,
provider_type: this.providerType, provider_type: this.providerType,
@ -467,19 +328,37 @@
const cloudStorage = await serverProxy.cloudStorages.create(cloudStorageData); const cloudStorage = await serverProxy.cloudStorages.create(cloudStorageData);
return new CloudStorage(cloudStorage); return new CloudStorage(cloudStorage);
}; },
},
});
CloudStorage.prototype.delete.implementation = async function () { Object.defineProperties(CloudStorage.prototype.delete, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(): Promise<void> {
const result = await serverProxy.cloudStorages.delete(this.id); const result = await serverProxy.cloudStorages.delete(this.id);
return result; return result;
}; },
},
});
CloudStorage.prototype.getContent.implementation = async function () { Object.defineProperties(CloudStorage.prototype.getContent, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(): Promise<any> {
const result = await serverProxy.cloudStorages.getContent(this.id, this.manifestPath); const result = await serverProxy.cloudStorages.getContent(this.id, this.manifestPath);
return result; return result;
}; },
},
});
CloudStorage.prototype.getPreview.implementation = async function getPreview() { Object.defineProperties(CloudStorage.prototype.getPreview, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(): Promise<string | ArrayBuffer> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
serverProxy.cloudStorages serverProxy.cloudStorages
.getPreview(this.id) .getPreview(this.id)
@ -498,14 +377,17 @@
reject(error); reject(error);
}); });
}); });
}; },
},
});
CloudStorage.prototype.getStatus.implementation = async function () { Object.defineProperties(CloudStorage.prototype.getStatus, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(): Promise<CloudStorageStatus> {
const result = await serverProxy.cloudStorages.getStatus(this.id); const result = await serverProxy.cloudStorages.getStatus(this.id);
return result; return result;
}; },
},
module.exports = { });
CloudStorage,
};
})();

@ -3,17 +3,31 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const User = require('./user').default; import User from './user';
const { ArgumentError } = require('./exceptions'); import { ArgumentError } from './exceptions';
/** export interface RawCommentData {
* Class representing a single comment id?: number;
* @memberof module:API.cvat.classes message?: string;
* @hideconstructor created_date?: string;
*/ updated_date?: string;
class Comment { owner?: any;
constructor(initialData) { }
const data = {
interface SerializedCommentData extends RawCommentData{
owner_id?: number;
issue?: number;
}
export default class Comment {
public readonly id: number;
public readonly createdDate: string;
public readonly updatedDate: string;
public readonly owner: User;
public message: string;
constructor(initialData: RawCommentData) {
const data: RawCommentData = {
id: undefined, id: undefined,
message: undefined, message: undefined,
created_date: undefined, created_date: undefined,
@ -35,23 +49,9 @@ class Comment {
Object.defineProperties( Object.defineProperties(
this, this,
Object.freeze({ Object.freeze({
/**
* @name id
* @type {number}
* @memberof module:API.cvat.classes.Comment
* @readonly
* @instance
*/
id: { id: {
get: () => data.id, get: () => data.id,
}, },
/**
* @name message
* @type {string}
* @memberof module:API.cvat.classes.Comment
* @instance
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
message: { message: {
get: () => data.message, get: () => data.message,
set: (value) => { set: (value) => {
@ -61,34 +61,12 @@ class Comment {
data.message = value; data.message = value;
}, },
}, },
/**
* @name createdDate
* @type {string}
* @memberof module:API.cvat.classes.Comment
* @readonly
* @instance
*/
createdDate: { createdDate: {
get: () => data.created_date, get: () => data.created_date,
}, },
/**
* @name updatedDate
* @type {string}
* @memberof module:API.cvat.classes.Comment
* @readonly
* @instance
*/
updatedDate: { updatedDate: {
get: () => data.updated_date, get: () => data.updated_date,
}, },
/**
* Instance of a user who has created the comment
* @name owner
* @type {module:API.cvat.classes.User}
* @memberof module:API.cvat.classes.Comment
* @readonly
* @instance
*/
owner: { owner: {
get: () => data.owner, get: () => data.owner,
}, },
@ -99,8 +77,8 @@ class Comment {
); );
} }
serialize() { public serialize(): SerializedCommentData {
const data = { const data: SerializedCommentData = {
message: this.message, message: this.message,
}; };
@ -120,5 +98,3 @@ class Comment {
return data; return data;
} }
} }
module.exports = Comment;

@ -60,7 +60,7 @@ export function checkExclusiveFields(obj, exclusive, ignore): void {
} }
} }
export function checkObjectType(name, value, type, instance): boolean { export function checkObjectType(name, value, type, instance?): boolean {
if (type) { if (type) {
if (typeof value !== type) { if (typeof value !== type) {
// specific case for integers which aren't native type in JS // specific case for integers which aren't native type in JS

@ -389,6 +389,22 @@ export enum CloudStorageCredentialsType {
KEY_FILE_PATH = 'KEY_FILE_PATH', KEY_FILE_PATH = 'KEY_FILE_PATH',
} }
/**
* Types of cloud storage statuses
* @enum {string}
* @name CloudStorageStatus
* @memberof module:API.cvat.enums
* @property {string} AVAILABLE 'AVAILABLE'
* @property {string} NOT_FOUND 'NOT_FOUND'
* @property {string} FORBIDDEN 'FORBIDDEN'
* @readonly
*/
export enum CloudStorageStatus {
AVAILABLE = 'AVAILABLE',
NOT_FOUND = 'NOT_FOUND',
FORBIDDEN = 'FORBIDDEN',
}
/** /**
* Membership roles * Membership roles
* @enum {string} * @enum {string}

@ -3,22 +3,38 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const quickhull = require('quickhull'); import quickhull from 'quickhull';
const PluginRegistry = require('./plugins').default; import { Job } from 'session';
const Comment = require('./comment'); import PluginRegistry from './plugins';
const User = require('./user').default; import Comment, { RawCommentData } from './comment';
const { ArgumentError } = require('./exceptions'); import User from './user';
const serverProxy = require('./server-proxy').default; import { ArgumentError } from './exceptions';
import serverProxy from './server-proxy';
interface RawIssueData {
id?: number;
job?: any;
position?: number[];
comments?: any;
frame?: number;
owner?: any;
resolved?: boolean;
created_date?: string;
}
/** export default class Issue {
* Class representing a single issue public readonly id: number;
* @memberof module:API.cvat.classes public readonly job: Job;
* @hideconstructor public readonly comments: Comment[];
*/ public readonly frame: number;
class Issue { public readonly owner: User;
constructor(initialData) { public readonly resolved: boolean;
const data = { public readonly createdDate: string;
public position: number[];
constructor(initialData: RawIssueData) {
const data: RawIssueData = {
id: undefined, id: undefined,
job: undefined, job: undefined,
position: undefined, position: undefined,
@ -48,25 +64,9 @@ class Issue {
Object.defineProperties( Object.defineProperties(
this, this,
Object.freeze({ Object.freeze({
/**
* @name id
* @type {number}
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
*/
id: { id: {
get: () => data.id, get: () => data.id,
}, },
/**
* Region of interests of the issue
* @name position
* @type {number[]}
* @memberof module:API.cvat.classes.Issue
* @instance
* @readonly
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
position: { position: {
get: () => data.position, get: () => data.position,
set: (value) => { set: (value) => {
@ -76,69 +76,21 @@ class Issue {
data.position = value; data.position = value;
}, },
}, },
/**
* ID of a job, the issue is linked with
* @name job
* @type {number}
* @memberof module:API.cvat.classes.Issue
* @instance
* @readonly
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
job: { job: {
get: () => data.job, get: () => data.job,
}, },
/**
* List of comments attached to the issue
* @name comments
* @type {module:API.cvat.classes.Comment[]}
* @memberof module:API.cvat.classes.Issue
* @instance
* @readonly
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
comments: { comments: {
get: () => [...data.comments], get: () => [...data.comments],
}, },
/**
* @name frame
* @type {number}
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
*/
frame: { frame: {
get: () => data.frame, get: () => data.frame,
}, },
/**
* @name createdDate
* @type {string}
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
*/
createdDate: { createdDate: {
get: () => data.created_date, get: () => data.created_date,
}, },
/**
* An instance of a user who has raised the issue
* @name owner
* @type {module:API.cvat.classes.User}
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
*/
owner: { owner: {
get: () => data.owner, get: () => data.owner,
}, },
/**
* The flag defines issue status
* @name resolved
* @type {module:API.cvat.classes.User}
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
*/
resolved: { resolved: {
get: () => data.resolved, get: () => data.resolved,
}, },
@ -149,7 +101,7 @@ class Issue {
); );
} }
static hull(coordinates) { public static hull(coordinates: number[]): number[] {
if (coordinates.length > 4) { if (coordinates.length > 4) {
const points = coordinates.reduce((acc, coord, index, arr) => { const points = coordinates.reduce((acc, coord, index, arr) => {
if (index % 2) acc.push({ x: arr[index - 1], y: coord }); if (index % 2) acc.push({ x: arr[index - 1], y: coord });
@ -164,82 +116,36 @@ class Issue {
return coordinates; return coordinates;
} }
/** // Method appends a comment to the issue
* @typedef {Object} CommentData // For a new issue it saves comment locally, for a saved issue it saves comment on the server
* @property {string} message a comment message public async comment(data: RawCommentData): Promise<void> {
* @global
*/
/**
* Method appends a comment to the issue
* For a new issue it saves comment locally, for a saved issue it saves comment on the server
* @method comment
* @memberof module:API.cvat.classes.Issue
* @param {CommentData} data
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async comment(data) {
const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.comment, data); const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.comment, data);
return result; return result;
} }
/** // The method resolves the issue
* The method resolves the issue // New issues are resolved locally, server-saved issues are resolved on the server
* New issues are resolved locally, server-saved issues are resolved on the server public async resolve(user: User): Promise<void> {
* @method resolve
* @memberof module:API.cvat.classes.Issue
* @param {module:API.cvat.classes.User} user
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async resolve(user) {
const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.resolve, user); const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.resolve, user);
return result; return result;
} }
/** // The method reopens the issue
* The method resolves the issue // New issues are reopened locally, server-saved issues are reopened on the server
* New issues are reopened locally, server-saved issues are reopened on the server public async reopen(): Promise<void> {
* @method reopen
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async reopen() {
const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.reopen); const result = await PluginRegistry.apiWrapper.call(this, Issue.prototype.reopen);
return result; return result;
} }
/** // The method deletes the issue
* The method deletes the issue // Deletes local or server-saved issues
* Deletes local or server-saved issues public async delete(): Promise<void> {
* @method delete
* @memberof module:API.cvat.classes.Issue
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async delete() {
await PluginRegistry.apiWrapper.call(this, Issue.prototype.delete); await PluginRegistry.apiWrapper.call(this, Issue.prototype.delete);
} }
serialize() { public serialize(): RawIssueData {
const { comments } = this; const { comments } = this;
const data = { const data: RawIssueData = {
position: this.position, position: this.position,
frame: this.frame, frame: this.frame,
comments: comments.map((comment) => comment.serialize()), comments: comments.map((comment) => comment.serialize()),
@ -265,7 +171,11 @@ class Issue {
} }
} }
Issue.prototype.comment.implementation = async function (data) { Object.defineProperties(Issue.prototype.comment, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(data: RawCommentData) {
if (typeof data !== 'object' || data === null) { if (typeof data !== 'object' || data === null) {
throw new ArgumentError(`The argument "data" must be an object. Got "${data}"`); throw new ArgumentError(`The argument "data" must be an object. Got "${data}"`);
} }
@ -283,11 +193,18 @@ Issue.prototype.comment.implementation = async function (data) {
} else { } else {
this.__internal.comments.push(comment); this.__internal.comments.push(comment);
} }
}; },
},
});
Issue.prototype.resolve.implementation = async function (user) { Object.defineProperties(Issue.prototype.resolve, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(user: User) {
if (!(user instanceof User)) { if (!(user instanceof User)) {
throw new ArgumentError(`The argument "user" must be an instance of a User class. Got "${typeof user}"`); throw new ArgumentError(`The argument "user" must be an
instance of a User class. Got "${typeof user}"`);
} }
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
@ -296,22 +213,34 @@ Issue.prototype.resolve.implementation = async function (user) {
} else { } else {
this.__internal.resolved = true; this.__internal.resolved = true;
} }
}; },
},
});
Issue.prototype.reopen.implementation = async function () { Object.defineProperties(Issue.prototype.reopen, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
const response = await serverProxy.issues.update(this.id, { resolved: false }); const response = await serverProxy.issues.update(this.id, { resolved: false });
this.__internal.resolved = response.resolved; this.__internal.resolved = response.resolved;
} else { } else {
this.__internal.resolved = false; this.__internal.resolved = false;
} }
}; },
},
});
Issue.prototype.delete.implementation = async function () { Object.defineProperties(Issue.prototype.delete, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
const { id } = this; const { id } = this;
if (id >= 0) { if (id >= 0) {
await serverProxy.issues.delete(id); await serverProxy.issues.delete(id);
} }
}; },
},
module.exports = Issue; });

@ -1,19 +1,23 @@
// Copyright (C) 2019-2022 Intel Corporation // Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const { detect } = require('detect-browser'); import { detect } from 'detect-browser';
const PluginRegistry = require('./plugins').default; import PluginRegistry from './plugins';
const { ArgumentError } = require('./exceptions'); import { LogType } from './enums';
const { LogType } = require('./enums'); import { ArgumentError } from './exceptions';
/** export class Log {
* Class representing a single log public readonly id: number;
* @memberof module:API.cvat.classes public readonly type: LogType;
* @hideconstructor public readonly time: Date;
*/
class Log { public payload: any;
constructor(logType, payload) {
protected onCloseCallback: (() => void) | null;
constructor(logType: LogType, payload: any) {
this.onCloseCallback = null; this.onCloseCallback = null;
this.type = logType; this.type = logType;
@ -21,11 +25,11 @@ class Log {
this.time = new Date(); this.time = new Date();
} }
onClose(callback) { public onClose(callback: () => void): void {
this.onCloseCallback = callback; this.onCloseCallback = callback;
} }
validatePayload() { public validatePayload(): void {
if (typeof this.payload !== 'object') { if (typeof this.payload !== 'object') {
throw new ArgumentError('Payload must be an object'); throw new ArgumentError('Payload must be an object');
} }
@ -38,7 +42,7 @@ class Log {
} }
} }
dump() { public dump(): any {
const payload = { ...this.payload }; const payload = { ...this.payload };
const body = { const body = {
name: this.type, name: this.type,
@ -58,38 +62,33 @@ class Log {
}; };
} }
/** // Method saves a durable log in a storage
* Method saves a durable log in a storage <br> // Note then you can call close() multiple times
* Note then you can call close() multiple times <br> // Log duration will be computed based on the latest call
* Log duration will be computed based on the latest call <br> // All payloads will be shallowly combined (all top level properties will exist)
* All payloads will be shallowly combined (all top level properties will exist) public async close(payload = {}): Promise<void> {
* @method close
* @memberof module:API.cvat.classes.Log
* @param {object} [payload] part of payload can be added when close a log
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async close(payload = {}) {
const result = await PluginRegistry.apiWrapper.call(this, Log.prototype.close, payload); const result = await PluginRegistry.apiWrapper.call(this, Log.prototype.close, payload);
return result; return result;
} }
} }
Log.prototype.close.implementation = function (payload) { Object.defineProperties(Log.prototype.close, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(payload: any) {
this.payload.duration = Date.now() - this.time.getTime(); this.payload.duration = Date.now() - this.time.getTime();
this.payload = { ...this.payload, ...payload }; this.payload = { ...this.payload, ...payload };
if (this.onCloseCallback) { if (this.onCloseCallback) {
this.onCloseCallback(); this.onCloseCallback();
} }
}; },
},
});
class LogWithCount extends Log { class LogWithCount extends Log {
validatePayload() { public validatePayload(): void {
Log.prototype.validatePayload.call(this); super.validatePayload.call(this);
if (!Number.isInteger(this.payload.count) || this.payload.count < 1) { if (!Number.isInteger(this.payload.count) || this.payload.count < 1) {
const message = `The field "count" is required for "${this.type}" log. It must be a positive integer`; const message = `The field "count" is required for "${this.type}" log. It must be a positive integer`;
throw new ArgumentError(message); throw new ArgumentError(message);
@ -98,8 +97,8 @@ class LogWithCount extends Log {
} }
class LogWithObjectsInfo extends Log { class LogWithObjectsInfo extends Log {
validatePayload() { public validatePayload(): void {
const generateError = (name, range) => { const generateError = (name: string, range: string): void => {
const message = `The field "${name}" is required for "${this.type}" log. ${range}`; const message = `The field "${name}" is required for "${this.type}" log. ${range}`;
throw new ArgumentError(message); throw new ArgumentError(message);
}; };
@ -139,13 +138,12 @@ class LogWithObjectsInfo extends Log {
} }
class LogWithWorkingTime extends Log { class LogWithWorkingTime extends Log {
validatePayload() { public validatePayload(): void {
Log.prototype.validatePayload.call(this); super.validatePayload.call(this);
if ( if (
!( !('working_time' in this.payload) ||
'working_time' in this.payload) || !(typeof this.payload.working_time === 'number') ||
!typeof this.payload.working_time === 'number' ||
this.payload.working_time < 0 this.payload.working_time < 0
) { ) {
const message = ` const message = `
@ -157,8 +155,8 @@ class LogWithWorkingTime extends Log {
} }
class LogWithExceptionInfo extends Log { class LogWithExceptionInfo extends Log {
validatePayload() { public validatePayload(): void {
Log.prototype.validatePayload.call(this); super.validatePayload.call(this);
if (typeof this.payload.message !== 'string') { if (typeof this.payload.message !== 'string') {
const message = `The field "message" is required for ${this.type} log. It must be a string`; const message = `The field "message" is required for ${this.type} log. It must be a string`;
@ -186,7 +184,7 @@ class LogWithExceptionInfo extends Log {
} }
} }
dump() { public dump(): any {
let body = super.dump(); let body = super.dump();
const { payload } = body; const { payload } = body;
const client = detect(); const client = detect();
@ -212,7 +210,7 @@ class LogWithExceptionInfo extends Log {
} }
} }
function logFactory(logType, payload) { export default function logFactory(logType: LogType, payload: any): Log {
const logsWithCount = [ const logsWithCount = [
LogType.deleteObject, LogType.deleteObject,
LogType.mergeObjects, LogType.mergeObjects,
@ -238,5 +236,3 @@ function logFactory(logType, payload) {
return new Log(logType, payload); return new Log(logType, payload);
} }
module.exports = logFactory;

@ -1,12 +1,13 @@
// Copyright (C) 2019-2022 Intel Corporation // Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const PluginRegistry = require('./plugins').default; import PluginRegistry from './plugins';
const serverProxy = require('./server-proxy').default; import serverProxy from './server-proxy';
const logFactory = require('./log'); import logFactory, { Log } from './log';
const { ArgumentError } = require('./exceptions'); import { LogType } from './enums';
const { LogType } = require('./enums'); import { ArgumentError } from './exceptions';
const WORKING_TIME_THRESHOLD = 100000; // ms, 1.66 min const WORKING_TIME_THRESHOLD = 100000; // ms, 1.66 min
@ -16,36 +17,49 @@ function sleep(ms): Promise<void> {
}); });
} }
interface IgnoreRule {
lastLog: Log | null;
timeThreshold?: number;
ignore: (previousLog: Log, currentPayload: any) => boolean;
}
class LoggerStorage { class LoggerStorage {
public clientID: string;
public lastLogTime: number;
public workingTime: number;
public collection: Array<Log>;
public ignoreRules: Record<LogType.zoomImage | LogType.changeAttribute, IgnoreRule>;
public isActiveChecker: (() => boolean) | null;
public saving: boolean;
constructor() { constructor() {
this.clientID = Date.now().toString().substr(-6); this.clientID = Date.now().toString().substr(-6);
this.lastLogTime = Date.now(); this.lastLogTime = Date.now();
this.workingTime = 0; this.workingTime = 0;
this.collection = []; this.collection = [];
this.ignoreRules = {}; // by event
this.isActiveChecker = null; this.isActiveChecker = null;
this.saving = false; this.saving = false;
this.ignoreRules = {
this.ignoreRules[LogType.zoomImage] = { [LogType.zoomImage]: {
lastLog: null, lastLog: null,
timeThreshold: 1000, timeThreshold: 1000,
ignore(previousLog) { ignore(previousLog: Log) {
return Date.now() - previousLog.time < this.timeThreshold; return (Date.now() - previousLog.time.getTime()) < this.timeThreshold;
}, },
}; },
[LogType.changeAttribute]: {
this.ignoreRules[LogType.changeAttribute] = {
lastLog: null, lastLog: null,
ignore(previousLog, currentPayload) { ignore(previousLog: Log, currentPayload: any) {
return ( return (
currentPayload.object_id === previousLog.payload.object_id && currentPayload.object_id === previousLog.payload.object_id &&
currentPayload.id === previousLog.payload.id currentPayload.id === previousLog.payload.id
); );
}, },
},
}; };
} }
updateWorkingTime() { protected updateWorkingTime(): void {
if (!this.isActiveChecker || this.isActiveChecker()) { if (!this.isActiveChecker || this.isActiveChecker()) {
const lastLogTime = Date.now(); const lastLogTime = Date.now();
const diff = lastLogTime - this.lastLogTime; const diff = lastLogTime - this.lastLogTime;
@ -54,7 +68,7 @@ class LoggerStorage {
} }
} }
async configure(isActiveChecker, activityHelper) { public async configure(isActiveChecker, activityHelper): Promise<void> {
const result = await PluginRegistry.apiWrapper.call( const result = await PluginRegistry.apiWrapper.call(
this, this,
LoggerStorage.prototype.configure, LoggerStorage.prototype.configure,
@ -64,18 +78,22 @@ class LoggerStorage {
return result; return result;
} }
async log(logType, payload = {}, wait = false) { public async log(logType: LogType, payload = {}, wait = false): Promise<Log> {
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.log, logType, payload, wait); const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.log, logType, payload, wait);
return result; return result;
} }
async save() { public async save(): Promise<void> {
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.save); const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.save);
return result; return result;
} }
} }
LoggerStorage.prototype.configure.implementation = function (isActiveChecker, userActivityCallback) { Object.defineProperties(LoggerStorage.prototype.configure, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(isActiveChecker: () => boolean, userActivityCallback: Array<any>) {
if (typeof isActiveChecker !== 'function') { if (typeof isActiveChecker !== 'function') {
throw new ArgumentError('isActiveChecker argument must be callable'); throw new ArgumentError('isActiveChecker argument must be callable');
} }
@ -86,15 +104,21 @@ LoggerStorage.prototype.configure.implementation = function (isActiveChecker, us
this.isActiveChecker = () => !!isActiveChecker(); this.isActiveChecker = () => !!isActiveChecker();
userActivityCallback.push(this.updateWorkingTime.bind(this)); userActivityCallback.push(this.updateWorkingTime.bind(this));
}; },
},
});
LoggerStorage.prototype.log.implementation = function (logType, payload, wait) { Object.defineProperties(LoggerStorage.prototype.log, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(logType: LogType, payload: any, wait: boolean) {
if (typeof payload !== 'object') { if (typeof payload !== 'object') {
throw new ArgumentError('Payload must be an object'); throw new ArgumentError('Payload must be an object');
} }
if (typeof wait !== 'boolean') { if (typeof wait !== 'boolean') {
throw new ArgumentError('Payload must be an object'); throw new ArgumentError('Wait must be boolean');
} }
if (logType in this.ignoreRules) { if (logType in this.ignoreRules) {
@ -122,7 +146,7 @@ LoggerStorage.prototype.log.implementation = function (logType, payload, wait) {
this.ignoreRules[logType].lastLog = log; this.ignoreRules[logType].lastLog = log;
} }
const pushEvent = () => { const pushEvent = (): void => {
this.updateWorkingTime(); this.updateWorkingTime();
log.validatePayload(); log.validatePayload();
log.onClose(null); log.onClose(null);
@ -144,9 +168,15 @@ LoggerStorage.prototype.log.implementation = function (logType, payload, wait) {
} }
return log; return log;
}; },
},
});
LoggerStorage.prototype.save.implementation = async function () { Object.defineProperties(LoggerStorage.prototype.save, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
while (this.saving) { while (this.saving) {
await sleep(100); await sleep(100);
} }
@ -154,9 +184,11 @@ LoggerStorage.prototype.save.implementation = async function () {
const collectionToSend = [...this.collection]; const collectionToSend = [...this.collection];
const lastLog = this.collection[this.collection.length - 1]; const lastLog = this.collection[this.collection.length - 1];
const logPayload = {}; const logPayload: any = {
logPayload.client_id = this.clientID; client_id: this.clientID,
logPayload.working_time = this.workingTime; working_time: this.workingTime,
};
if (this.isActiveChecker) { if (this.isActiveChecker) {
logPayload.is_active = this.isActiveChecker(); logPayload.is_active = this.isActiveChecker();
} }
@ -172,7 +204,7 @@ LoggerStorage.prototype.save.implementation = async function () {
try { try {
this.saving = true; this.saving = true;
await serverProxy.logs.save(collectionToSend.map((log) => log.dump())); await serverProxy.logs.save(collectionToSend.map((log) => log.dump()));
for (const rule of Object.values(this.ignoreRules)) { for (const rule of Object.values<IgnoreRule>(this.ignoreRules)) {
rule.lastLog = null; rule.lastLog = null;
} }
this.collection = []; this.collection = [];
@ -181,6 +213,8 @@ LoggerStorage.prototype.save.implementation = async function () {
} finally { } finally {
this.saving = false; this.saving = false;
} }
}; },
},
});
module.exports = new LoggerStorage(); export default new LoggerStorage();

@ -3,34 +3,54 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const { checkObjectType, isEnum } = require('./common'); import { checkObjectType, isEnum } from './common';
const config = require('./config'); import config from './config';
const { MembershipRole } = require('./enums'); import { MembershipRole } from './enums';
const { ArgumentError, ServerError } = require('./exceptions'); import { ArgumentError, ServerError } from './exceptions';
const PluginRegistry = require('./plugins').default; import PluginRegistry from './plugins';
const serverProxy = require('./server-proxy').default; import serverProxy from './server-proxy';
const User = require('./user').default; import User from './user';
/** interface RawOrganizationData {
* Class representing an organization id?: number,
* @memberof module:API.cvat.classes slug?: string,
*/ name?: string,
class Organization { description?: string,
/** created_date?: string,
* @param {object} initialData - Object which is used for initialization updated_date?: string,
* <br> It must contains keys: owner?: any,
* <br> <li style="margin-left: 10px;"> slug contact?: OrganizationContact,
}
* <br> It can contains keys:
* <br> <li style="margin-left: 10px;"> name interface OrganizationContact {
* <br> <li style="margin-left: 10px;"> description email?: string;
* <br> <li style="margin-left: 10px;"> owner location?: string;
* <br> <li style="margin-left: 10px;"> created_date phoneNumber?: string
* <br> <li style="margin-left: 10px;"> updated_date }
* <br> <li style="margin-left: 10px;"> contact
*/ interface Membership {
constructor(initialData) { user: User;
const data = { is_active: boolean;
joined_date: string;
role: MembershipRole;
invitation: {
created_date: string;
owner: User;
} | null;
}
export default class Organization {
public readonly id: number;
public readonly slug: string;
public readonly createdDate: string;
public readonly updatedDate: string;
public readonly owner: User;
public contact: OrganizationContact;
public name: string;
public description: string;
constructor(initialData: RawOrganizationData) {
const data: RawOrganizationData = {
id: undefined, id: undefined,
slug: undefined, slug: undefined,
name: undefined, name: undefined,
@ -66,7 +86,9 @@ class Organization {
checkObjectType('contact', data.contact, 'object'); checkObjectType('contact', data.contact, 'object');
for (const prop in data.contact) { for (const prop in data.contact) {
if (typeof data.contact[prop] !== 'string') { if (typeof data.contact[prop] !== 'string') {
throw ArgumentError(`Contact fields must be strings, tried to set ${typeof data.contact[prop]}`); throw new ArgumentError(
`Contact fields must be strings,tried to set ${typeof data.contact[prop]}`,
);
} }
} }
} }
@ -86,7 +108,7 @@ class Organization {
get: () => data.name, get: () => data.name,
set: (name) => { set: (name) => {
if (typeof name !== 'string') { if (typeof name !== 'string') {
throw ArgumentError(`Name property must be a string, tried to set ${typeof description}`); throw new ArgumentError(`Name property must be a string, tried to set ${typeof name}`);
} }
data.name = name; data.name = name;
}, },
@ -95,7 +117,7 @@ class Organization {
get: () => data.description, get: () => data.description,
set: (description) => { set: (description) => {
if (typeof description !== 'string') { if (typeof description !== 'string') {
throw ArgumentError( throw new ArgumentError(
`Description property must be a string, tried to set ${typeof description}`, `Description property must be a string, tried to set ${typeof description}`,
); );
} }
@ -106,11 +128,13 @@ class Organization {
get: () => ({ ...data.contact }), get: () => ({ ...data.contact }),
set: (contact) => { set: (contact) => {
if (typeof contact !== 'object') { if (typeof contact !== 'object') {
throw ArgumentError(`Contact property must be an object, tried to set ${typeof contact}`); throw new ArgumentError(`Contact property must be an object, tried to set ${typeof contact}`);
} }
for (const prop in contact) { for (const prop in contact) {
if (typeof contact[prop] !== 'string') { if (typeof contact[prop] !== 'string') {
throw ArgumentError(`Contact fields must be strings, tried to set ${typeof contact[prop]}`); throw new ArgumentError(
`Contact fields must be strings, tried to set ${typeof contact[prop]}`,
);
} }
} }
data.contact = { ...contact }; data.contact = { ...contact };
@ -128,37 +152,14 @@ class Organization {
}); });
} }
/** // Method updates organization data if it was created before, or creates a new organization
* Method updates organization data if it was created before, or creates a new organization public async save(): Promise<Organization> {
* @method save
* @returns {module:API.cvat.classes.Organization}
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async save() {
const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.save); const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.save);
return result; return result;
} }
/** // Method returns paginatable list of organization members
* Method returns paginatable list of organization members public async members(page = 1, page_size = 10): Promise<Membership[]> {
* @method save
* @returns {module:API.cvat.classes.Organization}
* @param page page number
* @param page_size number of results per page
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async members(page = 1, page_size = 10) {
const result = await PluginRegistry.apiWrapper.call( const result = await PluginRegistry.apiWrapper.call(
this, this,
Organization.prototype.members, Organization.prototype.members,
@ -169,75 +170,27 @@ class Organization {
return result; return result;
} }
/** // Method removes the organization
* Method removes the organization public async remove(): Promise<void> {
* @method remove
* @returns {module:API.cvat.classes.Organization}
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async remove() {
const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.remove); const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.remove);
return result; return result;
} }
/** // Method invites new members by email
* Method invites new members by email public async invite(email: string, role: MembershipRole): Promise<void> {
* @method invite
* @returns {module:API.cvat.classes.Organization}
* @param {string} email
* @param {string} role
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async invite(email, role) {
const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.invite, email, role); const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.invite, email, role);
return result; return result;
} }
/** // Method allows a user to get out from an organization
* Method allows a user to get out from an organization // The difference between deleteMembership is that membershipId is unknown in this case
* The difference between deleteMembership is that membershipId is unknown in this case public async leave(user: User): Promise<void> {
* @method leave
* @returns {module:API.cvat.classes.Organization}
* @memberof module:API.cvat.classes.Organization
* @param {module:API.cvat.classes.User} user
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async leave(user) {
const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.leave, user); const result = await PluginRegistry.apiWrapper.call(this, Organization.prototype.leave, user);
return result; return result;
} }
/** // Method allows to change a membership role
* Method allows to change a membership role public async updateMembership(membershipId: number, role: MembershipRole): Promise<void> {
* @method updateMembership
* @returns {module:API.cvat.classes.Organization}
* @param {number} membershipId
* @param {string} role
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async updateMembership(membershipId, role) {
const result = await PluginRegistry.apiWrapper.call( const result = await PluginRegistry.apiWrapper.call(
this, this,
Organization.prototype.updateMembership, Organization.prototype.updateMembership,
@ -247,20 +200,8 @@ class Organization {
return result; return result;
} }
/** // Method allows to kick a user from an organization
* Method allows to kick a user from an organization public async deleteMembership(membershipId: number): Promise<void> {
* @method deleteMembership
* @returns {module:API.cvat.classes.Organization}
* @param {number} membershipId
* @memberof module:API.cvat.classes.Organization
* @readonly
* @instance
* @async
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
*/
async deleteMembership(membershipId) {
const result = await PluginRegistry.apiWrapper.call( const result = await PluginRegistry.apiWrapper.call(
this, this,
Organization.prototype.deleteMembership, Organization.prototype.deleteMembership,
@ -270,7 +211,11 @@ class Organization {
} }
} }
Organization.prototype.save.implementation = async function () { Object.defineProperties(Organization.prototype.save, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
const organizationData = { const organizationData = {
name: this.name || this.slug, name: this.name || this.slug,
@ -291,9 +236,15 @@ Organization.prototype.save.implementation = async function () {
const result = await serverProxy.organizations.create(organizationData); const result = await serverProxy.organizations.create(organizationData);
return new Organization(result); return new Organization(result);
}; },
},
});
Organization.prototype.members.implementation = async function (orgSlug, page, pageSize) { Object.defineProperties(Organization.prototype.members, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(orgSlug: string, page: number, pageSize: number) {
checkObjectType('orgSlug', orgSlug, 'string'); checkObjectType('orgSlug', orgSlug, 'string');
checkObjectType('page', page, 'number'); checkObjectType('page', page, 'number');
checkObjectType('pageSize', pageSize, 'number'); checkObjectType('pageSize', pageSize, 'number');
@ -320,16 +271,28 @@ Organization.prototype.members.implementation = async function (orgSlug, page, p
result.results.count = result.count; result.results.count = result.count;
return result.results; return result.results;
}; },
},
});
Organization.prototype.remove.implementation = async function () { Object.defineProperties(Organization.prototype.remove, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
await serverProxy.organizations.delete(this.id); await serverProxy.organizations.delete(this.id);
config.organizationID = null; config.organizationID = null;
} }
}; },
},
});
Organization.prototype.invite.implementation = async function (email, role) { Object.defineProperties(Organization.prototype.invite, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(email: string, role: MembershipRole) {
checkObjectType('email', email, 'string'); checkObjectType('email', email, 'string');
if (!isEnum.bind(MembershipRole)(role)) { if (!isEnum.bind(MembershipRole)(role)) {
throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`); throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`);
@ -338,9 +301,15 @@ Organization.prototype.invite.implementation = async function (email, role) {
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
await serverProxy.organizations.invite(this.id, { email, role }); await serverProxy.organizations.invite(this.id, { email, role });
} }
}; },
},
});
Organization.prototype.updateMembership.implementation = async function (membershipId, role) { Object.defineProperties(Organization.prototype.updateMembership, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(membershipId: number, role: MembershipRole) {
checkObjectType('membershipId', membershipId, 'number'); checkObjectType('membershipId', membershipId, 'number');
if (!isEnum.bind(MembershipRole)(role)) { if (!isEnum.bind(MembershipRole)(role)) {
throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`); throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`);
@ -349,16 +318,28 @@ Organization.prototype.updateMembership.implementation = async function (members
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
await serverProxy.organizations.updateMembership(membershipId, { role }); await serverProxy.organizations.updateMembership(membershipId, { role });
} }
}; },
},
});
Organization.prototype.deleteMembership.implementation = async function (membershipId) { Object.defineProperties(Organization.prototype.deleteMembership, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(membershipId: number) {
checkObjectType('membershipId', membershipId, 'number'); checkObjectType('membershipId', membershipId, 'number');
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
await serverProxy.organizations.deleteMembership(membershipId); await serverProxy.organizations.deleteMembership(membershipId);
} }
}; },
},
});
Organization.prototype.leave.implementation = async function (user) { Object.defineProperties(Organization.prototype.leave, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(user: User) {
checkObjectType('user', user, null, User); checkObjectType('user', user, null, User);
if (typeof this.id === 'number') { if (typeof this.id === 'number') {
const result = await serverProxy.organizations.members(this.slug, 1, 10, { const result = await serverProxy.organizations.members(this.slug, 1, 10, {
@ -370,10 +351,12 @@ Organization.prototype.leave.implementation = async function (user) {
}); });
const [membership] = result.results; const [membership] = result.results;
if (!membership) { if (!membership) {
throw new ServerError(`Could not find membership for user ${user.username} in organization ${this.slug}`); throw new ServerError(
`Could not find membership for user ${user.username} in organization ${this.slug}`,
);
} }
await serverProxy.organizations.deleteMembership(membership.id); await serverProxy.organizations.deleteMembership(membership.id);
} }
}; },
},
module.exports = Organization; });

@ -7,7 +7,7 @@ import { StorageLocation } from './enums';
import { Storage } from './storage'; import { Storage } from './storage';
const PluginRegistry = require('./plugins').default; const PluginRegistry = require('./plugins').default;
const loggerStorage = require('./logger-storage'); const loggerStorage = require('./logger-storage').default;
const serverProxy = require('./server-proxy').default; const serverProxy = require('./server-proxy').default;
const { const {
getFrame, getFrame,
@ -27,7 +27,7 @@ const {
} = require('./enums'); } = require('./enums');
const { Label } = require('./labels'); const { Label } = require('./labels');
const User = require('./user').default; const User = require('./user').default;
const Issue = require('./issue'); const Issue = require('./issue').default;
const { FieldUpdateTrigger, checkObjectType } = require('./common'); const { FieldUpdateTrigger, checkObjectType } = require('./common');
function buildDuplicatedAPI(prototype) { function buildDuplicatedAPI(prototype) {

@ -14,7 +14,7 @@ jest.mock('../../src/server-proxy', () => {
// Initialize api // Initialize api
window.cvat = require('../../src/api'); window.cvat = require('../../src/api');
const { CloudStorage } = require('../../src/cloud-storage'); const CloudStorage= require('../../src/cloud-storage').default;
const { cloudStoragesDummyData } = require('../mocks/dummy-data.mock'); const { cloudStoragesDummyData } = require('../mocks/dummy-data.mock');
describe('Feature: get cloud storages', () => { describe('Feature: get cloud storages', () => {

@ -1,6 +1,8 @@
// Copyright (C) 2020-2022 Intel Corporation // Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import 'redux-thunk/extend-redux';
declare module '*.svg'; declare module '*.svg';
declare module 'cvat-core/src/api'; declare module 'cvat-core/src/api';

@ -35,6 +35,9 @@
"@types/resize-observer-browser": "^0.1.6", "@types/resize-observer-browser": "^0.1.6",
"antd": "~4.18.9", "antd": "~4.18.9",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"cvat-canvas": "link:./../cvat-canvas",
"cvat-canvas3d": "link:./../cvat-canvas3d",
"cvat-core": "link:./../cvat-core",
"dotenv-webpack": "^7.1.0", "dotenv-webpack": "^7.1.0",
"error-stack-parser": "^2.0.6", "error-stack-parser": "^2.0.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",

@ -64,7 +64,7 @@ function WebhooksPage(): JSX.Element | null {
} }
useEffect(() => { useEffect(() => {
if (projectsMatch) { if (projectsMatch && projectsMatch.params.id) {
const { id } = projectsMatch.params; const { id } = projectsMatch.params;
setOnCreateParams(`projectId=${id}`); setOnCreateParams(`projectId=${id}`);
dispatch(getWebhooksAsync({ ...updatedQuery, projectId: +id })); dispatch(getWebhooksAsync({ ...updatedQuery, projectId: +id }));

@ -1,4 +1,5 @@
// Copyright (C) 2021-2022 Intel Corporation // Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@ -13,5 +14,7 @@ import {
} from 'cvat-canvas3d/src/typescript/canvas3d'; } from 'cvat-canvas3d/src/typescript/canvas3d';
export { export {
Canvas3d, Canvas3dVersion, MouseInteraction, ViewType, CameraAction, ViewsDOM, CanvasMode, Canvas3d, Canvas3dVersion, MouseInteraction, ViewType, CameraAction, CanvasMode,
}; };
export type { ViewsDOM };

@ -3,7 +3,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// eslint-disable-next-line import/no-extraneous-dependencies
import { Canvas3d } from 'cvat-canvas3d/src/typescript/canvas3d'; import { Canvas3d } from 'cvat-canvas3d/src/typescript/canvas3d';
import { Canvas, RectDrawingMethod, CuboidDrawingMethod } from 'cvat-canvas-wrapper'; import { Canvas, RectDrawingMethod, CuboidDrawingMethod } from 'cvat-canvas-wrapper';
import { Webhook } from 'cvat-core-wrapper'; import { Webhook } from 'cvat-core-wrapper';

@ -4017,7 +4017,42 @@ custom-error-instance@2.1.1:
resolved "https://registry.yarnpkg.com/custom-error-instance/-/custom-error-instance-2.1.1.tgz#3cf6391487a6629a6247eb0ca0ce00081b7e361a" resolved "https://registry.yarnpkg.com/custom-error-instance/-/custom-error-instance-2.1.1.tgz#3cf6391487a6629a6247eb0ca0ce00081b7e361a"
integrity sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg== integrity sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==
"cvat-data@file:cvat-data": "cvat-canvas3d@link:./cvat-canvas3d":
version "0.0.1"
dependencies:
"@types/three" "^0.125.3"
camera-controls "^1.25.3"
three "^0.126.1"
"cvat-canvas@link:./cvat-canvas":
version "2.15.4"
dependencies:
"@types/polylabel" "^1.0.5"
polylabel "^1.1.0"
svg.draggable.js "2.2.2"
svg.draw.js "^2.0.4"
svg.js "2.7.1"
svg.resize.js "1.4.3"
svg.select.js "3.0.1"
"cvat-core@link:./cvat-core":
version "7.0.0"
dependencies:
axios "^0.27.2"
browser-or-node "^2.0.0"
cvat-data "link:./cvat-data"
detect-browser "^5.2.1"
error-stack-parser "^2.0.2"
form-data "^4.0.0"
jest-config "^28.1.2"
js-cookie "^3.0.1"
json-logic-js "^2.0.1"
platform "^1.3.5"
quickhull "^1.0.3"
store "^2.0.12"
tus-js-client "^2.3.0"
"cvat-data@link:./cvat-data":
version "1.0.2" version "1.0.2"
dependencies: dependencies:
async-mutex "^0.3.2" async-mutex "^0.3.2"

Loading…
Cancel
Save