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

BigQuery nested field UI #5175

Merged
merged 21 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
16 changes: 14 additions & 2 deletions clients/admin-ui/cypress/e2e/discovery-detection.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,20 @@ describe("discovery and detection", () => {
cy.visit(
`${DATA_DETECTION_ROUTE}/my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20`,
);
cy.wait("@getDetectionFields");
});

it("should show columns for fields", () => {
cy.getByTestId("fidesTable-body").should("exist");
cy.getByTestId("column-name").should("contain", "Field name");
});

it("should not allow navigation via row clicking", () => {
it("should allow navigation via row clicking on nested fields", () => {
cy.getByTestId("row-my_bigquery_monitor-address").click();
cy.url().should("contain", "address");
});

it("should not allow navigation via row clicking on non-nested fields", () => {
cy.getByTestId("row-my_bigquery_monitor-User_geography").click();
cy.url().should("not.contain", "User_geography");
});
Expand Down Expand Up @@ -351,7 +358,12 @@ describe("discovery and detection", () => {
});
});

it("should not allow navigation via row clicking", () => {
it("should allow navigation via row clicking on nested fields", () => {
cy.getByTestId("row-my_bigquery_monitor-address").click();
cy.url().should("contain", "address");
});

it("should not allow navigation via row clicking on non-nested fields", () => {
cy.getByTestId(
"row-my_bigquery_monitor-User_geography-col-name",
).click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,31 @@
"data_type": "string",
"schema_name": "test_dataset_1",
"database_name": "prj-bigquery-418515"
},
{
"urn": "my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address",
"user_assigned_data_categories": [],
"name": "address",
"description": null,
"monitor_config_id": "my_bigquery_monitor",
"updated_at": null,
"source_modified": null,
"classifications": [],
"diff_status": "addition",
"child_diff_statuses": {},
"table_name": "consent-reports-20",
"parent_table_urn": "my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20",
"data_type": "string",
"schema_name": "test_dataset_1",
"database_name": "prj-bigquery-418515",
"sub_field_urns": [
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.City",
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.State",
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.Zip"
]
}
],
"total": 9,
"total": 10,
"page": 1,
"size": 25,
"pages": 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,35 @@
"data_type": "string",
"schema_name": "test_dataset_1",
"database_name": "prj-bigquery-418515"
},
{
"urn": "my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address",
"user_assigned_data_categories": [],
"name": "address",
"description": null,
"monitor_config_id": "my_bigquery_monitor",
"updated_at": null,
"source_modified": null,
"classifications": [
{
"label": "system",
"score": 1.0,
"aggregated_score": 0.55,
"classification_paradigm": "context"
}
],
"diff_status": "addition",
"child_diff_statuses": {},
"table_name": "consent-reports-20",
"parent_table_urn": "my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20",
"schema_name": "test_dataset_1",
"database_name": "prj-bigquery-418515",
"sub_field_urns": [
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.city",
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.country",
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.postal_code",
"my_bigquery_monitor.prj-bigquery-418515.test_dataset_1.consent-reports-20.address.state"
]
}
],
"total": 9,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createIcon } from "fidesui";

export const DatabaseIcon = createIcon({
displayName: "DatabaseIcon",
viewBox: "0 0 12 12",
path: (
<path
fill="currentColor"
d="M6 12C4.32222 12 2.90278 11.7417 1.74167 11.225C0.580556 10.7083 0 10.0778 0 9.33333V2.66667C0 1.93333 0.586111 1.30556 1.75833 0.783333C2.93056 0.261111 4.34444 0 6 0C7.65556 0 9.06944 0.261111 10.2417 0.783333C11.4139 1.30556 12 1.93333 12 2.66667V9.33333C12 10.0778 11.4194 10.7083 10.2583 11.225C9.09722 11.7417 7.67778 12 6 12ZM6 4.01667C6.98889 4.01667 7.98333 3.875 8.98333 3.59167C9.98333 3.30833 10.5444 3.00556 10.6667 2.68333C10.5444 2.36111 9.98611 2.05556 8.99167 1.76667C7.99722 1.47778 7 1.33333 6 1.33333C4.98889 1.33333 3.99722 1.475 3.025 1.75833C2.05278 2.04167 1.48889 2.35 1.33333 2.68333C1.48889 3.01667 2.05278 3.32222 3.025 3.6C3.99722 3.87778 4.98889 4.01667 6 4.01667ZM6 7.33333C6.46667 7.33333 6.91667 7.31111 7.35 7.26667C7.78333 7.22222 8.19722 7.15833 8.59167 7.075C8.98611 6.99167 9.35833 6.88889 9.70833 6.76667C10.0583 6.64444 10.3778 6.50556 10.6667 6.35V4.35C10.3778 4.50556 10.0583 4.64444 9.70833 4.76667C9.35833 4.88889 8.98611 4.99167 8.59167 5.075C8.19722 5.15833 7.78333 5.22222 7.35 5.26667C6.91667 5.31111 6.46667 5.33333 6 5.33333C5.53333 5.33333 5.07778 5.31111 4.63333 5.26667C4.18889 5.22222 3.76944 5.15833 3.375 5.075C2.98056 4.99167 2.61111 4.88889 2.26667 4.76667C1.92222 4.64444 1.61111 4.50556 1.33333 4.35V6.35C1.61111 6.50556 1.92222 6.64444 2.26667 6.76667C2.61111 6.88889 2.98056 6.99167 3.375 7.075C3.76944 7.15833 4.18889 7.22222 4.63333 7.26667C5.07778 7.31111 5.53333 7.33333 6 7.33333ZM6 10.6667C6.51111 10.6667 7.03056 10.6278 7.55833 10.55C8.08611 10.4722 8.57222 10.3694 9.01667 10.2417C9.46111 10.1139 9.83333 9.96945 10.1333 9.80833C10.4333 9.64722 10.6111 9.48333 10.6667 9.31667V7.68333C10.3778 7.83889 10.0583 7.97778 9.70833 8.1C9.35833 8.22222 8.98611 8.325 8.59167 8.40833C8.19722 8.49167 7.78333 8.55556 7.35 8.6C6.91667 8.64444 6.46667 8.66667 6 8.66667C5.53333 8.66667 5.07778 8.64444 4.63333 8.6C4.18889 8.55556 3.76944 8.49167 3.375 8.40833C2.98056 8.325 2.61111 8.22222 2.26667 8.1C1.92222 7.97778 1.61111 7.83889 1.33333 7.68333V9.33333C1.38889 9.5 1.56389 9.66111 1.85833 9.81667C2.15278 9.97222 2.52222 10.1139 2.96667 10.2417C3.41111 10.3694 3.9 10.4722 4.43333 10.55C4.96667 10.6278 5.48889 10.6667 6 10.6667Z"
/>
),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createIcon } from "fidesui";

export const DatasetIcon = createIcon({
displayName: "DatasetIcon",
viewBox: "0 0 16 16",
path: (
<path
fill="currentColor"
d="M2 14V2H14V14H2ZM3.33333 12.6667H12.6667V3.33333H3.33333V12.6667ZM4.66667 7.33333H7.33333V4.66667H4.66667V7.33333ZM8.66667 7.33333H11.3333V4.66667H8.66667V7.33333ZM4.66667 11.3333H7.33333V8.66667H4.66667V11.3333ZM8.66667 11.3333H11.3333V8.66667H8.66667V11.3333Z"
/>
),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createIcon } from "fidesui";

export const FieldIcon = createIcon({
displayName: "FieldIcon",
viewBox: "0 0 16 16",
path: (
<path
d="M12.6667 12.6667V10.6667H3.33333V12.6667H12.6667ZM12.6667 9.33333V6.66667H3.33333V9.33333H12.6667ZM12.6667 5.33333V3.33333H3.33333V5.33333H12.6667ZM3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667V3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13056 2.96667 2 3.33333 2H12.6667C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333V12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14H3.33333Z"
fill="currentColor"
/>
),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createIcon } from "fidesui";

export const TableIcon = createIcon({
displayName: "TableIcon",
viewBox: "0 0 16 16",
path: (
<path
fill="currentColor"
d="M2 14V2H14V14H2ZM3.33333 6H12.6667V3.33333H3.33333V6ZM6.88333 9.33333H9.11667V7.33333H6.88333V9.33333ZM6.88333 12.6667H9.11667V10.6667H6.88333V12.6667ZM3.33333 9.33333H5.55V7.33333H3.33333V9.33333ZM10.45 9.33333H12.6667V7.33333H10.45V9.33333ZM3.33333 12.6667H5.55V10.6667H3.33333V12.6667ZM10.45 12.6667H12.6667V10.6667H10.45V12.6667Z"
/>
),
});
60 changes: 39 additions & 21 deletions clients/admin-ui/src/features/common/table/v2/FidesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ type Props<T> = {
rowActionBar?: ReactNode;
footer?: ReactNode;
onRowClick?: (row: T, e: React.MouseEvent<HTMLTableCellElement>) => void;
/**
* Optional function to filter whether onRowClick should be enabled based on
* the row data. If not provided, onRowClick will be enabled for all rows.
*/
getRowIsClickable?: (row: T) => boolean;
renderRowTooltipLabel?: (row: Row<T>) => string | undefined;
emptyTableNotice?: ReactNode;
overflow?: "auto" | "visible" | "hidden";
Expand All @@ -199,32 +204,42 @@ const TableBody = <T,>({
tableInstance,
rowActionBar,
onRowClick,
getRowIsClickable,
renderRowTooltipLabel,
displayAllColumns,
emptyTableNotice,
}: Omit<Props<T>, "footer" | "enableSorting" | "onSort"> & {
displayAllColumns: string[];
}) => (
<Tbody data-testid="fidesTable-body">
{rowActionBar}
{tableInstance.getRowModel().rows.map((row) => (
<FidesRow<T>
key={row.id}
row={row}
onRowClick={onRowClick}
renderRowTooltipLabel={renderRowTooltipLabel}
displayAllColumns={displayAllColumns}
/>
))}
{tableInstance.getRowModel().rows.length === 0 &&
!tableInstance.getState()?.globalFilter &&
emptyTableNotice && (
<Tr>
<Td colSpan={100}>{emptyTableNotice}</Td>
</Tr>
)}
</Tbody>
);
}) => {
const getRowClickHandler = (row: T) => {
if (!getRowIsClickable) {
return onRowClick;
}
return getRowIsClickable(row) ? onRowClick : undefined;
};

return (
<Tbody data-testid="fidesTable-body">
{rowActionBar}
{tableInstance.getRowModel().rows.map((row) => (
<FidesRow<T>
key={row.id}
row={row}
onRowClick={getRowClickHandler(row.original)}
renderRowTooltipLabel={renderRowTooltipLabel}
displayAllColumns={displayAllColumns}
/>
))}
{tableInstance.getRowModel().rows.length === 0 &&
!tableInstance.getState()?.globalFilter &&
emptyTableNotice && (
<Tr>
<Td colSpan={100}>{emptyTableNotice}</Td>
</Tr>
)}
</Tbody>
);
};

const MemoizedTableBody = React.memo(
TableBody,
Expand All @@ -244,6 +259,7 @@ export const FidesTableV2 = <T,>({
rowActionBar,
footer,
onRowClick,
getRowIsClickable,
renderRowTooltipLabel,
emptyTableNotice,
overflow = "auto",
Expand Down Expand Up @@ -366,6 +382,7 @@ export const FidesTableV2 = <T,>({
tableInstance={tableInstance}
rowActionBar={rowActionBar}
onRowClick={onRowClick}
getRowIsClickable={getRowIsClickable}
renderRowTooltipLabel={renderRowTooltipLabel}
displayAllColumns={displayAllColumns}
emptyTableNotice={emptyTableNotice}
Expand All @@ -375,6 +392,7 @@ export const FidesTableV2 = <T,>({
tableInstance={tableInstance}
rowActionBar={rowActionBar}
onRowClick={onRowClick}
getRowIsClickable={getRowIsClickable}
renderRowTooltipLabel={renderRowTooltipLabel}
displayAllColumns={displayAllColumns}
emptyTableNotice={emptyTableNotice}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ButtonSpinner, CheckIcon, HStack, ViewOffIcon } from "fidesui";
import { useState } from "react";

import { TOP_LEVEL_FIELD_URN_LENGTH } from "~/features/data-discovery-and-detection/utils/isNestedField";
import { DiffStatus, StagedResource } from "~/types/api";

import ActionButton from "./ActionButton";
Expand All @@ -22,11 +23,16 @@ const DiscoveryItemActions = ({ resource }: DiscoveryItemActionsProps) => {

const [isProcessingAction, setIsProcessingAction] = useState(false);

const { diff_status: diffStatus, child_diff_statuses: childDiffStatus } =
resource;
const {
diff_status: diffStatus,
child_diff_statuses: childDiffStatus,
urn,
} = resource;

// No actions for database level
if (resourceType === StagedResourceType.DATABASE) {
const isSubField = urn.split(".").length > TOP_LEVEL_FIELD_URN_LENGTH;

// No actions for database level or for nested field subfields
if (resourceType === StagedResourceType.DATABASE || isSubField) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,73 @@
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "fidesui";
import NextLink from "next/link";
import { useRouter } from "next/router";

import { DatabaseIcon } from "~/features/common/Icon/detection-discovery-resource-types/DatabaseIcon";
import { DatasetIcon } from "~/features/common/Icon/detection-discovery-resource-types/DatasetIcon";
import { FieldIcon } from "~/features/common/Icon/detection-discovery-resource-types/FieldIcon";
import { TableIcon } from "~/features/common/Icon/detection-discovery-resource-types/TableIcon";

interface DiscoveryMonitorBreadcrumbsProps {
resourceUrn?: string;
parentTitle: string;
parentLink: string;
onPathClick?: (urn: string) => void;
}

const MONITOR_BREADCRUMB_ICONS = [
<DatabaseIcon key="database" />,
<DatasetIcon key="dataset" boxSize={6} />,
<TableIcon key="table" boxSize={6} />,
<FieldIcon key="field" boxSize={6} />,
];

const DiscoveryMonitorBreadcrumbs = ({
resourceUrn,
parentTitle,
parentLink,
onPathClick = () => {},
}: DiscoveryMonitorBreadcrumbsProps) => {
const urnParts = resourceUrn ? resourceUrn.split(".") : [];

return (
<Breadcrumb
separator="->"
fontSize="2xl"
fontWeight="semibold"
mb={5}
data-testid="results-breadcrumb"
>
<BreadcrumbItem color={resourceUrn ? "gray.500" : "black"}>
<BreadcrumbLink as={NextLink} href={parentLink}>
{parentTitle}
</BreadcrumbLink>
</BreadcrumbItem>
const router = useRouter();

{!resourceUrn ? (
<BreadcrumbItem color={resourceUrn ? "gray.500" : "black"}>
if (!resourceUrn) {
return (
<Breadcrumb
separator="/"
fontWeight={800}
mb={5}
data-testid="results-breadcrumb"
>
<BreadcrumbItem>
<BreadcrumbLink>All activity</BreadcrumbLink>
</BreadcrumbItem>
) : null}
</Breadcrumb>
);
}

const urnParts = resourceUrn.split(".");

return (
<Breadcrumb separator="/" mb={5} data-testid="results-breadcrumb">
{urnParts.map((urnPart, index) => {
// don't display the first or second parts of the URN (monitor ID or
// database) because there's no table view for them
if (index === 0 || index === 1) {
// don't render anything at the monitor level because there's no view for it
if (index === 0) {
return null;
}

// at the database level, link should go to "all activity" view
const isDatabase = index === 1;
const isLastPart = index === urnParts.length - 1;

return (
<BreadcrumbItem
key={urnPart}
color={isLastPart ? "black" : "gray.500"}
fontWeight={isLastPart ? "semibold" : "normal"}
color={isLastPart ? "gray.800" : "gray.500"}
>
{MONITOR_BREADCRUMB_ICONS[index - 1]}
<BreadcrumbLink
ml={1}
onClick={() =>
onPathClick(urnParts.slice(0, index + 1).join("."))
isDatabase
? router.push(parentLink)
: onPathClick(urnParts.slice(0, index + 1).join("."))
}
>
{urnPart}
Expand All @@ -63,4 +78,5 @@ const DiscoveryMonitorBreadcrumbs = ({
</Breadcrumb>
);
};

export default DiscoveryMonitorBreadcrumbs;
Loading
Loading