From e09fa4e8c6f82150c51477f967317b351bbdcdda Mon Sep 17 00:00:00 2001 From: Adam Sachs Date: Thu, 21 May 2026 16:15:19 -0400 Subject: [PATCH 1/6] Remove /add-systems config wizard page The wizard page at /add-systems (ConfigWizard) along with its AWS and Okta scanner sub-steps is unused. Single-system creation is still available via /add-systems/manual and the inline AddNewSystemModal; bulk vendor add is still available at /add-systems/multiple. - Delete pages/add-systems/index.tsx and the features/config-wizard/ directory (AddSystem, AuthenticateAwsForm, AuthenticateOktaForm, AuthenticateScanner, ConfigWizardWalkthrough, ScanResults, ScannerLoading, OrganizationInfoForm, slices, helpers, types). - Drop ADD_SYSTEMS_ROUTE constant, "Add systems" nav entry and home module card. - Update breadcrumbs on /add-systems/manual and /add-systems/multiple to drop the now-dead parent link. - Point the empty-datamap GetStarted button at /add-systems/multiple. - Remove configWizardSlice from the redux store. - Update jest + cypress specs (delete config-wizard*.cy.ts, drop wizard setup steps from bulk-vendor-add.cy.ts, routes.cy.ts, systems.cy.ts, root-user-access.cy.ts). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../cypress/e2e/config-wizard-plus.cy.ts | 27 -- .../admin-ui/cypress/e2e/config-wizard.cy.ts | 171 ----------- .../cypress/e2e/root-user-access.cy.ts | 7 - clients/admin-ui/cypress/e2e/routes.cy.ts | 9 +- clients/admin-ui/cypress/e2e/systems.cy.ts | 5 +- .../e2e/systems/plus/bulk-vendor-add.cy.ts | 35 --- clients/admin-ui/src/app/store.ts | 2 - .../features/common/nav/nav-config.test.ts | 10 - .../src/features/common/nav/nav-config.tsx | 5 - .../src/features/common/nav/routes.ts | 1 - .../src/features/config-wizard/AddSystem.tsx | 164 ---------- .../config-wizard/AuthenticateAwsForm.tsx | 257 ---------------- .../config-wizard/AuthenticateOktaForm.tsx | 283 ------------------ .../config-wizard/AuthenticateScanner.tsx | 18 -- .../config-wizard/ConfigWizardWalkthrough.tsx | 30 -- .../config-wizard/OrganizationInfoForm.tsx | 159 ---------- .../features/config-wizard/ScanResults.tsx | 167 ----------- .../features/config-wizard/ScannerLoading.tsx | 69 ----- .../config-wizard/config-wizard.slice.ts | 110 ------- .../src/features/config-wizard/constants.tsx | 58 ---- .../src/features/config-wizard/helpers.tsx | 4 - .../features/config-wizard/scanner.slice.ts | 16 - .../src/features/config-wizard/types.ts | 7 - .../src/features/datamap/GetStarted.tsx | 4 +- clients/admin-ui/src/home/constants.ts | 12 - clients/admin-ui/src/home/tile-config.test.ts | 10 - .../admin-ui/src/pages/add-systems/index.tsx | 26 -- .../admin-ui/src/pages/add-systems/manual.tsx | 9 +- .../src/pages/add-systems/multiple.tsx | 9 +- 29 files changed, 8 insertions(+), 1676 deletions(-) delete mode 100644 clients/admin-ui/cypress/e2e/config-wizard-plus.cy.ts delete mode 100644 clients/admin-ui/cypress/e2e/config-wizard.cy.ts delete mode 100644 clients/admin-ui/src/features/config-wizard/AddSystem.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/AuthenticateAwsForm.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/AuthenticateOktaForm.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/AuthenticateScanner.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/ConfigWizardWalkthrough.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/OrganizationInfoForm.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/ScanResults.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/ScannerLoading.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/config-wizard.slice.ts delete mode 100644 clients/admin-ui/src/features/config-wizard/constants.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/helpers.tsx delete mode 100644 clients/admin-ui/src/features/config-wizard/scanner.slice.ts delete mode 100644 clients/admin-ui/src/features/config-wizard/types.ts delete mode 100644 clients/admin-ui/src/pages/add-systems/index.tsx diff --git a/clients/admin-ui/cypress/e2e/config-wizard-plus.cy.ts b/clients/admin-ui/cypress/e2e/config-wizard-plus.cy.ts deleted file mode 100644 index 5e05af67d4b..00000000000 --- a/clients/admin-ui/cypress/e2e/config-wizard-plus.cy.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { stubPlus } from "cypress/support/stubs"; - -import { ADD_SYSTEMS_ROUTE } from "~/features/common/nav/routes"; - -/** - * Fides+ config wizard. The legacy runtime (Pixie) cluster scanner was removed; - * AWS and Okta discovery flows are covered in config-wizard.cy.ts. - */ -describe("Config wizard with plus settings", () => { - beforeEach(() => { - cy.login(); - cy.intercept("GET", "/api/v1/organization/*", { - fixture: "organizations/default_organization.json", - }).as("getOrganization"); - stubPlus(true); - }); - - it("loads add systems when Plus health is available", () => { - cy.visit(ADD_SYSTEMS_ROUTE); - cy.wait("@getPlusHealth"); - cy.getByTestId("add-systems"); - cy.getByTestId("aws-btn").should("be.visible"); - cy.getByTestId("okta-btn").should("be.visible"); - cy.getByTestId("manual-options"); - cy.getByTestId("automated-options"); - }); -}); diff --git a/clients/admin-ui/cypress/e2e/config-wizard.cy.ts b/clients/admin-ui/cypress/e2e/config-wizard.cy.ts deleted file mode 100644 index 5b1393d6d2d..00000000000 --- a/clients/admin-ui/cypress/e2e/config-wizard.cy.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { stubSystemCrud, stubTaxonomyEntities } from "cypress/support/stubs"; - -import { ADD_SYSTEMS_ROUTE } from "~/features/common/nav/routes"; - -describe("Config Wizard", () => { - beforeEach(() => { - cy.login(); - cy.intercept("GET", "/api/v1/organization/*", { - fixture: "organizations/default_organization.json", - }).as("getOrganization"); - }); - - describe("AWS scan steps", () => { - beforeEach(() => { - stubSystemCrud(); - stubTaxonomyEntities(); - - cy.visit(ADD_SYSTEMS_ROUTE); - // Select AWS to move to form step. - cy.getByTestId("add-systems"); - cy.getByTestId("aws-btn").click(); - // Fill form - cy.getByTestId("authenticate-aws-form"); - cy.getByTestId("input-aws_access_key_id").type("fakeAccessKey"); - cy.getByTestId("input-aws_secret_access_key").type("fakeSecretAccessKey"); - cy.getByTestId("controlled-select-region_name").type("us-east-1{Enter}"); - }); - - it("Allows submitting the form and reviewing the results", () => { - cy.intercept("POST", "/api/v1/generate", { - fixture: "generate/system.json", - }).as("postGenerate"); - - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate"); - - cy.getByTestId("scan-results"); - cy.getByTestId(`checkbox-example-system-1`).click(); - cy.getByTestId("register-btn").click(); - - // The request while editing the form should match the generated system's body. - cy.intercept("POST", "/api/v1/system", { - fixture: "generate/system_to_review.json", - }).as("postSystem"); - cy.intercept("PUT", "/api/v1/system*", { - fixture: "generate/system_to_review.json", - }).as("putSystem"); - - // assert that the systems do POST - cy.intercept("POST", "/api/v1/system/upsert", []).as("upsertSystems"); - - cy.getAntModalConfirmButtons().contains("Continue").click(); - - cy.wait("@upsertSystems").then((interception) => { - assert.isNotNull(interception.response.body, "Upsert call has data"); - }); - }); - - it("Displays AWS error", () => { - cy.intercept("POST", "/api/v1/generate", { - statusCode: 403, - body: { - detail: "The security token included in the request is invalid.", - }, - }).as("postGenerate403"); - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate403"); - cy.getByTestId("error-page-result"); - cy.contains("Fides was unable to scan AWS"); - }); - - it("Displays generic error", () => { - cy.intercept("POST", "/api/v1/generate", { - statusCode: 500, - body: "Internal Server Error", - }).as("postGenerate500"); - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate500"); - cy.getByTestId("error-page-result"); - cy.contains("Fides was unable to scan your infrastructure"); - }); - - it("Allows stepping back to the previous step during an in-progress scan", () => { - cy.intercept("POST", "/api/v1/generate", { - delay: 1000, - statusCode: 503, - }).as("postGenerateDelayedAndCanceled"); - cy.getByTestId("submit-btn") - .click() - .then(() => { - cy.getByTestId("close-scan-in-progress").click(); - cy.contains("Cancel Scan!"); - cy.contains("Yes, Cancel").click(); - cy.getByTestId("add-systems"); - }); - }); - }); - describe("Okta scan steps", () => { - beforeEach(() => { - stubSystemCrud(); - stubTaxonomyEntities(); - - cy.visit(ADD_SYSTEMS_ROUTE); - // Select Okta to move to form step. - cy.getByTestId("add-systems"); - cy.getByTestId("okta-btn").click(); - // Fill form - cy.getByTestId("authenticate-okta-form"); - cy.getByTestId("input-orgUrl").type("https://dev-12345.okta.com"); - cy.getByTestId("input-clientId").type("0oa1abc2def3ghi4jkl5"); - cy.getByTestId("input-privateKey").type( - '{"kty":"RSA","kid":"test","n":"test","e":"AQAB","d":"test"}', - { parseSpecialCharSequences: false }, - ); - }); - - it("Allows submitting the form and reviewing the results", () => { - cy.intercept("POST", "/api/v1/generate", { - fixture: "generate/system.json", - }).as("postGenerate"); - - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate"); - - cy.getByTestId("scan-results"); - cy.getByTestId(`checkbox-example-system-1`).click(); - cy.getByTestId("register-btn").click(); - - // The request while editing the form should match the generated system's body. - cy.intercept("POST", "/api/v1/system", { - fixture: "generate/system_to_review.json", - }).as("postSystem"); - cy.intercept("PUT", "/api/v1/system*", { - fixture: "generate/system_to_review.json", - }).as("putSystem"); - - // assert that the systems do POST - cy.intercept("POST", "/api/v1/system/upsert", []).as("upsertSystems"); - - cy.getAntModalConfirmButtons().contains("Continue").click(); - - cy.wait("@upsertSystems").then((interception) => { - assert.isNotNull(interception.response.body, "Upsert call has data"); - }); - }); - - it("Displays 403 error", () => { - cy.intercept("POST", "/api/v1/generate", { - statusCode: 403, - body: { - detail: "The security token included in the request is invalid.", - }, - }).as("postGenerate403"); - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate403"); - cy.getByTestId("error-page-result"); - cy.contains("Fides was unable to scan your infrastructure"); - }); - - it("Displays 500 error", () => { - cy.intercept("POST", "/api/v1/generate", { - statusCode: 500, - body: "Internal Server Error", - }).as("postGenerate500"); - cy.getByTestId("submit-btn").click(); - cy.wait("@postGenerate500"); - cy.getByTestId("error-page-result"); - cy.contains("Fides was unable to scan your infrastructure"); - }); - }); -}); diff --git a/clients/admin-ui/cypress/e2e/root-user-access.cy.ts b/clients/admin-ui/cypress/e2e/root-user-access.cy.ts index 2ff520f596a..2d703b21355 100644 --- a/clients/admin-ui/cypress/e2e/root-user-access.cy.ts +++ b/clients/admin-ui/cypress/e2e/root-user-access.cy.ts @@ -213,13 +213,6 @@ describe("Root User Access", () => { cy.url().should("not.include", "/login"); }); - it("can navigate to Add systems page", () => { - cy.getByTestId("Data inventory-nav-group").click(); - cy.getByTestId("Add systems-nav-link").click(); - cy.url().should("include", "/add-systems"); - cy.url().should("not.include", "/login"); - }); - it("can navigate to Privacy requests page", () => { cy.getByTestId("Privacy requests-nav-group").click(); cy.getByTestId("Request manager-nav-link").click(); diff --git a/clients/admin-ui/cypress/e2e/routes.cy.ts b/clients/admin-ui/cypress/e2e/routes.cy.ts index fef126a808c..b625d9c798e 100644 --- a/clients/admin-ui/cypress/e2e/routes.cy.ts +++ b/clients/admin-ui/cypress/e2e/routes.cy.ts @@ -10,7 +10,7 @@ import { } from "cypress/support/stubs"; import { - ADD_SYSTEMS_ROUTE, + ADD_SYSTEMS_MULTIPLE_ROUTE, DATAMAP_ROUTE, LOCATIONS_ROUTE, PRIVACY_NOTICES_ROUTE, @@ -41,8 +41,6 @@ describe("Routes", () => { it("admins can access many routes", () => { cy.assumeRole(RoleRegistryEnum.OWNER); - cy.visit(ADD_SYSTEMS_ROUTE); - cy.getByTestId("add-systems"); cy.visit("/privacy-requests"); cy.getByTestId("privacy-requests"); cy.visit("/privacy-requests/configure"); @@ -66,9 +64,8 @@ describe("Routes", () => { RoleRegistryEnum.VIEWER_AND_APPROVER, ].forEach((role) => { cy.assumeRole(role); - // cannot access /add-systems - cy.visit(ADD_SYSTEMS_ROUTE); - cy.getByTestId("add-systems").should("not.exist"); + // cannot access /add-systems/multiple + cy.visit(ADD_SYSTEMS_MULTIPLE_ROUTE); cy.getByTestId("home-content"); // can access /privacy-requests cy.visit("/privacy-requests"); diff --git a/clients/admin-ui/cypress/e2e/systems.cy.ts b/clients/admin-ui/cypress/e2e/systems.cy.ts index a7e1633eaf8..ee529e4ff3c 100644 --- a/clients/admin-ui/cypress/e2e/systems.cy.ts +++ b/clients/admin-ui/cypress/e2e/systems.cy.ts @@ -8,7 +8,6 @@ import { import { ADD_SYSTEMS_MANUAL_ROUTE, - ADD_SYSTEMS_ROUTE, INTEGRATION_MANAGEMENT_ROUTE, SYSTEM_ROUTE, } from "~/features/common/nav/routes"; @@ -91,9 +90,7 @@ describe("System management page", () => { body: { ...system, privacy_declarations: [] }, }).as("getDemoSystem"); // Fill in the describe form based on fixture data - cy.visit(ADD_SYSTEMS_ROUTE); - cy.getByTestId("manual-btn").click(); - cy.url().should("contain", ADD_SYSTEMS_MANUAL_ROUTE); + cy.visit(ADD_SYSTEMS_MANUAL_ROUTE); cy.wait("@getSystemsPaginated"); cy.getByTestId("input-name").type(system.name); cy.getByTestId("input-description").type(system.description); diff --git a/clients/admin-ui/cypress/e2e/systems/plus/bulk-vendor-add.cy.ts b/clients/admin-ui/cypress/e2e/systems/plus/bulk-vendor-add.cy.ts index 7f50c2f20b8..7fa4acad114 100644 --- a/clients/admin-ui/cypress/e2e/systems/plus/bulk-vendor-add.cy.ts +++ b/clients/admin-ui/cypress/e2e/systems/plus/bulk-vendor-add.cy.ts @@ -9,9 +9,7 @@ import { } from "cypress/support/stubs"; import { - ADD_SYSTEMS_MANUAL_ROUTE, ADD_SYSTEMS_MULTIPLE_ROUTE, - ADD_SYSTEMS_ROUTE, DATAMAP_ROUTE, INDEX_ROUTE, } from "~/features/common/nav/routes"; @@ -46,39 +44,6 @@ describe("Plus Bulk Vendor Add", () => { .should("have.length.greaterThan", 0); }); - it("upgrade modal doesn't pop up if compass is enabled", () => { - cy.visit(ADD_SYSTEMS_ROUTE); - cy.getByTestId("multiple-btn").click(); - cy.wait("@getSystemVendors"); - cy.getByTestId("fidesTable"); - }); - - it("upgrade modal pops up if compass isn't enabled and redirects to manual add", () => { - stubPlus(true, { - core_fides_version: "2.2.0", - fidesplus_server: "healthy", - dictionary: { - enabled: false, - service_health: null, - service_error: null, - }, - tcf: { - enabled: true, - }, - fidesplus_version: "", - fides_cloud: { - enabled: false, - }, - }); - cy.visit(ADD_SYSTEMS_ROUTE); - cy.getByTestId("multiple-btn").click(); - cy.get(".ant-modal-confirm"); - cy.getAntModalConfirmButtons() - .find(".ant-btn:not(.ant-btn-primary):not(.ant-btn-dangerous)") - .click(); - cy.url().should("include", ADD_SYSTEMS_MANUAL_ROUTE); - }); - it("can add new systems and redirects to datamap", () => { stubDatamap(); cy.visit(ADD_SYSTEMS_MULTIPLE_ROUTE); diff --git a/clients/admin-ui/src/app/store.ts b/clients/admin-ui/src/app/store.ts index 1ada7b744ed..fd31d5b3717 100644 --- a/clients/admin-ui/src/app/store.ts +++ b/clients/admin-ui/src/app/store.ts @@ -25,7 +25,6 @@ import { featuresSlice } from "~/features/common/features"; import { healthApi } from "~/features/common/health.slice"; import { dirtyFormsSlice } from "~/features/common/hooks/dirty-forms.slice"; import { v3Api } from "~/features/common/v3-api.slice"; -import { configWizardSlice } from "~/features/config-wizard/config-wizard.slice"; import { connectionTypeSlice } from "~/features/connection-type"; import { tcfConfigSlice } from "~/features/consent-settings/tcf/tcf-config.slice"; import { discoveryDetectionSlice } from "~/features/data-discovery-and-detection/discovery-detection.slice"; @@ -80,7 +79,6 @@ const reducer = { [datamapSlice.name]: datamapSlice.reducer, [dirtyFormsSlice.name]: dirtyFormsSlice.reducer, [authSlice.name]: authSlice.reducer, - [configWizardSlice.name]: configWizardSlice.reducer, [connectionTypeSlice.name]: connectionTypeSlice.reducer, [tcfConfigSlice.name]: tcfConfigSlice.reducer, [dataSubjectsSlice.name]: dataSubjectsSlice.reducer, diff --git a/clients/admin-ui/src/features/common/nav/nav-config.test.ts b/clients/admin-ui/src/features/common/nav/nav-config.test.ts index 8d417d9c30d..6fcfefd6adc 100644 --- a/clients/admin-ui/src/features/common/nav/nav-config.test.ts +++ b/clients/admin-ui/src/features/common/nav/nav-config.test.ts @@ -36,7 +36,6 @@ describe("configureNavGroups", () => { // NOTE: the data map should _not_ include the Plus routes (/plus/datamap, /classify-systems, etc.) expect(findGroup(navGroups, "Data inventory").children).toMatchObject([ { title: "System inventory", path: routes.SYSTEM_ROUTE }, - { title: "Add systems", path: routes.ADD_SYSTEMS_ROUTE }, { title: "Manage datasets", path: routes.DATASET_ROUTE }, ]); @@ -64,7 +63,6 @@ describe("configureNavGroups", () => { expect(findGroup(navGroups, "Data inventory").children).toMatchObject([ { title: "Data lineage", path: routes.DATAMAP_ROUTE }, { title: "System inventory", path: routes.SYSTEM_ROUTE }, - { title: "Add systems", path: routes.ADD_SYSTEMS_ROUTE }, { title: "Manage datasets", path: routes.DATASET_ROUTE }, { title: "Data map report", path: routes.REPORTING_DATAMAP_ROUTE }, { title: "Asset report", path: routes.REPORTING_ASSETS_ROUTE }, @@ -145,7 +143,6 @@ describe("configureNavGroups", () => { // The data map should _not_ include the actual "/plus/datamap". expect(findGroup(navGroups, "Data inventory").children).toMatchObject([ { title: "System inventory", path: routes.SYSTEM_ROUTE }, - { title: "Add systems", path: routes.ADD_SYSTEMS_ROUTE }, { title: "Manage datasets", path: routes.DATASET_ROUTE }, ]); }); @@ -369,13 +366,6 @@ describe("findActiveNav", () => { path: routes.DATAMAP_ROUTE, }, }, - { - path: routes.ADD_SYSTEMS_ROUTE, - expected: { - title: "Data inventory", - path: routes.ADD_SYSTEMS_ROUTE, - }, - }, { path: routes.PRIVACY_EXPERIENCE_ROUTE, expected: { diff --git a/clients/admin-ui/src/features/common/nav/nav-config.tsx b/clients/admin-ui/src/features/common/nav/nav-config.tsx index d0cd032bb4a..5e9d4e14c91 100644 --- a/clients/admin-ui/src/features/common/nav/nav-config.tsx +++ b/clients/admin-ui/src/features/common/nav/nav-config.tsx @@ -103,11 +103,6 @@ export const NAV_CONFIG: NavConfigGroup[] = [ scopes: [ScopeRegistryEnum.SYSTEM_READ], keywords: ["data map", "data inventory", "assets"], }, - { - title: "Add systems", - path: routes.ADD_SYSTEMS_ROUTE, - scopes: [ScopeRegistryEnum.SYSTEM_CREATE], - }, { title: "Manage datasets", path: routes.DATASET_ROUTE, diff --git a/clients/admin-ui/src/features/common/nav/routes.ts b/clients/admin-ui/src/features/common/nav/routes.ts index 19bec8d2614..2c90338adf3 100644 --- a/clients/admin-ui/src/features/common/nav/routes.ts +++ b/clients/admin-ui/src/features/common/nav/routes.ts @@ -2,7 +2,6 @@ export const INDEX_ROUTE = "/"; // Data map group -export const ADD_SYSTEMS_ROUTE = "/add-systems"; export const ADD_SYSTEMS_MANUAL_ROUTE = "/add-systems/manual"; export const ADD_SYSTEMS_MULTIPLE_ROUTE = "/add-systems/multiple"; export const DATAMAP_ROUTE = "/datamap"; diff --git a/clients/admin-ui/src/features/config-wizard/AddSystem.tsx b/clients/admin-ui/src/features/config-wizard/AddSystem.tsx deleted file mode 100644 index 0dd31c70d47..00000000000 --- a/clients/admin-ui/src/features/config-wizard/AddSystem.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import { - ChakraBox as Box, - ChakraHeading as Heading, - ChakraSimpleGrid as SimpleGrid, - ChakraStack as Stack, - ChakraText as Text, - ManualSetupIcon, - useModal, -} from "fidesui"; -import { useRouter } from "next/router"; - -import { useAppDispatch } from "~/app/hooks"; -import { useFeatures } from "~/features/common/features"; -import { - ADD_SYSTEMS_MANUAL_ROUTE, - ADD_SYSTEMS_MULTIPLE_ROUTE, -} from "~/features/common/nav/routes"; -import { ValidTargets } from "~/types/api"; - -import CalloutNavCard from "../common/CalloutNavCard"; -import { AWSLogo } from "../common/logos/AWSLogo"; -import { OktaLogo } from "../common/logos/OktaLogo"; -import { changeStep, setAddSystemsMethod } from "./config-wizard.slice"; -import { SystemMethods } from "./types"; - -const SectionTitle = ({ children }: { children: string }) => ( - - {children} - -); - -const AddSystem = () => { - const dispatch = useAppDispatch(); - const router = useRouter(); - const modal = useModal(); - const { dictionaryService: isCompassEnabled } = useFeatures(); - - return ( - - - - Fides helps you map your systems to manage your privacy - - - In Fides, systems describe any services that store or process data for - your organization, including third-party APIs, web applications, - databases, and data warehouses. - - - - Fides can automatically discover new systems in your AWS - infrastructure or Okta accounts. For everything else, you may manually - add systems to your map. - - - - Manually add systems - - - - - - - - Automated infrastructure scanning - - - - - - - ); -}; - -export default AddSystem; diff --git a/clients/admin-ui/src/features/config-wizard/AuthenticateAwsForm.tsx b/clients/admin-ui/src/features/config-wizard/AuthenticateAwsForm.tsx deleted file mode 100644 index 74fbaa9642e..00000000000 --- a/clients/admin-ui/src/features/config-wizard/AuthenticateAwsForm.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { - Button, - Card, - Flex, - Form, - Input, - Select, - Typography, - useMessage, -} from "fidesui"; -import { useEffect, useState } from "react"; - -import { useAppDispatch, useAppSelector } from "~/app/hooks"; -import DocsLink from "~/features/common/DocsLink"; -import { ParsedError, parseError } from "~/features/common/helpers"; -import { - GenerateResponse, - GenerateTypes, - System, - ValidTargets, -} from "~/types/api"; -import { RTKErrorResult } from "~/types/errors/api"; - -import ErrorPage from "../common/errors/ErrorPage"; -import { NextBreadcrumb } from "../common/nav/NextBreadcrumb"; -import { - changeStep, - selectOrganizationFidesKey, - setSystemsForReview, -} from "./config-wizard.slice"; -import { AWS_REGION_OPTIONS } from "./constants"; -import { isSystem } from "./helpers"; -import { useGenerateMutation } from "./scanner.slice"; -import ScannerLoading from "./ScannerLoading"; - -const initialValues = { - aws_access_key_id: "", - aws_secret_access_key: "", - aws_session_token: "", - region_name: "", -}; - -type FormValues = typeof initialValues; - -export const AuthenticateAwsForm = () => { - const organizationKey = useAppSelector(selectOrganizationFidesKey); - const dispatch = useAppDispatch(); - const message = useMessage(); - const [form] = Form.useForm(); - - const [scannerError, setScannerError] = useState(); - - // Track submittable state - const allValues = Form.useWatch([], form); - const [submittable, setSubmittable] = useState(false); - - useEffect(() => { - const checkValidity = async () => { - try { - await form.validateFields({ validateOnly: true }); - setSubmittable(true); - } catch { - setSubmittable(false); - } - }; - checkValidity(); - }, [form, allValues]); - - const handleResults = (results: GenerateResponse["generate_results"]) => { - const systems: System[] = (results ?? []).filter(isSystem); - dispatch(setSystemsForReview(systems)); - dispatch(changeStep()); - message.success( - `Your scan was successfully completed, with ${systems.length} new systems detected!`, - ); - }; - const handleCancel = () => { - dispatch(changeStep(2)); - }; - - const [generate, { isLoading }] = useGenerateMutation(); - - const onFinish = async (values: FormValues) => { - setScannerError(undefined); - - try { - const result = await generate({ - organization_key: organizationKey, - generate: { - config: values, - target: ValidTargets.AWS, - type: GenerateTypes.SYSTEMS, - }, - }).unwrap(); - - handleResults(result.generate_results); - } catch (error) { - const parsedError = parseError(error as RTKErrorResult["error"], { - status: 500, - message: "Our system encountered a problem while connecting to AWS.", - }); - setScannerError(parsedError); - } - }; - - return ( - - { - e.preventDefault(); - handleCancel(); - }, - }, - { title: "Authenticate AWS scanner" }, - ]} - /> - - {isLoading && ( - - )} - - {scannerError && ( - <> - - {scannerError.status === 403 && ( - - To fix this issue, double check that you have granted the required - permissions to these credentials as part of your IAM policy. If - you need more help in configuring IAM policies, you can read about - them in the{" "} - - AWS documentation - - . - - )} - - )} - -
- - {!isLoading && !scannerError && ( - - - - To use the scanner to inventory systems in AWS, you must first - authenticate to your AWS cloud by providing the following - information: - - - - - - - - { - if (!value || !/\s/.test(value)) { - return Promise.resolve(); - } - return Promise.reject( - new Error("Cannot contain spaces"), - ); - }, - }, - ]} - > - - - - - - - - - { - if (!value) { - return Promise.resolve(); - } - try { - JSON.parse(value); - return Promise.resolve(); - } catch { - return Promise.reject( - new Error( - "Private key must be valid JSON. Paste the JWK downloaded from Okta.", - ), - ); - } - }, - }, - ]} - > - - - { - if (!value) { - return Promise.resolve(); - } - const scopes = value - .split(",") - .map((s: string) => s.trim()); - if ( - scopes.every( - (scope: string) => - scope.length > 0 && !/\s/.test(scope), - ) - ) { - return Promise.resolve(); - } - return Promise.reject( - new Error( - "Scopes must be a single scope or comma-separated list (e.g., 'okta.apps.read' or 'okta.apps.read, okta.users.read')", - ), - ); - }, - }, - ]} - > - - - - - )} - {!isLoading && ( - - - {!scannerError && ( - - )} - - )} - -
-
- ); -}; diff --git a/clients/admin-ui/src/features/config-wizard/AuthenticateScanner.tsx b/clients/admin-ui/src/features/config-wizard/AuthenticateScanner.tsx deleted file mode 100644 index 3b5a17641b5..00000000000 --- a/clients/admin-ui/src/features/config-wizard/AuthenticateScanner.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useAppSelector } from "~/app/hooks"; -import { ValidTargets } from "~/types/api"; - -import { AuthenticateAwsForm } from "./AuthenticateAwsForm"; -import { AuthenticateOktaForm } from "./AuthenticateOktaForm"; -import { selectAddSystemsMethod } from "./config-wizard.slice"; - -const AuthenticateScanner = () => { - const infrastructure = useAppSelector(selectAddSystemsMethod); - return ( - <> - {infrastructure === ValidTargets.AWS && } - {infrastructure === ValidTargets.OKTA && } - - ); -}; - -export default AuthenticateScanner; diff --git a/clients/admin-ui/src/features/config-wizard/ConfigWizardWalkthrough.tsx b/clients/admin-ui/src/features/config-wizard/ConfigWizardWalkthrough.tsx deleted file mode 100644 index 50e2dd119fe..00000000000 --- a/clients/admin-ui/src/features/config-wizard/ConfigWizardWalkthrough.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Flex } from "fidesui"; - -import { useAppSelector } from "~/app/hooks"; - -import AddSystem from "./AddSystem"; -import AuthenticateScanner from "./AuthenticateScanner"; -import { selectStep } from "./config-wizard.slice"; -import { OrganizationInfoForm } from "./OrganizationInfoForm"; -import ScanResults from "./ScanResults"; - -const ConfigWizardWalkthrough = () => { - const step = useAppSelector(selectStep); - - return ( - -
- {step === 1 ? : null} - {step === 2 ? : null} - {step === 3 ? : null} - {step === 4 ? ( -
- -
- ) : null} -
-
- ); -}; - -export default ConfigWizardWalkthrough; diff --git a/clients/admin-ui/src/features/config-wizard/OrganizationInfoForm.tsx b/clients/admin-ui/src/features/config-wizard/OrganizationInfoForm.tsx deleted file mode 100644 index bb498f4298f..00000000000 --- a/clients/admin-ui/src/features/config-wizard/OrganizationInfoForm.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { - Button, - Card, - Flex, - Form, - Input, - Typography, - useMessage, -} from "fidesui"; -import { useEffect, useMemo, useState } from "react"; - -import { useAppDispatch } from "~/app/hooks"; -import { getErrorMessage } from "~/features/common/helpers"; -import { - DEFAULT_ORGANIZATION_FIDES_KEY, - useCreateOrganizationMutation, - useGetOrganizationByFidesKeyQuery, - useUpdateOrganizationMutation, -} from "~/features/organization"; -import { Organization } from "~/types/api"; -import { RTKErrorResult } from "~/types/errors/api"; - -import { changeStep, setOrganization } from "./config-wizard.slice"; - -interface FormValues { - name: string; - description: string; -} - -export const OrganizationInfoForm = () => { - const dispatch = useAppDispatch(); - const message = useMessage(); - const [form] = Form.useForm(); - const [hasSubmitted, setHasSubmitted] = useState(false); - - const [createOrganization] = useCreateOrganizationMutation(); - const [updateOrganization] = useUpdateOrganizationMutation(); - const { data: existingOrg, isLoading: isLoadingOrganization } = - useGetOrganizationByFidesKeyQuery(DEFAULT_ORGANIZATION_FIDES_KEY); - - // Track submittable state - const allValues = Form.useWatch([], form); - const [submittable, setSubmittable] = useState(false); - - useEffect(() => { - const checkValidity = async () => { - try { - await form.validateFields({ validateOnly: true }); - setSubmittable(true); - } catch { - setSubmittable(false); - } - }; - checkValidity(); - }, [form, allValues]); - - // Auto-skip step if org already has name+description - useEffect(() => { - if (isLoadingOrganization || hasSubmitted) { - return; - } - if (existingOrg?.name && existingOrg?.description) { - dispatch(changeStep()); - } - }, [isLoadingOrganization, existingOrg, dispatch, hasSubmitted]); - - const formInitialValues = useMemo( - () => ({ - name: existingOrg?.name ?? "", - description: existingOrg?.description ?? "", - }), - [existingOrg], - ); - - const handleSuccess = (organization: Organization) => { - dispatch(setOrganization(organization)); - dispatch(changeStep()); - }; - - const onFinish = async (values: FormValues) => { - setHasSubmitted(true); - const organizationBody = { - name: values.name ?? existingOrg?.name, - description: values.description ?? existingOrg?.description, - fides_key: existingOrg?.fides_key ?? DEFAULT_ORGANIZATION_FIDES_KEY, - organization_fides_key: DEFAULT_ORGANIZATION_FIDES_KEY, - }; - - try { - if (!existingOrg) { - await createOrganization(organizationBody).unwrap(); - } else { - await updateOrganization(organizationBody).unwrap(); - } - handleSuccess(organizationBody); - } catch (error) { - const errorMsg = getErrorMessage(error as RTKErrorResult["error"]); - message.error(errorMsg); - } - }; - - return ( -
- - Create your Organization - - -
- Provide your organization information. This information is used to - configure your organization in Fides for data map reporting - purposes. -
- - - - - - -
-
- - - -
-
- ); -}; diff --git a/clients/admin-ui/src/features/config-wizard/ScanResults.tsx b/clients/admin-ui/src/features/config-wizard/ScanResults.tsx deleted file mode 100644 index 04beb4e60b4..00000000000 --- a/clients/admin-ui/src/features/config-wizard/ScanResults.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { - Button, - ChakraBox as Box, - ChakraHStack as HStack, - ChakraStack as Stack, - ChakraText as Text, - useModal, -} from "fidesui"; -import { useRouter } from "next/router"; -import { useState } from "react"; - -import { useAppDispatch, useAppSelector } from "~/app/hooks"; -import ColumnDropdown, { - ColumnMetadata, -} from "~/features/common/ColumnDropdown"; -import { isErrorResult } from "~/features/common/helpers"; -import { useAPIHelper } from "~/features/common/hooks"; -import { useSystemOrDatamapRoute } from "~/features/common/hooks/useSystemOrDatamapRoute"; -import { SystemsCheckboxTable } from "~/features/common/SystemsCheckboxTable"; -import { useUpsertSystemsMutation } from "~/features/system"; -import { System } from "~/types/api"; - -import { NextBreadcrumb } from "../common/nav/NextBreadcrumb"; -import { - changeStep, - reset, - selectSystemsForReview, -} from "./config-wizard.slice"; - -const ALL_COLUMNS: ColumnMetadata[] = [ - { name: "Name", attribute: "name" }, - { name: "System type", attribute: "system_type" }, - { name: "Resource ID", attribute: "fidesctl_meta.resource_id" }, -]; - -const ScanResults = () => { - const systems = useAppSelector(selectSystemsForReview); - const dispatch = useAppDispatch(); - const router = useRouter(); - const { systemOrDatamapRoute } = useSystemOrDatamapRoute(); - - const modal = useModal(); - const [upsertSystems] = useUpsertSystemsMutation(); - const [selectedSystems, setSelectedSystems] = useState(systems); - const [selectedColumns, setSelectedColumns] = - useState(ALL_COLUMNS); - const { handleError } = useAPIHelper(); - - /** - * Wrapper around router.push which also cleans up the config wizard state - * so that if we navigate back, the flow will start over. This is useful here - * as this is the last step of the wizard, so when we navigate away, we can - * reset the state. - */ - const navigateAndReset = (route: string) => { - router.push(route).then(() => { - dispatch(reset()); - }); - }; - - const confirmRegisterSelectedSystems = async () => { - const response = await upsertSystems(selectedSystems); - - if (isErrorResult(response)) { - return handleError(response.error); - } - - return navigateAndReset(systemOrDatamapRoute); - }; - - const handleSubmit = () => { - if (systems.length > selectedSystems.length) { - modal.confirm({ - title: "Warning", - content: ( - - You're registering {selectedSystems.length} of {systems.length}{" "} - systems available. Do you want to continue with registration or - cancel and register all systems now? - - ), - okText: "Continue", - cancelText: "Cancel", - centered: true, - onOk: confirmRegisterSelectedSystems, - }); - } else { - confirmRegisterSelectedSystems(); - } - }; - - const handleCancel = () => { - dispatch(changeStep(2)); - }; - - return ( - - - { - e.preventDefault(); - handleCancel(); - }, - }, - { title: "Authenticate" }, - { title: "Scan results" }, - ]} - /> - - {systems.length === 0 ? ( - <> - - No results were found for your infrastructure scan. - - - - - - ) : ( - <> - - - Below are the results of your infrastructure scan. To continue, - select the systems you would like registered in your data map - and reports. - - - - - - - - - - - - - )} - - - ); -}; - -export default ScanResults; diff --git a/clients/admin-ui/src/features/config-wizard/ScannerLoading.tsx b/clients/admin-ui/src/features/config-wizard/ScannerLoading.tsx deleted file mode 100644 index f9a4de325f7..00000000000 --- a/clients/admin-ui/src/features/config-wizard/ScannerLoading.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - ChakraCloseButton as CloseButton, - ChakraHStack as HStack, - ChakraStack as Stack, - ChakraText as Text, - Spin, - useModal, -} from "fidesui"; - -const warningModalMessage = ( - <> - - Warning, you are about to cancel the scan! - - - If you cancel scanning, the scanner will stop and no systems will be - returned. - - - Are you sure you want to cancel? - - -); - -interface Props { - title: string; - onClose: () => void; -} -const ScannerLoading = ({ title, onClose }: Props) => { - const modal = useModal(); - - const openCancelWarning = () => { - modal.confirm({ - title: "Cancel Scan!", - content: warningModalMessage, - okText: "Yes, Cancel", - cancelText: "No, Continue Scanning", - centered: true, - onOk: onClose, - }); - }; - - return ( - - - - {title} - - - - - - - - - ); -}; - -export default ScannerLoading; diff --git a/clients/admin-ui/src/features/config-wizard/config-wizard.slice.ts b/clients/admin-ui/src/features/config-wizard/config-wizard.slice.ts deleted file mode 100644 index 91ac43693ba..00000000000 --- a/clients/admin-ui/src/features/config-wizard/config-wizard.slice.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit"; - -import type { RootState } from "~/app/store"; -import { DEFAULT_ORGANIZATION_FIDES_KEY } from "~/features/organization"; -import { Organization, System } from "~/types/api"; - -import { STEPS } from "./constants"; -import { AddSystemMethods, SystemMethods } from "./types"; - -export interface State { - step: number; - organization?: Organization; - /** - * The systems that were returned by a system scan, some of which the user will select for review. - * These are persisted to the backend after the "Describe" step. - */ - systemsForReview?: System[]; - addSystemsMethod: AddSystemMethods; -} - -const initialState: State = { - step: 0, - addSystemsMethod: SystemMethods.MANUAL, -}; - -export const configWizardSlice = createSlice({ - name: "configWizard", - initialState, - reducers: { - /** - * With no argument: increment to the next step. - * With an argument: switch to that step. - */ - changeStep: (draftState, action: PayloadAction) => { - const step = action.payload; - - if (step === undefined) { - draftState.step += 1; - } else { - draftState.step = step; - } - - // Ensure the step number stays in the valid range. - if (draftState.step < 1) { - draftState.step = 1; - } else if (STEPS.length < draftState.step) { - draftState.step = STEPS.length - 1; - } - }, - setOrganization: (draftState, action: PayloadAction) => { - draftState.organization = action.payload; - }, - setSystemsForReview: (draftState, action: PayloadAction) => { - draftState.systemsForReview = action.payload; - }, - chooseSystemsForReview: (draftState, action: PayloadAction) => { - const fidesKeySet = new Set(action.payload); - draftState.systemsForReview = (draftState.systemsForReview ?? []).filter( - (system) => fidesKeySet.has(system.fides_key), - ); - }, - setAddSystemsMethod: ( - draftState, - action: PayloadAction, - ) => { - draftState.addSystemsMethod = action.payload; - }, - /** - * Reset the wizard to its initial state, except for the organization which will probably - * be the same if the user comes back. - */ - reset: (draftState) => ({ - ...initialState, - organization: draftState.organization, - }), - }, -}); - -export const { - changeStep, - reset, - setOrganization, - setSystemsForReview, - chooseSystemsForReview, - setAddSystemsMethod, -} = configWizardSlice.actions; - -export const { reducer } = configWizardSlice; - -const selectConfigWizard = (state: RootState) => state.configWizard; - -export const selectStep = createSelector( - selectConfigWizard, - (state) => state.step, -); - -export const selectOrganizationFidesKey = createSelector( - selectConfigWizard, - (state) => state.organization?.fides_key ?? DEFAULT_ORGANIZATION_FIDES_KEY, -); - -export const selectSystemsForReview = createSelector( - selectConfigWizard, - (state) => state.systemsForReview ?? [], -); - -export const selectAddSystemsMethod = createSelector( - selectConfigWizard, - (state) => state.addSystemsMethod, -); diff --git a/clients/admin-ui/src/features/config-wizard/constants.tsx b/clients/admin-ui/src/features/config-wizard/constants.tsx deleted file mode 100644 index 8f7a4765168..00000000000 --- a/clients/admin-ui/src/features/config-wizard/constants.tsx +++ /dev/null @@ -1,58 +0,0 @@ -export const STEPS = [ - { - number: 1, - name: "Organization setup", - }, - { - number: 2, - name: "Add systems", - }, - { - number: 3, - name: "Authenticate scanner", - }, - { - number: 4, - name: "Scan results", - }, -]; - -// Source: https://docs.aws.amazon.com/general/latest/gr/rande.html -export const AWS_REGION_OPTIONS = [ - { label: "US East (Ohio)", value: "us-east-2" }, - { label: "US East (N. Virginia)", value: "us-east-1" }, - { label: "US West (N. California)", value: "us-west-1" }, - { label: "US West (Oregon)", value: "us-west-2" }, - { label: "Africa (Cape Town)", value: "af-south-1" }, - { label: "Asia Pacific (Hong Kong)", value: "ap-east-1" }, - { label: "Asia Pacific (Hyderabad)", value: "ap-south-2" }, - { label: "Asia Pacific (Jakarta)", value: "ap-southeast-3" }, - { label: "Asia Pacific (Malaysia)", value: "ap-southeast-5" }, - { label: "Asia Pacific (Melbourne)", value: "ap-southeast-4" }, - { label: "Asia Pacific (Mumbai)", value: "ap-south-1" }, - { label: "Asia Pacific (New Zealand)", value: "ap-southeast-6" }, - { label: "Asia Pacific (Osaka)", value: "ap-northeast-3" }, - { label: "Asia Pacific (Seoul)", value: "ap-northeast-2" }, - { label: "Asia Pacific (Singapore)", value: "ap-southeast-1" }, - { label: "Asia Pacific (Sydney)", value: "ap-southeast-2" }, - { label: "Asia Pacific (Taipei)", value: "ap-east-2" }, - { label: "Asia Pacific (Thailand)", value: "ap-southeast-7" }, - { label: "Asia Pacific (Tokyo)", value: "ap-northeast-1" }, - { label: "Canada (Central)", value: "ca-central-1" }, - { label: "Canada West (Calgary)", value: "ca-west-1" }, - { label: "China (Beijing)", value: "cn-north-1" }, - { label: "China (Ningxia)", value: "cn-northwest-1" }, - { label: "Europe (Frankfurt)", value: "eu-central-1" }, - { label: "Europe (Ireland)", value: "eu-west-1" }, - { label: "Europe (London)", value: "eu-west-2" }, - { label: "Europe (Milan)", value: "eu-south-1" }, - { label: "Europe (Paris)", value: "eu-west-3" }, - { label: "Europe (Spain)", value: "eu-south-2" }, - { label: "Europe (Stockholm)", value: "eu-north-1" }, - { label: "Europe (Zurich)", value: "eu-central-2" }, - { label: "Israel (Tel Aviv)", value: "il-central-1" }, - { label: "Mexico (Central)", value: "mx-central-1" }, - { label: "Middle East (Bahrain)", value: "me-south-1" }, - { label: "Middle East (UAE)", value: "me-central-1" }, - { label: "South America (São Paulo)", value: "sa-east-1" }, -]; diff --git a/clients/admin-ui/src/features/config-wizard/helpers.tsx b/clients/admin-ui/src/features/config-wizard/helpers.tsx deleted file mode 100644 index e7cba03a862..00000000000 --- a/clients/admin-ui/src/features/config-wizard/helpers.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { Dataset, System } from "~/types/api"; - -export const isSystem = (sd: System | Dataset): sd is System => - "system_type" in sd; diff --git a/clients/admin-ui/src/features/config-wizard/scanner.slice.ts b/clients/admin-ui/src/features/config-wizard/scanner.slice.ts deleted file mode 100644 index 8ee29644025..00000000000 --- a/clients/admin-ui/src/features/config-wizard/scanner.slice.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { baseApi } from "~/features/common/api.slice"; -import { GenerateRequestPayload, GenerateResponse } from "~/types/api"; - -const scannerApi = baseApi.injectEndpoints({ - endpoints: (build) => ({ - generate: build.mutation({ - query: (body) => ({ - url: `generate`, - method: "POST", - body, - }), - }), - }), -}); - -export const { useGenerateMutation } = scannerApi; diff --git a/clients/admin-ui/src/features/config-wizard/types.ts b/clients/admin-ui/src/features/config-wizard/types.ts deleted file mode 100644 index 2323bbf14ae..00000000000 --- a/clients/admin-ui/src/features/config-wizard/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ValidTargets } from "~/types/api"; - -export enum SystemMethods { - MANUAL = "manual", -} - -export type AddSystemMethods = ValidTargets | SystemMethods; diff --git a/clients/admin-ui/src/features/datamap/GetStarted.tsx b/clients/admin-ui/src/features/datamap/GetStarted.tsx index fc6b58270f5..c4d47cb04b9 100644 --- a/clients/admin-ui/src/features/datamap/GetStarted.tsx +++ b/clients/admin-ui/src/features/datamap/GetStarted.tsx @@ -6,7 +6,7 @@ import { ChakraText as Text, } from "fidesui"; -import { ADD_SYSTEMS_ROUTE } from "../common/nav/routes"; +import { ADD_SYSTEMS_MULTIPLE_ROUTE } from "../common/nav/routes"; const GetStarted = () => { return ( @@ -34,7 +34,7 @@ const GetStarted = () => { Let's get started!