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

Optimize TCF bundle with just-in-time GVL translations #5074

Merged
merged 22 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
28a159d
fix extra long strings being rendered in demo pages
gilluminate Jul 8, 2024
e3f2bd8
update logs to be more helpful
gilluminate Jul 8, 2024
3387920
remove circular dependency
gilluminate Jul 9, 2024
8d1132a
Side load GVL translations to reduce payload size
gilluminate Jul 9, 2024
f28acc7
Add fetchGvlTranslations to call existing endpoint
gilluminate Jul 10, 2024
b1f9980
set available_locales on the experience
gilluminate Jul 11, 2024
f9db42e
mimic filtered GVL Language response
gilluminate Jul 11, 2024
446dfcb
update cypress tests
gilluminate Jul 11, 2024
f875db2
fix more tests
gilluminate Jul 11, 2024
e03da81
offset GVL translation timing. stop `await`ing initOverlay
gilluminate Jul 11, 2024
3791ad6
reduce flakiness of test. just check success
gilluminate Jul 12, 2024
d54555b
fix admin-ui language issue: supply locales to preveiw
gilluminate Jul 12, 2024
08b5e4e
fix unit tests
gilluminate Jul 12, 2024
309b1ac
Update api.ts
gilluminate Jul 12, 2024
5a871a5
fix flaky test by not looking at data reliant on config
gilluminate Jul 13, 2024
8bdc809
error handling for gvl translations
gilluminate Jul 15, 2024
bbf90ce
add Cypress test for failing translation API fallback
gilluminate Jul 15, 2024
3a825ac
update Changelog. fix typo
gilluminate Jul 15, 2024
cf7d492
remove client-side parsing of available_locales
gilluminate Jul 16, 2024
b0b2456
update to use new version of translations API
gilluminate Jul 16, 2024
ae1c3ca
misc. clean up
gilluminate Jul 17, 2024
384a8ae
remove all references to gvl_translations
gilluminate Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The types of changes are:
- Moves some endpoints for property-specific messaging from OSS -> plus [#5069](https://github.com/ethyca/fides/pull/5069)
- Messaging page will now show a notice about using global mode [#5090](https://github.com/ethyca/fides/pull/5090)
- URL for deployment instructions when the webserver is running [#5088](https://github.com/ethyca/fides/pull/5088)
- Optimize TCF bundle with just-in-time GVL translations [#5074](https://github.com/ethyca/fides/pull/5074)

### Fixed
- Fixed bug with unescaped table names in mysql queries [#5072](https://github.com/ethyca/fides/pull/5072/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const buildBaseConfig = (
id: "pri_111",
region: "us_ca",
component: "banner_and_modal",
available_locales: experienceConfig.translations?.map((t) => t.language),
experience_config: {
id: "pri_222",
regions: ["us_ca"],
Expand Down
4 changes: 2 additions & 2 deletions clients/fides-js/__tests__/__fixtures__/mock_experience.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"updated_at": "2024-01-01T12:00:00.000000+00:00",
"region": "us_ca",
"component": "banner_and_modal",
"available_locales": ["en", "es"],
"experience_config": {
"id": "pri_222",
"created_at": "2024-01-01T12:00:00.000000+00:00",
Expand Down Expand Up @@ -132,6 +133,5 @@
]
}
],
"gvl": {},
"gvl_translations": {}
"gvl": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,9 @@
}
},
"es": {
"gvlSpecificationVersion": 3,
"vendorListVersion": 40,
"tcfPolicyVersion": 4,
Comment on lines +535 to +537
Copy link
Contributor Author

Choose a reason for hiding this comment

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

resolves a Typescript complaint that these were missing

"lastUpdated": "2024-01-01T12:00:00Z",
"purposes": {
"1": {
Expand Down
89 changes: 13 additions & 76 deletions clients/fides-js/__tests__/lib/i18n/i18n-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
selectBestExperienceConfigTranslation,
areLocalesEqual,
localizeModalLinkText,
loadMessagesFromGVLTranslations,
} from "~/lib/i18n";
import { loadTcfMessagesFromFiles } from "~/lib/tcf/i18n/tcf-i18n-utils";
import messagesEn from "~/lib/i18n/locales/en/messages.json";
Expand Down Expand Up @@ -80,7 +81,6 @@ describe("i18n-utils", () => {
];

const mockI18n = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
activate: jest.fn((locale: Locale): void => {
mockCurrentLocale = locale;
}),
Expand Down Expand Up @@ -263,12 +263,8 @@ describe("i18n-utils", () => {

describe("loadMessagesFromExperience", () => {
it("reads all messages from experience API response and loads into the i18n catalog", () => {
const updatedLocales = loadMessagesFromExperience(
mockI18n,
mockExperience
);
loadMessagesFromExperience(mockI18n, mockExperience);
const EXPECTED_NUM_TRANSLATIONS = 2;
expect(updatedLocales).toEqual(["en", "es"]);
expect(mockI18n.load).toHaveBeenCalledTimes(EXPECTED_NUM_TRANSLATIONS);
expect(mockI18n.load).toHaveBeenCalledWith("en", mockI18nCatalogLoad[0]);
expect(mockI18n.load).toHaveBeenCalledWith("es", mockI18nCatalogLoad[1]);
Expand Down Expand Up @@ -301,15 +297,12 @@ describe("i18n-utils", () => {

// Edit the experience data to match the legacy format (w/o translations)
delete mockExpNoTranslations.experience_config.translations;
mockExpNoTranslations.available_locales = ["en"];

// Load the "no translations" version of the experience and run tests
const updatedLocales = loadMessagesFromExperience(
mockI18n,
mockExpNoTranslations as any
);
loadMessagesFromExperience(mockI18n, mockExpNoTranslations as any);

const EXPECTED_NUM_TRANSLATIONS = 1;
expect(updatedLocales).toEqual(["en"]);
expect(mockI18n.load).toHaveBeenCalledTimes(EXPECTED_NUM_TRANSLATIONS);
expect(mockI18n.load).toHaveBeenCalledWith("en", mockI18nCatalogLoad[0]);
});
Expand All @@ -322,13 +315,12 @@ describe("i18n-utils", () => {
privacy_policy_url: "https://example.com/privacy",
override_language: "en",
};
const updatedLocales = loadMessagesFromExperience(
loadMessagesFromExperience(
mockI18n,
mockExperience,
experienceTranslationOverrides
);
const EXPECTED_NUM_TRANSLATIONS = 2;
expect(updatedLocales).toEqual(["en", "es"]);
expect(mockI18n.load).toHaveBeenCalledTimes(EXPECTED_NUM_TRANSLATIONS);
expect(mockI18n.load).toHaveBeenCalledWith("en", {
...mockI18nCatalogLoad[0],
Expand All @@ -350,47 +342,35 @@ describe("i18n-utils", () => {
privacy_policy_url: "https://example.com/privacy",
override_language: "ja",
};
const updatedLocales = loadMessagesFromExperience(
loadMessagesFromExperience(
mockI18n,
mockExperience,
experienceTranslationOverrides
);
const EXPECTED_NUM_TRANSLATIONS = 2;
expect(updatedLocales).toEqual(["en", "es"]);
expect(mockI18n.load).toHaveBeenCalledTimes(EXPECTED_NUM_TRANSLATIONS);
expect(mockI18n.load).toHaveBeenCalledWith("en", mockI18nCatalogLoad[0]);
expect(mockI18n.load).toHaveBeenCalledWith("es", mockI18nCatalogLoad[1]);
});

describe("when loading from a tcf_overlay experience", () => {
it("reads all messages from gvl_translations API response and loads into the i18n catalog", () => {
it("reads all messages from gvl translations API response and loads into the i18n catalog", () => {
// Mock out a partial response for a tcf_overlay including translations
const mockExpWithGVL = JSON.parse(JSON.stringify(mockExperience));
mockExpWithGVL.experience_config.component = "tcf_overlay";
mockExpWithGVL.gvl_translations = mockGVLTranslationsJSON;

// Load all the translations
const updatedLocales = loadMessagesFromExperience(
mockI18n,
mockExpWithGVL
);
loadMessagesFromGVLTranslations(mockI18n, mockGVLTranslationsJSON, [
"en",
"es",
]);

// First, confirm that the "regular" experience_config translations are loaded
const EXPECTED_NUM_TRANSLATIONS = 2;
expect(updatedLocales).toEqual(["en", "es"]);
expect(mockI18n.load).toHaveBeenCalledTimes(EXPECTED_NUM_TRANSLATIONS);
const [, loadedMessagesEn] = mockI18n.load.mock.calls[0];
const [, loadedMessagesEs] = mockI18n.load.mock.calls[1];
expect(loadedMessagesEn).toMatchObject({
"exp.accept_button_label": "Accept Test",
"exp.acknowledge_button_label": "Acknowledge Test",
});
expect(loadedMessagesEs).toMatchObject({
"exp.accept_button_label": "Aceptar Prueba",
"exp.acknowledge_button_label": "Reconocer Prueba",
});

// Confirm that the English gvl_translations are loaded
// Confirm that the English GVL translations are loaded
const expectedMessagesEn: Record<string, RegExp> = {
// Example purposes
"exp.tcf.purposes.1.name": /^Store and\/or access/,
Expand Down Expand Up @@ -421,7 +401,7 @@ describe("i18n-utils", () => {
expect(loadedMessagesEn[id]).toMatch(regex);
});

// Confirm that the Spanish gvl_translations are loaded
// Confirm that the Spanish GVL translations are loaded
const expectedMessagesEs: Record<string, RegExp> = {
// Example purposes
"exp.tcf.purposes.1.name": /^Almacenar la información/,
Expand Down Expand Up @@ -483,49 +463,6 @@ describe("i18n-utils", () => {
expect(getRecordCounts(loadedMessagesEn)).toMatchObject(expectedCounts);
expect(getRecordCounts(loadedMessagesEs)).toMatchObject(expectedCounts);
});

it("handles a mismatch between the experience_config and gvl_translations APIs by returning only locales available in both", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is all handled server-side now

// Mock out a partial response for a tcf_overlay including translations
const mockExpWithGVL = JSON.parse(JSON.stringify(mockExperience));
mockExpWithGVL.experience_config.component = "tcf_overlay";
mockExpWithGVL.gvl_translations = mockGVLTranslationsJSON;

// Modify "en" to be "EN" (uppercased!), which shouldn't be treated as a mismatch!
mockExpWithGVL.experience_config.translations[0].language = "EN";

// Replace "es" with "es-MX" in the experience_config.translations to force a mismatch
mockExpWithGVL.experience_config.translations[1].language = "es-MX";

// Confirm our test setup shows a mismatch between experience_config & gvl_translations
expect(
mockExpWithGVL.experience_config.translations.map(
(e: any) => e.language
)
).toEqual(["EN", "es-MX"]);
expect(Object.keys(mockExpWithGVL.gvl_translations)).toEqual([
"en",
"es",
]);

// Load all the translations
const updatedLocales = loadMessagesFromExperience(
mockI18n,
mockExpWithGVL
);

// Confirm that only the overlapping locales are loaded
expect(updatedLocales).toEqual(["EN"]);
const [, loadedMessagesEn] = mockI18n.load.mock.calls[0];
expect(loadedMessagesEn).toMatchObject({
"exp.accept_button_label": "Accept Test",
"exp.acknowledge_button_label": "Acknowledge Test",
"exp.tcf.purposes.1.name":
"Store and/or access information on a device",
"exp.tcf.purposes.1.description": expect.stringMatching(
/^Cookies, device or similar/
),
});
});
});
});

Expand Down
3 changes: 2 additions & 1 deletion clients/fides-js/src/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { h } from "preact";
import { I18n } from "../lib/i18n";
import { useI18n } from "../lib/i18n/i18n-context";
import { debugLog, FidesInitOptions } from "../fides";
import MenuItem from "./MenuItem";
import { FIDES_OVERLAY_WRAPPER } from "../lib/consent-constants";
import { debugLog } from "../lib/consent-utils";
import { FidesInitOptions } from "../lib/consent-types";

interface LanguageSelectorProps {
i18n: I18n;
Expand Down
8 changes: 6 additions & 2 deletions clients/fides-js/src/components/tcf/TcfVendors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,16 @@ const PagedVendorData = ({
otherVendors: VendorRecord[];
} = useMemo(
() => ({
gvlVendors: activeChunk.filter((v) => v.isGvl),
otherVendors: activeChunk.filter((v) => !v.isGvl),
gvlVendors: activeChunk?.filter((v) => v.isGvl),
otherVendors: activeChunk?.filter((v) => !v.isGvl),
}),
[activeChunk]
);

if (!activeChunk) {
return null;
}

Comment on lines +284 to +293
Copy link
Contributor Author

Choose a reason for hiding this comment

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

handling (unlikely) scenario of having 0 vendors configured for a given experience

return (
<Fragment>
<RecordsList<VendorRecord>
Expand Down
3 changes: 1 addition & 2 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Fides, FidesOptions } from "../docs";
import type { GPPFieldMapping, GPPSettings } from "./gpp/types";
import type {
GVLJson,
GVLTranslations,
TCFFeatureRecord,
TCFFeatureSave,
TCFPurposeConsentRecord,
Expand Down Expand Up @@ -414,8 +413,8 @@ export type PrivacyExperience = {
*/
experience_config?: ExperienceConfig; // NOTE: uses our client-side ExperienceConfig type
gvl?: GVLJson; // NOTE: uses our client-side GVLJson type
gvl_translations?: GVLTranslations;
meta?: ExperienceMeta;
available_locales?: string[];
};

/**
Expand Down
Loading
Loading