Skip to content

Commit

Permalink
Disable GPP stub when initialize=false is set until Fides.init()
Browse files Browse the repository at this point in the history
…is called (#5010)
  • Loading branch information
gilluminate authored Jun 20, 2024
1 parent 71daa53 commit e58c65b
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 52 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The types of changes are:
- New privacy request search to replace existing endpoint [#4987](https://github.com/ethyca/fides/pull/4987)
- Added new Google Cloud SQL for MySQL Connector [#4949](https://github.com/ethyca/fides/pull/4949)
- Add new options for integrations for discovery & detection [#5000](https://github.com/ethyca/fides/pull/5000)
- Add new `FidesInitializing` event for when FidesJS begins initialization [#5010](https://github.com/ethyca/fides/pull/5010)

### Changed
- Move new data map reporting table out of beta and remove old table from Data Lineage map. [#4963](https://github.com/ethyca/fides/pull/4963)
Expand All @@ -44,6 +45,8 @@ The types of changes are:
- Masked "Keyfile credentials" input on integration config form [#4971](https://github.com/ethyca/fides/pull/4971)
- Fixed validations for privacy declaration taxonomy labels when creating/updating a System [#4982](https://github.com/ethyca/fides/pull/4982)
- Allow property-specific messaging to work with non-custom templates [#4986](https://github.com/ethyca/fides/pull/4986)
- Fixed an issue where config object was being passed twice to `fides.js` output [#5010](https://github.com/ethyca/fides/pull/5010)
- Disabling Fides initializtion now also disables GPP initialization [#5010](https://github.com/ethyca/fides/pull/5010)

## [2.38.1](https://github.com/ethyca/fides/compare/2.38.0...2.38.1)

Expand Down
4 changes: 4 additions & 0 deletions clients/fides-js/docs/interfaces/FidesEvent.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ the browser, see the MDN docs:

### List of FidesEvent Types

- `FidesInitializing`: Dispatched when initialization begins, which happens
immediately once the FidesJS script is loaded. If `Fides.init()` is called
multiple times, this event will also be dispatched each time.

- `FidesInitialized`: Dispatched when initialization is complete and the
current user's consent preferences - either previously saved or applicable
defaults - have been set on the `Fides` global object.
Expand Down
15 changes: 10 additions & 5 deletions clients/fides-js/src/docs/fides-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* documentation, since it's mostly just noise there - the list of events on
* {@link FidesEvent} provides a good reference. But when coding, it's still
* useful to have this union type around!
*
*
* @private
*/
export type FidesEventType =
| "FidesInitializing"
| "FidesInitialized"
| "FidesUpdating"
| "FidesUpdated"
Expand Down Expand Up @@ -41,10 +42,14 @@ export type FidesEventType =
*
* ### List of FidesEvent Types
*
* - `FidesInitializing`: Dispatched when initialization begins, which happens
* immediately once the FidesJS script is loaded. If `Fides.init()` is called
* multiple times, this event will also be dispatched each time.
*
* - `FidesInitialized`: Dispatched when initialization is complete and the
* current user's consent preferences - either previously saved or applicable
* defaults - have been set on the `Fides` global object.
*
*
* - `FidesUpdating`: Dispatched when a user action (e.g. accepting all, saving
* changes, applying GPC) has started updating the user's consent preferences.
* This event is dispatched immediately once the changes are made, but before
Expand All @@ -58,7 +63,7 @@ export type FidesEventType =
* object, `fides_consent` cookie on the user's device, and the Fides API. To
* receive an event that fires before these changes are saved, use the
* `FidesUpdating` event instead.
*
*
* - `FidesUIShown`: Dispatched whenever a FidesJS UI component is rendered and
* shown to the current user (banner, modal, etc.). The specific component shown
* can be obtained from the `detail.extraDetails.servingComponent` property on
Expand Down Expand Up @@ -107,12 +112,12 @@ export interface FidesEvent extends CustomEvent {
/**
* Whether the user should be shown the consent experience. Only available on FidesInitialized events.
*/
shouldShowExperience?: boolean;
shouldShowExperience?: boolean;

/**
* What consent method (if any) caused this event.
*/
consentMethod?: "accept" | "reject" | "save" | "dismiss" | "gpc";
};
};
};
}
6 changes: 5 additions & 1 deletion clients/fides-js/src/fides-ext-gpp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,8 @@ const initializeGppCmpApi = () => {
cmpApi.setSignalStatus(SignalStatus.READY);
});
};
initializeGppCmpApi();
window.addEventListener("FidesInitializing", (event) => {
if (event.detail.extraDetails?.gppEnabled) {
initializeGppCmpApi();
}
});
13 changes: 13 additions & 0 deletions clients/fides-js/src/fides-tcf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ async function init(this: FidesGlobal, providedConfig?: FidesConfig) {
this.config = config; // no matter how the config is set, we want to store it on the global object
updateWindowFides(this);

dispatchFidesEvent(
"FidesInitializing",
undefined,
this.config.options.debug,
{
gppEnabled:
this.config.options.gppEnabled ||
this.config.experience?.gpp_settings?.enabled,
tcfEnabled: this.config.options.tcfEnabled,
}
);

const optionsOverrides: Partial<FidesInitOptionsOverrides> =
getOverridesByType<Partial<FidesInitOptionsOverrides>>(
OverrideType.OPTIONS,
Expand Down Expand Up @@ -235,6 +247,7 @@ const _Fides: FidesGlobal = {
fidesApiUrl: "",
serverSideFidesApiUrl: "",
tcfEnabled: true,
gppEnabled: false,
fidesEmbed: false,
fidesDisableSaveApi: false,
fidesDisableNoticesServedApi: false,
Expand Down
13 changes: 13 additions & 0 deletions clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ async function init(this: FidesGlobal, providedConfig?: FidesConfig) {

this.config = config; // no matter how the config is set, we want to store it on the global object

dispatchFidesEvent(
"FidesInitializing",
undefined,
this.config.options.debug,
{
gppEnabled:
this.config.options.gppEnabled ||
this.config.experience?.gpp_settings?.enabled,
tcfEnabled: this.config.options.tcfEnabled,
}
);

const optionsOverrides: Partial<FidesInitOptionsOverrides> =
getOverridesByType<Partial<FidesInitOptionsOverrides>>(
OverrideType.OPTIONS,
Expand Down Expand Up @@ -173,6 +185,7 @@ const _Fides: FidesGlobal = {
fidesApiUrl: "",
serverSideFidesApiUrl: "",
tcfEnabled: false,
gppEnabled: false,
fidesEmbed: false,
fidesDisableSaveApi: false,
fidesDisableNoticesServedApi: false,
Expand Down
3 changes: 3 additions & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ export interface FidesInitOptions {
// Whether we should show the TCF modal
tcfEnabled: boolean;

// Whether to include the GPP extension
gppEnabled: boolean;

// Whether we should "embed" the fides.js overlay UI (ie. “Layer 2”) into a web page instead of as a pop-up
// overlay, and never render the banner (ie. “Layer 1”).
fidesEmbed: boolean;
Expand Down
11 changes: 7 additions & 4 deletions clients/fides-js/src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ declare global {
* events. This is intentionally vague, but constrained to be basic (primitive)
* values for simplicity.
*/
export type FidesEventExtraDetails = Record<string, string | number | boolean>;
export type FidesEventExtraDetails = Record<
string,
string | number | boolean | undefined
>;

/**
* Defines the properties available on event.detail. Currently the FidesCookie
Expand Down Expand Up @@ -49,14 +52,14 @@ export type FidesEvent = CustomEvent<FidesEventDetail>;
*/
export const dispatchFidesEvent = (
type: FidesEventType,
cookie: FidesCookie,
cookie: FidesCookie | undefined,
debug: boolean,
extraDetails?: FidesEventExtraDetails
) => {
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
// Extracts consentMethod directly from the cookie instead of having to pass in duplicate data to this method
const constructedExtraDetails: FidesEventExtraDetails = {
consentMethod: cookie.fides_meta.consentMethod,
consentMethod: cookie?.fides_meta.consentMethod,
...extraDetails,
};
const event = new CustomEvent(type, {
Expand All @@ -68,7 +71,7 @@ export const dispatchFidesEvent = (
constructedExtraDetails?.servingComponent
? `from ${constructedExtraDetails.servingComponent} `
: ""
}with cookie ${JSON.stringify(cookie)} ${
}${cookie ? `with cookie ${JSON.stringify(cookie)} ` : ""}${
constructedExtraDetails
? `with extra details ${JSON.stringify(constructedExtraDetails)} `
: ""
Expand Down
46 changes: 37 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 @@ -19,9 +19,12 @@ describe("Fides-js GPP extension", () => {
/**
* Visit the fides-js-components-demo page with optional overrides on experience
*/
const visitDemoWithGPP = (props: {
overrideExperience?: (experience: PrivacyExperience) => PrivacyExperience;
}) => {
const visitDemoWithGPP = (
props: {
overrideExperience?: (experience: PrivacyExperience) => PrivacyExperience;
},
queryParams?: Cypress.VisitOptions["qs"]
) => {
cy.fixture("consent/experience_gpp.json").then((payload) => {
let experience = payload.items[0];
if (props.overrideExperience) {
Expand All @@ -31,13 +34,18 @@ describe("Fides-js GPP extension", () => {
experience
);
}
stubConfig({
options: {
isOverlayEnabled: true,
tcfEnabled: false,
stubConfig(
{
options: {
isOverlayEnabled: true,
tcfEnabled: false,
},
experience,
},
experience,
});
undefined,
undefined,
queryParams
);
});
};

Expand Down Expand Up @@ -67,6 +75,26 @@ describe("Fides-js GPP extension", () => {
});
});

describe("Fides is not initialized", () => {
beforeEach(() => {
visitDemoWithGPP({}, { init: false });
});
it("does not load the GPP extension", () => {
cy.get("@FidesInitializing").should("not.have.been.called");
cy.window().then((win) => {
expect(win.__gpp).to.eql(undefined);
});
});
it("can still load the GPP extension if initialized later", () => {
cy.window().then((win) => {
win.Fides.init(win.Fides.config);
cy.waitUntilFidesInitialized().then(() => {
expect(win.__gpp).to.not.eql(undefined);
});
});
});
});

describe("with TCF and GPP enabled", () => {
const tcfGppSettings = { enabled: true, enable_tcfeu_string: true };
beforeEach(() => {
Expand Down
3 changes: 2 additions & 1 deletion clients/privacy-center/cypress/e2e/consent-banner.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1649,11 +1649,12 @@ describe("Consent overlay", () => {
isOverlayEnabled: true,
},
});
cy.get("@FidesInitializing").should("have.been.calledOnce");
});

// NOTE: See definition of cy.visitConsentDemo in commands.ts for where we
// register listeners for these window events
it("emits a FidesInitialized but not any other events when initialized", () => {
it("emits a FidesInitialized but not any subsequent events when initialized", () => {
cy.window()
.its("Fides")
.its("consent")
Expand Down
14 changes: 7 additions & 7 deletions clients/privacy-center/cypress/e2e/fides-js.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ describe("fides.js API route", () => {
.to.match(/^\s+\(function/, "should be an IIFE")
.to.match(/\}\)\(\);\s+$/, "should be an IIFE");
expect(response.body)
.to.match(/var fidesConfig = \{/, "should bundle Fides.init")
.to.match(/Fides.init\(fidesConfig\);/, "should bundle Fides.init");
.to.match(/window.Fides.config = \{/, "should bundle Fides.init")
.to.match(/Fides.init\(\);/, "should call Fides.init");
const matches = response.body.match(
/var fidesConfig = (?<json>\{.*?\});/
/window.Fides.config = (?<json>\{.*?\});/
);
expect(matches).to.have.nested.property("groups.json");
expect(JSON.parse(matches.groups.json))
Expand All @@ -44,9 +44,9 @@ describe("fides.js API route", () => {
describe("when pre-fetching geolocation", () => {
it("returns geolocation if provided as a '?geolocation' query param", () => {
cy.request("/fides.js?geolocation=US-CA").then((response) => {
expect(response.body).to.match(/var fidesConfig = \{/);
expect(response.body).to.match(/window.Fides.config = \{/);
const matches = response.body.match(
/var fidesConfig = (?<json>\{.*?\});/
/window.Fides.config = (?<json>\{.*?\});/
);
expect(JSON.parse(matches.groups.json))
.to.have.nested.property("geolocation")
Expand All @@ -66,9 +66,9 @@ describe("fides.js API route", () => {
"CloudFront-Viewer-Country-Region": "IDF",
},
}).then((response) => {
expect(response.body).to.match(/var fidesConfig = \{/);
expect(response.body).to.match(/window.Fides.config = \{/);
const matches = response.body.match(
/var fidesConfig = (?<json>\{.*?\});/
/window.Fides.config = (?<json>\{.*?\});/
);
expect(JSON.parse(matches.groups.json))
.to.have.nested.property("geolocation")
Expand Down
4 changes: 4 additions & 0 deletions clients/privacy-center/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ Cypress.Commands.add(
}

// Add event listeners for Fides.js events
win.addEventListener(
"FidesInitializing",
cy.stub().as("FidesInitializing")
);
win.addEventListener(
"FidesInitialized",
cy.stub().as("FidesInitialized")
Expand Down
28 changes: 11 additions & 17 deletions clients/privacy-center/pages/api/fides-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,9 @@ export default async function handler(
}
}

// These query params are used for testing purposes only, and should not be used
// in production. They allow for the config to be injected by the test framework
// and delay the initialization of fides.js until the test framework is ready.
const { e2e: e2eQuery, tcf: tcfQuery } = req.query;
const isTestMode = e2eQuery === "true";
// This query param is used for testing purposes only, and should not be used
// in production.
const { tcf: tcfQuery } = req.query;

// We determine server-side whether or not to send the TCF bundle, which is based
// on whether or not the experience is marked as TCF. This means for TCF, we *must*
Expand Down Expand Up @@ -209,6 +207,7 @@ export default async function handler(
privacyCenterUrl: environment.settings.PRIVACY_CENTER_URL,
fidesApiUrl: environment.settings.FIDES_API_URL,
tcfEnabled,
gppEnabled,
serverSideFidesApiUrl:
environment.settings.SERVER_SIDE_FIDES_API_URL ||
environment.settings.FIDES_API_URL,
Expand Down Expand Up @@ -289,18 +288,13 @@ export default async function handler(
document.head.append(style);
`
: ""
}${
isTestMode // let end-to-end tests set the config and initialize as needed
? ""
: `
var fidesConfig = ${fidesConfigJSON};
window.Fides.config = ${fidesConfigJSON};
${skipInitialization ? "" : `window.Fides.init(fidesConfig);`}
${
environment.settings.DEBUG && skipInitialization
? `console.log("fides.js initialization skipped. Call window.Fides.init() manually.");`
: ""
}`
}
window.Fides.config = ${fidesConfigJSON};
${skipInitialization ? "" : `window.Fides.init();`}
${
environment.settings.DEBUG && skipInitialization
? `console.log("Fides initialization skipped. Call window.Fides.init() manually.");`
: ""
}
})();
`;
Expand Down
Loading

0 comments on commit e58c65b

Please sign in to comment.