[cvat-core] support cloud storage (#3313)
* Update server-proxy * Add cloud storage implementation && update enums * Add api && fix server-proxy * Add fixes * small update * Move from ui_support_cloud_storage * Remove temporary credentials && fix typos * Remove trailing spaces * Apply lost changes * manifest_set -> manifests * [cvat-core] Add status && updated getpreview for getting the desired error message from the server * Remove excess code * Fix missing * Add tests * move from cycle * Update CHANGELOG * Increase versionmain
parent
9f31fb3882
commit
720d79845b
@ -0,0 +1,520 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const PluginRegistry = require('./plugins');
|
||||||
|
const serverProxy = require('./server-proxy');
|
||||||
|
const { isBrowser, isNode } = require('browser-or-node');
|
||||||
|
const { ArgumentError } = require('./exceptions');
|
||||||
|
const { CloudStorageCredentialsType, CloudStorageProviderType } = require('./enums');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a cloud storage
|
||||||
|
* @memberof module:API.cvat.classes
|
||||||
|
*/
|
||||||
|
class CloudStorage {
|
||||||
|
// TODO: add storage availability status (avaliable/unavaliable)
|
||||||
|
constructor(initialData) {
|
||||||
|
const data = {
|
||||||
|
id: undefined,
|
||||||
|
display_name: undefined,
|
||||||
|
description: undefined,
|
||||||
|
credentials_type: undefined,
|
||||||
|
provider_type: undefined,
|
||||||
|
resource: undefined,
|
||||||
|
account_name: undefined,
|
||||||
|
key: undefined,
|
||||||
|
secret_key: undefined,
|
||||||
|
session_token: undefined,
|
||||||
|
specific_attributes: undefined,
|
||||||
|
owner: undefined,
|
||||||
|
created_date: undefined,
|
||||||
|
updated_date: undefined,
|
||||||
|
manifest_path: undefined,
|
||||||
|
manifests: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const property in data) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, property) && property in initialData) {
|
||||||
|
data[property] = initialData[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperties(
|
||||||
|
this,
|
||||||
|
Object.freeze({
|
||||||
|
/**
|
||||||
|
* @name id
|
||||||
|
* @type {integer}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @readonly
|
||||||
|
* @instance
|
||||||
|
*/
|
||||||
|
id: {
|
||||||
|
get: () => data.id,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Storage name
|
||||||
|
* @name displayName
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
displayName: {
|
||||||
|
get: () => data.display_name,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new ArgumentError(`Value must be string. ${typeof value} was found`);
|
||||||
|
} else if (!value.trim().length) {
|
||||||
|
throw new ArgumentError('Value must not be empty string');
|
||||||
|
}
|
||||||
|
data.display_name = value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Storage description
|
||||||
|
* @name description
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
description: {
|
||||||
|
get: () => data.description,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new ArgumentError('Value must be string');
|
||||||
|
}
|
||||||
|
data.description = value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Azure account name
|
||||||
|
* @name accountName
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
accountName: {
|
||||||
|
get: () => data.account_name,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (value.trim().length) {
|
||||||
|
data.account_name = value;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must not be empty');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError(`Value must be a string. ${typeof value} was found`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* AWS access key id
|
||||||
|
* @name accessKey
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
accessKey: {
|
||||||
|
get: () => data.key,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (value.trim().length) {
|
||||||
|
data.key = value;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must not be empty');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError(`Value must be a string. ${typeof value} was found`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* AWS secret key
|
||||||
|
* @name secretKey
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
secretKey: {
|
||||||
|
get: () => data.secret_key,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (value.trim().length) {
|
||||||
|
data.secret_key = value;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must not be empty');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError(`Value must be a string. ${typeof value} was found`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Session token
|
||||||
|
* @name token
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
token: {
|
||||||
|
get: () => data.session_token,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (value.trim().length) {
|
||||||
|
data.session_token = value;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must not be empty');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError(`Value must be a string. ${typeof value} was found`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Unique resource name
|
||||||
|
* @name resourceName
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
resourceName: {
|
||||||
|
get: () => data.resource,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new ArgumentError(`Value must be string. ${typeof value} was found`);
|
||||||
|
} else if (!value.trim().length) {
|
||||||
|
throw new ArgumentError('Value must not be empty');
|
||||||
|
}
|
||||||
|
data.resource = value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name manifestPath
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
manifestPath: {
|
||||||
|
get: () => data.manifest_path,
|
||||||
|
set: (value) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
data.manifest_path = value;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must be a string');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name providerType
|
||||||
|
* @type {module:API.cvat.enums.ProviderType}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
providerType: {
|
||||||
|
get: () => data.provider_type,
|
||||||
|
set: (key) => {
|
||||||
|
if (key !== undefined && !!CloudStorageProviderType[key]) {
|
||||||
|
data.provider_type = CloudStorageProviderType[key];
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must be one CloudStorageProviderType keys');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name credentialsType
|
||||||
|
* @type {module:API.cvat.enums.CloudStorageCredentialsType}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
credentialsType: {
|
||||||
|
get: () => data.credentials_type,
|
||||||
|
set: (key) => {
|
||||||
|
if (key !== undefined && !!CloudStorageCredentialsType[key]) {
|
||||||
|
data.credentials_type = CloudStorageCredentialsType[key];
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must be one CloudStorageCredentialsType keys');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name specificAttributes
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
specificAttributes: {
|
||||||
|
get: () => data.specific_attributes,
|
||||||
|
set: (attributesValue) => {
|
||||||
|
if (typeof attributesValue === 'string') {
|
||||||
|
const attrValues = new URLSearchParams(
|
||||||
|
Array.from(new URLSearchParams(attributesValue).entries()).filter(
|
||||||
|
([key, value]) => !!key && !!value,
|
||||||
|
),
|
||||||
|
).toString();
|
||||||
|
if (!attrValues) {
|
||||||
|
throw new ArgumentError('Value must match the key1=value1&key2=value2');
|
||||||
|
}
|
||||||
|
data.specific_attributes = attributesValue;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must be a string');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 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: {
|
||||||
|
get: () => data.owner,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name createdDate
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @readonly
|
||||||
|
* @instance
|
||||||
|
*/
|
||||||
|
createdDate: {
|
||||||
|
get: () => data.created_date,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name updatedDate
|
||||||
|
* @type {string}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @readonly
|
||||||
|
* @instance
|
||||||
|
*/
|
||||||
|
updatedDate: {
|
||||||
|
get: () => data.updated_date,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @name manifests
|
||||||
|
* @type {string[]}
|
||||||
|
* @memberof module:API.cvat.classes.CloudStorage
|
||||||
|
* @instance
|
||||||
|
* @throws {module:API.cvat.exceptions.ArgumentError}
|
||||||
|
*/
|
||||||
|
manifests: {
|
||||||
|
get: () => data.manifests,
|
||||||
|
set: (manifests) => {
|
||||||
|
if (Array.isArray(manifests)) {
|
||||||
|
for (const elem of manifests) {
|
||||||
|
if (typeof elem !== 'string') {
|
||||||
|
throw new ArgumentError('Each element of the manifests array must be a string');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.manifests = manifests;
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError('Value must be an array');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method updates data of a created cloud storage or creates new cloud storage
|
||||||
|
* @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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudStorage.prototype.save.implementation = async function () {
|
||||||
|
function prepareOptionalFields(cloudStorageInstance) {
|
||||||
|
const data = {};
|
||||||
|
if (cloudStorageInstance.description) {
|
||||||
|
data.description = cloudStorageInstance.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudStorageInstance.accountName) {
|
||||||
|
data.account_name = cloudStorageInstance.accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudStorageInstance.accessKey) {
|
||||||
|
data.key = cloudStorageInstance.accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudStorageInstance.secretKey) {
|
||||||
|
data.secret_key = cloudStorageInstance.secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudStorageInstance.token) {
|
||||||
|
data.session_token = cloudStorageInstance.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cloudStorageInstance.specificAttributes) {
|
||||||
|
data.specific_attributes = cloudStorageInstance.specificAttributes;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
if (typeof this.id !== 'undefined') {
|
||||||
|
// providr_type and recource should not change;
|
||||||
|
// send to the server only the values that have changed
|
||||||
|
const initialData = {};
|
||||||
|
if (this.displayName) {
|
||||||
|
initialData.display_name = this.displayName;
|
||||||
|
}
|
||||||
|
if (this.credentialsType) {
|
||||||
|
initialData.credentials_type = this.credentialsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.manifests) {
|
||||||
|
initialData.manifests = this.manifests;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudStorageData = {
|
||||||
|
...initialData,
|
||||||
|
...prepareOptionalFields(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
await serverProxy.cloudStorages.update(this.id, cloudStorageData);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create
|
||||||
|
const initialData = {
|
||||||
|
display_name: this.displayName,
|
||||||
|
credentials_type: this.credentialsType,
|
||||||
|
provider_type: this.providerType,
|
||||||
|
resource: this.resourceName,
|
||||||
|
manifests: this.manifests,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloudStorageData = {
|
||||||
|
...initialData,
|
||||||
|
...prepareOptionalFields(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloudStorage = await serverProxy.cloudStorages.create(cloudStorageData);
|
||||||
|
return new CloudStorage(cloudStorage);
|
||||||
|
};
|
||||||
|
|
||||||
|
CloudStorage.prototype.delete.implementation = async function () {
|
||||||
|
const result = await serverProxy.cloudStorages.delete(this.id);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
CloudStorage.prototype.getContent.implementation = async function () {
|
||||||
|
const result = await serverProxy.cloudStorages.getContent(this.id, this.manifestPath);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
CloudStorage.prototype.getPreview.implementation = async function getPreview() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
serverProxy.cloudStorages
|
||||||
|
.getPreview(this.id)
|
||||||
|
.then((result) => {
|
||||||
|
if (isNode) {
|
||||||
|
resolve(global.Buffer.from(result, 'binary').toString('base64'));
|
||||||
|
} else if (isBrowser) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloudStorage.prototype.getStatus.implementation = async function () {
|
||||||
|
const result = await serverProxy.cloudStorages.getStatus(this.id);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CloudStorage,
|
||||||
|
};
|
||||||
|
})();
|
||||||
@ -0,0 +1,178 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Setup mock for a server
|
||||||
|
jest.mock('../../src/server-proxy', () => {
|
||||||
|
const mock = require('../mocks/server-proxy.mock');
|
||||||
|
return mock;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize api
|
||||||
|
window.cvat = require('../../src/api');
|
||||||
|
|
||||||
|
const { CloudStorage } = require('../../src/cloud-storage');
|
||||||
|
const { cloudStoragesDummyData } = require('../mocks/dummy-data.mock');
|
||||||
|
|
||||||
|
describe('Feature: get cloud storages', () => {
|
||||||
|
test('get all cloud storages', async () => {
|
||||||
|
const result = await window.cvat.cloudStorages.get();
|
||||||
|
expect(Array.isArray(result)).toBeTruthy();
|
||||||
|
expect(result).toHaveLength(cloudStoragesDummyData.count);
|
||||||
|
for (const item of result) {
|
||||||
|
expect(item).toBeInstanceOf(CloudStorage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get cloud storage by id', async () => {
|
||||||
|
const result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
const cloudStorage = result[0];
|
||||||
|
|
||||||
|
expect(Array.isArray(result)).toBeTruthy();
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(cloudStorage).toBeInstanceOf(CloudStorage);
|
||||||
|
expect(cloudStorage.id).toBe(1);
|
||||||
|
expect(cloudStorage.providerType).toBe('AWS_S3_BUCKET');
|
||||||
|
expect(cloudStorage.credentialsType).toBe('KEY_SECRET_KEY_PAIR');
|
||||||
|
expect(cloudStorage.resourceName).toBe('bucket');
|
||||||
|
expect(cloudStorage.displayName).toBe('Demonstration bucket');
|
||||||
|
expect(cloudStorage.manifests).toHaveLength(1);
|
||||||
|
expect(cloudStorage.manifests[0]).toBe('manifest.jsonl');
|
||||||
|
expect(cloudStorage.specificAttributes).toBe('');
|
||||||
|
expect(cloudStorage.description).toBe('It is first bucket');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get a cloud storage by an unknown id', async () => {
|
||||||
|
const result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 10,
|
||||||
|
});
|
||||||
|
expect(Array.isArray(result)).toBeTruthy();
|
||||||
|
expect(result).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get a cloud storage by an invalid id', async () => {
|
||||||
|
expect(
|
||||||
|
window.cvat.cloudStorages.get({
|
||||||
|
id: '1',
|
||||||
|
}),
|
||||||
|
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get cloud storages by filters', async () => {
|
||||||
|
const filters = new Map([
|
||||||
|
['providerType', 'AWS_S3_BUCKET'],
|
||||||
|
['resourceName', 'bucket'],
|
||||||
|
['displayName', 'Demonstration bucket'],
|
||||||
|
['credentialsType', 'KEY_SECRET_KEY_PAIR'],
|
||||||
|
['description', 'It is first bucket'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await window.cvat.cloudStorages.get(Object.fromEntries(filters));
|
||||||
|
|
||||||
|
const [cloudStorage] = result;
|
||||||
|
expect(Array.isArray(result)).toBeTruthy();
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(cloudStorage).toBeInstanceOf(CloudStorage);
|
||||||
|
expect(cloudStorage.id).toBe(1);
|
||||||
|
filters.forEach((value, key) => {
|
||||||
|
expect(cloudStorage[key]).toBe(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get cloud storage by invalid filters', async () => {
|
||||||
|
expect(
|
||||||
|
window.cvat.cloudStorages.get({
|
||||||
|
unknown: '5',
|
||||||
|
}),
|
||||||
|
).rejects.toThrow(window.cvat.exceptions.ArgumentError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Feature: create a cloud storage', () => {
|
||||||
|
test('create new cloud storage without an id', async () => {
|
||||||
|
const cloudStorage = new window.cvat.classes.CloudStorage({
|
||||||
|
display_name: 'new cloud storage',
|
||||||
|
provider_type: 'AZURE_CONTAINER',
|
||||||
|
resource: 'newcontainer',
|
||||||
|
credentials_type: 'ACCOUNT_NAME_TOKEN_PAIR',
|
||||||
|
account_name: 'accountname',
|
||||||
|
session_token: 'x'.repeat(135),
|
||||||
|
manifests: ['manifest.jsonl'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await cloudStorage.save();
|
||||||
|
expect(typeof result.id).toBe('number');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Feature: update a cloud storage', () => {
|
||||||
|
test('update cloud storage with some new field values', async () => {
|
||||||
|
const newValues = new Map([
|
||||||
|
['displayName', 'new display name'],
|
||||||
|
['credentialsType', 'ANONYMOUS_ACCESS'],
|
||||||
|
['description', 'new description'],
|
||||||
|
['specificAttributes', 'region=eu-west-1'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
let result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
let [cloudStorage] = result;
|
||||||
|
|
||||||
|
for (const [key, value] of newValues) {
|
||||||
|
cloudStorage[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudStorage.save();
|
||||||
|
|
||||||
|
result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
[cloudStorage] = result;
|
||||||
|
|
||||||
|
newValues.forEach((value, key) => {
|
||||||
|
expect(cloudStorage[key]).toBe(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Update manifests in a cloud storage', async () => {
|
||||||
|
const newManifests = [
|
||||||
|
'sub1/manifest.jsonl',
|
||||||
|
'sub2/manifest.jsonl',
|
||||||
|
];
|
||||||
|
|
||||||
|
let result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
let [cloudStorage] = result;
|
||||||
|
|
||||||
|
cloudStorage.manifests = newManifests;
|
||||||
|
cloudStorage.save();
|
||||||
|
|
||||||
|
result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
[cloudStorage] = result;
|
||||||
|
|
||||||
|
expect(cloudStorage.manifests).toEqual(newManifests);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Feature: delete a cloud storage', () => {
|
||||||
|
test('delete a cloud storage', async () => {
|
||||||
|
let result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
await result[0].delete();
|
||||||
|
result = await window.cvat.cloudStorages.get({
|
||||||
|
id: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.isArray(result)).toBeTruthy();
|
||||||
|
expect(result).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue