Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show diff of individual package resources #36

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
37 changes: 27 additions & 10 deletions plugins/cad/src/components/Controls/YamlViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@
* limitations under the License.
*/

import Editor, { loader } from '@monaco-editor/react';
import Editor, { DiffEditor, loader } from '@monaco-editor/react';
import * as monaco from 'monaco-editor';
import React from 'react';

type YamlViewerProps = {
height?: string;
width?: string;
original?: string;
value: string;
showDiff?: boolean;
allowEdit?: boolean;
onUpdatedValue?: (newValue: string) => void;
};

export const YamlViewer = ({
height,
width,
original,
value,
showDiff,
allowEdit,
onUpdatedValue,
}: YamlViewerProps) => {
Expand All @@ -41,18 +43,33 @@ export const YamlViewer = ({
}
};

const sharedEditorOptions: monaco.editor.IStandaloneEditorConstructionOptions =
{
minimap: { enabled: false },
readOnly: !allowEdit,
scrollBeyondLastLine: false,
};

if (showDiff) {
return (
<DiffEditor
language="yaml"
original={original ?? ''}
modified={value}
options={{
renderSideBySide: false,
...sharedEditorOptions,
}}
/>
);
}

return (
<Editor
height={height}
width={width}
language="yaml"
value={value}
onChange={handleUpdatedValue}
options={{
minimap: { enabled: false },
readOnly: !allowEdit,
scrollBeyondLastLine: false,
}}
options={sharedEditorOptions}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { Table, TableColumn } from '@backstage/core-components';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { Button, Divider, IconButton, Menu, MenuItem } from '@material-ui/core';
import AddIcon from '@material-ui/icons/Add';
import DeleteIcon from '@material-ui/icons/Delete';
Expand Down Expand Up @@ -45,6 +46,7 @@ export enum ResourcesTableMode {

enum Dialog {
VIEWER = 'viewer',
DIFF_VIEWER = 'diff-viewer',
EDITOR = 'editor',
NONE = 'none',
}
Expand All @@ -59,6 +61,8 @@ type PackageRevisionResourcesTableProps = {
type ResourceRow = PackageResource & {
diffSummary: string;
isDeleted: boolean;
originalResource?: PackageResource;
currentResource?: PackageResource;
};

type KubernetesGKV = {
Expand All @@ -83,6 +87,7 @@ export const PackageRevisionResourcesTable = ({
}: PackageRevisionResourcesTableProps) => {
const [openDialog, setOpenDialog] = useState<Dialog>(Dialog.NONE);
const selectedDialogResource = useRef<DialogResource>();
const selectedDialogOriginalResource = useRef<DialogResource>();

const [addResourceAnchorEl, setAddResourceAnchorEl] =
React.useState<null | HTMLElement>(null);
Expand Down Expand Up @@ -126,13 +131,17 @@ export const PackageRevisionResourcesTable = ({
},
].sort((gvk1, gvk2) => (gvk1.kind > gvk2.kind ? 1 : -1));

const errorApi = useApi(errorApiRef);
const isEditMode = mode === ResourcesTableMode.EDIT;

const openResourceDialog = (
dialog: Dialog,
resource: DialogResource,
resource?: DialogResource,
originalResource?: DialogResource,
): void => {
selectedDialogResource.current = resource;
selectedDialogOriginalResource.current = originalResource;

setOpenDialog(dialog);
};

Expand All @@ -153,7 +162,7 @@ export const PackageRevisionResourcesTable = ({
onUpdatedResourcesMap(latestResourcesMap);
};

const rowOptions = (resourceRow: ResourceRow): JSX.Element[] => {
const renderRowOptions = (resourceRow: ResourceRow): JSX.Element[] => {
const options: JSX.Element[] = [];

if (isEditMode && !resourceRow.isDeleted) {
Expand Down Expand Up @@ -181,22 +190,53 @@ export const PackageRevisionResourcesTable = ({
return options;
};

const renderDiffColumn = (row: ResourceRow): JSX.Element | null => {
if (row.diffSummary) {
return (
<Button
variant="outlined"
style={{
position: 'absolute',
transform: 'translateY(-50%)',
}}
onClick={e => {
e.stopPropagation();
openResourceDialog(
Dialog.DIFF_VIEWER,
row.currentResource,
row.originalResource,
);
}}
>
{row.diffSummary}
</Button>
);
}

return null;
};

const columns: TableColumn<ResourceRow>[] = [
{ title: 'Kind', field: 'kind' },
{ title: 'Name', field: 'name' },
{ title: 'Namespace', field: 'namespace' },
{ title: '' },
{ title: '', render: resourceRow => <div>{rowOptions(resourceRow)}</div> },
{},
{ render: resourceRow => <div>{renderRowOptions(resourceRow)}</div> },
];

if (baseResourcesMap) {
columns[3] = { title: 'Diff', field: 'diffSummary' };
columns[3] = { title: 'Diff', render: renderDiffColumn };
}

const allResources = getPackageResourcesFromResourcesMap(
const packageResources = getPackageResourcesFromResourcesMap(
resourcesMap,
) as ResourceRow[];

const allResources: ResourceRow[] = packageResources.map(r => ({
...r,
currentResource: r,
}));

allResources.sort((resource1, resource2) => {
const resourceScore = (resource: ResourceRow): number => {
if (resource.kind === 'Kptfile') return 1000;
Expand Down Expand Up @@ -233,6 +273,7 @@ export const PackageRevisionResourcesTable = ({
diffSummary: 'Removed',
isDeleted: true,
yaml: '',
originalResource: resourceDiff.originalResource,
});
}

Expand All @@ -248,6 +289,8 @@ export const PackageRevisionResourcesTable = ({
);
}

thisResource.originalResource = resourceDiff.originalResource;

switch (resourceDiff.diffStatus) {
case ResourceDiffStatus.ADDED:
thisResource.diffSummary = 'Added';
Expand All @@ -258,7 +301,7 @@ export const PackageRevisionResourcesTable = ({
break;

case ResourceDiffStatus.UPDATED:
thisResource.diffSummary = `Updated (+${resourceDiff.linesAdded}, -${resourceDiff.linesRemoved})`;
thisResource.diffSummary = `Diff (+${resourceDiff.linesAdded}, -${resourceDiff.linesRemoved})`;
break;
case ResourceDiffStatus.UNCHANGED:
break;
Expand Down Expand Up @@ -406,18 +449,36 @@ export const PackageRevisionResourcesTable = ({
<ResourceViewerDialog
open={openDialog === Dialog.VIEWER}
onClose={closeDialog}
yaml={selectedDialogResource.current?.yaml ?? ''}
yaml={selectedDialogResource.current?.yaml}
originalYaml={selectedDialogOriginalResource.current?.yaml}
/>

<ResourceViewerDialog
open={openDialog === Dialog.DIFF_VIEWER}
onClose={closeDialog}
yaml={selectedDialogResource.current?.yaml}
originalYaml={selectedDialogOriginalResource.current?.yaml}
showDiff
/>

<Table<ResourceRow>
title="Resources"
options={{ search: false, paging: false }}
columns={columns}
data={allResources}
onRowClick={(_, resource) => {
if (resource) {
onRowClick={(_, row) => {
if (row) {
if (isEditMode && row.isDeleted) {
errorApi.post(new Error('Deleted resources cannot be updated.'));
return;
}

const dialog = isEditMode ? Dialog.EDITOR : Dialog.VIEWER;
openResourceDialog(dialog, resource);
openResourceDialog(
dialog,
row.currentResource,
row.originalResource,
);
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ import { FirstClassViewerSelector } from './components/FirstClassViewerSelector'
type ResourceViewerProps = {
open: boolean;
onClose: () => void;
yaml: string;
yaml?: string;
originalYaml?: string;
showDiff?: boolean;
};

const useStyles = makeStyles({
Expand All @@ -48,6 +50,8 @@ export const ResourceViewerDialog = ({
open,
onClose,
yaml,
originalYaml,
showDiff,
}: ResourceViewerProps) => {
const [showYamlView, setShowYamlView] = useState<boolean>(false);
const classes = useStyles();
Expand All @@ -58,9 +62,12 @@ export const ResourceViewerDialog = ({
}
}, [open]);

if (!yaml) return <div />;
if (!yaml && !originalYaml) return <div />;

const resourceYaml = loadYaml(yaml) as KubernetesResource;
const isDeleted = !yaml;

const thisYaml = yaml || originalYaml || '';
const resourceYaml = loadYaml(thisYaml) as KubernetesResource;

const toggleView = (): void => {
setShowYamlView(!showYamlView);
Expand All @@ -69,12 +76,12 @@ export const ResourceViewerDialog = ({
const { kind, apiVersion } = resourceYaml;
const resourceName = resourceYaml.metadata.name;

const displayYamlHeight = (yaml.split('\n').length + 1) * 18;
const displayYamlHeight = (thisYaml.split('\n').length + 1) * 18;

return (
<Dialog open={open} onClose={onClose} maxWidth="lg">
<DialogTitle>
{kind} {resourceName}
{kind} {resourceName} {isDeleted && '(Removed)'}
</DialogTitle>
<DialogContent>
<Fragment>
Expand All @@ -83,18 +90,24 @@ export const ResourceViewerDialog = ({
style={{ height: `${displayYamlHeight}px` }}
>
{showYamlView ? (
<YamlViewer value={yaml} />
<YamlViewer
value={thisYaml}
original={originalYaml}
showDiff={showDiff}
/>
) : (
<FirstClassViewerSelector
apiVersion={apiVersion}
kind={kind}
yaml={yaml}
originalYaml={originalYaml}
showDiff={showDiff}
/>
)}
</div>

<Button variant="text" color="primary" onClick={toggleView}>
Show {showYamlView ? 'Formatted View' : 'YAML View'}{' '}
Show {showYamlView ? 'Formatted View' : 'YAML View'}
</Button>
</Fragment>
</DialogContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import {
type FirstClassViewerSelectorProps = {
apiVersion: string;
kind: string;
yaml: string;
yaml?: string;
originalYaml?: string;
showDiff?: boolean;
};

const getCustomMetadataFn = (
Expand Down Expand Up @@ -64,12 +66,19 @@ export const FirstClassViewerSelector = ({
apiVersion,
kind,
yaml,
originalYaml,
showDiff,
}: FirstClassViewerSelectorProps) => {
const groupVersionKind = `${apiVersion}/${kind}`;

const customMetadataFn = getCustomMetadataFn(groupVersionKind);

return (
<StructuredMetadata yaml={yaml} getCustomMetadata={customMetadataFn} />
<StructuredMetadata
yaml={yaml}
originalYaml={originalYaml}
getCustomMetadata={customMetadataFn}
showDiff={showDiff}
/>
);
};
Loading