Skip to content

Commit 9b4237a

Browse files
authored
Action center: add consent category editing (#5739)
1 parent c9ea54c commit 9b4237a

File tree

11 files changed

+357
-20
lines changed

11 files changed

+357
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Changes can also be flagged with a GitHub label for tracking purposes. The URL o
2222
## [Unreleased](https://github.com/ethyca/fides/compare/2.54.0...main)
2323

2424
### Added
25+
- Added editing support for categories of consent on discovered assets [#5739](https://github.com/ethyca/fides/pull/5739)
2526
- Added a read-only consent category cell to Action Center aggregate system results table [#5737](https://github.com/ethyca/fides/pull/5737)
2627
- Added detail trays to items in data catalog view [#5729](https://github.com/ethyca/fides/pull/5729)
2728

clients/admin-ui/cypress/e2e/action-center.cy.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
stubActionCenter,
33
stubPlus,
44
stubSystemVendors,
5+
stubTaxonomyEntities,
56
stubVendorList,
67
} from "cypress/support/stubs";
78

@@ -275,8 +276,7 @@ describe("Action center", () => {
275276
cy.getByTestId("column-name").should("exist");
276277
cy.getByTestId("column-resource_type").should("exist");
277278
cy.getByTestId("column-system").should("exist");
278-
// TODO: [HJ-369] uncomment when data use column is implemented
279-
// cy.getByTestId("column-data_use").should("exist");
279+
cy.getByTestId("column-data_use").should("exist");
280280
cy.getByTestId("column-locations").should("exist");
281281
cy.getByTestId("column-domain").should("exist");
282282
// TODO: [HJ-344] uncomment when Discovery column is implemented
@@ -297,7 +297,7 @@ describe("Action center", () => {
297297
});
298298
cy.wait("@getSystemsPaginated");
299299
cy.getByTestId("system-select").antSelect("Fidesctl System");
300-
cy.wait("@setAssetSystem");
300+
cy.wait("@patchAssets");
301301
cy.getByTestId("system-select").should("not.exist");
302302
cy.getByTestId("success-alert").should(
303303
"contain",
@@ -326,8 +326,7 @@ describe("Action center", () => {
326326
cy.getByTestId("column-name").should("exist");
327327
cy.getByTestId("column-resource_type").should("exist");
328328
cy.getByTestId("column-system").should("exist");
329-
// TODO: [HJ-369] uncomment when data use column is implemented
330-
// cy.getByTestId("column-data_use").should("exist");
329+
cy.getByTestId("column-data_use").should("exist");
331330
cy.getByTestId("column-locations").should("exist");
332331
cy.getByTestId("column-domain").should("exist");
333332
// TODO: [HJ-344] uncomment when Discovery column is implemented
@@ -348,7 +347,7 @@ describe("Action center", () => {
348347
});
349348
cy.wait("@getSystemsPaginated");
350349
cy.getByTestId("system-select").antSelect("Fidesctl System");
351-
cy.wait("@setAssetSystem");
350+
cy.wait("@patchAssets");
352351
cy.getByTestId("system-select").should("not.exist");
353352
cy.getByTestId("success-alert").should(
354353
"contain",
@@ -369,7 +368,7 @@ describe("Action center", () => {
369368
});
370369
cy.wait("@getSystemsPaginated");
371370
cy.getByTestId("system-select").antSelect("Demo Marketing System");
372-
cy.wait("@setAssetSystem");
371+
cy.wait("@patchAssets");
373372
cy.getByTestId("success-alert").should("exist");
374373
cy.getByTestId("system-select").should("not.exist");
375374
cy.getByTestId("success-alert").should(
@@ -391,7 +390,7 @@ describe("Action center", () => {
391390
// adds new system
392391
cy.wait("@postSystemVendors");
393392
// assigns asset to new system
394-
cy.wait("@setAssetSystem");
393+
cy.wait("@patchAssets");
395394
cy.getByTestId("success-alert").should(
396395
"contain",
397396
'Test System has been added to your system inventory and the Browser Request "gtm.js" has been assigned to that system.',
@@ -466,6 +465,7 @@ describe("Action center", () => {
466465
"11 assets from Google Tag Manager have been added to the system inventory.",
467466
);
468467
});
468+
469469
it("should bulk assign assets to a system", () => {
470470
cy.getByTestId("bulk-actions-menu").should("be.disabled");
471471
cy.getByTestId("row-0-col-select").find("label").click();
@@ -478,11 +478,30 @@ describe("Action center", () => {
478478
cy.getByTestId("add-modal-content").should("be.visible");
479479
cy.getByTestId("system-select").antSelect("Fidesctl System");
480480
cy.getByTestId("save-btn").click();
481-
cy.wait("@setAssetSystem");
481+
cy.wait("@patchAssets");
482482
cy.getByTestId("success-alert").should(
483483
"contain",
484484
"3 assets have been assigned to Fidesctl System.",
485485
);
486486
});
487+
488+
it("should bulk add data uses to assets", () => {
489+
stubTaxonomyEntities();
490+
cy.getByTestId("bulk-actions-menu").should("be.disabled");
491+
cy.getByTestId("row-0-col-select").find("label").click();
492+
cy.getByTestId("row-2-col-select").find("label").click();
493+
cy.getByTestId("row-3-col-select").find("label").click();
494+
cy.getByTestId("selected-count").should("contain", "3 selected");
495+
cy.getByTestId("bulk-actions-menu").should("not.be.disabled");
496+
cy.getByTestId("bulk-actions-menu").click();
497+
cy.getByTestId("bulk-add-data-use").click();
498+
cy.getByTestId("taxonomy-select").antSelect("essential");
499+
cy.getByTestId("save-btn").click({ force: true });
500+
cy.wait("@patchAssets");
501+
cy.getByTestId("success-alert").should(
502+
"contain",
503+
"Consent categories added to 3 assets from Google Tag Manager.",
504+
);
505+
});
487506
});
488507
});

clients/admin-ui/cypress/support/stubs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ export const stubActionCenter = () => {
556556
}).as("addMonitorResultSystem");
557557
cy.intercept("PATCH", "/api/v1/plus/discovery-monitor/*/results", {
558558
response: 200,
559-
}).as("setAssetSystem");
559+
}).as("patchAssets");
560560
};
561561

562562
export const stubDataCatalog = () => {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
AntButton as Button,
3+
AntFlex as Flex,
4+
AntTypography as Typography,
5+
ModalProps,
6+
} from "fidesui";
7+
import { useState } from "react";
8+
9+
import FormModal from "~/features/common/modals/FormModal";
10+
import ConsentCategorySelect from "~/features/data-discovery-and-detection/action-center/ConsentCategorySelect";
11+
12+
const { Text } = Typography;
13+
14+
interface AddDataUsesModalProps extends Omit<ModalProps, "children"> {
15+
onSave: (dataUses: string[]) => void;
16+
isSaving?: boolean;
17+
}
18+
19+
const AddDataUsesModal = ({
20+
onSave,
21+
isSaving,
22+
onClose,
23+
...props
24+
}: AddDataUsesModalProps) => {
25+
const [selectedDataUses, setSelectedDataUses] = useState<string[]>([]);
26+
27+
const handleReset = () => {
28+
setSelectedDataUses([]);
29+
onClose();
30+
};
31+
32+
return (
33+
<FormModal title="Add consent category" {...props} onClose={handleReset}>
34+
<Flex vertical gap={20} className="pb-6 pt-4">
35+
<Text>
36+
Assign consent categories to selected assets. This configures the
37+
system for consent management. Consent categories apply to both
38+
individual assets and the system.
39+
</Text>
40+
<ConsentCategorySelect
41+
mode="tags"
42+
selectedTaxonomies={selectedDataUses}
43+
onSelect={(_, option) =>
44+
setSelectedDataUses([...selectedDataUses, option.value])
45+
}
46+
variant="outlined"
47+
/>
48+
</Flex>
49+
<Flex justify="space-between">
50+
<Button htmlType="reset" onClick={handleReset} data-testid="cancel-btn">
51+
Cancel
52+
</Button>
53+
<Button
54+
htmlType="submit"
55+
type="primary"
56+
disabled={!selectedDataUses.length}
57+
loading={isSaving}
58+
onClick={() => onSave(selectedDataUses)}
59+
data-testid="save-btn"
60+
>
61+
Save
62+
</Button>
63+
</Flex>
64+
</FormModal>
65+
);
66+
};
67+
68+
export default AddDataUsesModal;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
TaxonomySelect,
3+
TaxonomySelectOption,
4+
TaxonomySelectProps,
5+
} from "~/features/common/dropdown/TaxonomySelect";
6+
import useTaxonomies from "~/features/common/hooks/useTaxonomies";
7+
import { CONSENT_CATEGORIES } from "~/features/data-discovery-and-detection/action-center/utils/isConsentCategory";
8+
9+
const ConsentCategorySelect = ({
10+
selectedTaxonomies,
11+
...props
12+
}: TaxonomySelectProps) => {
13+
const { getDataUseDisplayNameProps, getDataUses } = useTaxonomies();
14+
const consentCategories = getDataUses().filter(
15+
(use) => use.active && CONSENT_CATEGORIES.includes(use.fides_key),
16+
);
17+
18+
const options: TaxonomySelectOption[] = consentCategories
19+
.filter((c) => !selectedTaxonomies.includes(c.fides_key))
20+
.map((consentCategory) => {
21+
const { name, primaryName } = getDataUseDisplayNameProps(
22+
consentCategory.fides_key,
23+
);
24+
return {
25+
value: consentCategory.fides_key,
26+
name,
27+
primaryName,
28+
description: consentCategory.description || "",
29+
};
30+
});
31+
return <TaxonomySelect options={options} {...props} />;
32+
};
33+
34+
export default ConsentCategorySelect;

clients/admin-ui/src/features/data-discovery-and-detection/action-center/action-center.slice.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { baseApi } from "~/features/common/api.slice";
22
import { getQueryParamsFromArray } from "~/features/common/utils";
3-
import { Page_StagedResourceAPIResponse_ } from "~/types/api";
43
import { PaginationQueryParams } from "~/types/common/PaginationQueryParams";
54

65
import {
6+
DiscoveredAssetPaginatedResponse,
7+
DiscoveredAssetResponse,
78
MonitorSummaryPaginatedResponse,
89
MonitorSystemAggregatePaginatedResponse,
910
} from "./types";
@@ -47,7 +48,7 @@ const actionCenterApi = baseApi.injectEndpoints({
4748
providesTags: ["Discovery Monitor Results"],
4849
}),
4950
getDiscoveredAssets: build.query<
50-
Page_StagedResourceAPIResponse_,
51+
DiscoveredAssetPaginatedResponse,
5152
{ key: string; system: string; search: string } & PaginationQueryParams
5253
>({
5354
query: ({ key, system, page = 1, size = 20, search }) => ({
@@ -138,6 +139,35 @@ const actionCenterApi = baseApi.injectEndpoints({
138139
}),
139140
invalidatesTags: ["Discovery Monitor Results"],
140141
}),
142+
updateAssetsDataUse: build.mutation<
143+
any,
144+
{ monitorId: string; urnList: string[]; dataUses: string[] }
145+
>({
146+
query: (params) => ({
147+
method: "PATCH",
148+
url: `/plus/discovery-monitor/${params.monitorId}/results`,
149+
body: params.urnList.map((urn) => ({
150+
urn,
151+
data_uses: params.dataUses,
152+
})),
153+
}),
154+
invalidatesTags: ["Discovery Monitor Results"],
155+
}),
156+
// generic update assets mutation, necessary for non-destructive bulk data use updates
157+
updateAssets: build.mutation<
158+
any,
159+
{
160+
monitorId: string;
161+
assets: DiscoveredAssetResponse[];
162+
}
163+
>({
164+
query: (params) => ({
165+
method: "PATCH",
166+
url: `/plus/discovery-monitor/${params.monitorId}/results`,
167+
body: params.assets,
168+
}),
169+
invalidatesTags: ["Discovery Monitor Results"],
170+
}),
141171
}),
142172
});
143173

@@ -150,4 +180,6 @@ export const {
150180
useAddMonitorResultAssetsMutation,
151181
useIgnoreMonitorResultAssetsMutation,
152182
useUpdateAssetsSystemMutation,
183+
useUpdateAssetsDataUseMutation,
184+
useUpdateAssetsMutation,
153185
} = actionCenterApi;

clients/admin-ui/src/features/data-discovery-and-detection/action-center/hooks/useDiscoveredAssetsColumns.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import {
44
DefaultCell,
55
IndeterminateCheckboxCell,
66
} from "~/features/common/table/v2";
7-
import { StagedResourceAPIResponse } from "~/types/api";
7+
import DiscoveredAssetDataUseCell from "~/features/data-discovery-and-detection/action-center/tables/cells/DiscoveredAssetDataUseCell";
8+
import { DiscoveredAssetResponse } from "~/features/data-discovery-and-detection/action-center/types";
89

910
import { DiscoveredAssetActionsCell } from "../tables/cells/DiscoveredAssetActionsCell";
1011
import { SystemCell } from "../tables/cells/SystemCell";
1112

1213
export const useDiscoveredAssetsColumns = () => {
13-
const columnHelper = createColumnHelper<StagedResourceAPIResponse>();
14+
const columnHelper = createColumnHelper<DiscoveredAssetResponse>();
1415

15-
const columns: ColumnDef<StagedResourceAPIResponse, any>[] = [
16+
const columns: ColumnDef<DiscoveredAssetResponse, any>[] = [
1617
columnHelper.display({
1718
id: "select",
1819
cell: ({ row }) => (
@@ -57,15 +58,17 @@ export const useDiscoveredAssetsColumns = () => {
5758
width: "auto",
5859
},
5960
}),
60-
/*
61-
// TODO: [HJ-369] uncomment when monitor supports categories of consent
6261
columnHelper.display({
6362
id: "data_use",
63+
cell: (props) => (
64+
<DiscoveredAssetDataUseCell asset={props.row.original} />
65+
),
6466
header: "Categories of consent",
6567
meta: {
6668
width: "auto",
69+
disableRowClick: true,
6770
},
68-
}), */
71+
}),
6972
columnHelper.accessor((row) => row.locations, {
7073
id: "locations",
7174
cell: (props) => (

0 commit comments

Comments
 (0)