...
 
Commits (19)
{
"name": "@rocket.chat/apps-engine",
"version": "1.4.2",
"version": "1.5.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......
{
"name": "@rocket.chat/apps-engine",
"version": "1.4.2",
"version": "1.5.2",
"description": "The engine code for the Rocket.Chat Apps which manages, runs, translates, coordinates and all of that.",
"main": "index",
"typings": "index",
......
......@@ -14,6 +14,10 @@ export enum AppStatus {
* An attempt to enable it again will fail, as it needs to be updated.
*/
COMPILER_ERROR_DISABLED = 'compiler_error_disabled',
/**
* The App was disable due to its license being invalid
*/
INVALID_LICENSE_DISABLED = 'invalid_license_disabled',
/** The App was disabled due to an unrecoverable error being thrown. */
ERROR_DISABLED = 'error_disabled',
/** The App was manually disabled by a user. */
......@@ -40,6 +44,7 @@ export class AppStatusUtilsDef {
case AppStatus.ERROR_DISABLED:
case AppStatus.MANUALLY_DISABLED:
case AppStatus.INVALID_SETTINGS_DISABLED:
case AppStatus.INVALID_LICENSE_DISABLED:
case AppStatus.DISABLED:
return true;
default:
......
......@@ -37,6 +37,20 @@ export interface IHttpRequest {
[key: string]: string,
};
timeout?: number;
/**
* if `true`, requires SSL certificates be valid.
*
* Defaul: `true`;
*/
strictSSL?: boolean;
/**
* If `true`, the server certificate is verified against the list of supplied CAs.
*
* Default: `true`.
*
* https://nodejs.org/api/tls.html#tls_tls_connect_options_callback
*/
rejectUnauthorized?: boolean;
}
export interface IHttpResponse {
......
import { ILivechatRoom } from '../livechat/ILivechatRoom';
import { IVisitor } from '../livechat/IVisitor';
export interface ILivechatRead {
getLivechatRooms(visitor: IVisitor, departmentId?: string): Promise<Array<ILivechatRoom>>;
getLivechatVisitors(query: object): Promise<Array<IVisitor>>;
}
import { ILivechatMessage, ILivechatRoom, ILivechatTransferData } from '../livechat';
import { IVisitor } from '../livechat/IVisitor';
import { IMessage, IMessageAttachment } from '../messages';
import { RocketChatAssociationModel } from '../metadata';
import { IRoom, RoomType } from '../rooms';
......@@ -46,6 +48,11 @@ export interface INotifier {
}
export interface IModifyUpdater {
/**
* Get the updater object responsible for the
* Livechat integrations
*/
getLivechatUpdater(): ILivechatUpdater;
/**
* Modifies an existing message.
* Raises an exception if a non-existent messageId is supplied
......@@ -104,6 +111,11 @@ export interface IModifyExtender {
}
export interface IModifyCreator {
/**
* Get the creator object responsible for the
* Livechat integrations
*/
getLivechatCreator(): ILivechatCreator;
/**
* Starts the process for building a new message object.
*
......@@ -113,6 +125,15 @@ export interface IModifyCreator {
*/
startMessage(data?: IMessage): IMessageBuilder;
/**
* Starts the process for building a new livechat message object.
*
* @param data (optional) the initial data to pass into the builder,
* the `id` property will be ignored
* @return an IMessageBuilder instance
*/
startLivechatMessage(data?: ILivechatMessage): ILivechatMessageBuilder;
/**
* Starts the process for building a new room.
*
......@@ -128,7 +149,49 @@ export interface IModifyCreator {
* @param builder the builder instance
* @return the resulting `id` of the resulting object
*/
finish(builder: IMessageBuilder | IRoomBuilder): Promise<string>;
finish(builder: IMessageBuilder | ILivechatMessageBuilder | IRoomBuilder): Promise<string>;
}
export interface ILivechatCreator {
/**
* Creates a room to connect the `visitor` to an `agent`.
*
* This method uses the Livechat routing method configured
* in the server
*
* @param visitor The Livechat Visitor that started the conversation
* @param agent The agent responsible for the room
*/
createRoom(visitor: IVisitor, agent: IUser): Promise<ILivechatRoom>;
/**
* Creates a Livechat visitor
*
* @param visitor Data of the visitor to be created
*/
createVisitor(visitor: IVisitor): Promise<string>;
/**
* Creates a token to be used when
* creating a new livechat visitor
*/
createToken(): string;
}
export interface ILivechatUpdater {
/**
* Transfer a Livechat visitor to another room
*
* @param visitor Visitor to be transfered
* @param transferData The data to execute the transfering
*/
transferVisitor(visitor: IVisitor, transferData: ILivechatTransferData): Promise<boolean>;
/**
* Closes a Livechat room
*
* @param room The room to be closed
* @param comment The comment explaining the reason for closing the room
*/
closeRoom(room: IRoom, comment: string): Promise<boolean>;
}
export interface IMessageExtender {
......@@ -377,6 +440,219 @@ export interface IMessageBuilder {
getMessage(): IMessage;
}
/**
* Interface for building out a livechat message.
* Please note, that a room and sender must be associated otherwise you will NOT
* be able to successfully save the message object.
*/
export interface ILivechatMessageBuilder {
kind: RocketChatAssociationModel.LIVECHAT_MESSAGE;
/**
* Provides a convient way to set the data for the message.
* Note: Providing an "id" field here will be ignored.
*
* @param message the message data to set
*/
setData(message: ILivechatMessage): ILivechatMessageBuilder;
/**
* Sets the room where this message should be sent to.
*
* @param room the room where to send
*/
setRoom(room: IRoom): ILivechatMessageBuilder;
/**
* Gets the room where this message was sent to.
*/
getRoom(): IRoom;
/**
* Sets the sender of this message.
*
* @param sender the user sending the message
*/
setSender(sender: IUser): ILivechatMessageBuilder;
/**
* Gets the User which sent the message.
*/
getSender(): IUser;
/**
* Sets the text of the message.
*
* @param text the actual text
*/
setText(text: string): ILivechatMessageBuilder;
/**
* Gets the message text.
*/
getText(): string;
/**
* Sets the emoji to use for the avatar, this overwrites the current avatar
* whether it be the user's or the avatar url provided.
*
* @param emoji the emoji code
*/
setEmojiAvatar(emoji: string): ILivechatMessageBuilder;
/**
* Gets the emoji used for the avatar.
*/
getEmojiAvatar(): string;
/**
* Sets the url which to display for the avatar, this overwrites the current
* avatar whether it be the user's or an emoji one.
*
* @param avatarUrl image url to use as the avatar
*/
setAvatarUrl(avatarUrl: string): ILivechatMessageBuilder;
/**
* Gets the url used for the avatar.
*/
getAvatarUrl(): string;
/**
* Sets the display text of the sender's username that is visible.
*
* @param alias the username alias to display
*/
setUsernameAlias(alias: string): ILivechatMessageBuilder;
/**
* Gets the display text of the sender's username that is visible.
*/
getUsernameAlias(): string;
/**
* Adds one attachment to the message's list of attachments, this will not
* overwrite any existing ones but just adds.
*
* @param attachment the attachment to add
*/
addAttachment(attachment: IMessageAttachment): ILivechatMessageBuilder;
/**
* Sets the attachments for the message, replacing and destroying all of the current attachments.
*
* @param attachments array of the attachments
*/
setAttachments(attachments: Array<IMessageAttachment>): ILivechatMessageBuilder;
/**
* Gets the attachments array for the message
*/
getAttachments(): Array<IMessageAttachment>;
/**
* Replaces an attachment at the given position (index).
* If there is no attachment at that position, there will be an error thrown.
*
* @param position the index of the attachment to replace
* @param attachment the attachment to replace with
*/
replaceAttachment(position: number, attachment: IMessageAttachment): ILivechatMessageBuilder;
/**
* Removes an attachment at the given position (index).
* If there is no attachment at that position, there will be an error thrown.
*
* @param position the index of the attachment to remove
*/
removeAttachment(position: number): ILivechatMessageBuilder;
/**
* Sets the user who is editing this message.
* This is required if you are modifying an existing message.
*
* @param user the editor
*/
setEditor(user: IUser): ILivechatMessageBuilder;
/**
* Gets the user who edited the message
*/
getEditor(): IUser;
/**
* Sets whether this message can group with others.
* This is desirable if you want to avoid confusion with other integrations.
*
* @param groupable whether this message can group with others
*/
setGroupable(groupable: boolean): ILivechatMessageBuilder;
/**
* Gets whether this message can group with others.
*/
getGroupable(): boolean;
/**
* Sets whether this message should have any URLs in the text
* parsed by Rocket.Chat and get the details added to the message's
* attachments.
*
* @param parseUrls whether URLs should be parsed in this message
*/
setParseUrls(parseUrls: boolean): ILivechatMessageBuilder;
/**
* Gets whether this message should have its URLs parsed
*/
getParseUrls(): boolean;
/**
* Set the token of the livechat visitor that
* sent the message
*
* @param token The Livechat visitor's token
*/
setToken(token: string): ILivechatMessageBuilder;
/**
* Gets the token of the livechat visitor that
* sent the message
*/
getToken(): string;
/**
* If the sender of the message is a Livechat Visitor,
* set the visitor who sent the message.
*
* If you set the visitor property of a message, the
* sender will be emptied
*
* @param visitor The visitor who sent the message
*/
setVisitor(visitor: IVisitor): ILivechatMessageBuilder;
/**
* Get the visitor who sent the message,
* if any
*/
getVisitor(): IVisitor;
/**
* Gets the resulting message that has been built up to the point of calling it.
*
* *Note:* This will error out if the Room has not been defined OR if the room
* is not of type RoomType.LIVE_CHAT.
*/
getMessage(): ILivechatMessage;
/**
* Returns a message builder based on the
* livechat message of this builder
*/
getMessageBuilder(): IMessageBuilder;
}
/**
* Interface for building out a room.
* Please note, a room creator, name, and type must be set otherwise you will NOT
......
import { IEnvironmentRead } from './IEnvironmentRead';
import { ILivechatRead } from './ILivechatRead';
import { IMessageRead } from './IMessageRead';
import { INotifier } from './IModify';
import { IPersistenceRead } from './IPersistenceRead';
import { IRoomRead } from './IRoomRead';
import { IUploadRead } from './IUploadRead';
import { IUserRead } from './IUserRead';
/**
......@@ -28,4 +30,7 @@ export interface IRead {
/** Gets the INotifier for notifying users/rooms. */
getNotifier(): INotifier;
getLivechatReader(): ILivechatRead;
getUploadReader(): IUploadRead;
}
import { IUpload } from '../uploads';
export interface IUploadRead {
getById(id: string): Promise<IUpload>;
getBufferById(id: string): Promise<Buffer>;
getBuffer(upload: IUpload): Promise<Buffer>;
}
......@@ -14,10 +14,13 @@ import {
IHttpResponse,
RequestMethod,
} from './IHttp';
import { ILivechatRead } from './ILivechatRead';
import { ILogEntry, LogMessageSeverity } from './ILogEntry';
import { ILogger } from './ILogger';
import { IMessageRead } from './IMessageRead';
import {
ILivechatMessageBuilder,
ILivechatUpdater,
IMessageBuilder,
IMessageExtender,
IModify,
......@@ -38,6 +41,7 @@ import { ISettingRead } from './ISettingRead';
import { ISettingsExtend } from './ISettingsExtend';
import { ISlashCommandsExtend } from './ISlashCommandsExtend';
import { ISlashCommandsModify } from './ISlashCommandsModify';
import { IUploadRead } from './IUploadRead';
import { IUserRead } from './IUserRead';
export {
......@@ -53,6 +57,9 @@ export {
IHttpPreResponseHandler,
IHttpRequest,
IHttpResponse,
ILivechatMessageBuilder,
ILivechatRead,
ILivechatUpdater,
ILogEntry,
ILogger,
IMessageBuilder,
......@@ -75,6 +82,7 @@ export {
ISettingsExtend,
ISlashCommandsExtend,
ISlashCommandsModify,
IUploadRead,
IUserRead,
LogMessageSeverity,
RequestMethod,
......
......@@ -24,7 +24,7 @@
"version": {
"description": "The version of this App which will be used for display publicly and letting users know there is an update. This uses the semver format.",
"type": "string",
"pattern": "\\bv?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[\\da-z\\-]+(?:\\.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:\\.[\\da-z\\-]+)*)?\\b",
"pattern": "^(?:\\d*)\\.(?:\\d*)\\.(?:\\d*)$",
"minLength": 5
},
"description": {
......@@ -34,7 +34,7 @@
"requiredApiVersion": {
"description": "The required version of the App's API which this App depends on. This uses the semver format.",
"type": "string",
"pattern": "\\bv?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[\\da-z\\-]+(?:\\.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:\\.[\\da-z\\-]+)*)?\\b",
"pattern": "^(?:\\^|~)?(?:\\d*)\\.(?:\\d*)\\.(?:\\d*)$",
"minLength": 5
},
"author": {
......
import { IMessage } from '../messages/IMessage';
import { IVisitor } from './IVisitor';
export interface ILivechatMessage extends IMessage {
visitor?: IVisitor;
token?: string;
}
import { IRoom } from '../rooms/IRoom';
import { IUser } from '../users';
import { IVisitor } from './IVisitor';
export interface ILivechatRoom extends IRoom {
visitor: IVisitor;
servedBy?: IUser;
responseBy?: IUser;
isWaitingResponse: boolean;
isOpen: boolean;
closedAt?: Date;
}
import { IUser } from '../users';
import { ILivechatRoom } from './ILivechatRoom';
export interface ILivechatTransferData {
currentRoom: ILivechatRoom;
targetAgent?: IUser;
targetDepartment?: string;
}
import { IVisitorEmail } from './IVisitorEmail';
import { IVisitorPhone } from './IVisitorPhone';
export interface IVisitor {
id?: string;
token: string;
username: string;
updatedAt: Date;
name: string;
department?: string;
phone?: Array<IVisitorPhone>;
visitorEmails: Array<IVisitorEmail>;
customFields?: { [key: string]: any };
}
export interface IVisitorEmail {
address: string;
}
export interface IVisitorPhone {
phoneNumber: string;
}
import { ILivechatMessage } from './ILivechatMessage';
import { ILivechatRoom } from './ILivechatRoom';
import { ILivechatTransferData } from './ILivechatTransferData';
import { IVisitor } from './IVisitor';
import { IVisitorEmail } from './IVisitorEmail';
import { IVisitorPhone } from './IVisitorPhone';
export {
ILivechatMessage,
ILivechatRoom,
ILivechatTransferData,
IVisitor,
IVisitorEmail,
IVisitorPhone,
};
import { IRoom } from '../rooms';
import { IUser } from '../users';
import { IMessageAttachment } from './IMessageAttachment';
import { IMessageFile } from './IMessageFile';
import { IMessageReactions } from './IMessageReaction';
export interface IMessage {
......@@ -15,6 +16,7 @@ export interface IMessage {
emoji?: string;
avatarUrl?: string;
alias?: string;
file?: IMessageFile;
attachments?: Array<IMessageAttachment>;
reactions?: IMessageReactions;
groupable?: boolean;
......
export interface IMessageFile {
_id: string;
name: string;
type: string;
}
......@@ -4,6 +4,7 @@ import { IMessageAttachment } from './IMessageAttachment';
import { IMessageAttachmentAuthor } from './IMessageAttachmentAuthor';
import { IMessageAttachmentField } from './IMessageAttachmentField';
import { IMessageAttachmentTitle } from './IMessageAttachmentTitle';
import { IMessageFile } from './IMessageFile';
import { IMessageReaction, IMessageReactions } from './IMessageReaction';
import { IPostMessageDeleted } from './IPostMessageDeleted';
import { IPostMessageSent } from './IPostMessageSent';
......@@ -26,6 +27,7 @@ export {
IMessageAttachmentTitle,
IMessageAttachmentField,
IMessageAction,
IMessageFile,
IMessageReactions,
IMessageReaction,
IPostMessageDeleted,
......
export enum RocketChatAssociationModel {
ROOM = 'room',
MESSAGE = 'message',
LIVECHAT_MESSAGE = 'livechat-message',
USER = 'user',
FILE = 'file',
MISC = 'misc',
......
......@@ -3,4 +3,10 @@ import { IAppAuthorInfo } from './IAppAuthorInfo';
import { IAppInfo } from './IAppInfo';
import { RocketChatAssociationModel, RocketChatAssociationRecord } from './RocketChatAssociations';
export { AppMethod, IAppAuthorInfo, IAppInfo, RocketChatAssociationModel, RocketChatAssociationRecord };
export {
AppMethod,
IAppAuthorInfo,
IAppInfo,
RocketChatAssociationModel,
RocketChatAssociationRecord,
};
import { IVisitor } from '../livechat';
import { IRoom } from '../rooms';
import { IUser } from '../users';
import { StoreType } from './StoreType';
export interface IUpload {
id: string;
name: string;
size: string;
type: string;
extension: string;
etag: string;
path: string;
token: string;
url: string;
progress: number;
uploading: boolean;
complete: boolean;
updatedAt: Date;
uploadedAt: Date;
store: StoreType;
room: IRoom;
visitor?: IVisitor;
user?: IUser;
}
export enum StoreType {
GridFS = 'GridFS:Uploads',
AmazonS3 = 'AmazonS3',
GoogleCloudStorage = 'GoogleCloudStorage',
Webdav = 'Webdav',
FileSystem = 'FileSystem',
}
import { IUpload } from './IUpload';
import { StoreType } from './StoreType';
export {
IUpload,
StoreType,
};
This diff is collapsed.
......@@ -8,11 +8,14 @@ import { AppMethod, IAppAuthorInfo, IAppInfo } from '../definition/metadata';
import { AppManager } from './AppManager';
import { NotEnoughMethodArgumentsError } from './errors';
import { AppConsole } from './logging';
import { AppLicenseValidationResult } from './marketplace/license';
import { IAppStorageItem } from './storage';
export class ProxiedApp implements IApp {
private previousStatus: AppStatus;
private latestLicenseValidationResult: AppLicenseValidationResult;
constructor(private readonly manager: AppManager,
private storageItem: IAppStorageItem,
private readonly app: App,
......@@ -143,4 +146,16 @@ export class ProxiedApp implements IApp {
public getAccessors(): IAppAccessors {
return this.app.getAccessors();
}
public getLatestLicenseValidationResult(): AppLicenseValidationResult {
return this.latestLicenseValidationResult;
}
public validateLicense(): Promise<void> {
const { marketplaceInfo } = this.getStorageItem();
this.latestLicenseValidationResult = new AppLicenseValidationResult();
return this.manager.getLicenseManager().validate(this.latestLicenseValidationResult, marketplaceInfo);
}
}
import { ILivechatCreator } from '../../definition/accessors/IModify';
import { ILivechatRoom } from '../../definition/livechat/ILivechatRoom';
import { IVisitor } from '../../definition/livechat/IVisitor';
import { IUser } from '../../definition/users';
import { AppBridges } from '../bridges';
export class LivechatCreator implements ILivechatCreator {
constructor(private readonly bridges: AppBridges, private readonly appId: string) { }
public createRoom(visitor: IVisitor, agent: IUser): Promise<ILivechatRoom> {
return this.bridges.getLivechatBridge().createRoom(visitor, agent, this.appId);
}
public createVisitor(visitor: IVisitor): Promise<string> {
return this.bridges.getLivechatBridge().createVisitor(visitor, this.appId);
}
public createToken(): string {
return (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15));
}
}
import { ILivechatMessageBuilder, IMessageBuilder } from '../../definition/accessors';
import { ILivechatMessage } from '../../definition/livechat/ILivechatMessage';
import { IVisitor } from '../../definition/livechat/IVisitor';
import { IMessage, IMessageAttachment } from '../../definition/messages';
import { RocketChatAssociationModel } from '../../definition/metadata';
import { IRoom, RoomType } from '../../definition/rooms';
import { IUser } from '../../definition/users';
import { MessageBuilder } from './MessageBuilder';
export class LivechatMessageBuilder implements ILivechatMessageBuilder {
public kind: RocketChatAssociationModel.LIVECHAT_MESSAGE;
private msg: ILivechatMessage;
constructor(message?: ILivechatMessage) {
this.kind = RocketChatAssociationModel.LIVECHAT_MESSAGE;
this.msg = message ? message : ({} as ILivechatMessage);
}
public setData(data: ILivechatMessage): ILivechatMessageBuilder {
delete data.id;
this.msg = data;
return this;
}
public setRoom(room: IRoom): ILivechatMessageBuilder {
this.msg.room = room;
return this;
}
public getRoom(): IRoom {
return this.msg.room;
}
public setSender(sender: IUser): ILivechatMessageBuilder {
this.msg.sender = sender;
delete this.msg.visitor;
return this;
}
public getSender(): IUser {
return this.msg.sender;
}
public setText(text: string): ILivechatMessageBuilder {
this.msg.text = text;
return this;
}
public getText(): string {
return this.msg.text;
}
public setEmojiAvatar(emoji: string): ILivechatMessageBuilder {
this.msg.emoji = emoji;
return this;
}
public getEmojiAvatar(): string {
return this.msg.emoji;
}
public setAvatarUrl(avatarUrl: string): ILivechatMessageBuilder {
this.msg.avatarUrl = avatarUrl;
return this;
}
public getAvatarUrl(): string {
return this.msg.avatarUrl;
}
public setUsernameAlias(alias: string): ILivechatMessageBuilder {
this.msg.alias = alias;
return this;
}
public getUsernameAlias(): string {
return this.msg.alias;
}
public addAttachment(attachment: IMessageAttachment): ILivechatMessageBuilder {
if (!this.msg.attachments) {
this.msg.attachments = new Array<IMessageAttachment>();
}
this.msg.attachments.push(attachment);
return this;
}
public setAttachments(attachments: Array<IMessageAttachment>): ILivechatMessageBuilder {
this.msg.attachments = attachments;
return this;
}
public getAttachments(): Array<IMessageAttachment> {
return this.msg.attachments;
}
public replaceAttachment(position: number, attachment: IMessageAttachment): ILivechatMessageBuilder {
if (!this.msg.attachments) {
this.msg.attachments = new Array<IMessageAttachment>();
}
if (!this.msg.attachments[position]) {
throw new Error(`No attachment found at the index of "${ position }" to replace.`);
}
this.msg.attachments[position] = attachment;
return this;
}
public removeAttachment(position: number): ILivechatMessageBuilder {
if (!this.msg.attachments) {
this.msg.attachments = new Array<IMessageAttachment>();
}
if (!this.msg.attachments[position]) {
throw new Error(`No attachment found at the index of "${ position }" to remove.`);
}
this.msg.attachments.splice(position, 1);
return this;
}
public setEditor(user: IUser): ILivechatMessageBuilder {
this.msg.editor = user;
return this;
}
public getEditor(): IUser {
return this.msg.editor;
}
public setGroupable(groupable: boolean): ILivechatMessageBuilder {
this.msg.groupable = groupable;
return this;
}
public getGroupable(): boolean {
return this.msg.groupable;
}
public setParseUrls(parseUrls: boolean): ILivechatMessageBuilder {
this.msg.parseUrls = parseUrls;
return this;
}
public getParseUrls(): boolean {
return this.msg.parseUrls;
}
public setToken(token: string): ILivechatMessageBuilder {
this.msg.token = token;
return this;
}
public getToken(): string {
return this.msg.token;
}
public setVisitor(visitor: IVisitor): ILivechatMessageBuilder {
this.msg.visitor = visitor;
delete this.msg.sender;
return this;
}
public getVisitor(): IVisitor {
return this.msg.visitor;
}
public getMessage(): ILivechatMessage {
if (!this.msg.room) {
throw new Error('The "room" property is required.');
}
if (this.msg.room.type !== RoomType.LIVE_CHAT) {
throw new Error('The room is not a Livechat room');
}
return this.msg;
}
public getMessageBuilder(): IMessageBuilder {
return new MessageBuilder(this.msg as IMessage);
}
}
import { ILivechatRead } from '../../definition/accessors/ILivechatRead';
import { ILivechatRoom } from '../../definition/livechat/ILivechatRoom';
import { IVisitor } from '../../definition/livechat/IVisitor';
import { ILivechatBridge } from '../bridges/ILivechatBridge';
export class LivechatRead implements ILivechatRead {
constructor(private readonly livechatBridge: ILivechatBridge, private readonly appId: string) { }
public getLivechatRooms(visitor: IVisitor, departmentId?: string): Promise<Array<ILivechatRoom>> {
return this.livechatBridge.findRooms(visitor, departmentId, this.appId);
}
public getLivechatVisitors(query: object): Promise<Array<IVisitor>> {
return this.livechatBridge.findVisitors(query, this.appId);
}
}
import { ILivechatUpdater } from '../../definition/accessors';
import { ILivechatRoom, ILivechatTransferData, IVisitor } from '../../definition/livechat';
import { AppBridges } from '../bridges';
export class LivechatUpdater implements ILivechatUpdater {
constructor(private readonly bridges: AppBridges, private readonly appId: string) { }
public transferVisitor(visitor: IVisitor, transferData: ILivechatTransferData): Promise<boolean> {
return this.bridges.getLivechatBridge().transferVisitor(visitor, transferData, this.appId);
}
public closeRoom(room: ILivechatRoom, comment: string): Promise<boolean> {
return this.bridges.getLivechatBridge().closeRoom(room, comment, this.appId);
}
}
import { IMessageBuilder, IModifyCreator, IRoomBuilder } from '../../definition/accessors';
import { ILivechatMessageBuilder, IMessageBuilder, IModifyCreator, IRoomBuilder } from '../../definition/accessors';
import { IMessage } from '../../definition/messages';
import { RocketChatAssociationModel } from '../../definition/metadata';
import { IRoom, RoomType } from '../../definition/rooms';
import { ILivechatCreator } from '../../definition/accessors/IModify';
import { ILivechatMessage } from '../../definition/livechat/ILivechatMessage';
import { AppBridges } from '../bridges';
import { LivechatCreator } from './LivechatCreator';
import { LivechatMessageBuilder } from './LivechatMessageBuilder';
import { MessageBuilder } from './MessageBuilder';
import { RoomBuilder } from './RoomBuilder';
export class ModifyCreator implements IModifyCreator {
constructor(private readonly bridges: AppBridges, private readonly appId: string) { }
private livechatCreator: LivechatCreator;
constructor(private readonly bridges: AppBridges, private readonly appId: string) {
this.livechatCreator = new LivechatCreator(bridges, appId);
}
public getLivechatCreator(): ILivechatCreator {
return this.livechatCreator;
}
public startMessage(data?: IMessage): IMessageBuilder {
if (data) {
......@@ -18,6 +30,14 @@ export class ModifyCreator implements IModifyCreator {
return new MessageBuilder(data);
}
public startLivechatMessage(data?: ILivechatMessage): ILivechatMessageBuilder {
if (data) {
delete data.id;
}
return new LivechatMessageBuilder(data);
}
public startRoom(data?: IRoom): IRoomBuilder {
if (data) {
delete data.id;
......@@ -26,10 +46,12 @@ export class ModifyCreator implements IModifyCreator {
return new RoomBuilder(data);
}
public finish(builder: IMessageBuilder | IRoomBuilder): Promise<string> {
public finish(builder: IMessageBuilder | ILivechatMessageBuilder | IRoomBuilder): Promise<string> {
switch (builder.kind) {
case RocketChatAssociationModel.MESSAGE:
return this._finishMessage(builder);
case RocketChatAssociationModel.LIVECHAT_MESSAGE:
return this._finishLivechatMessage(builder);
case RocketChatAssociationModel.ROOM:
return this._finishRoom(builder);
default:
......@@ -48,6 +70,23 @@ export class ModifyCreator implements IModifyCreator {
return this.bridges.getMessageBridge().create(result, this.appId);
}
private _finishLivechatMessage(builder: ILivechatMessageBuilder): Promise<string> {
if (builder.getSender() && !builder.getVisitor()) {
return this._finishMessage(builder.getMessageBuilder());
}
const result = builder.getMessage();
delete result.id;
if (!result.token && (!result.visitor || !result.visitor.token)) {
throw new Error('Invalid visitor sending the message');
}
result.token = result.visitor ? result.visitor.token : result.token;
return this.bridges.getLivechatBridge().createMessage(result, this.appId);
}
private _finishRoom(builder: IRoomBuilder): Promise<string> {
const result = builder.getRoom();
delete result.id;
......
import { IMessageBuilder, IModifyUpdater, IRoomBuilder } from '../../definition/accessors';
import { ILivechatUpdater, IMessageBuilder, IModifyUpdater, IRoomBuilder } from '../../definition/accessors';
import { RocketChatAssociationModel } from '../../definition/metadata';
import { IUser } from '../../definition/users';
import { AppBridges } from '../bridges';
import { LivechatUpdater } from './LivechatUpdater';
import { MessageBuilder } from './MessageBuilder';
import { RoomBuilder } from './RoomBuilder';
export class ModifyUpdater implements IModifyUpdater {
constructor(private readonly bridges: AppBridges, private readonly appId: string) { }
private livechatUpdater: ILivechatUpdater;
constructor(private readonly bridges: AppBridges, private readonly appId: string) {
this.livechatUpdater = new LivechatUpdater(this.bridges, this.appId);
}
public getLivechatUpdater(): ILivechatUpdater {
return this.livechatUpdater;
}
public async message(messageId: string, updater: IUser): Promise<IMessageBuilder> {
const msg = await this.bridges.getMessageBridge().getById(messageId, this.appId);
......
import {
IEnvironmentRead,
ILivechatRead,
IMessageRead,
INotifier,
IPersistenceRead,
IRead,
IRoomRead,
IUploadRead,
IUserRead,
} from '../../definition/accessors';
export class Reader implements IRead {
constructor(private env: IEnvironmentRead, private message: IMessageRead,
private persist: IPersistenceRead, private room: IRoomRead,
private user: IUserRead, private noti: INotifier) { }
constructor(
private env: IEnvironmentRead,
private message: IMessageRead,
private persist: IPersistenceRead,
private room: IRoomRead,
private user: IUserRead,
private noti: INotifier,
private livechat: ILivechatRead,
private upload: IUploadRead,
) { }
public getEnvironmentReader(): IEnvironmentRead {
return this.env;
......@@ -36,4 +45,12 @@ export class Reader implements IRead {
public getNotifier(): INotifier {
return this.noti;
}
public getLivechatReader(): ILivechatRead {
return this.livechat;
}
public getUploadReader(): IUploadRead {
return this.upload;
}
}
import { IUploadRead } from '../../definition/accessors';
import { IUpload } from '../../definition/uploads';
import { IUploadBridge } from '../bridges/IUploadBridge';
export class UploadRead implements IUploadRead {
constructor(private readonly uploadBridge: IUploadBridge, private readonly appId: string) { }
public getById(id: string): Promise<IUpload> {
return this.uploadBridge.getById(id, this.appId);
}
public getBuffer(upload: IUpload): Promise<Buffer> {
return this.uploadBridge.getBuffer(upload, this.appId);
}
public async getBufferById(id: string): Promise<Buffer> {
const upload = await this.uploadBridge.getById(id, this.appId);
return this.uploadBridge.getBuffer(upload, this.appId);
}
}
......@@ -5,6 +5,7 @@ import { EnvironmentalVariableRead } from './EnvironmentalVariableRead';
import { EnvironmentRead } from './EnvironmentRead';
import { Http } from './Http';
import { HttpExtend } from './HttpExtend';
import { LivechatRead } from './LivechatRead';
import { MessageBuilder } from './MessageBuilder';
import { MessageExtender } from './MessageExtender';
import { MessageRead } from './MessageRead';
......@@ -25,6 +26,7 @@ import { SettingRead } from './SettingRead';
import { SettingsExtend } from './SettingsExtend';
import { SlashCommandsExtend } from './SlashCommandsExtend';
import { SlashCommandsModify } from './SlashCommandsModify';
import { UploadRead } from './UploadRead';
import { UserRead } from './UserRead';
export {
......@@ -35,6 +37,7 @@ export {
EnvironmentRead,
Http,
HttpExtend,
LivechatRead,
MessageBuilder,
MessageExtender,
MessageRead,
......@@ -55,5 +58,6 @@ export {
SettingsExtend,
SlashCommandsExtend,
SlashCommandsModify,
UploadRead,
UserRead,
};
......@@ -6,10 +6,12 @@ import { IEnvironmentalVariableBridge } from './IEnvironmentalVariableBridge';
import { IHttpBridge } from './IHttpBridge';
import { IInternalBridge } from './IInternalBridge';
import { IListenerBridge } from './IListenerBridge';
import { ILivechatBridge } from './ILivechatBridge';
import { IMessageBridge } from './IMessageBridge';
import { IPersistenceBridge } from './IPersistenceBridge';
import { IRoomBridge } from './IRoomBridge';
import { IServerSettingBridge } from './IServerSettingBridge';
import { IUploadBridge } from './IUploadBridge';
import { IUserBridge } from './IUserBridge';
export abstract class AppBridges {
......@@ -19,11 +21,13 @@ export abstract class AppBridges {
public abstract getEnvironmentalVariableBridge(): IEnvironmentalVariableBridge;
public abstract getHttpBridge(): IHttpBridge;
public abstract getListenerBridge(): IListenerBridge;
public abstract getLivechatBridge(): ILivechatBridge;
public abstract getMessageBridge(): IMessageBridge;
public abstract getPersistenceBridge(): IPersistenceBridge;
public abstract getAppActivationBridge(): IAppActivationBridge;
public abstract getRoomBridge(): IRoomBridge;
public abstract getInternalBridge(): IInternalBridge;
public abstract getServerSettingBridge(): IServerSettingBridge;
public abstract getUploadBridge(): IUploadBridge;
public abstract getUserBridge(): IUserBridge;
}
import { ISetting } from '../../definition/settings';
export interface IInternalBridge {
getUsernamesOfRoomById(roomId: string): Array<string>;
getWorkspacePublicKey(): Promise<ISetting>;
}
import { ILivechatMessage, ILivechatRoom, ILivechatTransferData, IVisitor } from '../../definition/livechat';
import { IUser } from '../../definition/users';
export interface ILivechatBridge {
createMessage(message: ILivechatMessage, appId: string): Promise<string>;
getMessageById(messageId: string, appId: string): Promise<ILivechatMessage>;
updateMessage(message: ILivechatMessage, appId: string): Promise<void>;
createVisitor(visitor: IVisitor, appId: string): Promise<string>;
findVisitors(query: object, appId: string): Promise<Array<IVisitor>>;
transferVisitor(visitor: IVisitor, transferData: ILivechatTransferData, appId: string): Promise<boolean>;
createRoom(visitor: IVisitor, agent: IUser, appId: string): Promise<ILivechatRoom>;
closeRoom(room: ILivechatRoom, comment: string, appId: string): Promise<boolean>;
findRooms(visitor: IVisitor, departmentId: string | null, appId: string): Promise<Array<ILivechatRoom>>;
}
import { IUpload } from '../../definition/uploads';
export interface IUploadBridge {
getById(id: string, appId: string): Promise<IUpload>;
getBuffer(upload: IUpload, appId: string): Promise<Buffer>;
}
......@@ -4,4 +4,6 @@ export interface IUserBridge {
getById(id: string, appId: string): Promise<IUser>;
getByUsername(username: string, appId: string): Promise<IUser>;
getActiveUserCount(): Promise<number>;
}
......@@ -7,10 +7,12 @@ import { IEnvironmentalVariableBridge } from './IEnvironmentalVariableBridge';
import { IHttpBridge, IHttpBridgeRequestInfo } from './IHttpBridge';
import { IInternalBridge } from './IInternalBridge';
import { IListenerBridge } from './IListenerBridge';
import { ILivechatBridge } from './ILivechatBridge';
import { IMessageBridge } from './IMessageBridge';
import { IPersistenceBridge } from './IPersistenceBridge';
import { IRoomBridge } from './IRoomBridge';
import { IServerSettingBridge } from './IServerSettingBridge';
import { IUploadBridge } from './IUploadBridge';
import { IUserBridge } from './IUserBridge';
export {
......@@ -18,6 +20,7 @@ export {
IHttpBridge,
IHttpBridgeRequestInfo,
IListenerBridge,
ILivechatBridge,
IMessageBridge,
IPersistenceBridge,
IAppActivationBridge,
......@@ -28,5 +31,6 @@ export {
IInternalBridge,
IServerSettingBridge,
IUserBridge,
IUploadBridge,
AppBridges,
};
......@@ -33,6 +33,7 @@ export class AppCompiler {
noImplicitReturns: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
types: ['node'],
// Set this to true if you would like to see the module resolution process
traceResolution: false,
};
......
import { IAppInfo } from '../../definition/metadata';
import { AppLicenseValidationResult } from '../marketplace/license';
import { ProxiedApp } from '../ProxiedApp';
import { ICompilerError } from './ICompilerError';
......@@ -8,13 +8,17 @@ export class AppFabricationFulfillment {
public app: ProxiedApp;
public implemented: { [int: string]: boolean };
public compilerErrors: Array<ICompilerError>;
public licenseValidationResult: AppLicenseValidationResult;
public storageError: string;
constructor() {
this.compilerErrors = new Array<ICompilerError>();
this.licenseValidationResult = new AppLicenseValidationResult();
}
public setAppInfo(information: IAppInfo): void {
this.info = information;
this.licenseValidationResult.setAppId(information.id);
}
public getAppInfo(): IAppInfo {
......@@ -44,4 +48,20 @@ export class AppFabricationFulfillment {
public getCompilerErrors(): Array<ICompilerError> {
return this.compilerErrors;
}
public setStorageError(errorMessage: string): void {
this.storageError = errorMessage;
}
public getStorageError(): string {
return this.storageError;
}
public hasStorageError(): boolean {
return !!this.storageError;
}
public getLicenseValidationResult(): AppLicenseValidationResult {
return this.licenseValidationResult;
}
}
import { AppLicenseValidationResult } from '../marketplace/license/AppLicenseValidationResult';
export class InvalidLicenseError extends Error {
public constructor(public readonly validationResult: AppLicenseValidationResult) {
super('Invalid app license');
}
}
import { CommandAlreadyExistsError } from './CommandAlreadyExistsError';
import { CommandHasAlreadyBeenTouchedError } from './CommandHasAlreadyBeenTouchedError';
import { CompilerError } from './CompilerError';
import { InvalidLicenseError } from './InvalidLicenseError';
import { MustContainFunctionError } from './MustContainFunctionError';
import { MustExtendAppError } from './MustExtendAppError';
import { NotEnoughMethodArgumentsError } from './NotEnoughMethodArgumentsError';
......@@ -16,4 +17,5 @@ export {
MustExtendAppError,
NotEnoughMethodArgumentsError,
RequiredApiVersionError,
InvalidLicenseError,
};
......@@ -4,6 +4,7 @@ import {
EnvironmentRead,
Http,
HttpExtend,
LivechatRead,
MessageRead,
Modify,
Notifier,
......@@ -16,6 +17,7 @@ import {
SettingsExtend,
SlashCommandsExtend,
SlashCommandsModify,
UploadRead,
UserRead,
} from '../accessors';
import { ApiExtend } from '../accessors/ApiExtend';
......@@ -127,8 +129,10 @@ export class AppAccessorManager {
const room = new RoomRead(this.bridges.getRoomBridge(), appId);
const user = new UserRead(this.bridges.getUserBridge(), appId);
const noti = new Notifier(this.bridges.getMessageBridge(), appId);
const livechat = new LivechatRead(this.bridges.getLivechatBridge(), appId);
const upload = new UploadRead(this.bridges.getUploadBridge(), appId);
this.readers.set(appId, new Reader(env, msg, persist, room, user, noti));
this.readers.set(appId, new Reader(env, msg, persist, room, user, noti, livechat, upload));
}
return this.readers.get(appId);
......
import { AppManager } from '../AppManager';
import { IUserBridge } from '../bridges';
import { InvalidLicenseError } from '../errors';
import { IMarketplaceInfo } from '../marketplace';
import { AppLicenseValidationResult } from '../marketplace/license';
import { Crypto } from '../marketplace/license';
import { MarketplacePurchaseType } from '../marketplace/MarketplacePurchaseType';
enum LicenseVersion {
v1 = 1,
}
export class AppLicenseManager {
private readonly crypto: Crypto;
private readonly userBridge: IUserBridge;
constructor(private readonly manager: AppManager) {
this.crypto = new Crypto(this.manager.getBridges().getInternalBridge());
this.userBridge = this.manager.getBridges().getUserBridge();
}
public async validate(validationResult: AppLicenseValidationResult, appMarketplaceInfo?: IMarketplaceInfo): Promise<void> {
if (!appMarketplaceInfo || appMarketplaceInfo.purchaseType !== MarketplacePurchaseType.PurchaseTypeSubscription) {
return;
}
validationResult.setValidated(true);
const encryptedLicense = appMarketplaceInfo.subscriptionInfo.license.license;
if (!encryptedLicense) {
validationResult.addError('license', 'License for app is invalid');
throw new InvalidLicenseError(validationResult);
}
let license;
try {
license = await this.crypto.decryptLicense(encryptedLicense) as any;
} catch (err) {
validationResult.addError('publicKey', err.message);
throw new InvalidLicenseError(validationResult);
}
switch (license.version) {
case LicenseVersion.v1:
await this.validateV1(appMarketplaceInfo, license, validationResult);
break;
}
}
private async validateV1(appMarketplaceInfo: IMarketplaceInfo, license: any, validationResult: AppLicenseValidationResult): Promise<void> {
if (license.isBundle && (!appMarketplaceInfo.bundledIn || !appMarketplaceInfo.bundledIn.find((value) => value.bundleId === license.appId))) {
validationResult.addError('bundle', 'License issued for a bundle that does not contain the app');
} else if (!license.isBundle && license.appId !== appMarketplaceInfo.id) {
validationResult.addError('appId', `License hasn't been issued for this app`);
}
const renewal = new Date(licens