Skip to content

Commit

Permalink
Merge branch 'main' into aking/fidesplus-738/privacy-notice-empty-state
Browse files Browse the repository at this point in the history
  • Loading branch information
allisonking committed Apr 12, 2023
2 parents fe78379 + 65005e5 commit 8b91d63
Show file tree
Hide file tree
Showing 45 changed files with 1,101 additions and 841 deletions.
13 changes: 4 additions & 9 deletions .github/workflows/cli_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ env:
DEFAULT_PYTHON_VERSION: "3.10.11"

jobs:
Fides-Deploy:
# Basic smoke test of a local install of the fides Python CLI
Fides-Install:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand All @@ -32,14 +33,8 @@ jobs:
- name: Install Nox
run: pip install nox>=2022

- name: Build the sample image
run: nox -s "build(sample)"

- name: Install fides
run: pip install .

- name: Start the sample application
run: fides deploy up --no-pull --no-init

- name: Stop the sample application
run: fides deploy down
- name: Run `fides --version`
run: fides --version
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The types of changes are:
- Support for uploading custom connector templates via the UI [#2997](https://github.com/ethyca/fides/pull/2997)
- Add a backwards-compatible workflow for saving and propagating consent preferences with respect to Privacy Notices [#3016](https://github.com/ethyca/fides/pull/3016)
- Empty state for privacy notices [#3027](https://github.com/ethyca/fides/pull/3027)
- Added Data flow modal [#3008](https://github.com/ethyca/fides/pull/3008)
- Update datamap table export [#3038](https://github.com/ethyca/fides/pull/3038)

### Changed

Expand All @@ -52,6 +54,7 @@ The types of changes are:
### Developer Experience

- Nox commands for git tagging to support feature branch builds [#2979](https://github.com/ethyca/fides/pull/2979)
- Changed test environment (`nox -s fides_env`) to run `fides deploy` for local testing [#3071](https://github.com/ethyca/fides/pull/3017)

### Removed

Expand Down
32 changes: 32 additions & 0 deletions clients/admin-ui/cypress/e2e/systems.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,4 +565,36 @@ describe("System management page", () => {
});
});
});

describe("Data flow", () => {
beforeEach(() => {
stubSystemCrud();
stubTaxonomyEntities();
cy.fixture("systems/systems.json").then((systems) => {
cy.intercept("GET", "/api/v1/system/*", {
body: systems[1],
}).as("getFidesctlSystem");
});

cy.visit(SYSTEM_ROUTE);
cy.getByTestId("system-fidesctl_system").within(() => {
cy.getByTestId("more-btn").click();
cy.getByTestId("edit-btn").click();
});
cy.getByTestId("tab-Data flow").click();
});

it("Can navigate to the data flow tab", () => {
cy.getByTestId("data-flow-accordion").should("exist");
});

it("Can open both accordion items", () => {
cy.getByTestId("data-flow-accordion").within(()=>{
cy.getByTestId("data-flow-button-Source").click();
cy.getByTestId("data-flow-panel-Source").should("exist");
cy.getByTestId("data-flow-button-Destination").click();
cy.getByTestId("data-flow-panel-Destination").should("exist");
})
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Accordion } from "@fidesui/react";
import React from "react";

import { System } from "~/types/api/models/System";

import { DataFlowAccordionForm } from "./DataFlowAccordionForm";

type DataFlowFormProps = {
system: System;
isSystemTab?: boolean;
};

export const DataFlowAccordion = ({
system,
isSystemTab,
}: DataFlowFormProps) => (
<Accordion allowToggle data-testid="data-flow-accordion">
<DataFlowAccordionForm
system={system}
isIngress
isSystemTab={isSystemTab}
/>
<DataFlowAccordionForm system={system} isSystemTab={isSystemTab} />
</Accordion>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import {
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Button,
ButtonGroup,
Flex,
Spacer,
Stack,
Tag,
Text,
useDisclosure,
useToast,
} from "@fidesui/react";
import { isErrorResult } from "common/helpers";
import { FormGuard } from "common/hooks/useIsAnyFormDirty";
import { GearLightIcon } from "common/Icon";
import { DataFlowSystemsDeleteTable } from "common/system-data-flow/DataFlowSystemsDeleteTable";
import DataFlowSystemsModal from "common/system-data-flow/DataFlowSystemsModal";
import { errorToastParams, successToastParams } from "common/toast";
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 = {
dataFlowSystems: [] as DataFlow[],
};

export type FormValues = typeof defaultInitialValues;

type DataFlowAccordionItemProps = {
isIngress?: boolean;
system: System;
isSystemTab?: boolean;
};

export const DataFlowAccordionForm = ({
system,
isIngress,
isSystemTab,
}: DataFlowAccordionItemProps) => {
const toast = useToast();
const flowType = isIngress ? "Source" : "Destination";
const pluralFlowType = `${flowType}s`;
const dataFlowSystemsModal = useDisclosure();
const [updateSystemMutationTrigger] = useUpdateSystemMutation();

useGetAllSystemsQuery();
const systems = useAppSelector(selectAllSystems);

const initialDataFlows = useMemo(() => {
let dataFlows = isIngress ? system.ingress : system.egress;
if (!dataFlows) {
dataFlows = [];
}
const systemFidesKeys = systems ? systems.map((s) => s.fides_key) : [];

return dataFlows.filter((df) => systemFidesKeys.includes(df.fides_key));
}, [isIngress, system, systems]);

const [assignedDataFlow, setAssignedDataFlows] =
useState<DataFlow[]>(initialDataFlows);

useEffect(() => {
setAssignedDataFlows(initialDataFlows);
}, [initialDataFlows]);

const handleSubmit = async (
{ dataFlowSystems }: FormValues,
{ resetForm }: FormikHelpers<FormValues>
) => {
const updatedSystem = {
...system,
ingress: isIngress ? dataFlowSystems : system.ingress,
egress: !isIngress ? dataFlowSystems : system.egress,
};
const result = await updateSystemMutationTrigger(updatedSystem);

if (isErrorResult(result)) {
toast(errorToastParams("Failed to update data flows"));
} else {
toast(successToastParams(`${pluralFlowType} updated`));
}

resetForm({ values: { dataFlowSystems } });
};

return (
<AccordionItem>
<AccordionButton
height="68px"
paddingLeft={isSystemTab ? 6 : 2}
data-testid={`data-flow-button-${flowType}`}
>
<Flex
alignItems="center"
justifyContent="start"
flex={1}
textAlign="left"
>
<Text fontSize="sm" lineHeight={5} fontWeight="semibold" mr={2}>
{pluralFlowType}
</Text>
{/* Commented out until we get copy for the tooltips */}
{/* <QuestionTooltip label="helpful tip" /> */}

<Tag
ml={2}
backgroundColor="primary.400"
borderRadius="6px"
color="white"
>
{assignedDataFlow.length}
</Tag>
<Spacer />
<AccordionIcon />
</Flex>
</AccordionButton>
<AccordionPanel
backgroundColor="gray.50"
padding={6}
data-testid={`data-flow-panel-${flowType}`}
>
<Stack
borderRadius="md"
backgroundColor="gray.50"
aria-selected="true"
spacing={4}
data-testid="selected"
>
<Formik initialValues={defaultInitialValues} onSubmit={handleSubmit}>
{({ isSubmitting, dirty, resetForm }) => (
<Form>
<FormGuard
id={`${system.fides_key}:${flowType}`}
name={`${flowType} Data Flow`}
/>
<Button
colorScheme="primary"
size="xs"
width="fit-content"
onClick={dataFlowSystemsModal.onOpen}
data-testid="assign-systems-btn"
rightIcon={<GearLightIcon />}
marginBottom={4}
>
{`Configure ${pluralFlowType.toLocaleLowerCase()}`}
</Button>
<DataFlowSystemsDeleteTable
systems={systems}
dataFlows={assignedDataFlow}
onDataFlowSystemChange={setAssignedDataFlows}
/>

<ButtonGroup marginTop={6}>
<Button
size="sm"
variant="outline"
disabled={!dirty && assignedDataFlow === initialDataFlows}
onClick={() => {
setAssignedDataFlows(initialDataFlows);
resetForm({
values: { dataFlowSystems: initialDataFlows },
});
}}
>
Cancel
</Button>
<Button
size="sm"
colorScheme="primary"
type="submit"
isLoading={isSubmitting}
disabled={!dirty && assignedDataFlow === initialDataFlows}
data-testid="save-btn"
>
Save
</Button>
</ButtonGroup>
{/* By conditionally rendering the modal, we force it to reset its state
whenever it opens */}
{dataFlowSystemsModal.isOpen ? (
<DataFlowSystemsModal
currentSystem={system}
systems={systems}
isOpen={dataFlowSystemsModal.isOpen}
onClose={dataFlowSystemsModal.onClose}
dataFlowSystems={assignedDataFlow}
onDataFlowSystemChange={setAssignedDataFlows}
flowType={flowType}
/>
) : null}
</Form>
)}
</Formik>
</Stack>
</AccordionPanel>
</AccordionItem>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
IconButton,
Table,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
} from "@fidesui/react";
import { TrashCanSolidIcon } from "common/Icon/TrashCanSolidIcon";
import { useFormikContext } from "formik";
import React from "react";

import { DataFlow, System } from "~/types/api";

type Props = {
systems: System[];
dataFlows: DataFlow[];
onDataFlowSystemChange: (systems: DataFlow[]) => void;
};

export const DataFlowSystemsDeleteTable = ({
systems,
dataFlows,
onDataFlowSystemChange,
}: Props) => {
const { setFieldValue } = useFormikContext();

const dataFlowKeys = dataFlows.map((f) => f.fides_key);

const onDelete = (dataFlow: System) => {
const updatedDataFlows = dataFlows.filter(
(dataFlowSystem) => dataFlowSystem.fides_key !== dataFlow.fides_key
);
setFieldValue("dataFlowSystems", updatedDataFlows);
onDataFlowSystemChange(updatedDataFlows);
};

return (
<Table size="sm" data-testid="assign-systems-delete-table">
<Thead>
<Tr>
<Th>System</Th>
<Th />
</Tr>
</Thead>
<Tbody>
{systems
.filter((system) => dataFlowKeys.includes(system.fides_key))
.map((system) => (
<Tr
key={system.fides_key}
_hover={{ bg: "gray.50" }}
data-testid={`row-${system.fides_key}`}
>
<Td>
<Text fontSize="xs" lineHeight={4} fontWeight="medium">
{system.name}
</Text>
</Td>
<Td textAlign="end">
<IconButton
background="gray.50"
aria-label="Unassign data flow from system"
icon={<TrashCanSolidIcon />}
variant="outline"
size="sm"
onClick={() => onDelete(system)}
data-testid="unassign-btn"
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
);
};
Loading

0 comments on commit 8b91d63

Please sign in to comment.