diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index 6610fb4b56..412293eee1 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -13,6 +13,7 @@ import { crdStore } from "./crd.store"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { Input } from "../input"; import { AdditionalPrinterColumnsV1, CustomResourceDefinition } from "../../api/endpoints/crd.api"; +import { parseJsonPath } from "../../utils/jsonPath"; interface Props extends KubeObjectDetailsProps { } @@ -46,7 +47,7 @@ export class CrdResourceDetails extends React.Component { renderAdditionalColumns(crd: CustomResourceDefinition, columns: AdditionalPrinterColumnsV1[]) { return columns.map(({ name, jsonPath: jp }) => ( - {convertSpecValue(jsonPath.value(crd, jp.slice(1)))} + {convertSpecValue(jsonPath.value(crd, parseJsonPath(jp.slice(1))))} )); } diff --git a/src/renderer/components/+custom-resources/crd-resources.tsx b/src/renderer/components/+custom-resources/crd-resources.tsx index cca7ac7015..72209c80f7 100644 --- a/src/renderer/components/+custom-resources/crd-resources.tsx +++ b/src/renderer/components/+custom-resources/crd-resources.tsx @@ -12,6 +12,7 @@ import { autorun, computed } from "mobx"; import { crdStore } from "./crd.store"; import { TableSortCallback } from "../table"; import { apiManager } from "../../api/api-manager"; +import { parseJsonPath } from "../../utils/jsonPath"; interface Props extends RouteComponentProps { } @@ -61,7 +62,7 @@ export class CrdResources extends React.Component { }; extraColumns.forEach(column => { - sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, column.jsonPath.slice(1)); + sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1))); }); return ( @@ -91,10 +92,18 @@ export class CrdResources extends React.Component { renderTableContents={(crdInstance: KubeObject) => [ crdInstance.getName(), isNamespaced && crdInstance.getNs(), - ...extraColumns.map(column => ({ - renderBoolean: true, - children: JSON.stringify(jsonPath.value(crdInstance, column.jsonPath.slice(1))), - })), + ...extraColumns.map((column) => { + let value = jsonPath.value(crdInstance, parseJsonPath(column.jsonPath.slice(1))); + + if (Array.isArray(value) || typeof value === "object") { + value = JSON.stringify(value); + } + + return { + renderBoolean: true, + children: value, + }; + }), crdInstance.getAge(), ]} /> diff --git a/src/renderer/utils/__tests__/jsonPath.test.tsx b/src/renderer/utils/__tests__/jsonPath.test.tsx new file mode 100644 index 0000000000..53ef16dc05 --- /dev/null +++ b/src/renderer/utils/__tests__/jsonPath.test.tsx @@ -0,0 +1,41 @@ +import { parseJsonPath } from "../jsonPath"; + +describe("parseJsonPath", () => { + test("should convert \\. to use indexed notation", () => { + const res = parseJsonPath(".metadata.labels.kubesphere\\.io/alias-name"); + + expect(res).toBe(".metadata.labels['kubesphere.io/alias-name']"); + }); + + test("should convert keys with escpaped charatecrs to use indexed notation", () => { + const res = parseJsonPath(".metadata.labels.kubesphere\\\"io/alias-name"); + + expect(res).toBe(".metadata.labels['kubesphere\"io/alias-name']"); + }); + + test("should convert '-' to use indexed notation", () => { + const res = parseJsonPath(".metadata.labels.alias-name"); + + expect(res).toBe(".metadata.labels['alias-name']"); + }); + + test("should handle scenario when both \\. and indexed notation are present", () => { + const rest = parseJsonPath(".metadata.labels\\.serving['some.other.item']"); + + expect(rest).toBe(".metadata['labels.serving']['some.other.item']"); + }); + + + test("should not touch given jsonPath if no invalid characters present", () => { + const res = parseJsonPath(".status.conditions[?(@.type=='Ready')].status"); + + expect(res).toBe(".status.conditions[?(@.type=='Ready')].status"); + }); + + test("strips '\\' away from the result", () => { + const res = parseJsonPath(".metadata.labels['serving\\.knative\\.dev/configuration']"); + + expect(res).toBe(".metadata.labels['serving.knative.dev/configuration']"); + }); + +}); diff --git a/src/renderer/utils/jsonPath.ts b/src/renderer/utils/jsonPath.ts new file mode 100644 index 0000000000..ea31ffa80e --- /dev/null +++ b/src/renderer/utils/jsonPath.ts @@ -0,0 +1,35 @@ +// Helper to convert strings used for jsonPath where \. or - is present to use indexed notation, +// for example: .metadata.labels.kubesphere\.io/alias-name -> .metadata.labels['kubesphere\.io/alias-name'] + +export function parseJsonPath(jsonPath: string) { + let pathExpression = jsonPath; + + if (jsonPath.match(/[\\-]/g)) { // search for '\' and '-' + const [first, ...rest] = jsonPath.split(/(?<=\w)\./); // split jsonPath by '.' (\. cases are ignored) + + pathExpression = `${convertToIndexNotation(first, true)}${rest.map(value => convertToIndexNotation(value)).join("")}`; + } + + // strip '\' characters from the result + return pathExpression.replace(/\\/g, ""); +} + +function convertToIndexNotation(key: string, firstItem = false) { + if (key.match(/[\\-]/g)) { // check if found '\' and '-' in key + if (key.includes("[")) { // handle cases where key contains [...] + const keyToConvert = key.match(/^.*(?=\[)/g); // get the text from the key before '[' + + if (keyToConvert && keyToConvert[0].match(/[\\-]/g)) { // check if that part contains illegal characters + return key.replace(keyToConvert[0], `['${keyToConvert[0]}']`); // surround key with '[' and ']' + } else { + return `.${key}`; // otherwise return as is with leading '.' + } + } + + return `['${key}']`; + } else { // no illegal chracters found, do not touch + const prefix = firstItem ? "" : "."; + + return `${prefix}${key}`; + } +} \ No newline at end of file