You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
13 KiB
TypeScript
379 lines
13 KiB
TypeScript
// Copyright (C) 2021-2022 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
const { checkObjectType, isEnum } = require('./common');
|
|
const config = require('./config');
|
|
const { MembershipRole } = require('./enums');
|
|
const { ArgumentError, ServerError } = require('./exceptions');
|
|
const PluginRegistry = require('./plugins').default;
|
|
const serverProxy = require('./server-proxy');
|
|
const User = require('./user');
|
|
|
|
/**
|
|
* Class representing an organization
|
|
* @memberof module:API.cvat.classes
|
|
*/
|
|
class Organization {
|
|
/**
|
|
* @param {object} initialData - Object which is used for initialization
|
|
* <br> It must contains keys:
|
|
* <br> <li style="margin-left: 10px;"> slug
|
|
|
|
* <br> It can contains keys:
|
|
* <br> <li style="margin-left: 10px;"> name
|
|
* <br> <li style="margin-left: 10px;"> description
|
|
* <br> <li style="margin-left: 10px;"> owner
|
|
* <br> <li style="margin-left: 10px;"> created_date
|
|
* <br> <li style="margin-left: 10px;"> updated_date
|
|
* <br> <li style="margin-left: 10px;"> contact
|
|
*/
|
|
constructor(initialData) {
|
|
const data = {
|
|
id: undefined,
|
|
slug: undefined,
|
|
name: undefined,
|
|
description: undefined,
|
|
created_date: undefined,
|
|
updated_date: undefined,
|
|
owner: undefined,
|
|
contact: undefined,
|
|
};
|
|
|
|
for (const prop of Object.keys(data)) {
|
|
if (prop in initialData) {
|
|
data[prop] = initialData[prop];
|
|
}
|
|
}
|
|
|
|
if (data.owner) data.owner = new User(data.owner);
|
|
|
|
checkObjectType('slug', data.slug, 'string');
|
|
if (typeof data.name !== 'undefined') {
|
|
checkObjectType('name', data.name, 'string');
|
|
}
|
|
|
|
if (typeof data.description !== 'undefined') {
|
|
checkObjectType('description', data.description, 'string');
|
|
}
|
|
|
|
if (typeof data.id !== 'undefined') {
|
|
checkObjectType('id', data.id, 'number');
|
|
}
|
|
|
|
if (typeof data.contact !== 'undefined') {
|
|
checkObjectType('contact', data.contact, 'object');
|
|
for (const prop in data.contact) {
|
|
if (typeof data.contact[prop] !== 'string') {
|
|
throw ArgumentError(`Contact fields must be strings, tried to set ${typeof data.contact[prop]}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeof data.owner !== 'undefined' && data.owner !== null) {
|
|
checkObjectType('owner', data.owner, null, User);
|
|
}
|
|
|
|
Object.defineProperties(this, {
|
|
id: {
|
|
get: () => data.id,
|
|
},
|
|
slug: {
|
|
get: () => data.slug,
|
|
},
|
|
name: {
|
|
get: () => data.name,
|
|
set: (name) => {
|
|
if (typeof name !== 'string') {
|
|
throw ArgumentError(`Name property must be a string, tried to set ${typeof description}`);
|
|
}
|
|
data.name = name;
|
|
},
|
|
},
|
|
description: {
|
|
get: () => data.description,
|
|
set: (description) => {
|
|
if (typeof description !== 'string') {
|
|
throw ArgumentError(
|
|
`Description property must be a string, tried to set ${typeof description}`,
|
|
);
|
|
}
|
|
data.description = description;
|
|
},
|
|
},
|
|
contact: {
|
|
get: () => ({ ...data.contact }),
|
|
set: (contact) => {
|
|
if (typeof contact !== 'object') {
|
|
throw ArgumentError(`Contact property must be an object, tried to set ${typeof contact}`);
|
|
}
|
|
for (const prop in contact) {
|
|
if (typeof contact[prop] !== 'string') {
|
|
throw ArgumentError(`Contact fields must be strings, tried to set ${typeof contact[prop]}`);
|
|
}
|
|
}
|
|
data.contact = { ...contact };
|
|
},
|
|
},
|
|
owner: {
|
|
get: () => data.owner,
|
|
},
|
|
createdDate: {
|
|
get: () => data.created_date,
|
|
},
|
|
updatedDate: {
|
|
get: () => data.updated_date,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Method updates organization data if it was created before, or creates a new 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);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method returns paginatable list of organization members
|
|
* @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(
|
|
this,
|
|
Organization.prototype.members,
|
|
this.slug,
|
|
page,
|
|
page_size,
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method removes the organization
|
|
* @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);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method invites new members by email
|
|
* @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);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method allows a user to get out from an organization
|
|
* The difference between deleteMembership is that membershipId is unknown in this case
|
|
* @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);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method allows to change a membership role
|
|
* @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(
|
|
this,
|
|
Organization.prototype.updateMembership,
|
|
membershipId,
|
|
role,
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Method allows to kick a user from an organization
|
|
* @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(
|
|
this,
|
|
Organization.prototype.deleteMembership,
|
|
membershipId,
|
|
);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
Organization.prototype.save.implementation = async function () {
|
|
if (typeof this.id === 'number') {
|
|
const organizationData = {
|
|
name: this.name || this.slug,
|
|
description: this.description,
|
|
contact: this.contact,
|
|
};
|
|
|
|
const result = await serverProxy.organizations.update(this.id, organizationData);
|
|
return new Organization(result);
|
|
}
|
|
|
|
const organizationData = {
|
|
slug: this.slug,
|
|
name: this.name || this.slug,
|
|
description: this.description,
|
|
contact: this.contact,
|
|
};
|
|
|
|
const result = await serverProxy.organizations.create(organizationData);
|
|
return new Organization(result);
|
|
};
|
|
|
|
Organization.prototype.members.implementation = async function (orgSlug, page, pageSize) {
|
|
checkObjectType('orgSlug', orgSlug, 'string');
|
|
checkObjectType('page', page, 'number');
|
|
checkObjectType('pageSize', pageSize, 'number');
|
|
|
|
const result = await serverProxy.organizations.members(orgSlug, page, pageSize);
|
|
await Promise.all(
|
|
result.results.map((membership) => {
|
|
const { invitation } = membership;
|
|
membership.user = new User(membership.user);
|
|
if (invitation) {
|
|
return serverProxy.organizations
|
|
.invitation(invitation)
|
|
.then((invitationData) => {
|
|
membership.invitation = invitationData;
|
|
})
|
|
.catch(() => {
|
|
membership.invitation = null;
|
|
});
|
|
}
|
|
|
|
return Promise.resolve();
|
|
}),
|
|
);
|
|
|
|
result.results.count = result.count;
|
|
return result.results;
|
|
};
|
|
|
|
Organization.prototype.remove.implementation = async function () {
|
|
if (typeof this.id === 'number') {
|
|
await serverProxy.organizations.delete(this.id);
|
|
config.organizationID = null;
|
|
}
|
|
};
|
|
|
|
Organization.prototype.invite.implementation = async function (email, role) {
|
|
checkObjectType('email', email, 'string');
|
|
if (!isEnum.bind(MembershipRole)(role)) {
|
|
throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`);
|
|
}
|
|
|
|
if (typeof this.id === 'number') {
|
|
await serverProxy.organizations.invite(this.id, { email, role });
|
|
}
|
|
};
|
|
|
|
Organization.prototype.updateMembership.implementation = async function (membershipId, role) {
|
|
checkObjectType('membershipId', membershipId, 'number');
|
|
if (!isEnum.bind(MembershipRole)(role)) {
|
|
throw new ArgumentError(`Role must be one of: ${Object.values(MembershipRole).toString()}`);
|
|
}
|
|
|
|
if (typeof this.id === 'number') {
|
|
await serverProxy.organizations.updateMembership(membershipId, { role });
|
|
}
|
|
};
|
|
|
|
Organization.prototype.deleteMembership.implementation = async function (membershipId) {
|
|
checkObjectType('membershipId', membershipId, 'number');
|
|
if (typeof this.id === 'number') {
|
|
await serverProxy.organizations.deleteMembership(membershipId);
|
|
}
|
|
};
|
|
|
|
Organization.prototype.leave.implementation = async function (user) {
|
|
checkObjectType('user', user, null, User);
|
|
if (typeof this.id === 'number') {
|
|
const result = await serverProxy.organizations.members(this.slug, 1, 10, {
|
|
filter: JSON.stringify({
|
|
and: [{
|
|
'==': [{ var: 'user' }, user.id],
|
|
}],
|
|
}),
|
|
});
|
|
const [membership] = result.results;
|
|
if (!membership) {
|
|
throw new ServerError(`Could not find membership for user ${user.username} in organization ${this.slug}`);
|
|
}
|
|
await serverProxy.organizations.deleteMembership(membership.id);
|
|
}
|
|
};
|
|
|
|
module.exports = Organization;
|