// 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 *
It must contains keys: *
  • slug *
    It can contains keys: *
  • name *
  • description *
  • owner *
  • created_date *
  • updated_date *
  • 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;