UNPKG

kui-shell

Version:

This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool

640 lines 28.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const debug_1 = require("debug"); const minimist = require("yargs-parser"); const popup_core_1 = require("../popup-core"); const prompt_1 = require("../prompt"); const split_1 = require("../../repl/split"); const entity_1 = require("../../models/entity"); const table_1 = require("../models/table"); const basicModels_1 = require("../models/basicModels"); const diffTable_1 = require("../views/diffTable"); const settings_1 = require("../../core/settings"); const job_1 = require("../../core/job"); const types_1 = require("../../util/types"); const debug = debug_1.default('webapp/views/table'); var FinalState; (function (FinalState) { FinalState[FinalState["NotPendingLike"] = 0] = "NotPendingLike"; FinalState[FinalState["OnlineLike"] = 1] = "OnlineLike"; FinalState[FinalState["OfflineLike"] = 2] = "OfflineLike"; })(FinalState || (FinalState = {})); const fastPolling = 500; const mediumPolling = 3000; const finalPolling = (settings_1.theme && settings_1.theme.tablePollingInterval) || 5000; debug('table polling intervals', fastPolling, mediumPolling, finalPolling); const prepareTable = (tab, response) => { const { header, body, noSort } = response; if (header) { header.outerCSS = `${header.outerCSS || ''} header-cell`; if (header.attributes) { header.attributes.forEach(cell => { cell.outerCSS = `${cell.outerCSS || ''} header-cell`; }); } } return [header].concat(noSort ? body : table_1.sortBody(body)).filter(x => x); }; const hasReachedFinalState = (response) => { let reachedFinalState = false; if (table_1.isTable(response)) { if (response.body.length !== 0 && response.body.every(row => row.done)) { reachedFinalState = true; } return reachedFinalState; } else if (table_1.isMultiTable(response)) { let allDone = true; response.tables.map(table => { if (table.body.some(row => !row.done)) { allDone = false; } }); reachedFinalState = allDone; } return reachedFinalState; }; const findFinalStateFromCommand = (command) => { const { A: argv } = split_1._split(command, true, true); const options = minimist(argv); return options['final-state'] ? FinalState[options['finalState']] : ''; }; const calculateLadder = (initial) => { const ladder = [initial]; let current = initial; while (current < finalPolling) { if (current < 1000) { current = current + 250 < 1000 ? current + 250 : 1000; ladder.push(current); } else { ladder.push(current); current = current + 2000 < finalPolling ? current + 2000 : finalPolling; ladder.push(current); } } debug('ladder', ladder); return ladder; }; const registerWatcher = (tab, watchLimit = 100000, command, resultDom, tableViewInfo, formatRowOption) => { let job; const expectedFinalState = findFinalStateFromCommand(command); const initalPollingInterval = expectedFinalState === 'OfflineLike' || expectedFinalState === 'OnlineLike' ? fastPolling : mediumPolling; const ladder = calculateLadder(initalPollingInterval); const processRefreshResponse = (response) => { if (!table_1.isTable(response) && !table_1.isMultiTable(response)) { console.error('refresh result is not a table', response); throw new Error('refresh result is not a table'); } const reachedFinalState = hasReachedFinalState(response); return table_1.isTable(response) ? { table: prepareTable(tab, response), reachedFinalState } : { tables: response.tables.map(table => { return prepareTable(tab, table); }), reachedFinalState }; }; const refreshTable = () => __awaiter(void 0, void 0, void 0, function* () { debug(`refresh with ${command}`); let processedTableRow = []; let processedMultiTableRow = []; try { const { qexec } = yield Promise.resolve().then(() => require('../../repl/exec')); const response = yield qexec(command); const processedResponse = processRefreshResponse(response); processedTableRow = processedResponse.table; processedMultiTableRow = processedResponse.tables; if (processedResponse.reachedFinalState) { job.abort(); } else { const newTimer = ladder.shift(); if (newTimer) { job.abort(); job = new job_1.WatchableJob(tab, watchIt, newTimer + ~~(100 * Math.random())); job.start(); } } } catch (err) { if (err.code === 404) { if (expectedFinalState === 'OfflineLike') { debug('resource not found after status check, but that is ok because that is what we wanted'); job.abort(); } } else { while (resultDom.firstChild) { resultDom.removeChild(resultDom.firstChild); } job.abort(); throw err; } } const applyRefreshResult = (newRowModel, tableViewInfo) => { const diffs = table_1.diffTableRows(tableViewInfo.rowsModel, newRowModel); diffTable_1.applyDiffTable(diffs, tab, tableViewInfo.renderedTable, tableViewInfo.renderedRows, tableViewInfo.rowsModel, formatRowOption); }; if (Array.isArray(tableViewInfo)) { processedMultiTableRow.forEach((newRowModel, index) => { applyRefreshResult(newRowModel, tableViewInfo[index]); }); } else { applyRefreshResult(processedTableRow, tableViewInfo); } }); const watchIt = () => { if (--watchLimit < 0) { console.error('watchLimit exceeded'); job.abort(); } else { try { Promise.resolve(refreshTable()); } catch (err) { console.error('Error refreshing table', err); job.abort(); } } }; job = new job_1.WatchableJob(tab, watchIt, ladder.shift() + ~~(100 * Math.random())); job.start(); }; function formatIcon(fontawesome, cell) { if (/fa-check$/.test(fontawesome)) { cell.classList.add('radio-button-width'); const icon1 = document.createElement('i'); const icon2 = document.createElement('i'); icon1.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2zm0 26a12 12 0 1 1 12-12 12 12 0 0 1-12 12z"></path><path d="M16 10a6 6 0 1 0 6 6 6 6 0 0 0-6-6z"></path></svg>'; icon2.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2zm0 26a12 12 0 1 1 12-12 12 12 0 0 1-12 12z"></path></svg>'; icon1.classList.add('kui--radio-checked'); icon2.classList.add('kui--radio-unchecked'); const iconContainer = document.createElement('span'); iconContainer.appendChild(icon1); iconContainer.appendChild(icon2); return iconContainer; } else { const icon = document.createElement('i'); icon.classList.add('cell-inner'); icon.classList.add('graphical-icon'); if (/fa-network/.test(fontawesome)) { icon.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M26 14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-6a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2v4.1a5 5 0 0 0-3.9 3.9H14v-2a2 2 0 0 0-2-2h-2v-4.1a5 5 0 1 0-2 0V18H6a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-2h4.1a5 5 0 1 0 5.9-5.9V14zM6 9a3 3 0 1 1 3 3 3 3 0 0 1-3-3zm6 17H6v-6h6zm14-3a3 3 0 1 1-3-3 3 3 0 0 1 3 3zM20 6h6v6h-6z"></path></svg>'; } else if (/fa-times-circle/.test(fontawesome)) { icon.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M2 16A14 14 0 1 0 16 2 14 14 0 0 0 2 16zm23.15 7.75L8.25 6.85a12 12 0 0 1 16.9 16.9zM8.24 25.16a12 12 0 0 1-1.4-16.89l16.89 16.89a12 12 0 0 1-15.49 0z"></path></svg>'; } else if (/fa-question-circle/.test(fontawesome)) { icon.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M2 16A14 14 0 1 0 16 2 14 14 0 0 0 2 16zm23.15 7.75L8.25 6.85a12 12 0 0 1 16.9 16.9zM8.24 25.16a12 12 0 0 1-1.4-16.89l16.89 16.89a12 12 0 0 1-15.49 0z"></path></svg>'; } else if (/fa-check-circle/.test(fontawesome)) { icon.innerHTML = '<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2zm0 26a12 12 0 1 1 12-12 12 12 0 0 1-12 12z"></path><path d="M14 21.5l-5-4.96 1.59-1.57L14 18.35 21.41 11 23 12.58l-9 8.92z"></path></svg>'; } else { icon.className = fontawesome; } return icon; } } const formatCellValue = (key, value) => { const dateKey = { 'FIRST SEEN': true, 'LAST SEEN': true }; const formatAge = (value) => { const timestamp = new Date(value).getTime(); if (isNaN(timestamp)) { return value; } const ms = Date.now() - timestamp; const s = 1000 * Math.round(ms / 1000); const d = new Date(s); const hours = d.getUTCHours() > 0 ? `${d.getUTCHours()}h` : ''; const minutes = d.getUTCMinutes() > 0 ? `${d.getUTCMinutes()}m` : ''; const seconds = d.getUTCSeconds() > 0 ? `${d.getUTCSeconds()}s` : ''; return hours ? `${hours}${minutes}` : `${minutes}${seconds}`; }; return dateKey[key] && !dateKey[value] ? formatAge(value) : value; }; exports.formatOneRowResult = (tab, options = {}) => (entity) => { const isHeaderCell = /header-cell/.test(entity.outerCSS); const dom = document.createElement(isHeaderCell ? 'thead' : 'tbody'); dom.className = `entity ${entity.prettyType || ''} ${entity.type}`; dom.setAttribute('data-name', entity.name); entity.setSelected = () => { const currentSelection = dom.parentNode.querySelector('.selected-row'); if (currentSelection) { currentSelection.classList.remove('selected-row'); } dom.querySelector('.row-selection-context').classList.add('selected-row'); prompt_1.getCurrentPrompt().focus(); }; entity.setUnselected = () => { dom.querySelector('.row-selection-context').classList.remove('selected-row'); }; if (entity.packageName) { dom.setAttribute('data-package-name', entity.packageName); } const entityName = document.createElement('tr'); entityName.className = 'entity-attributes row-selection-context'; dom.appendChild(entityName); if (entity.rowCSS) { if (Array.isArray(entity.rowCSS)) { entity.rowCSS.forEach(_ => _ && entityName.classList.add(_)); } else { entityName.classList.add(entity.rowCSS); } } const entityNameGroup = document.createElement(isHeaderCell ? 'th' : 'td'); entityNameGroup.className = `entity-name-group ${entity.outerCSS}`; if (isHeaderCell) { entityName.classList.add('header-row'); entityName.parentNode.classList.add('header-row'); } if ((!options || !options.excludePackageName) && entity.packageName) { const packagePrefix = document.createElement('span'); packagePrefix.className = 'package-prefix sub-text'; packagePrefix.innerText = entity.packageName + '/'; entityNameGroup.appendChild(packagePrefix); } const entityNameClickable = document.createElement('span'); entityNameClickable.className = 'entity-name cell-inner'; if (!isHeaderCell) { entityNameClickable.classList.add('clickable'); } else { entityNameClickable.classList.add('bx--table-header-label'); } if (entity.nameCss) { if (Array.isArray(entity.nameCss)) { entity.nameCss.forEach(_ => entityNameClickable.classList.add(_)); } else { entityNameClickable.classList.add(entity.nameCss); } } entityNameGroup.appendChild(entityNameClickable); entityName.appendChild(entityNameGroup); if (entity.key) { entityNameClickable.setAttribute('data-key', entity.key); } else { entityNameClickable.setAttribute('data-key', 'NAME'); } const name = entity.nameDom || entity.prettyName || entity.name; if (entity.fontawesome) { const icon = formatIcon(entity.fontawesome, entityNameGroup); entityNameClickable.appendChild(icon); } else if (typeof name === 'string') { entityNameClickable.title = name; entityNameClickable.innerText = isHeaderCell ? name.toLowerCase() : name; } else if (name) { entityNameClickable.appendChild(name); } entityNameClickable.setAttribute('data-value', entity.prettyName || entity.name); if (entity.fullName) { entityNameClickable.setAttribute('title', entity.fullName); } if (entity.css) { if (Array.isArray(entity.css)) { entity.css.forEach(_ => entityNameClickable.classList.add(_)); } else { entityNameClickable.classList.add(entity.css); } } if (!entity.onclick) { entityNameClickable.classList.remove('clickable'); } else { if (popup_core_1.isPopup() || options.usePip) { entityNameClickable.onclick = (evt) => __awaiter(void 0, void 0, void 0, function* () { const { drilldown } = yield Promise.resolve().then(() => require('../picture-in-picture')); return drilldown(tab, entity.onclick, undefined, undefined, 'previous view')(evt); }); } else if (typeof entity.onclick === 'string') { entityNameClickable.onclick = () => __awaiter(void 0, void 0, void 0, function* () { if (!entity.onclickExec || entity.onclickExec === 'pexec') { const { pexec } = yield Promise.resolve().then(() => require('../../repl/exec')); pexec(entity.onclick, { tab, echo: !entity.onclickSilence }); } else { const { qexec } = yield Promise.resolve().then(() => require('../../repl/exec')); qexec(entity.onclick, undefined, undefined, { tab }); } }); } else if (entity_1.isMetadataBearing(entity.onclick)) { entityNameClickable.onclick = () => __awaiter(void 0, void 0, void 0, function* () { const { show } = yield Promise.resolve().then(() => require('../../models/mmr/show')); return show(tab, entity.onclick); }); } else { entityNameClickable.onclick = entity.onclick; } } const addCellToRow = (theCell) => { const { className, value, valueDom, innerClassName = '', parent = entityName, onclick, key, fontawesome, css = '', tag = 'span', tagClass } = theCell; const cell = document.createElement(isHeaderCell ? 'th' : 'td'); const inner = document.createElement(tag); cell.className = className || 'not-too-compact'; inner.className = innerClassName; inner.classList.add('cell-inner'); if (isHeaderCell) { inner.classList.add('bx--table-header-label'); } if (tagClass) { inner.classList.add(tagClass); } if (key) { inner.setAttribute('data-key', key); } if (css) { inner.classList.add(css); } if (fontawesome) { const addIcon = (theIcon) => { const icon = formatIcon(theIcon.fontawesome, cell); if (typeof onclick === 'function') { icon.onclick = onclick; icon.classList.add('clickable'); } if (theIcon.balloon) { const iconWrapper = document.createElement('span'); iconWrapper.setAttribute('data-balloon', theIcon.balloon); iconWrapper.setAttribute('data-balloon-pos', theIcon.balloonPos || 'right'); if (theIcon.balloonLength) { iconWrapper.setAttribute('data-balloon-length', theIcon.balloonLength); } iconWrapper.appendChild(icon); inner.appendChild(iconWrapper); } else { inner.appendChild(icon); } }; if (Array.isArray(fontawesome)) { cell.classList.add('text-center'); cell.classList.add('larger-text'); fontawesome.forEach(font => addIcon({ fontawesome: font })); } else { addIcon({ fontawesome }); inner.setAttribute('data-value', value); } } else if (valueDom) { if (Array.isArray(valueDom)) { const container = valueDom.reduce((container, node) => { container.appendChild(node); return container; }, document.createElement('div')); inner.appendChild(container); } else { Promise.resolve(valueDom).then(valueDom => { if (types_1.isHTML(valueDom)) { inner.appendChild(valueDom); } else { valueDom.appendChild(document.createTextNode(valueDom.toString())); } }); } } else if (value !== undefined) { Promise.resolve(value).then(value => { const formatedValue = formatCellValue(key, value); inner.title = formatedValue; inner.appendChild(document.createTextNode(isHeaderCell ? formatedValue.toLowerCase() : formatedValue || '\u00a0')); }); } else { console.error('Invalid cell model, no value field', theCell); } cell.appendChild(inner); parent.appendChild(cell); if (cell.classList.contains('header-cell')) { parent.classList.add('header-row'); parent.parentNode.classList.add('header-row'); } if (onclick) { cell.classList.add('clickable'); cell.onclick = (evt) => __awaiter(void 0, void 0, void 0, function* () { evt.stopPropagation(); if (popup_core_1.isPopup() || options.usePip) { const { drilldown } = yield Promise.resolve().then(() => require('../picture-in-picture')); return drilldown(tab, onclick, undefined, '.custom-content .padding-content', 'previous view')(evt); } else if (typeof onclick === 'string') { const { pexec } = yield Promise.resolve().then(() => require('../../repl/exec')); pexec(onclick, { tab }); } else { onclick(evt); } }); } const pulse = 'repeating-pulse'; if (options.useRepeatingEffect && key === 'STATUS' && (css.includes('yellow-background') || innerClassName.includes('yellow-background'))) { cell.classList.add(pulse); } return cell; }; if (entity.beforeAttributes) { entity.beforeAttributes.forEach(({ key, value, css = '', outerCSS = '', onclick, fontawesome }) => addCellToRow({ className: outerCSS, value, innerClassName: css, onclick, key, fontawesome })); } if (entity.attributes) { entity.attributes.forEach(({ key, value, valueDom, css = '', outerCSS = '', onclick, fontawesome, tag }) => { addCellToRow({ className: outerCSS, value, valueDom, innerClassName: css, onclick, key, fontawesome, tag }); }); } else { const addKind = () => { if (entity.kind || entity.prettyKind) { addCellToRow({ className: 'entity-kind', value: entity.prettyKind || entity.kind }); } }; const addStatus = () => { if (entity.status) { const cell = addCellToRow({ className: `entity-rule-status`, value: 'Pending', innerClassName: 'repeating-pulse', tag: 'badge', tagClass: 'gray-background' }); const capitalize = (str) => { return str[0].toUpperCase() + str.slice(1).toLowerCase(); }; Promise.resolve(entity.status).then(status => { const badge = cell.querySelector('badge'); badge.innerText = capitalize(status); badge.classList.remove('gray-background'); badge.classList.add(status === 'active' ? 'green-background' : 'red-background'); badge.classList.remove('repeating-pulse'); }); } }; const addVersion = () => { if (entity.version || entity.prettyVersion) { addCellToRow({ className: 'entity-version hide-with-sidecar', value: entity.prettyVersion || entity.version, innerClassName: 'slightly-deemphasize' }); } }; addKind(); addStatus(); addVersion(); } return dom; }; function adoptCarbonTableStyle(tableDom) { if (tableDom.getAttribute('kui-table-style') === 'Light') { tableDom.classList.add('bx--data-table--short'); } else if (tableDom.getAttribute('kui-table-style') === 'Medium') { tableDom.classList.add('bx--data-table--short'); } } function setStyle(tableDom, table) { if (table.style !== undefined && table_1.TableStyle[table.style] !== undefined) { tableDom.setAttribute('kui-table-style', table_1.TableStyle[table.style].toString()); } else if (settings_1.theme.tableStyle) { tableDom.setAttribute('kui-table-style', settings_1.theme.tableStyle); } adoptCarbonTableStyle(tableDom); } exports.formatTable = (tab, response, resultDom, options = {}) => { const formatRowOption = Object.assign(options, { useRepeatingEffect: !hasReachedFinalState(response) && basicModels_1.isWatchable(response) && response.watchByDefault }); const format = (table) => { const tableDom = document.createElement('table'); tableDom.classList.add('result-table'); tableDom.classList.add('bx--data-table'); let container; if (table.title) { const tableOuterWrapper = document.createElement('div'); const tableOuter = document.createElement('div'); const titleOuter = document.createElement('div'); const titleInner = document.createElement('div'); tableOuterWrapper.classList.add('result-table-outer-wrapper'); tableOuter.appendChild(titleOuter); titleOuter.appendChild(titleInner); tableOuterWrapper.appendChild(tableOuter); resultDom.appendChild(tableOuterWrapper); if (table.flexWrap) { const tableScroll = document.createElement('div'); tableScroll.classList.add('scrollable'); tableScroll.classList.add('scrollable-auto'); tableScroll.setAttribute('data-table-max-rows', typeof table.flexWrap === 'number' ? table.flexWrap.toString() : '8'); tableScroll.appendChild(tableDom); tableOuter.appendChild(tableScroll); } else { tableOuter.appendChild(tableDom); } tableOuter.classList.add('result-table-outer'); titleOuter.classList.add('result-table-title-outer'); titleOuter.classList.add('bx--data-table-header'); titleInner.classList.add('result-table-title'); titleInner.classList.add('bx--data-table-header__title'); titleInner.innerText = table.title; if (table.tableCSS) { tableOuterWrapper.classList.add(table.tableCSS); } if (table.fontawesome) { const awesomeWrapper = document.createElement('div'); const awesome = document.createElement('i'); awesomeWrapper.appendChild(awesome); titleOuter.appendChild(awesomeWrapper); awesome.className = table.fontawesome; if (table.fontawesomeCSS) { awesomeWrapper.classList.add(table.fontawesomeCSS); delete table.fontawesomeCSS; } if (table.fontawesomeBalloon) { awesomeWrapper.setAttribute('data-balloon', table.fontawesomeBalloon); awesomeWrapper.setAttribute('data-balloon-pos', 'left'); delete table.fontawesomeBalloon; } delete table.fontawesome; } container = tableOuterWrapper; } else { resultDom.appendChild(tableDom); container = tableDom; } container.classList.add('big-top-pad'); const prepareRows = prepareTable(tab, table); const rows = prepareRows.map(exports.formatOneRowResult(tab, formatRowOption)); rows.map(row => tableDom.appendChild(row)); setStyle(tableDom, table); const rowSelection = tableDom.querySelector('.selected-row'); if (rowSelection) { tableDom.classList.add('has-row-selection'); } return { renderedRows: rows, renderedTable: tableDom, rowsModel: prepareRows, tableModel: table }; }; const tableViewInfo = table_1.isMultiTable(response) ? response.tables.map(table => format(table)) : format(response); if (!hasReachedFinalState(response) && basicModels_1.isWatchable(response) && response.watchByDefault) { registerWatcher(tab, response.watchLimit, response.refreshCommand, resultDom, tableViewInfo, formatRowOption); } }; exports.formatTableResult = (tab, response) => { debug('formatTableResult', response); return prepareTable(tab, response).map(exports.formatOneRowResult(tab)); }; //# sourceMappingURL=table.js.map