Skip to content

Commit

Permalink
PROD-1617: Update when GPP API reports signal status: ready (#4635)
Browse files Browse the repository at this point in the history
  • Loading branch information
eastandwestwind authored Feb 29, 2024
1 parent c073382 commit c00494c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ The types of changes are:
- Add Great Britain as a consent option [#4628](https://github.com/ethyca/fides/pull/4628)
- Navbar update and new properties page [#4633](https://github.com/ethyca/fides/pull/4633)

### Changed
- Update when GPP API reports signal status: ready [#4635](https://github.com/ethyca/fides/pull/4635)


## [2.30.1](https://github.com/ethyca/fides/compare/2.30.0...2.30.1)

### Fixed
Expand Down
65 changes: 59 additions & 6 deletions clients/fides-js/src/fides-ext-gpp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ import {
import { makeStub } from "./lib/gpp/stub";
import { extractTCStringForCmpApi } from "./lib/tcf/events";
import {
allNoticesAreDefaultOptIn,
isPrivacyExperience,
shouldResurfaceConsent,
} from "./lib/consent-utils";
import { ETHYCA_CMP_ID } from "./lib/tcf/constants";
import type { Fides } from "./lib/initialize";
import type { OverrideOptions } from "./lib/consent-types";
import type {
CookieKeyConsent,
OverrideOptions,
PrivacyNoticeWithPreference,
} from "./lib/consent-types";
import { GPPUSApproach, GppFunction } from "./lib/gpp/types";
import { FidesEvent } from "./fides";
import {
Expand All @@ -49,6 +54,34 @@ declare global {
}
}

/**
* Special GPP util method to determine if user has existing prefs, including those on the cookie or fides string.
* Specifically, this method does not consider legacy consent has an existing pref, since they aren't relevant for GPP.
* @param consent: CookieKeyConsent | undefined
* @param fides_string: string | undefined
* @param notices: Array<PrivacyNoticeWithPreference> | undefined
* @return boolean
*/
const userHasExistingPrefs = (
consent: CookieKeyConsent | undefined,
fides_string: string | undefined,
notices: Array<PrivacyNoticeWithPreference> | undefined
): boolean => {
if (!consent) {
return false;
}
if (fides_string) {
return true;
}
return Boolean(
notices &&
Object.entries(consent).some(
([key, val]) =>
key in notices.map((i) => i.notice_key) && val !== undefined
)
);
};

/**
* Wrapper around setting a TC string on the CMP API object.
* Returns whether or not the TC string was set.
Expand Down Expand Up @@ -105,9 +138,18 @@ export const initializeGppCmpApi = () => {
window.addEventListener("FidesInitialized", (event) => {
const { experience } = window.Fides;
cmpApi.setSupportedAPIs(getSupportedApis());
// Set status to ready immediately upon initialization, if either:
// A. Consent should not be resurfaced
// B. User has no prefs and has all opt-in notices
if (
isPrivacyExperience(experience) &&
!shouldResurfaceConsent(experience, event.detail)
(!shouldResurfaceConsent(experience, event.detail) ||
(allNoticesAreDefaultOptIn(experience.privacy_notices) &&
!userHasExistingPrefs(
event.detail.consent,
event.detail.fides_string,
experience.privacy_notices
)))
) {
const tcSet = setTcString(event, cmpApi);
if (tcSet) {
Expand All @@ -126,13 +168,24 @@ export const initializeGppCmpApi = () => {
}
});

window.addEventListener("FidesUIShown", () => {
cmpApi.setSignalStatus(SignalStatus.NOT_READY);
cmpApi.setCmpDisplayStatus(CmpDisplayStatus.VISIBLE);

window.addEventListener("FidesUIShown", (event) => {
// Set US GPP notice fields
const { experience } = window.Fides;
if (isPrivacyExperience(experience)) {
// set signal status to ready only for users with no existing prefs and if notices are all opt-in by default
if (
allNoticesAreDefaultOptIn(experience.privacy_notices) &&
!userHasExistingPrefs(
event.detail.consent,
event.detail.fides_string,
experience.privacy_notices
)
) {
cmpApi.setSignalStatus(SignalStatus.READY);
} else {
cmpApi.setSignalStatus(SignalStatus.NOT_READY);
}
cmpApi.setCmpDisplayStatus(CmpDisplayStatus.VISIBLE);
const sectionsChanged = setGppNoticesProvidedFromExperience({
cmpApi,
experience,
Expand Down
11 changes: 11 additions & 0 deletions clients/fides-js/src/lib/consent-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
OverrideOptions,
PrivacyExperience,
PrivacyNotice,
PrivacyNoticeWithPreference,
UserConsentPreference,
UserGeolocation,
} from "./consent-types";
Expand Down Expand Up @@ -58,6 +59,16 @@ export const isPrivacyExperience = (
return false;
};

export const allNoticesAreDefaultOptIn = (
notices: Array<PrivacyNoticeWithPreference> | undefined
): boolean =>
Boolean(
notices &&
notices.every(
(notice) => notice.default_preference === UserConsentPreference.OPT_IN
)
);

/**
* Construct user location str to be ingested by Fides API
* Returns null if geolocation cannot be constructed by provided params, e.g. us_ca
Expand Down
22 changes: 13 additions & 9 deletions clients/privacy-center/cypress/e2e/consent-banner-gpp.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("Fides-js GPP extension", () => {
.its("lastCall.args")
.then(([data, success]) => {
expect(success).to.eql(true);
expect(data.signalStatus).to.eql("not ready");
expect(data.signalStatus).to.eql("ready");
});
});
});
Expand Down Expand Up @@ -98,7 +98,7 @@ describe("Fides-js GPP extension", () => {
expect(data.eventName).to.eql("listenerRegistered");
const { cmpDisplayStatus, signalStatus, gppString } = data.pingData;
expect(cmpDisplayStatus).to.eql("visible");
expect(signalStatus).to.eql("not ready");
expect(signalStatus).to.eql("ready");
expect(gppString).to.eql("DBAA"); // empty string, header only
});

Expand Down Expand Up @@ -240,13 +240,13 @@ describe("Fides-js GPP extension", () => {
* Expected flow for a returning user who opens but then closes the modal without making a change:
* 1. listenerRegistered
* 2. User opens the modal
* 3. signalStatus = not ready
* 3. signalStatus = ready
* 4. cmpDisplayStatus = visible
* 5. User closes the modal without saving anything
* 5. User closes the modal which automatically triggers preference save
* 6. cmpDisplayStatus = hidden
* 7. signalStatus = ready
* 7. signalStatus = not ready
*/
it("can handle returning user closing the modal without a preference change", () => {
it("can handle returning user closing the modal", () => {
const cookie = mockCookie({
tcf_version_hash: TCF_VERSION_HASH,
});
Expand All @@ -265,12 +265,16 @@ describe("Fides-js GPP extension", () => {
win.__gpp("addEventListener", cy.stub().as("gppListener"));
});
cy.get("#fides-modal-link").click();
cy.get(".fides-modal-content .fides-close-button").click();
const expected = [
{ eventName: "listenerRegistered", data: true },
{ eventName: "signalStatus", data: "not ready" },
{ eventName: "signalStatus", data: "ready" },
{ eventName: "cmpDisplayStatus", data: "visible" },
{ eventName: "cmpDisplayStatus", data: "hidden" },
{ eventName: "signalStatus", data: "ready" },
{ eventName: "cmpDisplayStatus", data: "hidden" },
{ eventName: "sectionChange", data: "tcfeuv2" },
{ eventName: "signalStatus", data: "ready" },
];
cy.get("@gppListener")
.its("args")
Expand Down Expand Up @@ -319,7 +323,7 @@ describe("Fides-js GPP extension", () => {
applicableSections,
supportedAPIs,
} = data.pingData;
expect(signalStatus).to.eql("not ready");
expect(signalStatus).to.eql("ready");
expect(applicableSections).to.eql([]);
expect(supportedAPIs).to.eql([]);
expect(gppString).to.eql("DBAA");
Expand Down Expand Up @@ -355,7 +359,7 @@ describe("Fides-js GPP extension", () => {
.its("lastCall.args")
.then(([data, success]) => {
expect(success).to.eql(true);
expect(data.signalStatus).to.eql("not ready");
expect(data.signalStatus).to.eql("ready");
});
});
});
Expand Down

0 comments on commit c00494c

Please sign in to comment.