@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
130 lines (129 loc) • 5.67 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@mui/material/Box';
import { JSONPath } from 'jsonpath-plus';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import CRD from '../../lib/k8s/crd';
import { localeDate } from '../../lib/util';
import { CreateResourceButton, Link, Loader, PageGrid, SectionHeader } from '../common';
import BackLink from '../common/BackLink';
import Empty from '../common/EmptyContent';
import ResourceListView from '../common/Resource/ResourceListView';
export default function CustomResourceList() {
const { t } = useTranslation(['glossary', 'translation']);
const { crd: crdName } = useParams();
const [crd, error] = CRD.useGet(crdName);
if (!crd && !error) {
return _jsx(Loader, { title: t('translation|Loading custom resource definition') });
}
if (!!error) {
return (_jsx(Empty, { color: "error", children: t('translation|Error getting custom resource definition {{ crdName }}: {{ errorMessage }}', {
crdName,
errorMessage: error,
}) }));
}
return _jsx(CustomResourceListRenderer, { crd: crd });
}
function CustomResourceLink(props) {
const { resource, crd, ...otherProps } = props;
return (_jsx(Link, { sx: { cursor: 'pointer' }, routeName: "customresource", params: {
crName: resource.metadata.name,
crd: crd.metadata.name,
namespace: resource.metadata.namespace || '-',
}, activeCluster: resource.cluster, ...otherProps, children: resource.metadata.name }));
}
function CustomResourceListRenderer(props) {
const { crd } = props;
const { t } = useTranslation('glossary');
const CRClass = crd.makeCRClass();
return (_jsxs(PageGrid, { children: [_jsx(BackLink, {}), _jsx(SectionHeader, { title: crd.spec.names.kind, titleSideActions: [
_jsx(CreateResourceButton, { resourceClass: CRClass, resourceName: crd.spec.names.kind }),
], actions: [
_jsx(Box, { mr: 2, children: _jsx(Link, { routeName: "crd", params: { name: crd.metadata.name }, activeCluster: crd.cluster, children: t('glossary|CRD: {{ crdName }}', { crdName: crd.metadata.name }) }) }),
] }), _jsx(CustomResourceListTable, { crd: crd })] }));
}
function getValueWithJSONPath(item, jsonPath) {
let value;
try {
// Extract the value from the json item
value = JSONPath({ path: '$' + jsonPath, json: item.jsonData });
}
catch (err) {
console.error(`Failed to get value from JSONPath ${jsonPath} on CR item ${item}`);
}
// Make sure the value will be represented in string form (to account for
// e.g. cases where we may get an array).
return value?.toString() || '';
}
export function CustomResourceListTable(props) {
const { t } = useTranslation(['glossary', 'translation']);
const { crd, title = '' } = props;
const apiGroup = React.useMemo(() => {
return crd.getMainAPIGroup();
}, [crd]);
const CRClass = React.useMemo(() => {
return crd.makeCRClass();
}, [crd]);
if (!CRClass) {
return _jsx(Empty, { children: t('translation|No custom resources found') });
}
const additionalPrinterCols = React.useMemo(() => {
const currentVersion = apiGroup[1];
const colsFromSpec = crd.jsonData.spec.versions.find((version) => version.name === currentVersion)?.additionalPrinterColumns || [];
const cols = [];
for (let i = 0; i < colsFromSpec.length; i++) {
const idx = i;
const colSpec = colsFromSpec[idx];
// Skip creation date because we already show it by default
if (colSpec.jsonPath === '.metadata.creationTimestamp') {
continue;
}
cols.push({
label: colSpec.name,
getValue: resource => {
let value = getValueWithJSONPath(resource, colSpec.jsonPath);
if (colSpec.type === 'date') {
value = localeDate(new Date(value));
}
return value;
},
});
}
return cols;
}, [crd, apiGroup]);
const cols = React.useMemo(() => {
const colsToDisplay = [
{
label: t('translation|Name'),
getValue: resource => resource.metadata.name,
render: resource => _jsx(CustomResourceLink, { resource: resource, crd: crd }),
},
...additionalPrinterCols,
'age',
];
if (crd.isNamespacedScope) {
colsToDisplay.splice(1, 0, 'namespace');
}
return colsToDisplay;
}, [crd, additionalPrinterCols]);
return (_jsx(ResourceListView, { title: title, headerProps: {
noNamespaceFilter: !crd.isNamespaced,
titleSideActions: [],
}, resourceClass: CRClass, columns: cols }));
}