Skip to content

Commit

Permalink
About card edit improvements (#2950)
Browse files Browse the repository at this point in the history
* Update to Edit link in Card component.

* prettier

* new line

* Added changeset

* Actually run prettier. . .

* Added functionality to determine source of url for icon choice. Fixed failed tests.

* Added test case for new edithref attribute

* Prettier

* forgot a file

* correct url substring sanitation

* corrected url substring sanitation

* Added handling of Bitbucket Cloud and Gitlab.

* Added tests for each GitLab and BitBucket edits.

* Update to leverage git-url-parse

* update test to reflect owner change
  • Loading branch information
NetPenguins committed Nov 9, 2020
1 parent f059eaf commit 2d0bd1b
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/thick-lobsters-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---

Improved the edit link to open the component yaml in edit mode in corresponding SCM. Broke out logic for createEditLink to be reused.
1 change: 1 addition & 0 deletions plugins/catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@material-ui/lab": "4.0.0-alpha.45",
"@types/react": "^16.9",
"classnames": "^2.2.6",
"git-url-parse": "^11.4.0",
"moment": "^2.26.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
Expand Down
68 changes: 67 additions & 1 deletion plugins/catalog/src/components/AboutCard/AboutCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import { AboutCard } from './AboutCard';

describe('<AboutCard />', () => {
describe('<AboutCard /> GitHub', () => {
it('renders info and "view source" link', () => {
const entity = {
apiVersion: 'v1',
Expand All @@ -42,5 +42,71 @@ describe('<AboutCard />', () => {
'href',
'https://github.com/backstage/backstage/blob/master/software.yaml',
);
expect(getByText('View Source').closest('a')).toHaveAttribute(
'edithref',
'https://github.com/backstage/backstage/edit/master/software.yaml',
);
});
});

describe('<AboutCard /> GitLab', () => {
it('renders info and "view source" link', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
annotations: {
'backstage.io/managed-by-location':
'gitlab:https://gitlab.com/backstage/backstage/-/blob/master/software.yaml',
},
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(<AboutCard entity={entity} />);
expect(getByText('service')).toBeInTheDocument();
expect(getByText('View Source').closest('a')).toHaveAttribute(
'href',
'https://gitlab.com/backstage/backstage/-/blob/master/software.yaml',
);
expect(getByText('View Source').closest('a')).toHaveAttribute(
'edithref',
'https://gitlab.com/backstage/backstage/-/edit/master/software.yaml',
);
});
});

describe('<AboutCard /> BitBucket', () => {
it('renders info and "view source" link', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
annotations: {
'backstage.io/managed-by-location':
'bitbucket:https://bitbucket.org/backstage/backstage/src/master/software.yaml',
},
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(<AboutCard entity={entity} />);
expect(getByText('service')).toBeInTheDocument();
expect(getByText('View Source').closest('a')).toHaveAttribute(
'href',
'https://bitbucket.org/backstage/backstage/src/master/software.yaml',
);
expect(getByText('View Source').closest('a')).toHaveAttribute(
'edithref',
'https://bitbucket.org/backstage/backstage/src/master/software.yaml?mode=edit&spa=0&at=master',
);
});
});
33 changes: 23 additions & 10 deletions plugins/catalog/src/components/AboutCard/AboutCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import EditIcon from '@material-ui/icons/Edit';
import GitHubIcon from '@material-ui/icons/GitHub';
import React from 'react';
import { IconLinkVertical } from './IconLinkVertical';
import { findLocationForEntityMeta } from '../../data/utils';
import { createEditLink, determineUrlType } from '../createEditLink';

const useStyles = makeStyles(theme => ({
links: {
Expand Down Expand Up @@ -79,18 +81,24 @@ const iconMap: Record<string, React.ReactNode> = {
github: <GitHubIcon />,
};

type CodeLinkInfo = { icon?: React.ReactNode; href?: string };
type CodeLinkInfo = {
icon?: React.ReactNode;
edithref?: string;
href?: string;
};

function getCodeLinkInfo(entity: Entity): CodeLinkInfo {
const location =
entity?.metadata?.annotations?.['backstage.io/managed-by-location'];

const location = findLocationForEntityMeta(entity?.metadata);
if (location) {
// split by first `:`
// e.g. "github:https://github.com/backstage/backstage/blob/master/software.yaml"
const [type, target] = location.split(/:(.+)/);

return { icon: iconMap[type], href: target };
const type =
location.type === 'url'
? determineUrlType(location.target)
: location.type;
return {
icon: iconMap[type],
edithref: createEditLink(location),
href: location.target,
};
}
return {};
}
Expand All @@ -109,7 +117,12 @@ export function AboutCard({ entity, variant }: AboutCardProps) {
<CardHeader
title="About"
action={
<IconButton href={codeLink.href || '#'} aria-label="Edit">
<IconButton
aria-label="Edit"
onClick={() => {
window.open(codeLink.edithref || '#', '_blank');
}}
>
<EditIcon />
</IconButton>
}
Expand Down
11 changes: 2 additions & 9 deletions plugins/catalog/src/components/CatalogTable/CatalogTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Entity, LocationSpec } from '@backstage/catalog-model';
import { Entity } from '@backstage/catalog-model';
import { Table, TableColumn, TableProps } from '@backstage/core';
import { Chip, Link } from '@material-ui/core';
import Edit from '@material-ui/icons/Edit';
Expand All @@ -22,6 +22,7 @@ import { Alert } from '@material-ui/lab';
import React from 'react';
import { generatePath, Link as RouterLink } from 'react-router-dom';
import { findLocationForEntityMeta } from '../../data/utils';
import { createEditLink } from '../createEditLink';
import { useStarredEntities } from '../../hooks/useStarredEntities';
import { entityRoute, entityRouteParams } from '../../routes';
import {
Expand Down Expand Up @@ -120,14 +121,6 @@ export const CatalogTable = ({
};
},
(rowData: Entity) => {
const createEditLink = (location: LocationSpec): string => {
switch (location.type) {
case 'github':
return location.target.replace('/blob/', '/edit/');
default:
return location.target;
}
};
const location = findLocationForEntityMeta(rowData.metadata);
return {
icon: () => <Edit fontSize="small" />,
Expand Down
77 changes: 77 additions & 0 deletions plugins/catalog/src/components/createEditLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { LocationSpec } from '@backstage/catalog-model';
import gitUrlParse from 'git-url-parse';

/**
* Creates the edit link for components yaml file
* @see LocationSpec
* @param location The LocationSpec being used to determine entity SCM location
* @returns string representing the edit location based on SCM path
*/

export const createEditLink = (location: LocationSpec): string | undefined => {
try {
const urlData = gitUrlParse(location.target);
const url = new URL(location.target);
switch (location.type) {
case 'github':
case 'gitlab':
return location.target.replace('/blob/', '/edit/');
case 'bitbucket':
url.searchParams.set('mode', 'edit');
url.searchParams.set('spa', '0');
url.searchParams.set('at', urlData.ref);
return url.toString();
case 'url':
if (
urlData.source === 'github.com' ||
urlData.source === 'gitlab.com/'
) {
return location.target.replace('/blob/', '/edit/');
} else if (urlData.source === 'bitbucket.org') {
url.searchParams.set('mode', 'edit');
url.searchParams.set('spa', '0');
url.searchParams.set('at', urlData.ref);
return url.toString();
}
return location.target;
default:
return location.target;
}
} catch {
return undefined;
}
};

/**
* Determines type based on passed in url. This is used to set the icon associated with the type of entity
* @param url
* @returns string representing type of icon to be used
*/
export const determineUrlType = (url: string): string => {
const urlData = gitUrlParse(url);

if (urlData.source === 'github.com') {
return 'github';
} else if (urlData.source === 'bitbucket.org') {
return 'bitbucket';
} else if (urlData.source === 'gitlab.com') {
return 'gitlab';
}
return 'url';
};

0 comments on commit 2d0bd1b

Please sign in to comment.