src/users/UserManager.js
import {EventEmitter} from '../eventemitter';
import {bindMethods} from '../utils';
import {ResolveQueue} from './ResolveQueue';
import {
UserCollection,
USER_ADDRESS_PATTERN,
USER_ADDRESS_PREFIX,
} from './UserCollection';
/** @private */
export const Events = {
/** Resolved users changed event */
UPDATED: 'updated',
};
/**
* Element that enables user resolution.
* Previously resolved users are stored in a local collection that is updated
* when some of the requested addresses change.
* @private
*/
export class UserManager extends EventEmitter {
/**
* Constructs an users manager
* @protected
*/
constructor(wacProxy) {
super();
/**
* Property used to determine if UserManager has been intialized
* and wait until initialization success.
* @private
* @type {Promise}
*/
this._init = null;
/**
* WacProxy instance.
* @private
* @type {WacProxy}
*/
this._stack = wacProxy;
/**
* Collection of already resolved Users.
* @private
* @type {UserCollection}
*/
this._collection = null;
/**
* Queue that resolve addresses through WacProxy.
* @private
* @type {ResolveQueue}
*/
this._resolveQueue = null;
this.setMaxListeners(0);
bindMethods(this, [
'onResolveUpdated',
]);
}
/**
* Initialize UsersManager.
* It retrieves already resolved Users and subscribed addresses from WacProxy
* and subscribes to resolution updates.
* @return {Promise} resolved after intialization.
*/
init() {
if (!this._init) {
this._stack.getResolver().emitter.on('update', this.onResolveUpdated);
this._init = (async () => {
const items = await this._stack.getResolver().getResolvedUsers();
this._collection = new UserCollection(this._stack.getCurrentSession().domain);
this._collection.updateUsers(items.resolved, items.subscribed);
this._resolveQueue = new ResolveQueue(this._stack, this._collection);
})();
}
return this._init;
}
/**
* Deinitialize UsersManager.
*/
uninit() {
this._stack.getResolver().emitter.off('update', this.onResolveUpdated);
this._init = null;
this._collection = null;
this._resolveQueue = null;
}
/**
* Get the address associated with given user Id.
* @param {String} userId The user identifier used to build the user address.
* @return {String} The user address.
*/
getAddress(userId) {
return `${USER_ADDRESS_PREFIX}${userId}`;
}
/**
* Get the userId associated with given user address.
* @param {String} address A user address.
* @return {String} The user ID of that user address.
*/
getUserId(address) {
return address.replace(USER_ADDRESS_PATTERN, '$2');
}
/**
* Checks if given address is a WAC user address
* @param {String} address the address to check.
* @return {Boolean} true if given address is a WAC user address.
*/
isAddress(address) {
return USER_ADDRESS_PATTERN.test(address);
}
/**
* Obtains own user WAC address
* @return {String} address of current user
*/
getOwnAddress() {
return this.getAddress(this._stack.getCurrentSession().user);
}
/**
* Checks if given address is own WAC user address
* @param {String} address the address to check.
* @return {Boolean} true if given address is own WAC user address.
*/
isOwnAddress(address) {
return this.getUserId(address) === this._stack.getCurrentSession().user;
}
/**
* Request an address resolution.
* When there is an user in local collection that resolves given address, such user
* will be returned.
* Otherwise a resolution will be requested through resolve queue to WacStack. If resolution success
* local collection will be updated.
* @param {String} address The address to be resolved.
* @return {Promise<User|undefined>} resolved user or undefined if there is no user that
* conforms to given address.
*/
async resolveUser(address) {
if (!this._init) {
throw new Error('not initialized');
}
await this._init;
return this._collection.resolve(address) || this._resolveQueue.resolve(address);
}
/**
* Returns an user username given a gateway username.
* @param {String} gatewayUsername The gateway username to search.
* @param {Boolean} [ignoreErrors=true] When true, if no user with such gateway
* username is found, gatewayUsername value will be returned instead an error.
* @return {Promise<String>} User username on success.
*/
getUsername(gatewayUsername, ignoreErrors = true) {
let localUser = this._collection.resolve(gatewayUsername);
if (localUser && localUser.gatewayUsername === gatewayUsername) {
return Promise.resolve(localUser.username);
}
return this._stack.getResolver().getUserByGatewayUsername(gatewayUsername)
.then(userId => this.resolveUser(userId))
.then(user => user.username)
.catch((error) => {
if (ignoreErrors) {
return gatewayUsername;
}
throw error;
});
}
/**
* Returns the gateway username associated with an user username.
* @param {String} username The user's username.
* @param {Boolean} [ignoreErrors=true] When true, if no user is found,
* username value will be returned instead an error.
* @return {Promise<String>} The gateway username on success.
*/
getGatewayUsername(username, ignoreErrors = true) {
return this.resolveUser(username)
.then(user => user.gatewayUsername ? user.gatewayUsername : this._stack.getResolver().getGatewayUsernameByUser(user.id))
.catch((error) => {
if (ignoreErrors) {
return username;
}
throw error;
});
}
getGatewayUsernames(usernames, ignoreErrors = true) {
return Promise.all(usernames.map(username => this.getGatewayUsername(username, ignoreErrors)));
}
/**
* Callback called when WacProxy notifies that resolved and subscribed items
* have changed. This can happen:
* - When a resolved user changes some of their fields or ceases to exist.
* - When a new user is created and satisfies one of the addresses to which we have subscribed
* @private
* @param {Object} items object that contains resolved users and subscribed addresses.
*/
onResolveUpdated(items) {
this._collection.updateUsers(items.resolved, items.subscribed);
this.emit(Events.UPDATED);
}
}