diff --git a/CHANGELOG.md b/CHANGELOG.md index 3314054151..2a8744a44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ The types of changes are: - Serve GVL languages as they are requested [#5112](https://github.com/ethyca/fides/pull/5112) - Changed text on system integrations tab to direct to new integration management [#5097](https://github.com/ethyca/fides/pull/5097) - Updates to consent experience styling [#5085](https://github.com/ethyca/fides/pull/5085) +- Improve performance by removing the need to load every system into redux store [#5135](https://github.com/ethyca/fides/pull/5135) - Use the `user_id` from a Segment Trait instead of an `email` when deleting a user in Segment [#5004](https://github.com/ethyca/fides/pull/5004) - Moves some endpoints for property-specific messaging from OSS -> plus [#5069](https://github.com/ethyca/fides/pull/5069) - Text changes in monitor config table and form [#5142](https://github.com/ethyca/fides/pull/5142) diff --git a/clients/admin-ui/cypress/e2e/routes.cy.ts b/clients/admin-ui/cypress/e2e/routes.cy.ts index f0ff757d14..9c13c04c0a 100644 --- a/clients/admin-ui/cypress/e2e/routes.cy.ts +++ b/clients/admin-ui/cypress/e2e/routes.cy.ts @@ -16,7 +16,7 @@ describe("Routes", () => { describe("permissions", () => { beforeEach(() => { // For these tests, let's say we always have systems and connectors - cy.intercept("GET", "/api/v1/system", { + cy.intercept("GET", "/api/v1/system*", { fixture: "systems/systems.json", }).as("getSystems"); cy.intercept("GET", "/api/v1/connection*", { @@ -29,7 +29,6 @@ describe("Routes", () => { cy.assumeRole(RoleRegistryEnum.OWNER); cy.visit("/"); cy.visit(ADD_SYSTEMS_ROUTE); - cy.wait("@getSystems"); cy.getByTestId("add-systems"); cy.visit("/privacy-requests"); cy.getByTestId("privacy-requests"); diff --git a/clients/admin-ui/cypress/e2e/systems-classify.cy.ts b/clients/admin-ui/cypress/e2e/systems-classify.cy.ts deleted file mode 100644 index 6741653fe0..0000000000 --- a/clients/admin-ui/cypress/e2e/systems-classify.cy.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { stubPlus, stubTaxonomyEntities } from "cypress/support/stubs"; - -describe.skip("Classify systems page", () => { - beforeEach(() => { - cy.login(); - cy.intercept("GET", "/api/v1/system", { - fixture: "systems/systems.json", - }).as("getSystems"); - }); - - it("Should reroute if not in plus", () => { - stubPlus(false); - cy.visit("/classify-systems"); - cy.url().should("eql", `${Cypress.config().baseUrl}/`); - }); - - describe("With plus enabled", () => { - beforeEach(() => { - stubPlus(true); - stubTaxonomyEntities(); - cy.intercept("GET", "/api/v1/plus/classify*", { - fixture: "classify/list-systems.json", - }).as("getClassifyList"); - cy.intercept("GET", "/api/v1/system", { - fixture: "systems/systems.json", - }).as("getSystems"); - }); - - it("Should be accessible to plus users", () => { - cy.visit("/classify-systems"); - cy.getByTestId("systems-classify-table"); - }); - - it("Should render an empty state if no classifications are found", () => { - cy.intercept("GET", "/api/v1/plus/classify*", { - body: [], - }).as("getEmptyClassifyList"); - cy.visit("/classify-systems"); - cy.getByTestId("no-classifications"); - }); - - it("Should render a proper description based on the existence of classification and data flows", () => { - cy.intercept("GET", "/api/v1/plus/classify/details/*", { - fixture: "classify/system-details.json", - }); - cy.visit("/classify-systems"); - - // No data flows exist on the system - cy.getByTestId("row-fidesctl_system").click(); - cy.getByTestId("no-data-flows"); - cy.getByTestId("save-btn").should("exist"); - cy.getByTestId("close-drawer-btn").click(); - - // Only ingresses exist - cy.getByTestId("row-demo_analytics_system").click(); - cy.getByTestId("data-flow-with-classification").contains("receives data"); - cy.getByTestId("save-btn").should("exist"); - cy.getByTestId("close-drawer-btn").click(); - - // Only egresses exist - cy.getByTestId("row-demo_marketing_system").click(); - cy.getByTestId("data-flow-with-classification").contains("sends data"); - cy.getByTestId("save-btn").should("exist"); - cy.getByTestId("close-drawer-btn").click(); - - // No classifications determined, though classify did run - cy.visit("/classify-systems"); // visit again to clear the redux cache - cy.fixture("classify/system-details.json").then((details) => { - cy.intercept("GET", "/api/v1/plus/classify/details/*", { - body: { ...details, ingress: [], egress: [] }, - }).as("getClassifyDetailsEmpty"); - }); - cy.getByTestId("row-demo_marketing_system").click(); - cy.wait("@getClassifyDetailsEmpty"); - cy.getByTestId("no-classification"); - cy.getByTestId("save-btn").should("exist"); - cy.getByTestId("close-drawer-btn").click(); - - // Classification is still in progress - cy.visit("/classify-systems"); - cy.fixture("classify/system-details.json").then((details) => { - cy.intercept("GET", "/api/v1/plus/classify/details/*", { - body: { ...details, ingress: [], egress: [], status: "Processing" }, - }).as("getClassifyDetailsProcessing"); - }); - cy.wait("@getSystems"); - cy.getByTestId("row-demo_marketing_system").click(); - cy.wait("@getClassifyDetailsProcessing"); - cy.getByTestId("processing"); - cy.getByTestId("save-btn").should("not.exist"); - cy.getByTestId("close-drawer-btn").click(); - - // No classification ever occurred - cy.visit("/classify-systems"); - cy.intercept("GET", "/api/v1/plus/classify/details/*", { - body: {}, - }).as("getClassifyEmpty"); - cy.getByTestId("row-demo_marketing_system").click(); - cy.wait("@getClassifyEmpty"); - cy.getByTestId("no-classification-instance"); - cy.getByTestId("classification-status-badge").contains("Unknown"); - cy.getByTestId("save-btn").should("exist"); - }); - - it("Can edit a system's data flows", () => { - cy.intercept("GET", "/api/v1/plus/classify/details/*", { - fixture: "classify/system-details.json", - }); - cy.intercept("PUT", "/api/v1/plus/classify/*", { body: undefined }).as( - "putClassifyInstance" - ); - cy.intercept("PUT", "/api/v1/system*", { - fixture: "systems/system.json", - }).as("putSystem"); - cy.visit("/classify-systems"); - - // Open up an ingress - cy.getByTestId("row-demo_analytics_system").click(); - cy.getByTestId("accordion-item-ingress").click(); - cy.getByTestId("accordion-item-demo_marketing_system").click(); - // Should have the classified suggestion - cy.getByTestId("classified-select").contains("system"); - // Select a category from the taxonomy. - cy.getByTestId("data-category-dropdown").click(); - cy.getByTestId("data-category-checkbox-tree") - .contains("User Data") - .click(); - cy.getByTestId("data-category-done-btn").click(); - cy.getByTestId("classified-select").contains("user"); - - // Trigger a save - cy.getByTestId("save-btn").click(); - cy.wait("@putSystem").then((interception) => { - const { body } = interception.request; - expect(body.ingress[0].data_categories).to.eql(["system", "user"]); - }); - cy.wait("@putClassifyInstance").then((interception) => { - const { body } = interception.request; - expect(body).to.eql({ - dataset_fides_key: "demo_analytics_system", - status: "Reviewed", - }); - }); - cy.getByTestId("toast-success-msg"); - }); - }); -}); diff --git a/clients/admin-ui/cypress/e2e/user-management.cy.ts b/clients/admin-ui/cypress/e2e/user-management.cy.ts index 7b1b002a86..7a94595853 100644 --- a/clients/admin-ui/cypress/e2e/user-management.cy.ts +++ b/clients/admin-ui/cypress/e2e/user-management.cy.ts @@ -371,7 +371,6 @@ describe("User management", () => { beforeEach(() => { cy.visit(`/user-management/profile/${USER_1_ID}`); cy.getByTestId("tab-Permissions").click(); - cy.wait("@getSystems"); cy.wait("@getUserManagedSystems"); }); @@ -391,7 +390,6 @@ describe("User management", () => { beforeEach(() => { cy.visit(`/user-management/profile/${USER_1_ID}`); cy.getByTestId("tab-Permissions").click(); - cy.wait("@getSystems"); cy.wait("@getUserManagedSystems"); cy.getByTestId("assign-systems-delete-table"); }); diff --git a/clients/admin-ui/src/features/common/CommonSubscriptions.tsx b/clients/admin-ui/src/features/common/CommonSubscriptions.tsx index 36f09fdb67..443a9d9f83 100644 --- a/clients/admin-ui/src/features/common/CommonSubscriptions.tsx +++ b/clients/admin-ui/src/features/common/CommonSubscriptions.tsx @@ -1,13 +1,13 @@ import { useAppSelector } from "~/app/hooks"; import { useGetHealthQuery } from "~/features/common/health.slice"; import { useGetHealthQuery as useGetPlusHealthQuery } from "~/features/plus/plus.slice"; -import { useGetAllSystemsQuery } from "~/features/system/system.slice"; +import { useGetSystemsQuery } from "~/features/system"; import { selectThisUsersScopes } from "~/features/user-management"; const useCommonSubscriptions = () => { useGetHealthQuery(); useGetPlusHealthQuery(); - useGetAllSystemsQuery(); + useGetSystemsQuery({ page: 1, size: 1 }); // used to preload systems count on selectSystemsCount useAppSelector(selectThisUsersScopes); }; diff --git a/clients/admin-ui/src/features/common/features/features.slice.ts b/clients/admin-ui/src/features/common/features/features.slice.ts index bdecb7326b..86726417f9 100644 --- a/clients/admin-ui/src/features/common/features/features.slice.ts +++ b/clients/admin-ui/src/features/common/features/features.slice.ts @@ -6,7 +6,7 @@ import { type RootState } from "~/app/store"; import { selectHealth } from "~/features/common/health.slice"; import { selectInitialConnections } from "~/features/datastore-connections"; import { selectHealth as selectPlusHealth } from "~/features/plus/plus.slice"; -import { selectAllSystems } from "~/features/system"; +import { selectSystemsCount } from "~/features/system"; import flagDefaults from "~/flags.json"; import { configureFlags, flagsForEnv } from "./config"; @@ -146,7 +146,7 @@ export type Features = { export const useFeatures = (): Features => { const health = useAppSelector(selectHealth); const plusHealth = useAppSelector(selectPlusHealth); - const allSystems = useAppSelector(selectAllSystems); + const systemsCount = useAppSelector(selectSystemsCount); const initialConnections = useAppSelector(selectInitialConnections); const version = health?.version; @@ -163,8 +163,6 @@ export const useFeatures = (): Features => { const tcf = plusHealth ? !!plusHealth.tcf.enabled : false; - const systemsCount = allSystems?.length ?? 0; - const connectionsCount = initialConnections?.total ?? 0; const { flags } = useFlags(); diff --git a/clients/admin-ui/src/features/common/system-data-flow/DataFlowAccordionForm.tsx b/clients/admin-ui/src/features/common/system-data-flow/DataFlowAccordionForm.tsx index e129e2f711..8461e5fd22 100644 --- a/clients/admin-ui/src/features/common/system-data-flow/DataFlowAccordionForm.tsx +++ b/clients/admin-ui/src/features/common/system-data-flow/DataFlowAccordionForm.tsx @@ -22,12 +22,10 @@ import { import { Form, Formik, FormikHelpers } from "formik"; import React, { useEffect, useMemo, useState } from "react"; -import { useAppSelector } from "~/app/hooks"; import { useGetAllSystemsQuery, useUpdateSystemMutation, } from "~/features/system"; -import { selectAllSystems } from "~/features/system/system.slice"; import { DataFlow, System } from "~/types/api"; const defaultInitialValues = { @@ -53,8 +51,7 @@ export const DataFlowAccordionForm = ({ const dataFlowSystemsModal = useDisclosure(); const [updateSystemMutationTrigger] = useUpdateSystemMutation(); - useGetAllSystemsQuery(); - const systems = useAppSelector(selectAllSystems); + const { data: systems = [] } = useGetAllSystemsQuery(); const initialDataFlows = useMemo(() => { let dataFlows = isIngress ? system.ingress : system.egress; diff --git a/clients/admin-ui/src/features/config-wizard/ScanResults.tsx b/clients/admin-ui/src/features/config-wizard/ScanResults.tsx index 376970793c..0ea5aa0c8c 100644 --- a/clients/admin-ui/src/features/config-wizard/ScanResults.tsx +++ b/clients/admin-ui/src/features/config-wizard/ScanResults.tsx @@ -20,19 +20,14 @@ import { useAPIHelper } from "~/features/common/hooks"; import { useSystemOrDatamapRoute } from "~/features/common/hooks/useSystemOrDatamapRoute"; import WarningModal from "~/features/common/modals/WarningModal"; import { SystemsCheckboxTable } from "~/features/common/SystemsCheckboxTable"; -import { - setSystemsToClassify, - useUpsertSystemsMutation, -} from "~/features/system"; +import { useUpsertSystemsMutation } from "~/features/system"; import { System } from "~/types/api"; import { changeStep, reset, - selectAddSystemsMethod, selectSystemsForReview, } from "./config-wizard.slice"; -import { SystemMethods } from "./types"; const ALL_COLUMNS: ColumnMetadata[] = [ { name: "Name", attribute: "name" }, @@ -55,7 +50,6 @@ const ScanResults = () => { const [selectedSystems, setSelectedSystems] = useState(systems); const [selectedColumns, setSelectedColumns] = useState(ALL_COLUMNS); - const method = useAppSelector(selectAddSystemsMethod); const { handleError } = useAPIHelper(); /** @@ -77,15 +71,6 @@ const ScanResults = () => { return handleError(response.error); } - /* - * Eventually, all scanners will go through some sort of classify flow. - * But for now, only the data flow scanner does - */ - if (method === SystemMethods.DATA_FLOW) { - dispatch(setSystemsToClassify(selectedSystems)); - return navigateAndReset("/classify-systems"); - } - return navigateAndReset(systemOrDatamapRoute); }; diff --git a/clients/admin-ui/src/features/configure-consent/AddVendor.tsx b/clients/admin-ui/src/features/configure-consent/AddVendor.tsx index aa185f9fdf..32cb3d86d3 100644 --- a/clients/admin-ui/src/features/configure-consent/AddVendor.tsx +++ b/clients/admin-ui/src/features/configure-consent/AddVendor.tsx @@ -20,7 +20,10 @@ import { selectDictEntry, useGetAllDictionaryEntriesQuery, } from "~/features/plus/plus.slice"; -import { selectAllSystems, useCreateSystemMutation } from "~/features/system"; +import { + useCreateSystemMutation, + useLazyGetSystemsQuery, +} from "~/features/system"; import { selectLockedForGVL, selectSuggestions, @@ -64,7 +67,7 @@ const AddVendor = ({ const dispatch = useAppDispatch(); - const systems = useAppSelector(selectAllSystems); + const [getSystemQueryTrigger] = useLazyGetSystemsQuery(); const ValidationSchema = useMemo( () => @@ -72,9 +75,14 @@ const AddVendor = ({ name: Yup.string() .required() .label("Vendor name") - .test("is-unique", "", (value, context) => { - const takenSystemNames = systems.map((s) => s.name); - if (takenSystemNames.some((name) => name === value)) { + .test("is-unique", "", async (value, context) => { + const { data } = await getSystemQueryTrigger({ + page: 1, + size: 10, + search: value, + }); + const similarSystemNames = data?.items || []; + if (similarSystemNames.some((s) => s.name === value)) { return context.createError({ message: `You already have a vendor called "${value}". Please specify a unique name for this vendor.`, }); @@ -82,7 +90,7 @@ const AddVendor = ({ return true; }), }), - [systems] + [getSystemQueryTrigger] ); // Subscribe and get dictionary values diff --git a/clients/admin-ui/src/features/plus/plus.slice.ts b/clients/admin-ui/src/features/plus/plus.slice.ts index 85251b2672..f81a003adf 100644 --- a/clients/admin-ui/src/features/plus/plus.slice.ts +++ b/clients/admin-ui/src/features/plus/plus.slice.ts @@ -10,7 +10,6 @@ import { } from "~/features/dataset/dataset.slice"; import { CreateSaasConnectionConfig } from "~/features/datastore-connections"; import { CreateSaasConnectionConfigResponse } from "~/features/datastore-connections/types"; -import { selectSystemsToClassify } from "~/features/system"; import { AllowList, AllowListUpdate, @@ -507,15 +506,6 @@ export const selectDatasetClassifyInstances = createSelector( ({ data: instances }) => instances ?? emptyClassifyInstances ); -export const selectSystemClassifyInstances = createSelector( - [(state) => state, selectSystemsToClassify], - (state, systems) => - plusApi.endpoints.getAllClassifyInstances.select({ - resource_type: GenerateTypes.SYSTEMS, - fides_keys: systems?.map((s) => s.fides_key), - })(state)?.data ?? emptyClassifyInstances -); - const emptyClassifyInstanceMap: Map = new Map(); @@ -537,11 +527,6 @@ export const selectDatasetClassifyInstanceMap = createSelector( (instances) => instancesToMap(instances) ); -export const selectSystemClassifyInstanceMap = createSelector( - selectSystemClassifyInstances, - (instances) => instancesToMap(instances) -); - /** * This is the root of ClassifyInstance selectors that parallel the dataset's structure. These used * the cached getClassifyDataset response state, which is a query using the "active" dataset's diff --git a/clients/admin-ui/src/features/system/ClassifySystemsTable.tsx b/clients/admin-ui/src/features/system/ClassifySystemsTable.tsx deleted file mode 100644 index ae405f7171..0000000000 --- a/clients/admin-ui/src/features/system/ClassifySystemsTable.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import ClassifyResultsToggle from "common/ClassifyResultsToggle"; -import { Flex, Stack, Table, Tbody, Td, Text, Th, Thead, Tr } from "fidesui"; -import { useState } from "react"; - -import { useAppDispatch, useAppSelector } from "~/app/hooks"; -import { SystemTableCell } from "~/features/common/SystemsCheckboxTable"; -import ClassificationStatusBadge from "~/features/plus/ClassificationStatusBadge"; -import { selectSystemClassifyInstanceMap } from "~/features/plus/plus.slice"; -import { GenerateTypes, System } from "~/types/api"; - -import EditClassifySystemDrawer from "./EditClassifySystemDrawer"; -import { - selectActiveClassifySystem, - setActiveClassifySystemFidesKey, -} from "./system.slice"; - -const ClassifySystemsTable = ({ systems }: { systems: System[] }) => { - const dispatch = useAppDispatch(); - const classifyInstanceMap = useAppSelector(selectSystemClassifyInstanceMap); - const activeSystem = useAppSelector(selectActiveClassifySystem); - - const handleClick = (system: System) => { - dispatch(setActiveClassifySystemFidesKey(system.fides_key)); - }; - const [hideEmpty, setHideEmpty] = useState(false); - const filteredSystems = hideEmpty - ? systems.filter( - (system) => classifyInstanceMap.get(system.fides_key)?.has_labels - ) - : systems; - - return ( - - - - Only Show Findings - - - - - - - - - - - - {filteredSystems.map((system) => { - const classifyInstance = classifyInstanceMap.get(system.fides_key); - return ( - handleClick(system)} - > - - - - ); - })} - -
System NameClassification Status
- - - -
- {activeSystem ? ( - dispatch(setActiveClassifySystemFidesKey(undefined))} - /> - ) : null} -
- ); -}; - -export default ClassifySystemsTable; diff --git a/clients/admin-ui/src/features/system/SystemInformationForm.tsx b/clients/admin-ui/src/features/system/SystemInformationForm.tsx index 342069cec5..5e5f59801f 100644 --- a/clients/admin-ui/src/features/system/SystemInformationForm.tsx +++ b/clients/admin-ui/src/features/system/SystemInformationForm.tsx @@ -50,8 +50,9 @@ import { transformSystemToFormValues, } from "~/features/system/form"; import { - selectAllSystems, useCreateSystemMutation, + useGetAllSystemsQuery, + useLazyGetSystemsQuery, useUpdateSystemMutation, } from "~/features/system/system.slice"; import SystemFormInputGroup from "~/features/system/SystemFormInputGroup"; @@ -91,7 +92,7 @@ const SystemInformationForm = ({ withHeader, children, }: Props) => { - const systems = useAppSelector(selectAllSystems); + const { data: systems = [] } = useGetAllSystemsQuery(); const dispatch = useAppDispatch(); const customFields = useCustomFields({ @@ -115,17 +116,25 @@ const SystemInformationForm = ({ [passedInSystem, customFields.customFieldValues] ); + const [getSystemQueryTrigger] = useLazyGetSystemsQuery(); + const ValidationSchema = useMemo( () => Yup.object().shape({ name: Yup.string() .required() .label("System name") - .test("is-unique", "", (value, context) => { - const takenSystemNames = systems - .map((s) => s.name) - .filter((name) => name !== initialValues.name); - if (takenSystemNames.some((name) => name === value)) { + .test("is-unique", "", async (value, context) => { + const { data } = await getSystemQueryTrigger({ + page: 1, + size: 10, + search: value, + }); + const systemResults = data?.items || []; + const similarSystemNames = systemResults.filter( + (s) => s.name !== initialValues.name + ); + if (similarSystemNames.some((s) => s.name === value)) { return context.createError({ message: `You already have a system called "${value}". Please specify a unique name for this system.`, }); @@ -134,7 +143,7 @@ const SystemInformationForm = ({ }), privacy_policy: Yup.string().min(1).url().nullable(), }), - [systems, initialValues.name] + [getSystemQueryTrigger, initialValues.name] ); const features = useFeatures(); diff --git a/clients/admin-ui/src/features/system/SystemRegisterSuccess.tsx b/clients/admin-ui/src/features/system/SystemRegisterSuccess.tsx deleted file mode 100644 index c2ddd07327..0000000000 --- a/clients/admin-ui/src/features/system/SystemRegisterSuccess.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { - Badge, - Box, - Button, - chakra, - Heading, - Stack, - StepperCircleCheckmarkIcon, - Table, - TableContainer, - Tbody, - Td, - Text, - Th, - Thead, - Tr, -} from "fidesui"; -import { useRouter } from "next/router"; - -import { useAppDispatch, useAppSelector } from "~/app/hooks"; -import { SYSTEM_ROUTE } from "~/features/common/nav/v2/routes"; -import { - selectAllSystems, - setActiveSystem, -} from "~/features/system/system.slice"; -import { System } from "~/types/api"; - -interface Props { - system: System; - onAddNextSystem: () => void; -} -const SystemRegisterSuccess = ({ system, onAddNextSystem }: Props) => { - const allRegisteredSystems = useAppSelector(selectAllSystems); - const dispatch = useAppDispatch(); - const router = useRouter(); - const otherSystems = allRegisteredSystems - ? allRegisteredSystems.filter( - (registeredSystem) => registeredSystem.name !== system.name - ) - : []; - - const systemName = system.name ?? system.fides_key; - - const onFinish = () => { - dispatch(setActiveSystem(undefined)); - - router.push(SYSTEM_ROUTE); - }; - - return ( - - - - - Success - - {systemName} successfully registered! - - {systemName} has been successfully added to the registry! - - - - - - - - - - - - - {otherSystems.map((s) => ( - - - - - ))} - -
System Name
{systemName} - -
{s.name} - -
-
- You can continue to add more systems now or finish. - - - - - -
-
- ); -}; -export default SystemRegisterSuccess; diff --git a/clients/admin-ui/src/features/system/history/SystemHistoryTable.tsx b/clients/admin-ui/src/features/system/history/SystemHistoryTable.tsx index 88c402d535..bde3460e4f 100644 --- a/clients/admin-ui/src/features/system/history/SystemHistoryTable.tsx +++ b/clients/admin-ui/src/features/system/history/SystemHistoryTable.tsx @@ -9,7 +9,7 @@ import { selectAllDictEntries, useGetSystemHistoryQuery, } from "~/features/plus/plus.slice"; -import { selectAllSystems } from "~/features/system/system.slice"; +import { useGetAllSystemsQuery } from "~/features/system/system.slice"; import { SystemHistoryResponse } from "~/types/api"; import { SystemResponse } from "~/types/api/models/SystemResponse"; @@ -41,7 +41,7 @@ const SystemHistoryTable = ({ system }: Props) => { const [selectedHistory, setSelectedHistory] = useState(null); const dictionaryOptions = useAppSelector(selectAllDictEntries); - const systems = useAppSelector(selectAllSystems); + const { data: systems = [] } = useGetAllSystemsQuery(); const systemHistories = data?.items || []; diff --git a/clients/admin-ui/src/features/system/system.slice.ts b/clients/admin-ui/src/features/system/system.slice.ts index 41fc0a864a..c19f8a1928 100644 --- a/clients/admin-ui/src/features/system/system.slice.ts +++ b/clients/admin-ui/src/features/system/system.slice.ts @@ -179,6 +179,7 @@ const systemApi = baseApi.injectEndpoints({ export const { useGetSystemsQuery, + useLazyGetSystemsQuery, useGetAllSystemsQuery, useGetSystemByFidesKeyQuery, useCreateSystemMutation, @@ -209,26 +210,10 @@ export const systemSlice = createSlice({ ) => { draftState.activeSystem = action.payload; }, - setActiveClassifySystemFidesKey: ( - draftState, - action: PayloadAction - ) => { - draftState.activeClassifySystemFidesKey = action.payload; - }, - setSystemsToClassify: ( - draftState, - action: PayloadAction - ) => { - draftState.systemsToClassify = action.payload; - }, }, }); -export const { - setActiveSystem, - setActiveClassifySystemFidesKey, - setSystemsToClassify, -} = systemSlice.actions; +export const { setActiveSystem } = systemSlice.actions; export const { reducer } = systemSlice; @@ -239,29 +224,14 @@ export const selectActiveSystem = createSelector( (state) => state.activeSystem ); -export const selectActiveClassifySystemFidesKey = createSelector( - selectSystem, - (state) => state.activeClassifySystemFidesKey -); - -const emptySelectAllSystems: SystemResponse[] = []; -export const selectAllSystems = createSelector( - [(RootState) => RootState, systemApi.endpoints.getAllSystems.select()], - (RootState, { data }) => data || emptySelectAllSystems -); - -export const selectActiveClassifySystem = createSelector( - [selectAllSystems, selectActiveClassifySystemFidesKey], - (allSystems, fidesKey) => { - if (fidesKey === undefined) { - return undefined; - } - const system = allSystems?.find((s) => s.fides_key === fidesKey); - return system; - } -); - -export const selectSystemsToClassify = createSelector( - selectSystem, - (state) => state.systemsToClassify +/** + * Selects the number of systems + * By using the paginated getSystems endpoint, we can get the total number of systems + */ +export const selectSystemsCount = createSelector( + [ + (RootState) => RootState, + systemApi.endpoints.getSystems.select({ page: 1, size: 1 }), + ], + (RootState, { data }) => data?.total || 0 ); diff --git a/clients/admin-ui/src/pages/classify-systems/index.tsx b/clients/admin-ui/src/pages/classify-systems/index.tsx deleted file mode 100644 index ece0ac18fa..0000000000 --- a/clients/admin-ui/src/pages/classify-systems/index.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { Button, Heading, HStack, Spinner, Stack, Text } from "fidesui"; -import type { NextPage } from "next"; -import NextLink from "next/link"; -import { useRouter } from "next/router"; -import { ReactNode, useEffect } from "react"; -import { useDispatch } from "react-redux"; - -import { useAppSelector } from "~/app/hooks"; -import { usePollForClassifications } from "~/features/common/classifications"; -import { useSystemOrDatamapRoute } from "~/features/common/hooks/useSystemOrDatamapRoute"; -import Layout from "~/features/common/Layout"; -import { useGetHealthQuery } from "~/features/plus/plus.slice"; -import { - selectSystemsToClassify, - setSystemsToClassify, - useGetAllSystemsQuery, -} from "~/features/system"; -import ClassifySystemsTable from "~/features/system/ClassifySystemsTable"; -import { GenerateTypes } from "~/types/api"; - -const ClassifySystemsLayout = ({ children }: { children: ReactNode }) => ( - - - - Classified systems - - {children} - - -); - -const ClassifySystems: NextPage = () => { - const router = useRouter(); - const dispatch = useDispatch(); - const { systemOrDatamapRoute } = useSystemOrDatamapRoute(); - const { isSuccess: hasPlus, isLoading: isLoadingPlus } = useGetHealthQuery(); - /** - * TODO: fides#1744 - * Because there is currently no way to associate a scan with its classification, - * we attempt to get the classification results for only the systems in review. - * However, because this is its own page that can be navigated to directly, - * we can't necessarily rely on knowing which systems were put into review - * Therefore, as a fallback, query all systems. - */ - const systemsToClassify = useAppSelector(selectSystemsToClassify); - const { isLoading: isLoadingSystems, data: allSystems } = - useGetAllSystemsQuery(undefined, { - skip: !hasPlus || (systemsToClassify && systemsToClassify.length > 0), - }); - const systems = systemsToClassify || allSystems; - useEffect(() => { - if (allSystems && allSystems.length) { - dispatch(setSystemsToClassify(allSystems)); - } - }, [dispatch, allSystems]); - - const { - isLoading: isLoadingClassifications, - data: classifications, - isClassificationFinished, - } = usePollForClassifications({ - resourceType: GenerateTypes.SYSTEMS, - fidesKeys: systems?.map((s) => s.fides_key), - skip: !hasPlus, - }); - - useEffect(() => { - if (!isLoadingPlus && !hasPlus) { - router.push("/"); - } - }, [router, hasPlus, isLoadingPlus]); - - const isLoading = isLoadingSystems || isLoadingClassifications; - - if (isLoading) { - return ( - - - - ); - } - - if (!classifications || classifications.length === 0) { - return ( - - - No classifications found. Have you run a scan? - - - ); - } - - return ( - - - {isClassificationFinished - ? "All systems have been classifed by Fides." - : "Systems are still being classified by Fides."} - - {systems && systems.length ? ( - - ) : ( - "No systems with classifications found" - )} - - - - - ); -}; - -export default ClassifySystems;