Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PROD-2117 frontend generate served notice history guid and pass to be #4933

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ The types of changes are:
### Changed
- Set default ports for local development of client projects (:3001 for privacy center and :3000 for admin-ui) [#4912](https://github.com/ethyca/fides/pull/4912)
- Update privacy center port to :3001 for nox [#4918](https://github.com/ethyca/fides/pull/4918)
- Optimize speed by generating the uuids in the client side for consent requests [#4933](https://github.com/ethyca/fides/pull/4933)


## [2.37.0](https://github.com/ethyca/fides/compare/2.36.0...2.37.0)

Expand Down
2 changes: 1 addition & 1 deletion clients/fides-js/src/components/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import {
shouldResurfaceConsent,
} from "../lib/consent-utils";
import { dispatchFidesEvent } from "../lib/events";
import { useHasMounted } from "../lib/hooks";
import type { I18n } from "../lib/i18n";

import ConsentModal from "./ConsentModal";
import ConsentContent from "./ConsentContent";
import "./fides.css";
import { blockPageScrolling, unblockPageScrolling } from "../lib/ui-utils";
import { FIDES_OVERLAY_WRAPPER } from "../lib/consent-constants";
import { useHasMounted } from "../lib/hooks";

interface RenderBannerProps {
isOpen: boolean;
Expand Down
9 changes: 5 additions & 4 deletions clients/fides-js/src/components/notices/NoticeOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "../fides.css";

import { FunctionComponent, h } from "preact";

import { useCallback, useEffect, useMemo, useState } from "preact/hooks";

import { getConsentContext } from "../../lib/consent-context";
Expand All @@ -21,7 +22,6 @@ import {
updateCookieFromNoticePreferences,
} from "../../lib/cookie";
import { dispatchFidesEvent } from "../../lib/events";
import { useConsentServed } from "../../lib/hooks";
import {
selectBestExperienceConfigTranslation,
selectBestNoticeTranslation,
Expand All @@ -34,6 +34,7 @@ import Overlay from "../Overlay";
import { OverlayProps } from "../types";
import { NoticeToggleProps, NoticeToggles } from "./NoticeToggles";
import { useI18n } from "../../lib/i18n/i18n-context";
import { useConsentServed } from "../../lib/hooks";

/**
* Define a special PrivacyNoticeItem, where we've narrowed the list of
Expand Down Expand Up @@ -148,7 +149,7 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
};
});

const { servedNotice } = useConsentServed({
const { servedNoticeHistoryId } = useConsentServed({
privacyExperienceConfigHistoryId,
privacyNoticeHistoryIds: privacyNoticeItems.reduce((ids, e) => {
const id = e.bestTranslation?.privacy_notice_history_id;
Expand Down Expand Up @@ -197,7 +198,7 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
options,
userLocationString: fidesRegionString,
cookie,
servedNoticeHistoryId: servedNotice?.served_notice_history_id,
servedNoticeHistoryId,
updateCookie: (oldCookie) =>
updateCookieFromNoticePreferences(
oldCookie,
Expand All @@ -214,7 +215,7 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
options,
privacyExperienceConfigHistoryId,
privacyNoticeItems,
servedNotice,
servedNoticeHistoryId,
]
);

Expand Down
8 changes: 4 additions & 4 deletions clients/fides-js/src/components/tcf/TcfOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { transformTcfPreferencesToCookieKeys } from "../../lib/cookie";
import InitialLayer from "./InitialLayer";
import TcfTabs from "./TcfTabs";
import Button from "../Button";
import { useConsentServed } from "../../lib/hooks";
import VendorInfoBanner from "./VendorInfoBanner";
import { dispatchFidesEvent } from "../../lib/events";
import { selectBestExperienceConfigTranslation } from "../../lib/i18n";
Expand All @@ -47,6 +46,7 @@ import {
transformUserPreferenceToBoolean,
} from "../../lib/shared-consent-utils";
import { useI18n } from "../../lib/i18n/i18n-context";
import { useConsentServed } from "../../lib/hooks";

const resolveConsentValueFromTcfModel = (
model:
Expand Down Expand Up @@ -255,7 +255,7 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
return undefined;
}, [experience, i18n]);

const { servedNotice } = useConsentServed({
const { servedNoticeHistoryId } = useConsentServed({
privacyExperienceConfigHistoryId,
privacyNoticeHistoryIds: [],
options,
Expand All @@ -280,7 +280,7 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
cookie,
debug: options.debug,
tcf,
servedNoticeHistoryId: servedNotice?.served_notice_history_id,
servedNoticeHistoryId,
updateCookie: (oldCookie) =>
updateCookie(oldCookie, tcf, enabledIds, experience),
});
Expand All @@ -292,7 +292,7 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
fidesRegionString,
options,
privacyExperienceConfigHistoryId,
servedNotice,
servedNoticeHistoryId,
]
);

Expand Down
13 changes: 13 additions & 0 deletions clients/fides-js/src/lib/common-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
export const raise = (message: string) => {
throw new Error(message);
};

/**
* Extracts the id value of each object in the list and returns a list
* of IDs, either strings or numbers based on the IDs' type.
*/
export const extractIds = <T extends { id: string | number }[]>(
modelList?: T
): any[] => {
if (!modelList) {
return [];
}
return modelList.map((model) => model.id);
};
2 changes: 2 additions & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,8 @@ export enum ServingComponent {
* Request body when indicating that notices were served in the UI
*/
export type RecordConsentServedRequest = {
served_notice_history_id: string; // a generated uuidv4 string

browser_identity: Identity;
code?: string;
privacy_notice_history_ids?: Array<string>;
Expand Down
4 changes: 4 additions & 0 deletions clients/fides-js/src/lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as useConsentServed } from "./useConsentServed";
export { default as useHasMounted } from "./useHasMounted";
export { default as useUUID4 } from "./useUUID4";
export { default as useDisclosure } from "./useDisclosure";
Original file line number Diff line number Diff line change
@@ -1,81 +1,14 @@
import { useEffect, useState, useCallback } from "preact/hooks";
import { FidesEvent } from "./events";
import { useEffect, useCallback, useState } from "preact/hooks";
import { v4 as uuidv4 } from "uuid";
import { FidesEvent } from "../events";
import {
FidesInitOptions,
PrivacyExperience,
RecordConsentServedRequest,
ServingComponent,
RecordsServedResponse,
} from "./consent-types";
import { patchNoticesServed } from "../services/api";

/**
* Hook which tracks if the app has mounted yet.
*
* Used to make sure the server and client UIs match for hydration
* Adapted from https://www.joshwcomeau.com/react/the-perils-of-rehydration/
*/
export const useHasMounted = () => {
const [hasMounted, setHasMounted] = useState(false);

useEffect(() => {
setHasMounted(true);
}, []);

return hasMounted;
};

/**
* Hook to facilitate showing/hiding while adhering to WAI
* based on chakra-ui's `useDisclosure`
*/
export const useDisclosure = ({ id }: { id: string }) => {
const [isOpen, setIsOpen] = useState(false);

const onClose = useCallback(() => setIsOpen(false), []);
const onOpen = useCallback(() => setIsOpen(true), []);

const onToggle = useCallback(() => {
if (isOpen) {
onClose();
} else {
onOpen();
}
}, [isOpen, onOpen, onClose]);

const getButtonProps = () => ({
"aria-expanded": isOpen,
"aria-controls": id,
onClick: onToggle,
});

const getDisclosureProps = () => ({
id,
className: isOpen ? "fides-disclosure-visible" : "fides-disclosure-hidden",
});

return {
isOpen,
onOpen,
onClose,
onToggle,
getButtonProps,
getDisclosureProps,
};
};

/**
* Extracts the id value of each object in the list and returns a list
* of IDs, either strings or numbers based on the IDs' type.
*/
const extractIds = <T extends { id: string | number }[]>(
modelList?: T
): any[] => {
if (!modelList) {
return [];
}
return modelList.map((model) => model.id);
};
} from "../consent-types";
import { patchNoticesServed } from "../../services/api";
import { extractIds } from "../common-utils";

export const useConsentServed = ({
options,
Expand All @@ -92,8 +25,9 @@ export const useConsentServed = ({
userGeography?: string;
acknowledgeMode?: boolean;
}) => {
const [servedNotice, setServedNotice] =
useState<RecordsServedResponse | null>(null);
const [servedNoticeHistoryId, setServedNoticeHistoryId] = useState<string>(
uuidv4()
);

const handleUIEvent = useCallback(
async (event: FidesEvent) => {
Expand All @@ -114,8 +48,13 @@ export const useConsentServed = ({
return;
}

// Create new uuid for each served notice
const newUUID = uuidv4();
setServedNoticeHistoryId(newUUID);

// Construct the notices-served API request and send!
const request: RecordConsentServedRequest = {
served_notice_history_id: newUUID,
browser_identity: event.detail.identity,
privacy_experience_config_history_id:
privacyExperienceConfigHistoryId || "",
Expand Down Expand Up @@ -145,13 +84,12 @@ export const useConsentServed = ({
),
serving_component: String(event.detail.extraDetails.servingComponent),
};
const result = await patchNoticesServed({

// Send the request to the notices-served API
patchNoticesServed({
request,
options,
});
if (result) {
setServedNotice(result);
}
},
[
privacyExperienceConfigHistoryId,
Expand All @@ -170,5 +108,6 @@ export const useConsentServed = ({
};
}, [handleUIEvent]);

return { servedNotice };
return { servedNoticeHistoryId };
};
export default useConsentServed;
41 changes: 41 additions & 0 deletions clients/fides-js/src/lib/hooks/useDisclosure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useCallback, useState } from "preact/hooks";

/**
* Hook to facilitate showing/hiding while adhering to WAI
* based on chakra-ui's `useDisclosure`
*/
const useDisclosure = ({ id }: { id: string }) => {
const [isOpen, setIsOpen] = useState(false);

const onClose = useCallback(() => setIsOpen(false), []);
const onOpen = useCallback(() => setIsOpen(true), []);

const onToggle = useCallback(() => {
if (isOpen) {
onClose();
} else {
onOpen();
}
}, [isOpen, onOpen, onClose]);

const getButtonProps = () => ({
"aria-expanded": isOpen,
"aria-controls": id,
onClick: onToggle,
});

const getDisclosureProps = () => ({
id,
className: isOpen ? "fides-disclosure-visible" : "fides-disclosure-hidden",
});

return {
isOpen,
onOpen,
onClose,
onToggle,
getButtonProps,
getDisclosureProps,
};
};
export default useDisclosure;
18 changes: 18 additions & 0 deletions clients/fides-js/src/lib/hooks/useHasMounted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect, useState } from "preact/hooks";

/**
* Hook which tracks if the app has mounted yet.
*
* Used to make sure the server and client UIs match for hydration
* Adapted from https://www.joshwcomeau.com/react/the-perils-of-rehydration/
*/
const useHasMounted = () => {
const [hasMounted, setHasMounted] = useState(false);

useEffect(() => {
setHasMounted(true);
}, []);

return hasMounted;
};
export default useHasMounted;
14 changes: 14 additions & 0 deletions clients/fides-js/src/lib/hooks/useUUID4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useState } from "preact/hooks";
import { v4 as uuidv4 } from "uuid";

/**
* Custom hook that generates a UUIDv4.
* The returned value stays the same for the lifetime of the component.
* @returns The generated UUIDv4.
*/
const useUUID4 = () => {
const [uuid] = useState<string>(uuidv4());

return uuid;
};
export default useUUID4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to me to get imported anywhere, is it still needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, it's not needed. It was used in an earlier approach, but then had to change it. I left it as it might be useful for someone, but I can easly remove it too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think it'll still be useful in the future I don't have a problem leaving it in, just wanted to make sure it wasn't a total oversight!

2 changes: 2 additions & 0 deletions clients/fides-js/src/lib/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ContainerNode } from "preact";
import { v4 as uuidv4 } from "uuid";

import {
DEFAULT_MODAL_LINK_LABEL,
Expand Down Expand Up @@ -168,6 +169,7 @@ const automaticallyApplyGPCPreferences = async ({

if (gpcApplied) {
await updateConsentPreferences({
servedNoticeHistoryId: uuidv4(),
consentPreferencesToSave,
privacyExperienceConfigHistoryId,
experience: effectiveExperience,
Expand Down
Loading
Loading