Skip to content

Commit

Permalink
Fides-js consent reporting calls (#3845)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisonking authored Jul 27, 2023
1 parent 6e1b4dd commit b50664e
Show file tree
Hide file tree
Showing 22 changed files with 595 additions and 149 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ The types of changes are:

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

### Added
- Additional consent reporting calls from `fides-js` [#3845](https://github.com/ethyca/fides/pull/3845)
- Additional consent reporting calls from privacy center [#3847](https://github.com/ethyca/fides/pull/3847)

### Fixed
- Fix datamap zoom for low system counts [#3835](https://github.com/ethyca/fides/pull/3835)

Expand Down
7 changes: 3 additions & 4 deletions clients/fides-js/src/components/ConsentButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,21 @@ const ConsentButtons = ({
onManagePreferencesClick,
onSave,
enabledKeys,
isAcknowledge,
isInModal,
}: {
experience: PrivacyExperience;
onSave: (noticeKeys: NoticeKeys) => void;
onManagePreferencesClick?: () => void;
enabledKeys: NoticeKeys;
isAcknowledge: boolean;
isInModal?: boolean;
}) => {
if (!experience.experience_config || !experience.privacy_notices) {
return null;
}

const { experience_config: config, privacy_notices: notices } = experience;
const isAllNoticeOnly = notices.every(
(n) => n.consent_mechanism === ConsentMechanism.NOTICE_ONLY
);

const handleAcceptAll = () => {
onSave(notices.map((n) => n.notice_key));
Expand All @@ -43,7 +42,7 @@ const ConsentButtons = ({
);
};

if (isAllNoticeOnly) {
if (isAcknowledge) {
return (
<div
className={`fides-acknowledge-button-container ${
Expand Down
34 changes: 32 additions & 2 deletions clients/fides-js/src/components/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { h, FunctionComponent } from "preact";
import { useEffect, useState, useCallback, useMemo } from "preact/hooks";
import {
ConsentMechanism,
ConsentMethod,
FidesOptions,
PrivacyExperience,
PrivacyNotice,
SaveConsentPreference,
ServingComponent,
} from "../lib/consent-types";
import ConsentBanner from "./ConsentBanner";

Expand All @@ -20,8 +22,9 @@ import { FidesCookie } from "../lib/cookie";
import "./fides.css";
import { useA11yDialog } from "../lib/a11y-dialog";
import ConsentModal from "./ConsentModal";
import { useHasMounted } from "../lib/hooks";
import { useConsentServed, useHasMounted } from "../lib/hooks";
import ConsentButtons from "./ConsentButtons";
import { dispatchFidesEvent } from "../lib/events";

export interface OverlayProps {
options: FidesOptions;
Expand Down Expand Up @@ -59,8 +62,11 @@ const Overlay: FunctionComponent<OverlayProps> = ({
const handleOpenModal = useCallback(() => {
if (instance) {
instance.show();
dispatchFidesEvent("FidesUIShown", cookie, options.debug, {
servingComponent: ServingComponent.OVERLAY,
});
}
}, [instance]);
}, [instance, cookie, options.debug]);

const handleCloseModal = useCallback(() => {
if (instance) {
Expand Down Expand Up @@ -105,11 +111,31 @@ const Overlay: FunctionComponent<OverlayProps> = ({
[experience]
);

useEffect(() => {
if (showBanner && bannerIsOpen) {
dispatchFidesEvent("FidesUIShown", cookie, options.debug, {
servingComponent: ServingComponent.BANNER,
});
}
}, [showBanner, cookie, options.debug, bannerIsOpen]);

const privacyNotices = useMemo(
() => experience.privacy_notices ?? [],
[experience.privacy_notices]
);

const isAllNoticeOnly = privacyNotices.every(
(n) => n.consent_mechanism === ConsentMechanism.NOTICE_ONLY
);

const { servedNotices } = useConsentServed({
notices: privacyNotices,
options,
userGeography: fidesRegionString,
acknowledgeMode: isAllNoticeOnly,
privacyExperienceId: experience.id,
});

const handleUpdatePreferences = useCallback(
(enabledPrivacyNoticeKeys: Array<PrivacyNotice["notice_key"]>) => {
const consentPreferencesToSave = privacyNotices.map((notice) => {
Expand All @@ -126,6 +152,7 @@ const Overlay: FunctionComponent<OverlayProps> = ({
consentMethod: ConsentMethod.button,
userLocationString: fidesRegionString,
cookie,
servedNotices,
});
// Make sure our draft state also updates
setDraftEnabledNoticeKeys(enabledPrivacyNoticeKeys);
Expand All @@ -136,6 +163,7 @@ const Overlay: FunctionComponent<OverlayProps> = ({
fidesRegionString,
experience.id,
options.fidesApiUrl,
servedNotices,
]
);

Expand Down Expand Up @@ -171,6 +199,7 @@ const Overlay: FunctionComponent<OverlayProps> = ({
handleUpdatePreferences(keys);
setBannerIsOpen(false);
}}
isAcknowledge={isAllNoticeOnly}
/>
}
/>
Expand All @@ -191,6 +220,7 @@ const Overlay: FunctionComponent<OverlayProps> = ({
handleUpdatePreferences(keys);
handleCloseModal();
}}
isAcknowledge={isAllNoticeOnly}
/>
}
options={options}
Expand Down
3 changes: 3 additions & 0 deletions clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ const automaticallyApplyGPCPreferences = (
cookie: FidesCookie,
fidesRegionString: string | null,
fidesApiUrl: string,
debug: boolean,
effectiveExperience?: PrivacyExperience | null
) => {
if (!effectiveExperience || !effectiveExperience.privacy_notices) {
Expand Down Expand Up @@ -174,6 +175,7 @@ const automaticallyApplyGPCPreferences = (
consentMethod: ConsentMethod.gpc,
userLocationString: fidesRegionString || undefined,
cookie,
debug,
});
}
};
Expand Down Expand Up @@ -276,6 +278,7 @@ const init = async ({
cookie,
fidesRegionString,
options.fidesApiUrl,
options.debug,
effectiveExperience
);
}
Expand Down
6 changes: 5 additions & 1 deletion clients/fides-js/src/integrations/gtm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ export const gtm = () => {
if (window.Fides?.initialized) {
pushFidesVariableToGTM({
type: "FidesInitialized",
detail: { consent: window.Fides.consent },
detail: {
consent: window.Fides.consent,
fides_meta: window.Fides.fides_meta,
identity: window.Fides.identity,
},
});
}
};
29 changes: 29 additions & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export type PrivacyPreferencesRequest = {
export type ConsentOptionCreate = {
privacy_notice_history_id: string;
preference: UserConsentPreference;
served_notice_history_id?: string;
};

export type Identity = {
Expand All @@ -208,6 +209,34 @@ export enum GpcStatus {
OVERRIDDEN = "overridden",
}

// Consent reporting
export enum ServingComponent {
OVERLAY = "overlay",
BANNER = "banner",
PRIVACY_CENTER = "privacy_center",
}
/**
* Request body when indicating that notices were served in the UI
*/
export type NoticesServedRequest = {
browser_identity: Identity;
code?: string;
privacy_notice_history_ids: Array<string>;
privacy_experience_id?: string;
user_geography?: string;
acknowledge_mode?: boolean;
serving_component: ServingComponent;
};
/**
* Schema that surfaces the last version of a notice that was shown to a user
*/
export type LastServedNoticeSchema = {
id: string;
updated_at: string;
privacy_notice_history: PrivacyNotice;
served_notice_history_id: string;
};

// ------------------LEGACY TYPES BELOW -------------------

export type ConditionalValue = {
Expand Down
31 changes: 18 additions & 13 deletions clients/fides-js/src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@ import { debugLog } from "./consent-utils";
* Defines the available event names:
* - FidesInitialized: dispatched when initialization is complete, from Fides.init()
* - FidesUpdated: dispatched when preferences are updated, from updateConsentPreferences() or Fides.init()
* - FidesUIShown: dispatched when either the banner or modal is shown to the user
*/
export type FidesEventType = "FidesInitialized" | "FidesUpdated";
export type FidesEventType =
| "FidesInitialized"
| "FidesUpdated"
| "FidesUIShown";

// Bonus points: update the WindowEventMap interface with our custom event types
declare global {
interface WindowEventMap {
FidesInitialized: FidesEvent;
FidesUpdated: FidesEvent;
FidesUIShown: FidesEvent;
}
}

/**
* Defines the properties available on event.detail. As of now, these are an
* explicit subset of properties from the Fides cookie
* TODO: add identity and meta?
* Defines the properties available on event.detail. Currently the FidesCookie
* and an extra field `meta` for any other details that the event wants to pass
* around.
*/
export type FidesEventDetail = Pick<FidesCookie, "consent">;
export type FidesEventDetail = FidesCookie & {
extraDetails?: Record<string, string>;
};

export type FidesEvent = CustomEvent<FidesEventDetail>;

Expand All @@ -42,18 +49,16 @@ export type FidesEvent = CustomEvent<FidesEventDetail>;
export const dispatchFidesEvent = (
type: FidesEventType,
cookie: FidesCookie,
debug: boolean
debug: boolean,
extraDetails?: Record<string, string>
) => {
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
// Pick a subset of the Fides cookie properties to provide as event detail
const { consent }: FidesEventDetail = cookie;
const detail: FidesEventDetail = { consent };
const event = new CustomEvent(type, { detail });
const event = new CustomEvent(type, {
detail: { ...cookie, extraDetails },
});
debugLog(
debug,
`Dispatching event type ${type} with cookie consent ${JSON.stringify(
cookie?.consent
)}`
`Dispatching event type ${type} with cookie ${JSON.stringify(cookie)}`
);
window.dispatchEvent(event);
}
Expand Down
68 changes: 68 additions & 0 deletions clients/fides-js/src/lib/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { useEffect, useState, useCallback } from "preact/hooks";
import { FidesEvent } from "./events";
import {
FidesOptions,
LastServedNoticeSchema,
NoticesServedRequest,
PrivacyNotice,
ServingComponent,
} from "./consent-types";
import { patchNoticesServed } from "../services/fides/api";

/**
* Hook which tracks if the app has mounted yet.
Expand Down Expand Up @@ -54,3 +63,62 @@ export const useDisclosure = ({ id }: { id: string }) => {
getDisclosureProps,
};
};

export const useConsentServed = ({
notices,
options,
userGeography,
privacyExperienceId,
acknowledgeMode,
}: {
notices: PrivacyNotice[];
options: FidesOptions;
userGeography?: string;
privacyExperienceId?: string;
acknowledgeMode?: boolean;
}) => {
const [servedNotices, setServedNotices] = useState<
LastServedNoticeSchema[] | undefined
>(undefined);

const handleUIEvent = useCallback(
async (event: FidesEvent) => {
// Only send notices-served request when we show via the modal since that
// is the only time we show all notices
if (
!event.detail.extraDetails ||
event.detail.extraDetails.servingComponent !== ServingComponent.OVERLAY
) {
return;
}
const request: NoticesServedRequest = {
browser_identity: event.detail.identity,
privacy_experience_id: privacyExperienceId,
user_geography: userGeography,
acknowledge_mode: acknowledgeMode,
privacy_notice_history_ids: notices.map(
(n) => n.privacy_notice_history_id
),
serving_component: event.detail.extraDetails.servingComponent,
};
const result = await patchNoticesServed({
request,
fidesApiUrl: options.fidesApiUrl,
debug: options.debug,
});
if (result) {
setServedNotices(result);
}
},
[notices, options, acknowledgeMode, privacyExperienceId, userGeography]
);

useEffect(() => {
window.addEventListener("FidesUIShown", handleUIEvent);
return () => {
window.removeEventListener("FidesUIShown", handleUIEvent);
};
}, [handleUIEvent]);

return { servedNotices };
};
Loading

0 comments on commit b50664e

Please sign in to comment.