api/src/data/logUtils.ts

Summary

Maintainability
F
3 wks
Test Coverage
import {
  putCreateLog as putCreateLogC,
  putDeleteLog as putDeleteLogC,
  putUpdateLog as putUpdateLogC
} from 'erxes-api-utils';
import * as _ from 'underscore';

import {
  IPipelineDocument,
  IStageDocument
} from '../db/models/definitions/boards';
import { IChannelDocument } from '../db/models/definitions/channels';
import { ICompanyDocument } from '../db/models/definitions/companies';
import { ACTIVITY_CONTENT_TYPES } from '../db/models/definitions/constants';
import { ICustomerDocument } from '../db/models/definitions/customers';
import {
  IDealDocument,
  IProductDocument
} from '../db/models/definitions/deals';
import {
  IEngageMessage,
  IEngageMessageDocument
} from '../db/models/definitions/engages';
import { IGrowthHackDocument } from '../db/models/definitions/growthHacks';
import { IIntegrationDocument } from '../db/models/definitions/integrations';
import {
  ICategoryDocument,
  ITopicDocument
} from '../db/models/definitions/knowledgebase';
import { IPipelineTemplateDocument } from '../db/models/definitions/pipelineTemplates';
import { IScriptDocument } from '../db/models/definitions/scripts';
import { ITaskDocument } from '../db/models/definitions/tasks';
import { ITicketDocument } from '../db/models/definitions/tickets';
import { IUserDocument } from '../db/models/definitions/users';
import {
  Boards,
  Brands,
  Checklists,
  Companies,
  Customers,
  Deals,
  Forms,
  GrowthHacks,
  Integrations,
  KnowledgeBaseArticles,
  KnowledgeBaseCategories,
  KnowledgeBaseTopics,
  PipelineLabels,
  Pipelines,
  ProductCategories,
  Products,
  Segments,
  Stages,
  Tags,
  Tasks,
  Tickets,
  Users,
  UsersGroups
} from '../db/models/index';
import { debugError } from '../debuggers';
import messageBroker from '../messageBroker';
import { callAfterMutation } from '../pluginUtils';
import { MODULE_NAMES, RABBITMQ_QUEUES } from './constants';
import {
  buildLabelList,
  INameLabel,
  ISchemaMap,
  LOG_MAPPINGS
} from './resolvers/queries/logs';
import {
  getSubServiceDomain,
  registerOnboardHistory,
  sendRequest,
  sendToWebhook
} from './utils';

export type LogDesc = {
  [key: string]: any;
} & { name: any };

interface ILogNameParams {
  idFields: string[];
  foreignKey: string;
  prevList?: LogDesc[];
}

interface ILogParams extends ILogNameParams {
  collection: any;
  nameFields: string[];
}

interface IContentTypeParams {
  contentType: string;
  contentTypeId: string;
}

/**
 * @param object - Previous state of the object
 * @param newData - Requested update data
 * @param updatedDocument - State after any updates to the object
 */
export interface ILogDataParams {
  type: string;
  description?: string;
  object: any;
  newData?: object;
  extraDesc?: object[];
  updatedDocument?: any;
}

export interface ILogQueryParams {
  start?: string;
  end?: string;
  userId?: string;
  action?: string | { $in: string[] };
  page?: number;
  perPage?: number;
  type?: string | { $in: string[] };
  objectId?: string | { $in: string[] };
  $or: any[];
}

export interface IActivityLogQueryParams {
  contentId?: any;
  contentType?: string;
  action?: any;
}

interface IDescriptions {
  description?: string;
  extraDesc?: LogDesc[];
}

interface IDescriptionParams {
  action: string;
  type: string;
  obj: any;
  updatedDocument?: any;
}

type BoardItemDocument =
  | IDealDocument
  | ITaskDocument
  | ITicketDocument
  | IGrowthHackDocument;

const LOG_ACTIONS = {
  CREATE: 'create',
  UPDATE: 'update',
  DELETE: 'delete'
};

export const ACTIVITY_LOG_ACTIONS = {
  ADD: 'add',
  CREATE_BOARD_ITEM: 'createBoardItem',
  CREATE_BOARD_ITEM_MOVEMENT_LOG: 'createBoardItemMovementLog',
  CREATE_BOARD_ITEMS: 'createBoardItems',
  CREATE_ARCHIVE_LOG: 'createArchiveLog',
  CREATE_ASSIGNE_LOG: 'createAssigneLog',
  CREATE_COC_LOG: 'createCocLog',
  CREATE_COC_LOGS: 'createCocLogs',
  CREATE_SEGMENT_LOG: 'createSegmentLog',
  CREATE_CHECKLIST_LOG: 'createChecklistLog',
  CREATE_TAG_LOG: 'createTagLog',
  REMOVE_ACTIVITY_LOG: 'removeActivityLog',
  REMOVE_ACTIVITY_LOGS: 'removeActivityLogs'
};

// used in internalNotes mutations
const findContentItemName = async (
  contentType: string,
  contentTypeId: string
): Promise<string> => {
  let name: string = '';

  if (contentType === MODULE_NAMES.DEAL) {
    const deal = await Deals.getDeal(contentTypeId);

    if (deal && deal.name) {
      name = deal.name;
    }
  }
  if (contentType === MODULE_NAMES.CUSTOMER) {
    const customer = await Customers.getCustomer(contentTypeId);

    if (customer) {
      name = Customers.getCustomerName(customer);
    }
  }
  if (contentType === MODULE_NAMES.COMPANY) {
    const company = await Companies.getCompany(contentTypeId);

    if (company) {
      name = Companies.getCompanyName(company);
    }
  }
  if (contentType === MODULE_NAMES.TASK) {
    const task = await Tasks.getTask(contentTypeId);

    if (task && task.name) {
      name = task.name;
    }
  }
  if (contentType === MODULE_NAMES.TICKET) {
    const ticket = await Tickets.getTicket(contentTypeId);

    if (ticket && ticket.name) {
      name = ticket.name;
    }
  }
  if (contentType === MODULE_NAMES.GROWTH_HACK) {
    const gh = await GrowthHacks.getGrowthHack(contentTypeId);

    if (gh && gh.name) {
      name = gh.name;
    }
  }
  if (contentType === MODULE_NAMES.USER) {
    const user = await Users.getUser(contentTypeId);

    if (user) {
      name = user.username || user.email || '';
    }
  }
  if (contentType === MODULE_NAMES.PRODUCT) {
    const product = await Products.getProduct({ _id: contentTypeId });

    if (product) {
      name = product.name;
    }
  }

  return name;
};

const gatherUsernames = async (params: ILogNameParams): Promise<LogDesc[]> => {
  const { idFields, foreignKey, prevList } = params;

  return gatherNames({
    collection: Users,
    idFields,
    foreignKey,
    prevList,
    nameFields: ['email', 'username']
  });
};

const gatherIntegrationNames = async (
  params: ILogNameParams
): Promise<LogDesc[]> => {
  const { idFields, foreignKey, prevList } = params;

  return gatherNames({
    collection: Integrations,
    idFields,
    foreignKey,
    prevList,
    nameFields: ['name']
  });
};

export const gatherTagNames = async (
  params: ILogNameParams
): Promise<LogDesc[]> => {
  const { idFields, foreignKey, prevList } = params;

  return gatherNames({
    collection: Tags,
    idFields,
    foreignKey,
    prevList,
    nameFields: ['name']
  });
};

const gatherBrandNames = async (params: ILogNameParams): Promise<LogDesc[]> => {
  const { idFields, foreignKey, prevList } = params;

  return gatherNames({
    collection: Brands,
    idFields,
    foreignKey,
    prevList,
    nameFields: ['name']
  });
};

/**
 * Finds name field from given collection
 * @param params.collection Collection to find
 * @param params.idFields Id fields saved in collection
 * @param params.foreignKey Name of id fields
 * @param params.prevList Array to save found id with name
 * @param params.nameFields List of values to be mapped to id field
 */
const gatherNames = async (params: ILogParams): Promise<LogDesc[]> => {
  const {
    collection,
    idFields,
    foreignKey,
    prevList,
    nameFields = []
  } = params;
  let options: LogDesc[] = [];

  if (prevList && prevList.length > 0) {
    options = prevList;
  }

  const uniqueIds = _.compact(_.uniq(idFields));

  for (const id of uniqueIds) {
    const item = await collection.findOne({ _id: id });
    let name: string = `item with id "${id}" has been deleted`;

    if (item) {
      for (const n of nameFields) {
        if (item[n]) {
          name = item[n];
        }
      }
    }

    options.push({ [foreignKey]: id, name });
  }

  return options;
};

const findItemName = async ({
  contentType,
  contentTypeId
}: IContentTypeParams): Promise<string> => {
  let item: any;
  let name: string = '';

  if (contentType === ACTIVITY_CONTENT_TYPES.DEAL) {
    item = await Deals.findOne({ _id: contentTypeId });
  }

  if (contentType === ACTIVITY_CONTENT_TYPES.TASK) {
    item = await Tasks.findOne({ _id: contentTypeId });
  }

  if (contentType === ACTIVITY_CONTENT_TYPES.TICKET) {
    item = await Tickets.findOne({ _id: contentTypeId });
  }

  if (contentType === ACTIVITY_CONTENT_TYPES.GROWTH_HACK) {
    item = await GrowthHacks.getGrowthHack(contentTypeId);
  }

  if (item && item.name) {
    name = item.name;
  }

  return name;
};

const gatherCompanyFieldNames = async (
  doc: ICompanyDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.parentCompanyId) {
    options = await gatherNames({
      collection: Companies,
      idFields: [doc.parentCompanyId],
      foreignKey: 'parentCompanyId',
      prevList: options,
      nameFields: ['primaryName']
    });
  }

  if (doc.ownerId) {
    options = await gatherUsernames({
      idFields: [doc.ownerId],
      foreignKey: 'ownerId',
      prevList: options
    });
  }

  if (doc.mergedIds && doc.mergedIds.length > 0) {
    options = await gatherNames({
      collection: Companies,
      idFields: doc.mergedIds,
      foreignKey: 'mergedIds',
      prevList: options,
      nameFields: ['primaryName']
    });
  }

  if (doc.tagIds && doc.tagIds.length > 0) {
    options = await gatherTagNames({
      idFields: doc.tagIds,
      foreignKey: 'tagIds',
      prevList: options
    });
  }

  return options;
};

const gatherCustomerFieldNames = async (
  doc: ICustomerDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.ownerId) {
    options = await gatherUsernames({
      idFields: [doc.ownerId],
      foreignKey: 'ownerId',
      prevList: options
    });
  }

  if (doc.integrationId) {
    options = await gatherIntegrationNames({
      idFields: [doc.integrationId],
      foreignKey: 'integrationId',
      prevList: options
    });
  }

  if (doc.tagIds && doc.tagIds.length > 0) {
    options = await gatherTagNames({
      idFields: doc.tagIds,
      foreignKey: 'tagIds',
      prevList: options
    });
  }

  if (doc.mergedIds) {
    options = await gatherNames({
      collection: Customers,
      idFields: doc.mergedIds,
      foreignKey: 'mergedIds',
      prevList: options,
      nameFields: ['firstName']
    });
  }

  return options;
};

const gatherBoardItemFieldNames = async (
  doc: BoardItemDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.userId) {
    options = await gatherUsernames({
      idFields: [doc.userId],
      foreignKey: 'userId',
      prevList: options
    });
  }

  if (doc.assignedUserIds && doc.assignedUserIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.assignedUserIds,
      foreignKey: 'assignedUserIds',
      prevList: options
    });
  }

  if (doc.watchedUserIds && doc.watchedUserIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.watchedUserIds,
      foreignKey: 'watchedUserIds',
      prevList: options
    });
  }

  if (doc.labelIds && doc.labelIds.length > 0) {
    options = await gatherNames({
      collection: PipelineLabels,
      idFields: doc.labelIds,
      foreignKey: 'labelIds',
      prevList: options,
      nameFields: ['name']
    });
  }

  options = await gatherNames({
    collection: Stages,
    idFields: [doc.stageId],
    foreignKey: 'stageId',
    prevList: options,
    nameFields: ['name']
  });

  if (doc.initialStageId) {
    options = await gatherNames({
      collection: Stages,
      idFields: [doc.initialStageId],
      foreignKey: 'initialStageId',
      prevList: options,
      nameFields: ['name']
    });
  }

  if (doc.modifiedBy) {
    options = await gatherUsernames({
      idFields: [doc.modifiedBy],
      foreignKey: 'modifiedBy',
      prevList: options
    });
  }

  return options;
};

const gatherDealFieldNames = async (
  doc: IDealDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  options = await gatherBoardItemFieldNames(doc, options);

  if (doc.productsData && doc.productsData.length > 0) {
    options = await gatherNames({
      collection: Products,
      idFields: doc.productsData.map(p => p.productId),
      foreignKey: 'productId',
      prevList: options,
      nameFields: ['name']
    });
  }

  return options;
};

const gatherEngageFieldNames = async (
  doc: IEngageMessageDocument | IEngageMessage,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.segmentIds && doc.segmentIds.length > 0) {
    options = await gatherNames({
      collection: Segments,
      idFields: doc.segmentIds,
      foreignKey: 'segmentIds',
      prevList: options,
      nameFields: ['name']
    });
  }

  if (doc.brandIds && doc.brandIds.length > 0) {
    options = await gatherBrandNames({
      idFields: doc.brandIds,
      foreignKey: 'brandIds',
      prevList: options
    });
  }

  if (doc.tagIds && doc.tagIds.length > 0) {
    options = await gatherTagNames({
      idFields: doc.tagIds,
      foreignKey: 'tagIds',
      prevList: options
    });
  }

  if (doc.fromUserId) {
    options = await gatherUsernames({
      idFields: [doc.fromUserId],
      foreignKey: 'fromUserId',
      prevList: options
    });
  }

  if (doc.messenger && doc.messenger.brandId) {
    options = await gatherBrandNames({
      idFields: [doc.messenger.brandId],
      foreignKey: 'brandId',
      prevList: options
    });
  }

  return options;
};

const gatherChannelFieldNames = async (
  doc: IChannelDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.userId) {
    options = await gatherUsernames({
      idFields: [doc.userId],
      foreignKey: 'userId',
      prevList: options
    });
  }

  if (doc.memberIds && doc.memberIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.memberIds,
      foreignKey: 'memberIds',
      prevList: options
    });
  }

  if (doc.integrationIds && doc.integrationIds.length > 0) {
    options = await gatherIntegrationNames({
      idFields: doc.integrationIds,
      foreignKey: 'integrationIds',
      prevList: options
    });
  }

  return options;
};

const gatherGHFieldNames = async (
  doc: IGrowthHackDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  options = await gatherBoardItemFieldNames(doc, options);

  if (doc.votedUserIds && doc.votedUserIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.votedUserIds,
      foreignKey: 'votedUserIds',
      prevList: options
    });
  }

  return options;
};

const gatherIntegrationFieldNames = async (
  doc: IIntegrationDocument,
  prevList?: LogDesc[]
) => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.createdUserId) {
    options = await gatherUsernames({
      idFields: [doc.createdUserId],
      foreignKey: 'createdUserId',
      prevList: options
    });
  }

  if (doc.brandId) {
    options = await gatherBrandNames({
      idFields: [doc.brandId],
      foreignKey: 'brandId',
      prevList: options
    });
  }

  if (doc.tagIds && doc.tagIds.length > 0) {
    options = await gatherTagNames({
      idFields: doc.tagIds,
      foreignKey: 'tagIds',
      prevList: options
    });
  }

  if (doc.formId) {
    options = await gatherNames({
      collection: Forms,
      idFields: [doc.formId],
      foreignKey: 'formId',
      prevList: options,
      nameFields: ['title']
    });
  }

  return options;
};

const gatherKbTopicFieldNames = async (
  doc: ITopicDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  options = await gatherUsernames({
    idFields: [doc.createdBy],
    foreignKey: 'createdBy',
    prevList: options
  });

  options = await gatherUsernames({
    idFields: [doc.modifiedBy],
    foreignKey: 'modifiedBy',
    prevList: options
  });

  if (doc.brandId) {
    options = await gatherBrandNames({
      idFields: [doc.brandId],
      foreignKey: 'brandId',
      prevList: options
    });
  }

  if (doc.categoryIds && doc.categoryIds.length > 0) {
    // categories are removed alongside
    const categories = await KnowledgeBaseCategories.find(
      { _id: { $in: doc.categoryIds } },
      { title: 1 }
    );

    for (const cat of categories) {
      options.push({
        categoryIds: cat._id,
        name: cat.title
      });
    }
  }

  return options;
};

const gatherKbCategoryFieldNames = async (
  doc: ICategoryDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  const articles = await KnowledgeBaseArticles.find(
    { _id: { $in: doc.articleIds } },
    { title: 1 }
  );

  options = await gatherUsernames({
    idFields: [doc.createdBy],
    foreignKey: 'createdBy',
    prevList: options
  });

  options = await gatherUsernames({
    idFields: [doc.modifiedBy],
    foreignKey: 'modifiedBy',
    prevList: options
  });

  if (articles.length > 0) {
    for (const article of articles) {
      options.push({ articleIds: article._id, name: article.title });
    }
  }

  return options;
};

const gatherProductFieldNames = async (
  doc: IProductDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.tagIds && doc.tagIds.length > 0) {
    options = await gatherTagNames({
      idFields: doc.tagIds,
      foreignKey: 'tagIds',
      prevList: options
    });
  }

  if (doc.categoryId) {
    options = await gatherNames({
      collection: ProductCategories,
      idFields: [doc.categoryId],
      foreignKey: 'categoryId',
      prevList: options,
      nameFields: ['name']
    });
  }

  return options;
};

const gatherScriptFieldNames = async (
  doc: IScriptDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.messengerId) {
    options = await gatherIntegrationNames({
      idFields: [doc.messengerId],
      foreignKey: 'messengerId',
      prevList: options
    });
  }

  if (doc.kbTopicId) {
    options = await gatherNames({
      collection: KnowledgeBaseTopics,
      idFields: [doc.kbTopicId],
      foreignKey: 'kbTopicId',
      prevList: options,
      nameFields: ['title']
    });
  }

  if (doc.leadIds && doc.leadIds.length > 0) {
    options = await gatherIntegrationNames({
      idFields: doc.leadIds,
      foreignKey: 'leadIds',
      prevList: options
    });
  }

  return options;
};

const gatherPipelineFieldNames = async (
  doc: IPipelineDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  options = await gatherNames({
    collection: Boards,
    idFields: [doc.boardId],
    foreignKey: 'boardId',
    nameFields: ['name'],
    prevList: options
  });

  if (doc.userId) {
    options = await gatherUsernames({
      idFields: [doc.userId],
      foreignKey: 'userId',
      prevList: options
    });
  }

  if (doc.excludeCheckUserIds && doc.excludeCheckUserIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.excludeCheckUserIds,
      foreignKey: 'excludeCheckUserIds',
      prevList: options
    });
  }

  if (doc.memberIds && doc.memberIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.memberIds,
      foreignKey: 'memberIds',
      prevList: options
    });
  }

  if (doc.watchedUserIds && doc.watchedUserIds.length > 0) {
    options = await gatherUsernames({
      idFields: doc.watchedUserIds,
      foreignKey: 'watchedUserIds',
      prevList: options
    });
  }

  return options;
};

const gatherPipelineTemplateFieldNames = async (
  doc: IPipelineTemplateDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  options = await gatherUsernames({
    idFields: [doc.createdBy || ''],
    foreignKey: 'createdBy',
    prevList: options
  });

  if (doc.stages && doc.stages.length > 0) {
    options = await gatherNames({
      collection: Forms,
      idFields: doc.stages.map(s => s.formId),
      foreignKey: 'formId',
      prevList: options,
      nameFields: ['title']
    });
  }

  return options;
};

const gatherUserFieldNames = async (
  doc: IUserDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  // show only user group names of users for now
  options = await gatherNames({
    collection: UsersGroups,
    idFields: doc.groupIds || [],
    foreignKey: 'groupIds',
    nameFields: ['name'],
    prevList: options
  });

  return options;
};

const gatherStageFieldNames = async (
  doc: IStageDocument,
  prevList?: LogDesc[]
): Promise<LogDesc[]> => {
  let options: LogDesc[] = [];

  if (prevList) {
    options = prevList;
  }

  if (doc.userId) {
    options = await gatherUsernames({
      idFields: [doc.userId],
      foreignKey: 'userId',
      prevList: options
    });
  }
  if (doc.pipelineId) {
    options = await gatherNames({
      collection: Pipelines,
      idFields: [doc.pipelineId],
      foreignKey: 'pipelineId',
      prevList: options,
      nameFields: ['name']
    });
  }
  if (doc.formId) {
    options = await gatherNames({
      collection: Forms,
      idFields: [doc.formId],
      foreignKey: 'formId',
      prevList: options,
      nameFields: ['title']
    });
  }

  return options;
};

const gatherDescriptions = async (
  params: IDescriptionParams
): Promise<IDescriptions> => {
  const { action, type, obj, updatedDocument } = params;

  let extraDesc: LogDesc[] = [];
  let description: string = '';

  switch (type) {
    case MODULE_NAMES.BRAND:
    case MODULE_NAMES.BOARD_DEAL:
    case MODULE_NAMES.BOARD_GH:
    case MODULE_NAMES.BOARD_TASK:
    case MODULE_NAMES.BOARD_TICKET:
      if (obj.userId) {
        extraDesc = await gatherUsernames({
          idFields: [obj.userId],
          foreignKey: 'userId'
        });
      }

      description = `"${obj.name}" has been ${action}d`;

      break;
    case MODULE_NAMES.PIPELINE_DEAL:
    case MODULE_NAMES.PIPELINE_GH:
    case MODULE_NAMES.PIPELINE_TASK:
    case MODULE_NAMES.PIPELINE_TICKET:
      extraDesc = await gatherPipelineFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherPipelineFieldNames(updatedDocument, extraDesc);
      }

      description = `"${obj.name}" has been ${action}d`;

      break;
    case MODULE_NAMES.CHANNEL:
      extraDesc = await gatherChannelFieldNames(obj);
      description = `"${obj.name}" has been ${action}d`;

      if (updatedDocument) {
        extraDesc = await gatherChannelFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.CHECKLIST:
      const itemName = await findItemName({
        contentType: obj.contentType,
        contentTypeId: obj.contentTypeId
      });

      extraDesc = await gatherUsernames({
        idFields: [obj.createdUserId],
        foreignKey: 'createdUserId'
      });

      extraDesc.push({ contentTypeId: obj.contentTypeId, name: itemName });

      if (action === LOG_ACTIONS.CREATE) {
        description = `"${
          obj.title
        }" has been created in ${obj.contentType.toUpperCase()} "${itemName}"`;
      }
      if (action === LOG_ACTIONS.UPDATE) {
        description = `"${
          obj.title
        }" saved in ${obj.contentType.toUpperCase()} "${itemName}" has been edited`;
      }
      if (action === LOG_ACTIONS.DELETE) {
        description = `"${
          obj.title
        }" from ${obj.contentType.toUpperCase()} "${itemName}" has been removed`;
      }

      break;
    case MODULE_NAMES.CHECKLIST_ITEM:
      const checklist = await Checklists.getChecklist(obj.checklistId);

      extraDesc = await gatherUsernames({
        idFields: [obj.createdUserId],
        foreignKey: 'createdUserid'
      });

      extraDesc.push({ checklistId: checklist._id, name: checklist.title });

      if (action === LOG_ACTIONS.CREATE) {
        description = `"${obj.content}" has been added to "${checklist.title}"`;
      }
      if (action === LOG_ACTIONS.UPDATE) {
        description = `"${obj.content}" has been edited /checked/`;
      }
      if (action === LOG_ACTIONS.DELETE) {
        description = `"${obj.content}" has been removed from "${checklist.title}"`;
      }

      break;
    case MODULE_NAMES.COMPANY:
      extraDesc = await gatherCompanyFieldNames(obj);
      description = `"${obj.primaryName}" has been ${action}d`;

      if (updatedDocument) {
        extraDesc = await gatherCompanyFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.CUSTOMER:
      description = `"${obj.firstName}" has been ${action}d`;

      extraDesc = await gatherCustomerFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherCustomerFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.DEAL:
      description = `"${obj.name}" has been ${action}d`;
      extraDesc = await gatherDealFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherDealFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.EMAIL_TEMPLATE:
      description = `"${obj.name}" has been ${action}d`;

      break;
    case MODULE_NAMES.ENGAGE:
      description = `"${obj.title}" has been ${action}d`;
      extraDesc = await gatherEngageFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherEngageFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.GROWTH_HACK:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherGHFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherGHFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.IMPORT_HISTORY:
      description = `${obj._id}-${obj.date} has been removed`;

      extraDesc = await gatherUsernames({
        idFields: [obj.userId],
        foreignKey: 'userId',
        prevList: extraDesc
      });

      const param = {
        idFields: obj.ids,
        foreignKey: 'ids',
        prevList: extraDesc
      };

      switch (obj.contentType) {
        case MODULE_NAMES.COMPANY:
          extraDesc = await gatherNames({
            ...param,
            collection: Companies,
            nameFields: ['primaryName']
          });
          break;
        case MODULE_NAMES.CUSTOMER:
          extraDesc = await gatherNames({
            ...param,
            collection: Customers,
            nameFields: ['firstName']
          });
          break;
        case MODULE_NAMES.PRODUCT:
          extraDesc = await gatherNames({
            ...param,
            collection: Products,
            nameFields: ['name']
          });
          break;
        default:
          break;
      }

      break;
    case MODULE_NAMES.INTEGRATION:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherIntegrationFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherIntegrationFieldNames(
          updatedDocument,
          extraDesc
        );
      }

      break;
    case MODULE_NAMES.INTERNAL_NOTE:
      description = `Note of type ${obj.contentType} has been ${action}d`;

      extraDesc = [
        {
          contentTypeId: obj.contentTypeId,
          name: await findContentItemName(obj.contentType, obj.contentTypeId)
        }
      ];

      extraDesc = await gatherUsernames({
        idFields: [obj.createdUserId],
        foreignKey: 'createdUserId',
        prevList: extraDesc
      });

      break;
    case MODULE_NAMES.KB_TOPIC:
      description = `"${obj.title}" has been ${action}d`;

      extraDesc = await gatherKbTopicFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherKbTopicFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.KB_CATEGORY:
      description = `"${obj.title}" has been ${action}d`;

      extraDesc = await gatherKbCategoryFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherKbCategoryFieldNames(
          updatedDocument,
          extraDesc
        );
      }

      break;
    case MODULE_NAMES.KB_ARTICLE:
      description = `"${obj.title}" has been ${action}d`;

      extraDesc = await gatherUsernames({
        idFields: [obj.createdBy],
        foreignKey: 'createdBy'
      });

      if (obj.modifiedBy) {
        extraDesc = await gatherUsernames({
          idFields: [obj.modifiedBy],
          foreignKey: 'modifiedBy',
          prevList: extraDesc
        });
      }

      if (updatedDocument && updatedDocument.modifiedBy) {
        extraDesc = await gatherUsernames({
          idFields: [updatedDocument.modifiedBy],
          foreignKey: 'modifiedBy',
          prevList: extraDesc
        });
      }

      break;
    case MODULE_NAMES.PERMISSION:
      description = `Permission of module "${obj.module}", action "${obj.action}" assigned to `;

      if (obj.groupId) {
        const group = await UsersGroups.getGroup(obj.groupId);

        description = `${description} user group "${group.name}" `;

        extraDesc.push({ groupId: obj.groupId, name: group.name });
      }

      if (obj.userId) {
        const permUser = await Users.getUser(obj.userId);

        description = `${description} user "${permUser.email}" has been ${action}d`;

        extraDesc.push({
          userId: obj.userId,
          name: permUser.username || permUser.email
        });
      }

      break;
    case MODULE_NAMES.PIPELINE_LABEL:
      description = `"${obj.name}" has been ${action}d`;

      const pipeline = await Pipelines.findOne({ _id: obj.pipelineId });

      extraDesc = await gatherUsernames({
        idFields: [obj.createdBy],
        foreignKey: 'createdBy'
      });

      if (pipeline) {
        extraDesc.push({ pipelineId: pipeline._id, name: pipeline.name });
      }

      break;
    case MODULE_NAMES.PIPELINE_TEMPLATE:
      extraDesc = await gatherPipelineTemplateFieldNames(obj);

      description = `"${obj.name}" has been created`;

      if (updatedDocument) {
        extraDesc = await gatherPipelineTemplateFieldNames(
          updatedDocument,
          extraDesc
        );
      }

      break;
    case MODULE_NAMES.PRODUCT:
      description = `${obj.name} has been ${action}d`;

      extraDesc = await gatherProductFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherProductFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.PRODUCT_CATEGORY:
      description = `"${obj.name}" has been ${action}d`;

      const parentIds: string[] = [];

      if (obj.parentId) {
        parentIds.push(obj.parentId);
      }

      if (updatedDocument && updatedDocument.parentId !== obj.parentId) {
        parentIds.push(updatedDocument.parentId);
      }

      if (parentIds.length > 0) {
        extraDesc = await gatherNames({
          collection: ProductCategories,
          idFields: parentIds,
          foreignKey: 'parentId',
          nameFields: ['name']
        });
      }

      break;
    case MODULE_NAMES.RESPONSE_TEMPLATE:
      description = `"${obj.name}" has been created`;

      const brandIds: string[] = [];

      if (obj.brandId) {
        brandIds.push(obj.brandId);
      }

      if (
        updatedDocument &&
        updatedDocument.brandId &&
        updatedDocument.brandId !== obj.brandId
      ) {
        brandIds.push(updatedDocument.brandId);
      }

      if (brandIds.length > 0) {
        extraDesc = await gatherBrandNames({
          idFields: brandIds,
          foreignKey: 'brandId'
        });
      }

      break;
    case MODULE_NAMES.SCRIPT:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherScriptFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherScriptFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.SEGMENT:
      const parents: string[] = [];

      if (obj.subOf) {
        parents.push(obj.subOf);
      }

      if (
        updatedDocument &&
        updatedDocument.subOf &&
        updatedDocument.subOf !== obj.subOf
      ) {
        parents.push(updatedDocument.subOf);
      }

      if (parents.length > 0) {
        extraDesc = await gatherNames({
          collection: Segments,
          idFields: parents,
          foreignKey: 'subOf',
          nameFields: ['name']
        });
      }

      description = `"${obj.name}" has been ${action}d`;

      break;
    case MODULE_NAMES.TASK:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherBoardItemFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherBoardItemFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.TICKET:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherBoardItemFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherBoardItemFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.USER:
      description = `"${obj.username || obj.email}" has been ${action}d`;

      extraDesc = await gatherUserFieldNames(obj);

      if (updatedDocument) {
        extraDesc = await gatherUserFieldNames(updatedDocument, extraDesc);
      }

      break;
    case MODULE_NAMES.STAGE_DEAL:
    case MODULE_NAMES.STAGE_TASK:
    case MODULE_NAMES.STAGE_TICKET:
    case MODULE_NAMES.STAGE_GH:
      description = `"${obj.name}" has been ${action}d`;

      extraDesc = await gatherStageFieldNames(obj, extraDesc);

      if (updatedDocument) {
        extraDesc = await gatherStageFieldNames(updatedDocument, extraDesc);
      }

      break;

    default:
      break;
  }

  return { extraDesc, description };
};

/**
 * Prepares a create log request to log server
 * @param params Log document params
 * @param user User information from mutation context
 */
export const putCreateLog = async (
  params: ILogDataParams,
  user: IUserDocument
) => {
  await registerOnboardHistory({ type: `${params.type}Create`, user });

  await sendToWebhook(LOG_ACTIONS.CREATE, params.type, params);

  callAfterMutation({ ...params, action: LOG_ACTIONS.CREATE }, user);

  messageBroker().sendMessage(RABBITMQ_QUEUES.AUTOMATIONS_TRIGGER, {
    type: `${params.type}`,
    targets: [params.object]
  });

  return putCreateLogC(messageBroker, gatherDescriptions, params, user);
};

/**
 * Prepares a create log request to log server
 * @param params Log document params
 * @param user User information from mutation context
 */
export const putUpdateLog = async (
  params: ILogDataParams,
  user: IUserDocument
) => {
  await sendToWebhook(LOG_ACTIONS.UPDATE, params.type, params);

  callAfterMutation({ ...params, action: LOG_ACTIONS.UPDATE }, user);

  messageBroker().sendMessage(RABBITMQ_QUEUES.AUTOMATIONS_TRIGGER, {
    type: `${params.type}`,
    targets: [params.updatedDocument]
  });

  return putUpdateLogC(messageBroker, gatherDescriptions, params, user);
};

/**
 * Prepares a create log request to log server
 * @param params Log document params
 * @param user User information from mutation context
 */
export const putDeleteLog = async (
  params: ILogDataParams,
  user: IUserDocument
) => {
  await sendToWebhook(LOG_ACTIONS.DELETE, params.type, params);

  callAfterMutation({ ...params, action: LOG_ACTIONS.DELETE }, user);

  messageBroker().sendMessage(RABBITMQ_QUEUES.AUTOMATIONS_TRIGGER, {
    type: `${params.type}`,
    targets: [params.object]
  });

  return putDeleteLogC(messageBroker, gatherDescriptions, params, user);
};

/**
 * Sends a request to logs api
 * @param {Object} param0 Request
 */
export const fetchLogs = async (
  params: ILogQueryParams | IActivityLogQueryParams,
  type = 'logs'
) => {
  const LOGS_DOMAIN = getSubServiceDomain({ name: 'LOGS_API_DOMAIN' });

  try {
    const response = await sendRequest({
      url: `${LOGS_DOMAIN}/${type}`,
      method: 'get',
      body: { params: JSON.stringify(params) }
    });
    return response;
  } catch (e) {
    debugError(
      `Failed to connect to logs api. Check whether LOGS_API_DOMAIN env is missing or logs api is not running: ${e.message}`
    );
  }
};

export const sendToLog = (channel: string, data) =>
  messageBroker().sendMessage(channel, data);

interface IActivityLogParams {
  action: string;
  data: any;
}

export const putActivityLog = async (params: IActivityLogParams) => {
  const { action, data } = params;

  if ([ACTIVITY_LOG_ACTIONS.CREATE_BOARD_ITEM_MOVEMENT_LOG].includes(action)) {
    await sendToWebhook(action, data.contentType, params);
  }

  try {
    if (data.target) {
      messageBroker().sendMessage(RABBITMQ_QUEUES.AUTOMATIONS_TRIGGER, {
        type: `${data.contentType}`,
        targets: [data.target]
      });
    }

    return messageBroker().sendMessage('putActivityLog', params);
  } catch (e) {
    return e.message;
  }
};

export const getDbSchemaLabels = async (type: string) => {
  let fieldNames: INameLabel[] = [];

  const found: ISchemaMap | undefined = LOG_MAPPINGS.find(m => m.name === type);

  if (found) {
    const schemas: any = found.schemas || [];

    for (const schema of schemas) {
      // schema comes as either mongoose schema or plain object
      const names: string[] = Object.getOwnPropertyNames(schema.obj || schema);

      for (const name of names) {
        const field: any = schema.obj ? schema.obj[name] : schema[name];

        if (field && field.label) {
          fieldNames.push({ name, label: field.label });
        }

        // nested object field names
        if (typeof field === 'object' && field.type && field.type.obj) {
          fieldNames = fieldNames.concat(buildLabelList(field.type.obj));
        }
      }
    } // end schema for loop
  } // end schema name mapping

  return fieldNames;
};