grigori-gru/jira-to-matrix

View on GitHub
src/bot/actions/create-room.ts

Summary

Maintainability
B
6 hrs
Test Coverage
import { fromString } from 'html-to-text';
import marked from 'marked';
import { getLogger } from '../../modules/log';
import { translate } from '../../locales';
import { infoBody } from '../../lib/messages';
import { CreateRoomData, TaskTracker, DescriptionFields } from '../../types';
import { errorTracing } from '../../lib/utils';
import { LINE_BREAKE_TAG, INDENT } from '../../lib/consts';
import { BaseAction } from './base-action';
import { ChatFasade } from '../../messengers/chat-fasade';

const logger = getLogger(module);

export const getOpenedDescriptionBlock = data => [LINE_BREAKE_TAG, INDENT, data].join('');

export const getClosedDescriptionBlock = data => [getOpenedDescriptionBlock(data), LINE_BREAKE_TAG].join('');

// eslint-disable-next-line

interface CreateIssueRoomOptions {
    key: string;
    summary: string;
    projectKey: string;
    descriptionFields: DescriptionFields;
    roomName: string;
    statusColors: string[] | string;
}

interface CreateMilestoneRoomOptions {
    key: string;
    summary: string;
    roomName: string;
    members: string[];
    topic: string;
}

export class CreateRoom extends BaseAction<ChatFasade, TaskTracker> {
    getEpicInfo(epicLink) {
        epicLink === translate('miss')
            ? ''
            : `            <br>Epic link:
                ${getOpenedDescriptionBlock(epicLink)}
                ${getClosedDescriptionBlock(this.taskTracker.getViewUrl(epicLink))}`;
    }

    getPost(body) {
        const post = `
            Assignee:
                ${getOpenedDescriptionBlock(body.assigneeName)}
            <br>Reporter:
                ${getOpenedDescriptionBlock(body.reporterName)}
            <br>Type:
                ${getClosedDescriptionBlock(body.typeName)}
            <br>Estimate time:
                ${getClosedDescriptionBlock(body.estimateTime)}
            <br>Description:
                ${getClosedDescriptionBlock(marked(body.description))}
            <br>Priority:
                ${getClosedDescriptionBlock(body.priority)}`;

        const epicInfo = this.getEpicInfo(body.epicLink);

        return [post, epicInfo].join('\n');
    }

    getDescription(descriptionFields: DescriptionFields): { body: string; htmlBody: string } {
        try {
            const htmlBody = this.getPost(descriptionFields);
            const body = fromString(htmlBody);

            return { body, htmlBody };
        } catch (err) {
            throw errorTracing('getDescription', err);
        }
    }

    async createMilestoneRoom({ key, summary, roomName, members, topic }: CreateMilestoneRoomOptions): Promise<void> {
        try {
            const milestoneWatchersChatIds = await Promise.all(
                members.map(displayName => this.currentChatItem.getUserIdByDisplayName(displayName)),
            );

            const options = {
                room_alias_name: key,
                invite: milestoneWatchersChatIds,
                name: roomName,
                topic,
                purpose: summary,
            };

            const roomId = await this.currentChatItem.createRoom(options);

            logger.info(`Created room for ${key}: ${roomId}`);

            await this.currentChatItem.sendHtmlMessage(roomId, infoBody, infoBody);
        } catch (err) {
            throw errorTracing('createIssueRoom', err);
        }
    }

    async createIssueRoom({
        key,
        summary,
        projectKey,
        descriptionFields,
        roomName,
        statusColors,
    }: CreateIssueRoomOptions): Promise<string> {
        try {
            const autoinviteUsers = await this.getAutoinviteUsers(projectKey, descriptionFields.typeName);

            const issueWatchers = await this.taskTracker.getIssueWatchers(key);
            const issueWatchersChatIds = await Promise.all(
                issueWatchers.map(({ displayName, userId }) =>
                    userId
                        ? this.currentChatItem.getChatUserId(userId)
                        : this.currentChatItem.getUserIdByDisplayName(displayName),
                ),
            );

            const invite = [...issueWatchersChatIds, ...autoinviteUsers];

            const topic = this.taskTracker.getViewUrl(key);

            const avatarUrl = await this.getAvatarLink(key, statusColors);

            const options = {
                room_alias_name: key,
                invite,
                name: roomName,
                topic,
                purpose: summary,
                avatarUrl,
            };

            const roomId = await this.currentChatItem.createRoom(options);

            logger.info(`Created room for ${key}: ${roomId}`);
            const { body, htmlBody } = this.getDescription(descriptionFields);

            await this.currentChatItem.sendHtmlMessage(roomId, body, htmlBody);
            await this.currentChatItem.sendHtmlMessage(roomId, infoBody, infoBody);

            if (statusColors === this.defaultAvatarColor) {
                const issueLabelExist = translate('issueLabelNotExist');
                await this.currentChatItem.sendHtmlMessage(roomId, issueLabelExist, issueLabelExist);
            }

            return roomId;
        } catch (err) {
            throw errorTracing('createIssueRoom', err);
        }
    }

    async createProjectRoom(projectKey) {
        try {
            const { lead, name: projectName } = await this.taskTracker.getProject(projectKey);
            const name = this.currentChatItem.composeRoomName(projectKey, projectName);
            const leadUserId = await this.currentChatItem.getUserIdByDisplayName(lead);
            const topic = this.taskTracker.getViewUrl(projectKey);

            const options = {
                room_alias_name: projectKey,
                invite: [leadUserId],
                name,
                topic,
            };

            const roomId = await this.currentChatItem.createRoom(options);
            logger.info(`Created room for project ${projectKey}: ${roomId}`);
        } catch (err) {
            throw errorTracing('createProjectRoom', err);
        }
    }

    async getCreateIsueOptions(keyOrId, hookLabels, issueBody): Promise<CreateIssueRoomOptions> {
        const currentIssueColor = await this.taskTracker.getCurrentIssueColor(keyOrId, hookLabels);
        const statusColors = currentIssueColor.length ? currentIssueColor : this.defaultAvatarColor;

        return {
            key: this.taskTracker.selectors.getIssueKey(issueBody)!,
            summary: this.taskTracker.selectors.getSummary(issueBody)!,
            descriptionFields: this.taskTracker.selectors.getDescriptionFields(issueBody)!,
            projectKey: this.taskTracker.selectors.getProjectKey(issueBody)!,
            roomName: this.taskTracker.selectors.getRoomName(issueBody),
            statusColors,
        };
    }

    hasEmptyPorperty(issue: CreateRoomData['issue']): boolean {
        return !Object.values(issue).find(el => !el);
    }

    async run({ issue, projectKey, milestoneId }: CreateRoomData): Promise<boolean> {
        try {
            const keyOrId = issue.key || issue.id;
            if (issue && keyOrId) {
                const issueBody = await this.taskTracker.getIssueSafety(keyOrId);
                if (!issueBody) {
                    logger.warn(`Issue ${keyOrId} is not exists`);

                    return false;
                }
                const checkedIssue = await this.getCreateIsueOptions(keyOrId, issue.hookLabels, issueBody);
                if (!(await this.currentChatItem.getRoomIdByName(checkedIssue.key))) {
                    const roomId = await this.createIssueRoom(checkedIssue);
                    await this.taskTracker.sendMessage(
                        checkedIssue.key,
                        translate('roomCreatedMessage', {
                            link: this.taskTracker.createLink(this.chatApi.getRoomLink(roomId), translate('chat')),
                        }),
                    );
                }

                if (milestoneId) {
                    const milestoneKey = this.taskTracker.selectors.getMilestoneKey(issueBody, milestoneId);
                    // it checks if issue has no milestone
                    if (milestoneKey && !(await this.currentChatItem.getRoomIdByName(milestoneKey))) {
                        const milestoneUrl = this.taskTracker.getMilestoneUrl(issueBody);
                        const members = await this.taskTracker.getMilestoneWatchers(milestoneUrl);

                        const options: CreateMilestoneRoomOptions = {
                            key: milestoneKey,
                            summary: this.taskTracker.selectors.getMilestoneSummary(issueBody)!,
                            roomName: this.taskTracker.selectors.getMilestoneRoomName(issueBody)!,
                            members,
                            topic: this.taskTracker.selectors.getMilestoneViewUrl(issueBody)!,
                        };
                        await this.createMilestoneRoom(options);
                    }
                }
            }
            if (projectKey && this.config.features.createProjectRoom) {
                (await this.currentChatItem.getRoomIdByName(projectKey)) || (await this.createProjectRoom(projectKey));
            }

            return true;
        } catch (err) {
            throw errorTracing('create room', err);
        }
    }
}