Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@
"update": "Update"
},
"yaml": {
"YAML": "File"
"YAML": "File",
"showOnlyImportant": "Show only important fields"
},
"createMCP": {
"dialogTitle": "Create Managed Control Plane",
Expand Down
13 changes: 10 additions & 3 deletions src/components/Graphs/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Legend, LegendItem } from './Legend';
import { YamlViewDialog } from '../Yaml/YamlViewDialog';
import YamlViewer from '../Yaml/YamlViewer';
import { stringify } from 'yaml';
import { removeManagedFieldsProperty } from '../../utils/removeManagedFieldsProperty';
import { removeManagedFieldsAndFilterData, Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
import { useTranslation } from 'react-i18next';
import { useGraph } from './useGraph';
import { ManagedResourceItem } from '../../lib/shared/types';
Expand Down Expand Up @@ -44,7 +44,12 @@ const Graph: React.FC = () => {
const { nodes, edges, colorMap, loading, error } = useGraph(colorBy, handleYamlClick);

const yamlString = useMemo(
() => (yamlResource ? stringify(removeManagedFieldsProperty(yamlResource)) : ''),
() => (yamlResource ? stringify(removeManagedFieldsAndFilterData(yamlResource as unknown as Resource, true)) : ''),
[yamlResource],
);

const yamlStringToCopy = useMemo(
() => (yamlResource ? stringify(removeManagedFieldsAndFilterData(yamlResource as unknown as Resource, false)) : ''),
[yamlResource],
);

Expand Down Expand Up @@ -134,7 +139,9 @@ const Graph: React.FC = () => {
<YamlViewDialog
isOpen={yamlDialogOpen}
setIsOpen={setYamlDialogOpen}
dialogContent={<YamlViewer yamlString={yamlString} filename={yamlFilename} />}
dialogContent={
<YamlViewer yamlString={yamlString} yamlStringToCopy={yamlStringToCopy} filename={yamlFilename} />
}
/>
</div>
);
Expand Down
31 changes: 27 additions & 4 deletions src/components/Yaml/YamlLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { YamlViewButtonProps } from './YamlViewButtonWithLoader.tsx';
import { FC } from 'react';
import { FC, useMemo } from 'react';

import { stringify } from 'yaml';

Expand All @@ -9,24 +9,47 @@ import Loading from '../Shared/Loading.tsx';
import IllustratedError from '../Shared/IllustratedError.tsx';
import YamlViewer from './YamlViewer.tsx';
import { useApiResource } from '../../lib/api/useApiResource';
import { removeManagedFieldsProperty, Resource } from '../../utils/removeManagedFieldsProperty.ts';
import { removeManagedFieldsAndFilterData, Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';

export const YamlLoader: FC<YamlViewButtonProps> = ({ workspaceName, resourceType, resourceName }) => {
interface YamlLoaderProps extends YamlViewButtonProps {
showOnlyImportantData?: boolean;
setShowOnlyImportantData?: (showOnlyImportantData: boolean) => void;
}

export const YamlLoader: FC<YamlLoaderProps> = ({
workspaceName,
resourceType,
resourceName,
showOnlyImportantData = false,
setShowOnlyImportantData,
}) => {
const { isLoading, data, error } = useApiResource(
ResourceObject(workspaceName ?? '', resourceType, resourceName),
undefined,
true,
);
const { t } = useTranslation();
const yamlString = useMemo(() => {
if (isLoading || error) return '';
return stringify(removeManagedFieldsAndFilterData(data as Resource, showOnlyImportantData));
}, [data, error, isLoading, showOnlyImportantData]);

const yamlStringToCopy = useMemo(() => {
if (isLoading || error) return '';
return stringify(removeManagedFieldsAndFilterData(data as Resource, false));
}, [data, error, isLoading]);
if (isLoading) return <Loading />;
if (error) {
return <IllustratedError details={t('common.cannotLoadData')} />;
}

return (
<YamlViewer
yamlString={stringify(removeManagedFieldsProperty(data as Resource))}
yamlString={yamlString}
yamlStringToCopy={yamlStringToCopy}
filename={`${workspaceName ? `${workspaceName}_` : ''}${resourceType}_${resourceName}`}
setShowOnlyImportantData={setShowOnlyImportantData}
showOnlyImportantData={showOnlyImportantData}
/>
);
};
13 changes: 11 additions & 2 deletions src/components/Yaml/YamlViewButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import styles from './YamlViewer.module.css';
import { useTranslation } from 'react-i18next';
import YamlViewer from './YamlViewer.tsx';
import { stringify } from 'yaml';
import { removeManagedFieldsProperty, Resource } from '../../utils/removeManagedFieldsProperty.ts';
import { removeManagedFieldsAndFilterData, Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
import { YamlIcon } from './YamlIcon.tsx';
import { YamlViewDialog } from './YamlViewDialog.tsx';

Expand All @@ -13,12 +13,16 @@ export type YamlViewButtonProps = {
};

export const YamlViewButton: FC<YamlViewButtonProps> = ({ resourceObject }) => {
const [showOnlyImportantData, setShowOnlyImportantData] = useState(true);
const [isOpen, setIsOpen] = useState(false);
const { t } = useTranslation();
const resource = resourceObject as Resource;

const yamlString = useMemo(() => {
return stringify(removeManagedFieldsProperty(resource));
return stringify(removeManagedFieldsAndFilterData(resource, showOnlyImportantData));
}, [resource, showOnlyImportantData]);
const yamlStringToCopy = useMemo(() => {
return stringify(removeManagedFieldsAndFilterData(resource, false));
}, [resource]);
return (
<span>
Expand All @@ -27,10 +31,15 @@ export const YamlViewButton: FC<YamlViewButtonProps> = ({ resourceObject }) => {
setIsOpen={setIsOpen}
dialogContent={
<YamlViewer
yamlStringToCopy={yamlStringToCopy}
yamlString={yamlString}
filename={`${resource?.kind ?? ''}${resource?.metadata?.name ? '_' : ''}${resource?.metadata?.name ?? ''}`}
setShowOnlyImportantData={setShowOnlyImportantData}
showOnlyImportantData={showOnlyImportantData}
/>
}
setShowOnlyImportantData={setShowOnlyImportantData}
showOnlyImportantData={showOnlyImportantData}
/>

<Button
Expand Down
11 changes: 10 additions & 1 deletion src/components/Yaml/YamlViewButtonWithLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type YamlViewButtonProps = {
};

export const YamlViewButtonWithLoader: FC<YamlViewButtonProps> = ({ workspaceName, resourceType, resourceName }) => {
const [showOnlyImportantData, setShowOnlyImportantData] = useState(true);
const [isOpen, setIsOpen] = useState(false);
const { t } = useTranslation();
return (
Expand All @@ -21,8 +22,16 @@ export const YamlViewButtonWithLoader: FC<YamlViewButtonProps> = ({ workspaceNam
isOpen={isOpen}
setIsOpen={setIsOpen}
dialogContent={
<YamlLoader workspaceName={workspaceName} resourceName={resourceName} resourceType={resourceType} />
<YamlLoader
workspaceName={workspaceName}
resourceName={resourceName}
resourceType={resourceType}
showOnlyImportantData={showOnlyImportantData}
setShowOnlyImportantData={setShowOnlyImportantData}
/>
}
setShowOnlyImportantData={setShowOnlyImportantData}
showOnlyImportantData={showOnlyImportantData}
/>

<Button
Expand Down
27 changes: 24 additions & 3 deletions src/components/Yaml/YamlViewDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Bar, Button, Dialog } from '@ui5/webcomponents-react';
import { Bar, Button, CheckBox, Dialog } from '@ui5/webcomponents-react';

import { FC, ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
Expand All @@ -7,19 +7,40 @@ export type YamlViewDialogProps = {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
dialogContent: ReactNode;
showOnlyImportantData?: boolean;
setShowOnlyImportantData?: (showOnlyImportantData: boolean) => void;
};

export const YamlViewDialog: FC<YamlViewDialogProps> = ({ isOpen, setIsOpen, dialogContent }) => {
export const YamlViewDialog: FC<YamlViewDialogProps> = ({
isOpen,
setIsOpen,
dialogContent,
showOnlyImportantData,
setShowOnlyImportantData,
}) => {
const { t } = useTranslation();
const handleShowOnlyImportantData = () => {
setShowOnlyImportantData?.(!showOnlyImportantData);
};
return (
<Dialog
open={isOpen}
stretch
initialFocus={'closeButton'}
footer={
<Bar
startContent={
setShowOnlyImportantData && (
<CheckBox
text={t('yaml.showOnlyImportant')}
checked={showOnlyImportantData}
onChange={handleShowOnlyImportantData}
/>
)
}
design="Footer"
endContent={
<Button design="Emphasized" onClick={() => setIsOpen(false)}>
<Button design="Emphasized" id={'closeButton'} onClick={() => setIsOpen(false)}>
{t('common.close')}
</Button>
}
Expand Down
24 changes: 18 additions & 6 deletions src/components/Yaml/YamlViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ import styles from './YamlViewer.module.css';
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
import { useTranslation } from 'react-i18next';
import { useTheme } from '../../hooks/useTheme.ts';
type YamlViewerProps = { yamlString: string; filename: string };
const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
type YamlViewerProps = {
yamlString: string;
yamlStringToCopy: string;
filename: string;
showOnlyImportantData?: boolean;
setShowOnlyImportantData?: (showOnlyImportantData: boolean) => void;
};

// Download button is hidden now due to stakeholder request
const SHOW_DOWNLOAD_BUTTON = false;

const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename, yamlStringToCopy }) => {
const { t } = useTranslation();
const { isDarkTheme } = useTheme();
const { copyToClipboard } = useCopyToClipboard();
Expand All @@ -27,12 +37,14 @@ const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
return (
<div className={styles.container}>
<FlexBox className={styles.buttons} direction="Row" justifyContent="End" alignItems="Baseline" gap={16}>
<Button icon="copy" onClick={() => copyToClipboard(yamlString)}>
<Button icon="copy" onClick={() => copyToClipboard(yamlStringToCopy)}>
{t('buttons.copy')}
</Button>
<Button icon="download" onClick={downloadYaml}>
{t('buttons.download')}
</Button>
{SHOW_DOWNLOAD_BUTTON && (
<Button icon="download" onClick={downloadYaml}>
{t('buttons.download')}
</Button>
)}
</FlexBox>
<SyntaxHighlighter
language="yaml"
Expand Down
1 change: 1 addition & 0 deletions src/lib/api/types/shared/keyNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const CHARGING_TARGET_LABEL: string = 'openmcp.cloud.sap/charging-target'
export const CHARGING_TARGET_TYPE_LABEL: string = 'openmcp.cloud.sap/charging-target-type';
export const PROJECT_NAME_LABEL: string = 'openmcp.cloud/mcp-project';
export const WORKSPACE_LABEL: string = 'openmcp.cloud/mcp-workspace';
export const LAST_APPLIED_CONFIGURATION_ANNOTATION = 'kubectl.kubernetes.io/last-applied-configuration';
64 changes: 64 additions & 0 deletions src/utils/removeManagedFieldsAndFilterData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { LAST_APPLIED_CONFIGURATION_ANNOTATION } from '../lib/api/types/shared/keyNames';

export type Resource = {
apiVersion: string;
kind: string;
items?: Omit<Resource, 'items'>[];
metadata: {
name: string;
namespace?: string;
labels?: Record<string, string>;
annotations?: {
[LAST_APPLIED_CONFIGURATION_ANNOTATION]?: string;
[key: string]: string | undefined;
};
managedFields?: unknown;
creationTimestamp?: string;
finalizers?: string[];
generation?: number;
resourceVersion?: string;
uid?: string;
};
spec?: unknown;
status?: unknown;
};

const cleanUpResource = (
resource: Omit<Resource, 'items'>,
showOnlyImportantData: boolean,
): Omit<Resource, 'items'> => {
const newResource = { ...resource };

if (newResource.metadata) {
newResource.metadata = { ...newResource.metadata };
delete newResource.metadata.managedFields;

if (showOnlyImportantData) {
if (newResource.metadata.annotations) {
newResource.metadata.annotations = { ...newResource.metadata.annotations };
delete newResource.metadata.annotations[LAST_APPLIED_CONFIGURATION_ANNOTATION];
}
delete newResource.metadata.generation;
delete newResource.metadata.uid;
}
}

return newResource;
};

export const removeManagedFieldsAndFilterData = (
resourceObject: Resource,
showOnlyImportantData: boolean,
): Resource => {
if (!resourceObject) {
return {} as Resource;
}
if (resourceObject.items) {
return {
...cleanUpResource(resourceObject, showOnlyImportantData),
items: resourceObject.items.map((item) => cleanUpResource(item, showOnlyImportantData)),
};
}

return cleanUpResource(resourceObject, showOnlyImportantData);
};
36 changes: 0 additions & 36 deletions src/utils/removeManagedFieldsProperty.ts

This file was deleted.

Loading