Skip to content

Commit

Permalink
🐛 Render dependency versions as links (#1540)
Browse files Browse the repository at this point in the history
Resolves: #1340

On the Dependencies page, detail drawer, applications table: render the
version text as a link to the maven central repository. The link uses
the dependency's sha as the key in the maven central search.

Add component `ExternalLink` to standardize rendering links outside of
the app opening in a new tab.

Note: The resolution to #1338 will require a change to the application
table's query filter and response object. This will be done in PR #1536.
See konveyor/tackle2-hub#557 for the response
object change.

Screenshot:

![image](https://github.com/konveyor/tackle2-ui/assets/3985964/50f0c144-b4fe-4ba2-a625-3c8e6ae3be6c)

---------

Signed-off-by: Scott J Dickerson <sdickers@redhat.com>
Co-authored-by: Ian Bolton <ibolton@redhat.com>
  • Loading branch information
sjd78 and ibolton336 authored Nov 15, 2023
1 parent 64337c1 commit 2500fbc
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 2 deletions.
26 changes: 26 additions & 0 deletions client/src/app/components/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from "react";
import { Flex, FlexItem, Icon, Text } from "@patternfly/react-core";
import ExternalLinkAltIcon from "@patternfly/react-icons/dist/esm/icons/external-link-alt-icon";

/**
* Render a link open an external href in another tab with appropriate styling.
*/
export const ExternalLink: React.FC<{
href: string;
children: React.ReactNode;
}> = ({ href, children }) => (
<Flex spaceItems={{ default: "spaceItemsSm" }}>
<FlexItem>
<Text component="a" href={href} target="_blank">
{children}
</Text>
</FlexItem>
<FlexItem>
<Icon size="sm" status="info">
<ExternalLinkAltIcon />
</Icon>
</FlexItem>
</Flex>
);

export default ExternalLink;
31 changes: 29 additions & 2 deletions client/src/app/pages/dependencies/dependency-apps-table.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import {
Text,
TextContent,
Toolbar,
ToolbarContent,
Expand All @@ -21,12 +22,14 @@ import {
TableHeaderContentWithControls,
TableRowContentWithControls,
} from "@app/components/TableControls";
import { ExternalLink } from "@app/components/ExternalLink";
import { SimplePagination } from "@app/components/SimplePagination";
import { FilterToolbar, FilterType } from "@app/components/FilterToolbar";
import { useFetchAppDependencies } from "@app/queries/dependencies";
import { useFetchBusinessServices } from "@app/queries/businessservices";
import { useFetchTagsWithTagItems } from "@app/queries/tags";
import { getParsedLabel } from "@app/utils/rules-utils";
import { extractFirstSha } from "@app/utils/utils";

export interface IDependencyAppsTableProps {
dependency: AnalysisDependency;
Expand Down Expand Up @@ -184,7 +187,7 @@ export const DependencyAppsTable: React.FC<IDependencyAppsTableProps> = ({
<Tbody>
{currentPageAppDependencies?.map((appDependency, rowIndex) => (
<Tr
key={appDependency.name}
key={appDependency.id}
{...getTrProps({ item: appDependency })}
>
<TableRowContentWithControls
Expand All @@ -200,7 +203,7 @@ export const DependencyAppsTable: React.FC<IDependencyAppsTableProps> = ({
modifier="nowrap"
{...getTdProps({ columnKey: "version" })}
>
{appDependency.dependency.version}
<DependencyVersionColumn appDependency={appDependency} />
</Td>
<Td
width={20}
Expand Down Expand Up @@ -248,3 +251,27 @@ const DependencyManagementColumn = ({

return <TextContent>{isJavaDependency ? "Managed" : "Embedded"}</TextContent>;
};

const DependencyVersionColumn = ({
appDependency: {
dependency: { provider, name, version, sha },
},
}: {
appDependency: AnalysisAppDependency;
}) => {
const isJavaDependency = name && version && sha && provider === "java";

const mavenCentralLink = isJavaDependency
? `https://search.maven.org/search?q=1:${extractFirstSha(sha)}`
: undefined;

return (
<TextContent>
{mavenCentralLink ? (
<ExternalLink href={mavenCentralLink}>{version}</ExternalLink>
) : (
<Text>{version}</Text>
)}
</TextContent>
);
};
45 changes: 45 additions & 0 deletions client/src/app/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
gitUrlRegex,
standardURLRegex,
formatPath,
extractFirstSha,
} from "./utils";
import { Paths } from "@app/Paths";

Expand Down Expand Up @@ -157,6 +158,7 @@ describe("utils", () => {
expect(standardURLRegex.test(url)).toBe(true);
});
});

describe("formatPath function", () => {
it("should replace path parameters with values", () => {
const path = Paths.applicationsImportsDetails;
Expand All @@ -174,3 +176,46 @@ describe("formatPath function", () => {
expect(result).toBe("/applications/assessment/:assessmentId");
});
});

describe("SHA extraction", () => {
it("empty string is undefined", () => {
const first = extractFirstSha("");
expect(first).toBeUndefined();
});

it("no SHA is undefined", () => {
const first = extractFirstSha(
"The quick brown fox jumps over the lazy dog."
);
expect(first).toBeUndefined();
});

it("a SHA is found", () => {
const tests = [
"83cd2cd674a217ade95a4bb83a8a14f351f48bd0",
" 83cd2cd674a217ade95a4bb83a8a14f351f48bd0 ",
"83cd2cd674a217ade95a4bb83a8a14f351f48bd0 The quick brown fox jumps over the lazy dog.",
"The quick brown fox jumps over the lazy dog. 83cd2cd674a217ade95a4bb83a8a14f351f48bd0",
"The quick brown fox 83cd2cd674a217ade95a4bb83a8a14f351f48bd0 jumps over the lazy dog.",
];

for (const test of tests) {
const first = extractFirstSha(test);
expect(first).toBe("83cd2cd674a217ade95a4bb83a8a14f351f48bd0");
}
});

it("multiple SHAs are in the string, only the first is returned", () => {
const first = extractFirstSha(
"83cd2cd674a217ade95a4bb83a8a14f351f48bd0 9c04cd6372077e9b11f70ca111c9807dc7137e4b"
);
expect(first).toBe("83cd2cd674a217ade95a4bb83a8a14f351f48bd0");
});

it("multiple SHAs are in the string, only the first is returned even if it is shorter", () => {
const first = extractFirstSha(
"9c04cd6372077e9b11f70ca111c9807dc7137e4b 83cd2cd674a217ade95a4bb83a8a14f351f48bd0 b47cc0f104b62d4c7c30bcd68fd8e67613e287dc4ad8c310ef10cbadea9c4380"
);
expect(first).toBe("9c04cd6372077e9b11f70ca111c9807dc7137e4b");
});
});
17 changes: 17 additions & 0 deletions client/src/app/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,20 @@ export const formatPath = (path: Paths, data: any) => {

return url;
};

/**
* Regular expression to match a SHA hash in a string. Different versions of the SHA
* hash have different lengths. Check in descending length order so the longest SHA
* string can be captured.
*/
const SHA_REGEX =
/([a-f0-9]{128}|[a-f0-9]{96}|[a-f0-9]{64}|[a-f0-9]{56}|[a-f0-9]{40})/g;

/**
* In any given string, find the first thing that looks like a sha hash and return it.
* If nothing looks like a sha hash, return undefined.
*/
export const extractFirstSha = (str: string): string | undefined => {
const match = str.match(SHA_REGEX);
return match && match[0] ? match[0] : undefined;
};

0 comments on commit 2500fbc

Please sign in to comment.