grigori-gru/jira-to-matrix

View on GitHub
src/hook-parser/index.ts

Summary

Maintainability
A
25 mins
Test Coverage
import { Selectors, Parser, Issue } from '../types';
import { getLogger } from '../modules/log';
import * as messages from '../lib/messages';
import { redis, REDIS_IGNORE_PREFIX, REDIS_ROOM_KEY } from '../redis-client';

import { QueueHandler } from '../queue';
import { TaskTracker, Config } from '../types';

const logger = getLogger(module);

export class HookParser {
    testMode: boolean;
    ignoredUsers: string[];
    selectors: Selectors;
    parser: Parser;

    constructor(private taskTracker: TaskTracker, private config: Config, private queueHandler: QueueHandler) {
        this.testMode = this.config.testMode.on;
        this.ignoredUsers = [...this.config.usersToIgnore, ...this.config.testMode.users];
        this.selectors = taskTracker.selectors;
        this.parser = taskTracker.parser;
    }

    init(): HookParser {
        const taskTracker = this.taskTracker.init();

        return new HookParser(taskTracker, this.config, this.queueHandler);
    }

    getParserName(func) {
        return `get${func[0].toUpperCase()}${func.slice(1)}Data`;
    }

    getFuncRedisData(funcName, body) {
        const parserName = this.getParserName(funcName);
        const data = this.parser[parserName](body);
        const redisKey = this.selectors.getRedisKey(funcName, body);

        return { redisKey, funcName, data };
    }

    getFuncAndBody(body) {
        const botFunc = this.parser.getBotActions(body);
        const createRoomData = this.parser.isCreateRoom(body) && this.parser.getCreateRoomData(body);
        const roomsData = { redisKey: REDIS_ROOM_KEY, createRoomData };
        const funcsData = botFunc.map(funcName => this.getFuncRedisData(funcName, body));

        return [roomsData, ...funcsData];
    }

    /**
     * Is ignore data
     */
    async getParsedAndSaveToRedis(body: any): Promise<boolean> {
        try {
            this.taskTracker = this.taskTracker.init();

            if (await this.isIgnore(body)) {
                return false;
            }
            const parsedBody = this.getFuncAndBody(body);
            const handledKeys = (await Promise.all(parsedBody.map(el => this.queueHandler.saveIncoming(el)))).filter(
                Boolean,
            );

            await this.queueHandler.saveToHandled(handledKeys);

            return true;
        } catch (err) {
            logger.error('Error in parsing ', err);

            return false;
        }
    }

    isTestCreator(creator: string): boolean {
        const status = this.testMode ? !this.ignoredUsers.includes(creator) : this.ignoredUsers.includes(creator);
        if (status) {
            logger.warn(
                `Hook should be ignored because issue creator is ${creator} in test mode state: ${this.testMode}`,
            );
        }

        return status;
    }

    async isManuallyIgnore(project, taskType, type, body) {
        // example {INDEV: {taskType: ['task', 'error'], BBQ: ['task']}}
        const result = await redis.getAsync(REDIS_IGNORE_PREFIX);
        const redisIgnore = JSON.parse(result);
        if (!redisIgnore) {
            logger.debug('No redis ignore projects found!!!');
            return false;
        }
        const ignoreList = redisIgnore[project];
        if (!ignoreList) {
            return false;
        }

        return this.taskTracker.checkIgnoreList(ignoreList.taskType, taskType, type, body);
    }

    async getIssueForSearch(keyOrKeys: string | string[]): Promise<Issue> {
        if (Array.isArray(keyOrKeys)) {
            const res = await Promise.all(
                keyOrKeys.map(async key => {
                    try {
                        const issue = await this.taskTracker.getIssue(key);

                        return issue;
                    } catch (error) {
                        return false;
                    }
                }),
            );

            const [issue] = res.filter(Boolean);

            if (!issue) {
                throw new Error('Issue not found for push hook');
            }

            return issue;
        }

        return await this.taskTracker.getIssue(keyOrKeys);
    }

    async getManuallyIgnore(body) {
        const type = this.selectors.getHookType(body);
        if (this.taskTracker.isAvoidHookType(type)) {
            return false;
        }

        const keyOrId = await this.taskTracker.getKeyOrIdForCheckIgnore(body);
        const issue = await this.getIssueForSearch(keyOrId);
        const projectKey = this.selectors.getProjectKey(issue);
        const descriptionFields = this.selectors.getDescriptionFields(body);

        const status = await this.isManuallyIgnore(projectKey, descriptionFields?.typeName, type, body);
        if (status) {
            logger.warn('Hook is ignored by user manually');
        }

        return status;
    }

    async isIgnoreCreator(body) {
        if (this.selectors.isCommentEvent(body)) {
            const creator = this.selectors.getCreator(body);
            // if (creator && this.ignoredUsers.includes(creator)) {
            //     logger.warn(`Comment author is ignore user "${creator}", skip hook`);

            //     return true;
            // }

            if (creator === this.config.taskTracker.user) {
                logger.warn(`Comment author is task tracker user "${creator}", skip hook`);

                return true;
            }
        }
        const keyOrId = await this.taskTracker.getKeyOrIdForCheckIgnore(body);

        const issue = await this.getIssueForSearch(keyOrId!);
        const issueCreator = this.selectors.getIssueCreator(issue)!;

        return this.isTestCreator(issueCreator);
    }

    async isIgnoreHook(body): Promise<boolean> {
        const status = await this.taskTracker.isIgnoreHook(body);
        if (status) {
            logger.warn('Hook should be ignored because its type is cannot be handle');
        }

        return status;
    }

    async getIgnoreProject(body) {
        await this.taskTracker.testJiraRequest();
        const webhookEvent = this.selectors.getBodyWebhookEvent(body);
        const timestamp = this.selectors.getBodyTimestamp(body);
        const issueName = this.selectors.getIssueName(body);

        const ignoreStatus =
            (await this.isIgnoreHook(body)) ||
            (await this.getManuallyIgnore(body)) ||
            (await this.isIgnoreCreator(body));

        return { timestamp, webhookEvent, ignoreStatus, issueName };
    }

    async isIgnore(body: any) {
        const projectStatus = await this.getIgnoreProject(body);
        const msg = messages.getWebhookStatusLog({ projectStatus });

        logger.info(msg);

        // return userStatus.ignoreStatus || projectStatus.ignoreStatus;
        return projectStatus.ignoreStatus;
    }
}