UNPKG

@mondaycom/apps-cli

Version:

A cli tool to manage apps (and monday-code projects) in monday.com

189 lines (188 loc) 9.01 kB
import { Flags } from '@oclif/core'; import { AuthenticatedCommand } from '../../commands-base/authenticated-command.js'; import { APP_VERSION_ID_TO_ENTER } from '../../consts/messages.js'; import { streamMessages } from '../../services/client-channel-service.js'; import { DynamicChoicesService } from '../../services/dynamic-choices-service.js'; import { logsStream } from '../../services/notification-service.js'; import { PromptService } from '../../services/prompt-service.js'; import { EventSource, LogType } from '../../types/commands/logs.js'; import logger from '../../utils/logger.js'; import { addRegionToFlags, chooseRegionIfNeeded, getRegionFromString } from '../../utils/region.js'; import { TIME_IN_MILLISECONDS } from '../../utils/time-utils.js'; import { getDayDiff, isDate, isDefined } from '../../utils/validations.js'; const SUPPORTED_HISTORY_FLAGS = ' [supported only if eventSource=live]'; const DAY_IN_MS = Number(TIME_IN_MILLISECONDS.DAY); const LOGS_TYPE_TO_LISTEN_PROMPT_MESSAGE = 'Logs type: "http" for http events, "console" for stdout'; const EVENT_SOURCE = 'Source: "live" for live events, "History" for fetching events from the past'; const EVENT_SEARCH_FOR_TEXT = 'text: a text in regex that will be searched among the logs text' + SUPPORTED_HISTORY_FLAGS; const LOGS_MAX_RANGE_BETWEEN_DATES = 3; const LOGS_PROMPT_END_DATE = `End date can be up to ${LOGS_MAX_RANGE_BETWEEN_DATES} days since`; const eventSourcePrompt = async () => PromptService.promptList(EVENT_SOURCE, [EventSource.LIVE, EventSource.HISTORY], EventSource.LIVE); const logsTypePrompt = async () => PromptService.promptList(LOGS_TYPE_TO_LISTEN_PROMPT_MESSAGE, [LogType.CONSOLE, LogType.HTTP], LogType.CONSOLE); const relationships = [ { type: 'none', flags: [ { name: 'eventSource', when: (flags) => { return new Promise(res => { res(flags.eventSource !== EventSource.HISTORY); }); }, }, ], }, ]; export default class Logs extends AuthenticatedCommand { static description = 'Stream logs'; static examples = ['<%= config.bin %> <%= command.id %> -i APP_VERSION_ID -t LOGS_TYPE']; static flags = Logs.serializeFlags(addRegionToFlags({ appVersionId: Flags.integer({ char: 'i', aliases: ['v'], description: APP_VERSION_ID_TO_ENTER, }), logsType: Flags.string({ char: 't', description: LOGS_TYPE_TO_LISTEN_PROMPT_MESSAGE, }), eventSource: Flags.string({ char: 's', description: EVENT_SOURCE, }), logsStartDate: Flags.string({ char: 'f', description: 'Start date (MM/DD/YYYY HH:mm) e.g. "03/24/1983 15:45"' + SUPPORTED_HISTORY_FLAGS, relationships, }), logsEndDate: Flags.string({ char: 'e', description: 'End date (MM/DD/YYYY HH:mm) e.g. "03/25/1983 16:45"' + SUPPORTED_HISTORY_FLAGS, relationships, }), logSearchFromText: Flags.string({ char: 'r', description: EVENT_SEARCH_FOR_TEXT, relationships, }), })); static args = {}; DEBUG_TAG = 'logs'; async run() { try { const { flags } = await this.parse(Logs); const { logsStartDate, logsEndDate, logSearchFromText, region: strRegion } = flags; const region = getRegionFromString(strRegion); const appVersionId = await this.getAppVersionId(flags.appVersionId); const selectedRegion = await chooseRegionIfNeeded(region, { appVersionId }); const eventSource = (flags.eventSource || (await eventSourcePrompt())); const logsType = await this.getLogType(eventSource, flags.logsType); const logsFilterCriteria = await this.getLogsFilterCriteria(eventSource, logsStartDate, logsEndDate, logSearchFromText); const args = { appVersionId, logsType, logsFilterCriteria, }; this.preparePrintCommand(this, { appVersionId, logsType, eventSource, logsStartDate: logsFilterCriteria?.fromDate && `"${logsFilterCriteria.fromDate.toString()}"`, logsEndDate: logsFilterCriteria?.toDate && `"${logsFilterCriteria.toDate.toString()}"`, logSearchFromText: logsFilterCriteria?.text, }); const clientChannel = await logsStream(args.appVersionId, args.logsType, logsFilterCriteria, selectedRegion); await streamMessages(clientChannel); } catch (error) { logger.debug(error, this.DEBUG_TAG); // need to signal to the parent process that the command failed process.exit(1); } } async getAppVersionId(appVersionId) { if (!appVersionId) { const { appVersionId: inputAppVersionId } = await DynamicChoicesService.chooseAppAndAppVersion(true, true); return inputAppVersionId; } return appVersionId; } async getLogType(eventSource, logsType) { const logsTypeCandidate = (logsType || (await logsTypePrompt())); let calcLogsType = logsTypeCandidate; if (eventSource === EventSource.HISTORY) { switch (logsTypeCandidate) { case LogType.CONSOLE: { calcLogsType = LogType.CONSOLE_HISTORY; break; } case LogType.HTTP: { calcLogsType = LogType.HTTP_HISTORY; break; } } } return calcLogsType; } async getLogsToDate(fromDate, fromDatePlus1Day, options, logsEndDate) { const toDate = isDate(logsEndDate) ? new Date(logsEndDate) : await PromptService.promptDateTimePicker(LOGS_PROMPT_END_DATE, fromDatePlus1Day, options); const dayDiff = getDayDiff(fromDate, toDate); if (!isDefined(dayDiff)) { console.error('Something went wrong in logs date calculations.'); process.exit(1); } if (dayDiff < 0) { logger.error('Logs end date is earlier the start date.'); return PromptService.promptDateTimePicker(LOGS_PROMPT_END_DATE, fromDatePlus1Day, options); } if (dayDiff > LOGS_MAX_RANGE_BETWEEN_DATES) { logger.error(`Logs dates range is greater then ${LOGS_MAX_RANGE_BETWEEN_DATES} days.`); return PromptService.promptDateTimePicker(LOGS_PROMPT_END_DATE, fromDatePlus1Day, options); } return toDate; } async readDateInput(title, inputDate) { const yesterday = new Date(Date.now() - DAY_IN_MS); return isDate(inputDate) ? new Date(inputDate) : PromptService.promptDateTimePicker(title, yesterday); } async readTextLogSearchInput(title, isLogTypeHistory, inputText) { return isLogTypeHistory && inputText === undefined ? PromptService.promptInput(title) : inputText; } buildDatePickerConfiguration = (fromDate, maxDate) => { const options = { validate: (selectedDate) => { const selectedDateSmallerThenMaxDate = selectedDate < maxDate; const selectedDateHigherThenFromDate = selectedDate > fromDate; if (!selectedDateSmallerThenMaxDate) { logger.log(`\n Max date to select: "${maxDate.toString()}"`); return false; } if (!selectedDateHigherThenFromDate) { logger.log(`\n Min date date to select: "${fromDate.toString()}"`); return false; } return true; }, }; return options; }; async getLogsFilterCriteria(logsType, logsStartDate, logsEndDate, logSearchFromText) { const isLogTypeHistory = logsType === EventSource.HISTORY; if (!isLogTypeHistory) { return null; } const fromDate = await this.readDateInput('Start date', logsStartDate); const fromDateInMS = fromDate.getTime(); const fromDatePlus1Day = new Date(fromDateInMS + DAY_IN_MS); const maxDateInMS = DAY_IN_MS * LOGS_MAX_RANGE_BETWEEN_DATES; const minuteInMS = Number(TIME_IN_MILLISECONDS.MINUTE); const maxDate = new Date(fromDateInMS + maxDateInMS + minuteInMS); const datePickerOptions = this.buildDatePickerConfiguration(fromDate, maxDate); const toDate = await this.getLogsToDate(fromDate, fromDatePlus1Day, datePickerOptions, logsEndDate); const text = await this.readTextLogSearchInput('Search for text (please put searching value in side a quotes)', isLogTypeHistory, logSearchFromText); return { fromDate, toDate, text }; } }