Skip to content

Commit

Permalink
Privacy declaration with composite ID (#2905)
Browse files Browse the repository at this point in the history
Co-authored-by: Kelsey Thomas <101993653+Kelsey-Ethyca@users.noreply.github.com>
  • Loading branch information
allisonking and Kelsey-Ethyca committed Mar 24, 2023
1 parent 36f55e7 commit eadb57b
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 47 deletions.
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ The types of changes are:

## [Unreleased](https://github.com/ethyca/fides/compare/2.9.1...main)

<<<<<<< HEAD
=======
### Added

* Allow users to configure their username and password via the config file [#2884](https://github.com/ethyca/fides/pull/2884)

### Changed

* Improved standard layout for large width screens and polished misc. pages [#2869](https://github.com/ethyca/fides/pull/2869)
* Changed UI paths in the admin-ui [#2869](https://github.com/ethyca/fides/pull/2892)
* `/add-systems/new` --> `/add-systems/manual`
* `/system` --> `/systems`
* Added individual ID routes for systems [#2902](https://github.com/ethyca/fides/pull/2902)
* Deprecated adding scopes to users directly; you can only add roles. [#2848](https://github.com/ethyca/fides/pull/2848/files)
* Changed About Fides page to say "Fides Core Version:" over "Version". [#2899](https://github.com/ethyca/fides/pull/2899)
* Polish Admin UI header & navigation [#2897](https://github.com/ethyca/fides/pull/2897)
* Give new users a "viewer" role by default [#2900](https://github.com/ethyca/fides/pull/2900)


### Fixed

* Restricted Contributors from being able to create Owners [#2888](https://github.com/ethyca/fides/pull/2888)
* Allow multiple data uses as long as their processing activity name is different [#2905](https://github.com/ethyca/fides/pull/2905)

### Fixed
* Allow for dynamic aspect ratio for logo on Privacy Center 404 [#2895](https://github.com/ethyca/fides/pull/2895)
* Allow for dynamic aspect ratio for logo on consent page [#2895](https://github.com/ethyca/fides/pull/2895)

>>>>>>> 5f901e174 (Privacy declaration with composite ID (#2905))
## [2.9.1](https://github.com/ethyca/fides/compare/2.9.0...2.9.1)

### Added
Expand Down
56 changes: 54 additions & 2 deletions clients/admin-ui/cypress/e2e/systems.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,30 @@ describe("System management page", () => {
beforeEach(() => {
stubSystemCrud();
stubTaxonomyEntities();
<<<<<<< HEAD
cy.visit("/system");
=======
cy.fixture("systems/systems.json").then((systems) => {
cy.intercept("GET", "/api/v1/system/*", {
body: systems[0],
}).as("getFidesctlSystem");
});
cy.visit(SYSTEM_ROUTE);
});

it("Can go directly to a system's edit page", () => {
cy.visit("/systems/configure/fidesctl_system");
cy.wait("@getFidesctlSystem");
cy.getByTestId("input-name").should("have.value", "Fidesctl System");

cy.intercept("GET", "/api/v1/system/*", {
statusCode: 404,
}).as("getNotFoundSystem");

// and can render a not found state
cy.visit("/systems/configure/system-that-does-not-exist");
cy.getByTestId("system-not-found");
>>>>>>> 5f901e174 (Privacy declaration with composite ID (#2905))
});

it("Can go through the edit flow", () => {
Expand Down Expand Up @@ -293,6 +316,7 @@ describe("System management page", () => {
cy.wait(["@getDataCategories", "@getDataSubjects", "@getDataUses"]);
cy.getByTestId("new-declaration-form").within(() => {
cy.getByTestId("input-data_use").type(`${secondDataUse}{enter}`);
cy.getByTestId("input-name").type(`test-1{enter}`);
cy.getByTestId("input-data_categories").type(`user.biometric{enter}`);
cy.getByTestId("input-data_subjects").type(`anonymous{enter}`);
cy.getByTestId("save-btn").click();
Expand Down Expand Up @@ -410,18 +434,24 @@ describe("System management page", () => {
stubTaxonomyEntities();
});

<<<<<<< HEAD
it("warns when a data use is being added that is already used", () => {
cy.visit("/system");
=======
it("warns when a data use and processing activity is being added that is already used", () => {
cy.visit(SYSTEM_ROUTE);
>>>>>>> 5f901e174 (Privacy declaration with composite ID (#2905))
cy.getByTestId("system-fidesctl_system").within(() => {
cy.getByTestId("more-btn").click();
cy.getByTestId("edit-btn").click();
});
// "improve.system" is already being used
// "improve.system" and "Store system data." are already being used
cy.getByTestId("tab-Data uses").click();
cy.getByTestId("add-btn").click();
cy.wait(["@getDataCategories", "@getDataSubjects", "@getDataUses"]);
cy.getByTestId("new-declaration-form").within(() => {
cy.getByTestId("input-data_use").type(`improve.system{enter}`);
cy.getByTestId("input-name").type(`Store system data.{enter}`);
cy.getByTestId("input-data_categories").type(`user.biometric{enter}`);
cy.getByTestId("input-data_subjects").type(`anonymous{enter}`);
cy.getByTestId("save-btn").click();
Expand Down Expand Up @@ -454,14 +484,36 @@ describe("System management page", () => {
cy.getByTestId(`accordion-header-improve.system`);
cy.getByTestId(`accordion-header-advertising`).click();

// try to change 'advertising' to 'improve.system'
// try to change 'advertising' to 'improve.system' and make their names match
cy.getByTestId("advertising-form").within(() => {
cy.getByTestId("input-data_use").type(`improve.system{enter}`);
cy.getByTestId("input-name").clear().type(`Store system data.{enter}`);
cy.getByTestId("save-btn").click();
});
cy.getByTestId("toast-error-msg");
});

it("can have multiple of the same data use if the names are different", () => {
cy.visit(SYSTEM_ROUTE);
cy.getByTestId("system-fidesctl_system").within(() => {
cy.getByTestId("more-btn").click();
cy.getByTestId("edit-btn").click();
});
// "improve.system" and "Store system data." are already being used
// use "improve.system" again but a different name
cy.getByTestId("tab-Data uses").click();
cy.getByTestId("add-btn").click();
cy.wait(["@getDataCategories", "@getDataSubjects", "@getDataUses"]);
cy.getByTestId("new-declaration-form").within(() => {
cy.getByTestId("input-data_use").type(`improve.system{enter}`);
cy.getByTestId("input-name").type(`A different description.{enter}`);
cy.getByTestId("input-data_categories").type(`user.biometric{enter}`);
cy.getByTestId("input-data_subjects").type(`anonymous{enter}`);
cy.getByTestId("save-btn").click();
});
cy.getByTestId("toast-success-msg");
});

describe("delete privacy declaration", () => {
beforeEach(() => {
cy.intercept("GET", "/api/v1/system", {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,33 @@ import {
} from "@fidesui/react";
import { Form, Formik } from "formik";

import { PrivacyDeclaration } from "~/types/api";

import {
DataProps,
PrivacyDeclarationFormComponents,
usePrivacyDeclarationForm,
ValidationSchema,
} from "./PrivacyDeclarationForm";
import { PrivacyDeclarationWithId } from "./types";

interface AccordionProps extends DataProps {
privacyDeclarations: PrivacyDeclaration[];
privacyDeclarations: PrivacyDeclarationWithId[];
onEdit: (
oldDeclaration: PrivacyDeclaration,
newDeclaration: PrivacyDeclaration
oldDeclaration: PrivacyDeclarationWithId,
newDeclaration: PrivacyDeclarationWithId
) => Promise<boolean>;
onDelete: (declaration: PrivacyDeclaration) => Promise<boolean>;
onDelete: (declaration: PrivacyDeclarationWithId) => Promise<boolean>;
}

const PrivacyDeclarationAccordionItem = ({
privacyDeclaration,
onEdit,
onDelete,
...dataProps
}: { privacyDeclaration: PrivacyDeclaration } & Omit<
}: { privacyDeclaration: PrivacyDeclarationWithId } & Omit<
AccordionProps,
"privacyDeclarations"
>) => {
const handleEdit = (newValues: PrivacyDeclaration) =>
const handleEdit = (newValues: PrivacyDeclarationWithId) =>
onEdit(privacyDeclaration, newValues);

const { initialValues, renderHeader, handleSubmit } =
Expand Down Expand Up @@ -100,7 +99,7 @@ const PrivacyDeclarationAccordion = ({
// The closest is 'data_use' but that is only enforced on the frontend and can change
// This results in the "Saved" indicator not appearing if you change the 'data_use' in the form
// The fix would be to enforce a key, either on the backend, or through a significant workaround on the frontend
key={dec.data_use}
key={dec.id}
privacyDeclaration={dec}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
PrivacyDeclaration,
} from "~/types/api";

import { PrivacyDeclarationWithId } from "./types";

export const ValidationSchema = Yup.object().shape({
data_categories: Yup.array(Yup.string())
.min(1, "Must assign at least one data category")
Expand All @@ -46,6 +48,20 @@ const defaultInitialValues: PrivacyDeclaration = {

type FormValues = typeof defaultInitialValues;

const transformPrivacyDeclarationToHaveId = (
privacyDeclaration: PrivacyDeclaration
) => ({
...privacyDeclaration,
id: privacyDeclaration.name
? `${privacyDeclaration.data_use} - ${privacyDeclaration.name}`
: privacyDeclaration.data_use,
});

export const transformPrivacyDeclarationsToHaveId = (
privacyDeclarations: PrivacyDeclaration[]
): PrivacyDeclarationWithId[] =>
privacyDeclarations.map(transformPrivacyDeclarationToHaveId);

export interface DataProps {
allDataCategories: DataCategory[];
allDataUses: DataUse[];
Expand All @@ -72,7 +88,7 @@ export const PrivacyDeclarationFormComponents = ({
: [];

const handleDelete = async () => {
await onDelete(initialValues);
await onDelete(transformPrivacyDeclarationToHaveId(initialValues));
deleteModal.onClose();
};

Expand Down Expand Up @@ -179,7 +195,9 @@ export const usePrivacyDeclarationForm = ({
(du) => du.fides_key === initialValues.data_use
)[0];
if (thisDataUse) {
return thisDataUse.name;
return initialValues.name
? `${thisDataUse.name} - ${initialValues.name}`
: thisDataUse.name;
}
return undefined;
}, [allDataUses, initialValues]);
Expand All @@ -188,7 +206,10 @@ export const usePrivacyDeclarationForm = ({
values: FormValues,
formikHelpers: FormikHelpers<FormValues>
) => {
const success = await onSubmit(values, formikHelpers);
const success = await onSubmit(
transformPrivacyDeclarationToHaveId(values),
formikHelpers
);
if (success) {
// Reset state such that isDirty will be checked again before next save
formikHelpers.resetForm({ values });
Expand Down Expand Up @@ -225,11 +246,11 @@ export const usePrivacyDeclarationForm = ({

interface Props {
onSubmit: (
values: PrivacyDeclaration,
values: PrivacyDeclarationWithId,
formikHelpers: FormikHelpers<PrivacyDeclaration>
) => Promise<boolean>;
onDelete: (declaration: PrivacyDeclaration) => Promise<boolean>;
initialValues?: PrivacyDeclaration;
onDelete: (declaration: PrivacyDeclarationWithId) => Promise<boolean>;
initialValues?: PrivacyDeclarationWithId;
}

export const PrivacyDeclarationForm = ({
Expand Down
Loading

0 comments on commit eadb57b

Please sign in to comment.