import { parseBoolean } from '../../lib/utils/common_utils'; /** * Adds the line number property * @param Object line * @param Number lineNumber */ export const parseLine = (line = {}, lineNumber) => ({ ...line, lineNumber, }); /** * When a line has `section_header` set to true, we create a new * structure to allow to nest the lines that belong to the * collapsible section * * @param Object line * @param Number lineNumber */ export const parseHeaderLine = (line = {}, lineNumber) => ({ isClosed: parseBoolean(line.section_options?.collapsed), isHeader: true, line: parseLine(line, lineNumber), lines: [], }); /** * Finds the matching header section * for the section_duration object and adds it to it * * { * isHeader: true, * line: { * content: [], * lineNumber: 0, * section_duration: "", * }, * lines: [] * } * * @param Array data * @param Object durationLine */ export function addDurationToHeader(data, durationLine) { data.forEach((el) => { if (el.line && el.line.section === durationLine.section) { el.line.section_duration = durationLine.section_duration; } }); } /** * Check is the current section belongs to a collapsible section * * @param Array acc * @param Object last * @param Object section * * @returns Boolean */ export const isCollapsibleSection = (acc = [], last = {}, section = {}) => acc.length > 0 && last.isHeader === true && !section.section_duration && section.section === last.line.section; /** * Returns the lineNumber of the last line in * a parsed log * * @param Array acc * @returns Number */ export const getIncrementalLineNumber = (acc) => { let lineNumberValue; const lastIndex = acc.length - 1; const lastElement = acc[lastIndex]; const nestedLines = lastElement.lines; if (lastElement.isHeader && !nestedLines.length && lastElement.line) { lineNumberValue = lastElement.line.lineNumber; } else if (lastElement.isHeader && nestedLines.length) { lineNumberValue = nestedLines[nestedLines.length - 1].lineNumber; } else { lineNumberValue = lastElement.lineNumber; } return lineNumberValue === 0 ? 1 : lineNumberValue + 1; }; /** * Parses the job log content into a structure usable by the template * * For collaspible lines (section_header = true): * - creates a new array to hold the lines that are collapsible, * - adds a isClosed property to handle toggle * - adds a isHeader property to handle template logic * - adds the section_duration * For each line: * - adds the index as lineNumber * * @param Array lines * @param Array accumulator * @returns Array parsed log lines */ export const logLinesParser = (lines = [], accumulator = []) => lines.reduce( (acc, line, index) => { const lineNumber = accumulator.length > 0 ? getIncrementalLineNumber(acc) : index; const last = acc[acc.length - 1]; // If the object is an header, we parse it into another structure if (line.section_header) { acc.push(parseHeaderLine(line, lineNumber)); } else if (isCollapsibleSection(acc, last, line)) { // if the object belongs to a nested section, we append it to the new `lines` array of the // previously formatted header last.lines.push(parseLine(line, lineNumber)); } else if (line.section_duration) { // if the line has section_duration, we look for the correct header to add it addDurationToHeader(acc, line); } else { // otherwise it's a regular line acc.push(parseLine(line, lineNumber)); } return acc; }, [...accumulator], ); /** * Finds the repeated offset, removes the old one * * Returns a new array with the updated log without * the repeated offset * * @param Array newLog * @param Array oldParsed * @returns Array * */ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => { const cloneOldLog = [...oldParsed]; const lastIndex = cloneOldLog.length - 1; const last = cloneOldLog[lastIndex]; const firstNew = newLog[0]; if (last && firstNew) { if (last.offset === firstNew.offset || (last.line && last.line.offset === firstNew.offset)) { cloneOldLog.splice(lastIndex); } else if (last.lines && last.lines.length) { const lastNestedIndex = last.lines.length - 1; const lastNested = last.lines[lastNestedIndex]; if (lastNested.offset === firstNew.offset) { last.lines.splice(lastNestedIndex); } } } return cloneOldLog; }; /** * When the trace is not complete, backend may send the last received line * in the new response. * * We need to check if that is the case by looking for the offset property * before parsing the incremental part * * @param array oldLog * @param array newLog */ export const updateIncrementalTrace = (newLog = [], oldParsed = []) => { const parsedLog = findOffsetAndRemove(newLog, oldParsed); return logLinesParser(newLog, parsedLog); };