diff --git a/.changeset/eight-cougars-collect.md b/.changeset/eight-cougars-collect.md new file mode 100644 index 0000000000..07b3a52ca1 --- /dev/null +++ b/.changeset/eight-cougars-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +better rendering of nested data; rendered as yaml now diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 3099edf9d8..657f656949 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -14,109 +14,41 @@ * limitations under the License. */ -import React, { Fragment, ReactElement } from 'react'; -import { - withStyles, - createStyles, - WithStyles, - Theme, -} from '@material-ui/core/styles'; +import React, { Fragment } from 'react'; import startCase from 'lodash/startCase'; import Typography from '@material-ui/core/Typography'; import { MetadataTable, MetadataTableItem, - MetadataList, - MetadataListItem, } from './MetadataTable'; +import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; +import { CodeSnippet } from '../CodeSnippet'; +import jsyaml from 'js-yaml'; export type StructuredMetadataTableListClassKey = 'root'; -const listStyle = createStyles({ - root: { - margin: '0 0', - listStyleType: 'none', - }, -}); - export type StructuredMetadataTableNestedListClassKey = 'root'; -const nestedListStyle = (theme: Theme) => - createStyles({ - root: { - ...listStyle.root, - paddingLeft: theme.spacing(1), - }, - }); - -interface StyleProps extends WithStyles { - children?: React.ReactNode; -} -// Sub Components -const StyledList = withStyles(listStyle, { - name: 'BackstageStructuredMetadataTableList', -})(({ classes, children }: StyleProps) => ( - {children} -)); -const StyledNestedList = withStyles(nestedListStyle, { - name: 'BackstageStructuredMetadataTableNestedList', -})(({ classes, children }: StyleProps) => ( - {children} -)); - -function renderList(list: Array, options: Options, nested: boolean) { - const values = list.map((item: any, index: number) => ( - - {toValue(item, options, nested)} - - )); - return nested ? ( - {values} - ) : ( - {values} - ); -} - -function renderMap( - map: { [key: string]: any }, - options: Options, - nested: boolean, -) { - const values = Object.keys(map).map(key => { - const value = toValue(map[key], options, true); - return ( - - - {`${options.titleFormat(key)}: `} - - {value} - - ); - }); - - return nested ? ( - {values} - ) : ( - {values} - ); -} - function toValue( - value: ReactElement | object | Array | boolean, - options: Options, - nested: boolean, + value: object | Array | boolean | string, ) { if (React.isValidElement(value)) { return {value}; } - if (value !== null && typeof value === 'object' && !Array.isArray(value)) { - return renderMap(value, options, nested); - } - - if (Array.isArray(value)) { - return renderList(value, options, nested); + if (value !== null && typeof value === 'object') { + return } if (typeof value === 'boolean') { @@ -129,8 +61,8 @@ function toValue( ); } -const ItemValue = ({ value, options }: { value: any; options: Options }) => ( - {toValue(value, options, false)} +const ItemValue = ({ value }: { value: any }) => ( + {toValue(value)} ); const TableItem = ({ @@ -139,12 +71,12 @@ const TableItem = ({ options, }: { title: string; - value: any; + value: JsonObject | JsonArray | JsonValue; options: Options; }) => { return ( - + ); }; diff --git a/plugins/kubernetes/dev/index.tsx b/plugins/kubernetes/dev/index.tsx index 2de5a17f25..008d6ce54b 100644 --- a/plugins/kubernetes/dev/index.tsx +++ b/plugins/kubernetes/dev/index.tsx @@ -34,6 +34,7 @@ import fixture2 from '../src/__fixtures__/2-deployments.json'; import fixture3 from '../src/__fixtures__/1-cronjobs.json'; import fixture4 from '../src/__fixtures__/2-cronjobs.json'; import fixture5 from '../src/__fixtures__/1-rollouts.json'; +import fixture6 from '../src/__fixtures__/3-ingresses.json'; import { TestApiProvider } from '@backstage/test-utils'; const mockEntity: Entity = { @@ -200,5 +201,18 @@ createDevApp() ), }) + .addPage({ + path: '/fixture-6', + title: 'Fixture 6', + element: ( + + + + + + ), + }) .registerPlugin(kubernetesPlugin) .render(); diff --git a/plugins/kubernetes/src/__fixtures__/3-ingresses.json b/plugins/kubernetes/src/__fixtures__/3-ingresses.json new file mode 100644 index 0000000000..94359c8239 --- /dev/null +++ b/plugins/kubernetes/src/__fixtures__/3-ingresses.json @@ -0,0 +1,372 @@ +{ + "ingresses": [ + { + "metadata": { + "name": "app-service-endpoint.dice-roller.backstage.io", + "namespace": "dice-roller", + "uid": "00000000-0000-0000-0000-000000000000", + "resourceVersion": "4749260703", + "generation": 1, + "creationTimestamp": "2023-08-16T14:52:16Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "ingress-class": "istio-internal" + }, + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "spec", + "metacontroller.k8s.io/last-applied-configuration": "spec", + "nginx.ingress.kubernetes.io/auth-signin": "https://backstage.io/authenticate", + "nginx.ingress.kubernetes.io/configuration-snippet": "config", + "nginx.ingress.kubernetes.io/service-upstream": "true", + "nginx.ingress.kubernetes.io/upstream-vhost": "app-service-endpoint.dice-roller.backstage.services" + }, + "ownerReferences": [ + { + "apiVersion": "networking.backstage.com/v1beta1", + "kind": "MeshIntegration", + "name": "app-service-endpoint", + "uid": "00000000-0000-0000-0000-000000000000", + "controller": true, + "blockOwnerDeletion": true + } + ], + "managedFields": [ + { + "manager": "nginx-ingress-controller", + "operation": "Update", + "apiVersion": "networking.k8s.io/v1", + "time": "2023-08-16T14:52:56Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:status": { + "f:loadBalancer": { + "f:ingress": {} + } + } + }, + "subresource": "status" + }, + { + "manager": "kubectl-client-side-apply", + "operation": "Update", + "apiVersion": "networking.k8s.io/v1", + "time": "2023-12-15T16:33:17Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:nginx.ingress.kubernetes.io/auth-signin": {}, + "f:nginx.ingress.kubernetes.io/configuration-snippet": {}, + "f:nginx.ingress.kubernetes.io/service-upstream": {}, + "f:nginx.ingress.kubernetes.io/upstream-vhost": {} + }, + "f:labels": { + ".": {}, + "f:backstage.io/kubernetes-id": {}, + "f:ingress-class": {} + } + }, + "f:spec": { + "f:ingressClassName": {}, + "f:rules": {}, + "f:tls": {} + } + } + }, + { + "manager": "kubectl-label", + "operation": "Update", + "apiVersion": "networking.k8s.io/v1", + "time": "2024-02-21T15:00:44Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:labels": { + "f:controller-uid": {} + } + } + } + }, + { + "manager": "metacontroller", + "operation": "Update", + "apiVersion": "networking.k8s.io/v1", + "time": "2024-02-21T15:01:01Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:metacontroller.k8s.io/last-applied-configuration": {} + }, + "f:ownerReferences": { + ".": {}, + "k:{\"uid\":\"00000000-0000-0000-0000-000000000000\"}": {} + } + } + } + } + ] + }, + "spec": { + "ingressClassName": "istio-internal", + "tls": { + "hosts": ["app-service-endpoint.dice-roller.backstage.io"], + "secretName": "dice-roller-wildcard-certificate" + }, + "rules": [ + { + "host": "app-service-endpoint.dice-roller.backstage.io", + "http": { + "paths": [ + { + "pathType": "ImplementationSpecific", + "backend": { + "service": { + "name": "app-service-endpoint", + "port": { + "number": 8080 + } + } + } + }, + { + "pathType": "Second item in the list", + "backend": { + "service": { + "name": "app-service-endpoint", + "port": { + "number": 8080 + } + } + } + } + ] + } + } + ] + }, + "status": { + "loadBalancer": { + "ingress": [ + { + "ip": "1.2.3.4" + } + ] + } + } + } + ], + "deployments": [ + { + "metadata": { + "annotations": { + "deployment.kubernetes.io/revision": "2", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"backstage.io/kubernetes-id\":\"dice-roller\"},\"name\":\"dice-roller\",\"namespace\":\"default\"},\"spec\":{\"replicas\":10,\"selector\":{\"matchLabels\":{\"app\":\"dice-roller\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"dice-roller\",\"backstage.io/kubernetes-id\":\"dice-roller\"}},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}}}\n" + }, + "creationTimestamp": "2020-09-23T12:00:55.000Z", + "generation": 3, + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "managedFields": [ + { + "apiVersion": "apps/v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + }, + "f:labels": { + ".": {}, + "f:backstage.io/kubernetes-id": {} + } + }, + "f:spec": { + "f:progressDeadlineSeconds": {}, + "f:replicas": {}, + "f:revisionHistoryLimit": {}, + "f:selector": { + "f:matchLabels": { + ".": {}, + "f:app": {} + } + }, + "f:strategy": { + "f:rollingUpdate": { + ".": {}, + "f:maxSurge": {}, + "f:maxUnavailable": {} + }, + "f:type": {} + }, + "f:template": { + "f:metadata": { + "f:labels": { + ".": {}, + "f:app": {}, + "f:backstage.io/kubernetes-id": {} + } + }, + "f:spec": { + "f:containers": { + "k:{\"name\":\"nginx\"}": { + ".": {}, + "f:image": {}, + "f:imagePullPolicy": {}, + "f:name": {}, + "f:ports": { + ".": {}, + "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": { + ".": {}, + "f:containerPort": {}, + "f:protocol": {} + } + }, + "f:resources": {}, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {} + } + }, + "f:dnsPolicy": {}, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:securityContext": {}, + "f:terminationGracePeriodSeconds": {} + } + } + } + }, + "manager": "kubectl", + "operation": "Update", + "time": "2020-09-25T09:58:50.000Z" + }, + { + "apiVersion": "apps/v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:deployment.kubernetes.io/revision": {} + } + }, + "f:status": { + "f:availableReplicas": {}, + "f:conditions": { + ".": {}, + "k:{\"type\":\"Available\"}": { + ".": {}, + "f:lastTransitionTime": {}, + "f:lastUpdateTime": {}, + "f:message": {}, + "f:reason": {}, + "f:status": {}, + "f:type": {} + }, + "k:{\"type\":\"Progressing\"}": { + ".": {}, + "f:lastTransitionTime": {}, + "f:lastUpdateTime": {}, + "f:message": {}, + "f:reason": {}, + "f:status": {}, + "f:type": {} + } + }, + "f:observedGeneration": {}, + "f:readyReplicas": {}, + "f:replicas": {}, + "f:updatedReplicas": {} + } + }, + "manager": "kube-controller-manager", + "operation": "Update", + "time": "2020-09-25T09:58:55.000Z" + } + ], + "name": "dice-roller", + "namespace": "default", + "resourceVersion": "593230", + "selfLink": "/apis/apps/v1/namespaces/default/deployments/dice-roller", + "uid": "7551e949-42d1-4061-83c5-9da107186e47" + }, + "spec": { + "progressDeadlineSeconds": 600, + "replicas": 10, + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "app": "dice-roller" + } + }, + "strategy": { + "rollingUpdate": { + "maxSurge": "25%", + "maxUnavailable": "25%" + }, + "type": "RollingUpdate" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "dice-roller", + "backstage.io/kubernetes-id": "dice-roller" + } + }, + "spec": { + "containers": [ + { + "image": "nginx:1.14.2", + "imagePullPolicy": "IfNotPresent", + "name": "nginx", + "ports": [ + { + "containerPort": 80, + "protocol": "TCP" + } + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "terminationGracePeriodSeconds": 30 + } + } + }, + "status": { + "availableReplicas": 10, + "conditions": [ + { + "lastTransitionTime": "2020-09-23T12:00:55.000Z", + "lastUpdateTime": "2020-09-24T11:39:28.000Z", + "message": "ReplicaSet \"dice-roller-6c8646bfd\" has successfully progressed.", + "reason": "NewReplicaSetAvailable", + "status": "True", + "type": "Progressing" + }, + { + "lastTransitionTime": "2020-09-25T09:58:55.000Z", + "lastUpdateTime": "2020-09-25T09:58:55.000Z", + "message": "Deployment has minimum availability.", + "reason": "MinimumReplicasAvailable", + "status": "True", + "type": "Available" + } + ], + "observedGeneration": 3, + "readyReplicas": 10, + "replicas": 10, + "updatedReplicas": 10 + } + } + ] +}