From ef9a3b6158db8e1d726c06375db8febc3698e7dc Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Mon, 8 May 2023 10:14:27 -0400 Subject: [PATCH 01/10] Add (failing) tests for fides.js cache behaviour --- .../privacy-center/cypress/e2e/fides-js.cy.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 clients/privacy-center/cypress/e2e/fides-js.cy.ts diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts new file mode 100644 index 0000000000..696efa9699 --- /dev/null +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -0,0 +1,98 @@ +describe("fides.js", () => { + it("returns the fides.js packaged bundled with the global config", () => { + cy.request("/fides.js").then(response => { + expect(response.status).to.eq(200); + expect(response) + .to.have.property("headers") + .to.have.property("content-type") + .to.eql("application/javascript"); + // TODO: ensure bundle has config + // TODO: ensure JS is valid + }); + }); + + it.only("caches in the browser", () => { + cy.intercept("/fides.js").as("fidesJS"); + + // Load the demo page 3 times, and check /fides.js is called *at most* once + // NOTE: Depending on browser cache, it might not be called at all - so zero + // times is a valid number of calls + cy.visit("/fides-js-demo.html"); + cy.visit("/fides-js-demo.html"); + cy.visit("/fides-js-demo.html"); + cy.get("@fidesJS.all").its("length").should("be.within", 0, 1); + }); + + describe("when generating cache-control headers", () => { + beforeEach(() => { + cy.request("/fides.js").then(response => { + expect(response.status).to.eq(200); + cy.wrap(response.headers).as("headers"); + cy.get("@headers").should("have.property", "etag").as("etag"); + cy.get("@headers").should("have.property", "cache-control").as("cacheHeaders"); + }); + }); + + it("stores publicly for at least one hour, at most one day", () => { + cy.get("@cacheHeaders").should("match", /public/); + cy.get("@cacheHeaders") + .invoke("match", /max-age=(?\d+)/) + .its("groups.expiry") + .then(parseInt) + .should("be.within", 3600, 86400); + }); + + it("generates 'etag' that is consistent when re-requested", () => { + cy.request("/fides.js").should("have.nested.property", "headers.etag").then((etag) => { + cy.get("@etag").should("eq", etag); + }); + }); + + it("generates 'etag' that varies based on location query params", () => { + cy.request("/fides.js?location=US-CA").should("have.nested.property", "headers.etag").as("USCATag").then((etag) => { + cy.get("@etag").should("not.eq", etag); + }); + + // Fetch a second time with a different location param + cy.request("/fides.js?location=FR").should("have.nested.property", "headers.etag").then((etag) => { + cy.get("@etag").should("not.eq", etag); + cy.get("@USCATag").should("not.eq", etag); + }); + }); + + it("generates 'etag' that varies based on Cloudfront location headers", () => { + cy.request({ + url: "/fides.js", + headers: { + "Cloudfront-Viewer-Country": "US", + "Cloudfront-Viewer-Country-Region": "CA", + } + }).should("have.nested.property", "headers.etag").as("USCATag").then((etag) => { + cy.get("@etag").should("not.eq", etag); + }); + + // Fetch a second time with different location headers + cy.request({ + url: "/fides.js", + headers: { + "Cloudfront-Viewer-Country": "FR", + } + }).should("have.nested.property", "headers.etag").as("headersTag").then((etag) => { + cy.get("@etag").should("not.eq", etag); + cy.get("@USCATag").should("not.eq", etag); + }); + }); + + it("returns 'vary' header for supported Cloudfront location headers", () => { + const expected = [ + "Cloudfront-Viewer-Country", + "Cloudfront-Viewer-Country-Region", + ]; + cy.get("@headers").should("have.property", "vary").then((vary) => { + expected.forEach((header) => { + expect(vary).includes(header); + }); + }); + }); + }); +}); From 9e4e61aba073dfb8c82d15fe4f27968d5f24476d Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Mon, 8 May 2023 22:57:41 -0400 Subject: [PATCH 02/10] Add a few extra assertions to the fides.js response body --- .../privacy-center/cypress/e2e/fides-js.cy.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts index 696efa9699..aded61fe8e 100644 --- a/clients/privacy-center/cypress/e2e/fides-js.cy.ts +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -1,17 +1,31 @@ describe("fides.js", () => { - it("returns the fides.js packaged bundled with the global config", () => { + it.only("returns the fides.js packaged bundled with the global config", () => { cy.request("/fides.js").then(response => { expect(response.status).to.eq(200); expect(response) .to.have.property("headers") .to.have.property("content-type") .to.eql("application/javascript"); - // TODO: ensure bundle has config - // TODO: ensure JS is valid + + // Run a few checks on the "bundled" response body, which should: + // 1) Be an IIFE that... + // 2) ...includes a call to Fides.init with a config JSON that... + // 3) ...is populated with the config.json options + expect(response.body) + .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"); + const matches = response.body.match(/var fidesConfig = (?\{.*?\});/); + expect(matches).to.have.nested.property("groups.json") + expect(JSON.parse(matches.groups.json)) + .to.have.nested.property("consent.options") + .to.have.length(3) }); }); - it.only("caches in the browser", () => { + it("caches in the browser", () => { cy.intercept("/fides.js").as("fidesJS"); // Load the demo page 3 times, and check /fides.js is called *at most* once From 31a6cd3ccfb4ced2c57682b25872e45c146632a7 Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Tue, 9 May 2023 09:16:21 -0400 Subject: [PATCH 03/10] Add some basic cache-control headers to cache the fides.js bundle --- clients/package-lock.json | 6 ++++++ clients/privacy-center/cypress/e2e/fides-js.cy.ts | 2 +- clients/privacy-center/package.json | 1 + clients/privacy-center/pages/api/fides-js.ts | 9 +++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clients/package-lock.json b/clients/package-lock.json index 55dc6990ea..d93809573e 100644 --- a/clients/package-lock.json +++ b/clients/package-lock.json @@ -5575,6 +5575,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/cache-control-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cache-control-parser/-/cache-control-parser-2.0.4.tgz", + "integrity": "sha512-/FyoH+1kaAHoJC86Yz+ix0+l4DCisx/+nNjZs4HRFQcyuUJv5O9TsEu9pXi4e56kV7veou6k3QMCl6LRGo0qdQ==" + }, "node_modules/cachedir": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", @@ -15944,6 +15949,7 @@ "@fidesui/react": "^0.0.23", "@fontsource/inter": "^4.5.15", "@reduxjs/toolkit": "^1.9.3", + "cache-control-parser": "^2.0.4", "fides-js": "*", "formik": "^2.2.9", "framer-motion": "^5", diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts index aded61fe8e..94e8b312fb 100644 --- a/clients/privacy-center/cypress/e2e/fides-js.cy.ts +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -1,5 +1,5 @@ describe("fides.js", () => { - it.only("returns the fides.js packaged bundled with the global config", () => { + it("returns the fides.js packaged bundled with the global config", () => { cy.request("/fides.js").then(response => { expect(response.status).to.eq(200); expect(response) diff --git a/clients/privacy-center/package.json b/clients/privacy-center/package.json index bc61c9a322..9fdbe55771 100644 --- a/clients/privacy-center/package.json +++ b/clients/privacy-center/package.json @@ -26,6 +26,7 @@ "@fidesui/react": "^0.0.23", "@fontsource/inter": "^4.5.15", "@reduxjs/toolkit": "^1.9.3", + "cache-control-parser": "^2.0.4", "fides-js": "*", "formik": "^2.2.9", "framer-motion": "^5", diff --git a/clients/privacy-center/pages/api/fides-js.ts b/clients/privacy-center/pages/api/fides-js.ts index 76f430ef69..d1d18c1ab2 100644 --- a/clients/privacy-center/pages/api/fides-js.ts +++ b/clients/privacy-center/pages/api/fides-js.ts @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import { promises as fsPromises } from "fs"; import type { NextApiRequest, NextApiResponse } from "next"; +import { CacheControl, stringify } from "cache-control-parser"; import { ConsentOption, FidesConfig } from "fides-js"; import { loadPrivacyCenterEnvironment } from "~/app/server-environment"; @@ -57,9 +58,17 @@ export default async function handler( })(); `; + // Calculate the cache-control headers + // TODO: ...configuration? + const cacheHeaders: CacheControl = { + "max-age": 3600, + "public": true, + }; + // Send the bundled script, ready to be loaded directly into a page! res .status(200) .setHeader("Content-Type", "application/javascript") + .setHeader("Cache-Control", stringify(cacheHeaders)) .send(script); } From b26f49f49ff1c2c2ba3769657f012af72d96ce2b Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Tue, 9 May 2023 09:17:01 -0400 Subject: [PATCH 04/10] Add the ability to inject location into the fides.js bundle based on headers/params --- .../privacy-center/cypress/e2e/fides-js.cy.ts | 11 +++++- clients/privacy-center/pages/api/fides-js.ts | 36 +++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts index 94e8b312fb..5e39247061 100644 --- a/clients/privacy-center/cypress/e2e/fides-js.cy.ts +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -1,5 +1,5 @@ describe("fides.js", () => { - it("returns the fides.js packaged bundled with the global config", () => { + it("returns the fides.js package bundled with the global config", () => { cy.request("/fides.js").then(response => { expect(response.status).to.eq(200); expect(response) @@ -25,6 +25,15 @@ describe("fides.js", () => { }); }); + describe("when location is provided to the fides.js API", () => { + it("returns location provided as a ?location query param"); + it("returns error for an invalid ?location query param"); + it("returns location provided as ?country®ion query params"); + it("returns error for invalid ?country®ion query params"); + it("returns location provided as Cloudfront location headers"); + it("returns error for invalid Cloudfront location headers"); + }); + it("caches in the browser", () => { cy.intercept("/fides.js").as("fidesJS"); diff --git a/clients/privacy-center/pages/api/fides-js.ts b/clients/privacy-center/pages/api/fides-js.ts index d1d18c1ab2..b1467b70e1 100644 --- a/clients/privacy-center/pages/api/fides-js.ts +++ b/clients/privacy-center/pages/api/fides-js.ts @@ -6,6 +6,10 @@ import { CacheControl, stringify } from "cache-control-parser"; import { ConsentOption, FidesConfig } from "fides-js"; import { loadPrivacyCenterEnvironment } from "~/app/server-environment"; +const FIDES_JS_MAX_AGE_SECONDS = 60 * 60; // one hour +const CLOUDFRONT_HEADER_COUNTRY = "cloudfront-viewer-country"; +const CLOUDFRONT_HEADER_REGION = "cloudfront-viewer-region"; + /** * Server-side API route to generate the customized "fides.js" script * based on the current configuration values. @@ -17,6 +21,33 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { + // TODO: hoist these two "lookup location" blocks of logic to a helper function + // Optionally, the caller can request a customized "fides.js" bundle for a + // specific geo-location (e.g. "US-CA"). This location can be found in 3 ways: + // 1) Providing a supported geo-location header (e.g. "Cloudfront-Viewer-Country: US") + // 2) Providing an explicit "location" query param (e.g. /fides.js?location=US-CA) + // 3) (future) Performing a geo-ip lookup for the request IP address + let location: string | undefined; + if (typeof req.headers[CLOUDFRONT_HEADER_COUNTRY] === "string") { + location = req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; + if (typeof req.headers[CLOUDFRONT_HEADER_REGION] === "string") { + location += "-" + req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; + } + } + + // Check for any provided "geo-location" query params (optional) + const { location: locationParam, country, region } = req.query; + if (typeof locationParam === "string" && locationParam) { + location = locationParam; + } else if (typeof country === "string" && country) { + location = country; + if (typeof region === "string" && region) { + location += "-" + region; + } + } + + // TODO: validate the location, slightly + // Load the configured consent options (data uses, defaults, etc.) from environment const environment = await loadPrivacyCenterEnvironment(); let options: ConsentOption[] = []; @@ -30,10 +61,11 @@ export default async function handler( } // Create the FidesConfig object that will be used to initialize fides.js - const fidesConfig: FidesConfig = { + const fidesConfig: FidesConfig & { location?: string } = { consent: { options, }, + location, }; const fidesConfigJSON = JSON.stringify(fidesConfig); @@ -61,7 +93,7 @@ export default async function handler( // Calculate the cache-control headers // TODO: ...configuration? const cacheHeaders: CacheControl = { - "max-age": 3600, + "max-age": FIDES_JS_MAX_AGE_SECONDS, "public": true, }; From 91701f68a470b483fd977249c8af681a1df6f45d Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Tue, 9 May 2023 23:25:16 -0400 Subject: [PATCH 05/10] Add support for injecting location into bundle via headers or query param --- clients/package-lock.json | 132 ++++++++++++++++++ .../server-environment.test.ts | 0 ...entities.ts => browser-identities.test.ts} | 0 .../__tests__/common/location.test.ts | 108 ++++++++++++++ clients/privacy-center/common/location.ts | 59 ++++++++ .../privacy-center/cypress/e2e/fides-js.cy.ts | 127 +++++++++++------ clients/privacy-center/package.json | 1 + clients/privacy-center/pages/api/fides-js.ts | 51 ++----- 8 files changed, 399 insertions(+), 79 deletions(-) rename clients/privacy-center/__tests__/{config => app}/server-environment.test.ts (100%) rename clients/privacy-center/__tests__/common/{browser-identities.ts => browser-identities.test.ts} (100%) create mode 100644 clients/privacy-center/__tests__/common/location.test.ts create mode 100644 clients/privacy-center/common/location.ts diff --git a/clients/package-lock.json b/clients/package-lock.json index d93809573e..b597ed1ddd 100644 --- a/clients/package-lock.json +++ b/clients/package-lock.json @@ -4670,6 +4670,19 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -6049,6 +6062,18 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -8017,6 +8042,15 @@ "tslib": "^2.1.0" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -11179,11 +11213,26 @@ "tmpl": "1.0.5" } }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -11199,6 +11248,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -11212,6 +11270,18 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -11813,6 +11883,36 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, + "node_modules/node-mocks-http": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.2.tgz", + "integrity": "sha512-xhWwC0dh35R9rf0j3bRZXuISXdHxxtMx0ywZQBwjrg3yl7KpRETzogfeCamUIjltpn0Fxvs/ZhGJul1vPLrdJQ==", + "dev": true, + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", @@ -12572,6 +12672,15 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -13022,6 +13131,15 @@ } ] }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -15177,6 +15295,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -15993,6 +16124,7 @@ "jest-environment-jsdom": "^29.5.0", "lint-staged": "^13.2.0", "msw": "^1.2.1", + "node-mocks-http": "^1.12.2", "openapi-typescript-codegen": "^0.23.0", "prettier": "^2.8.7", "typescript": "4.9.5", diff --git a/clients/privacy-center/__tests__/config/server-environment.test.ts b/clients/privacy-center/__tests__/app/server-environment.test.ts similarity index 100% rename from clients/privacy-center/__tests__/config/server-environment.test.ts rename to clients/privacy-center/__tests__/app/server-environment.test.ts diff --git a/clients/privacy-center/__tests__/common/browser-identities.ts b/clients/privacy-center/__tests__/common/browser-identities.test.ts similarity index 100% rename from clients/privacy-center/__tests__/common/browser-identities.ts rename to clients/privacy-center/__tests__/common/browser-identities.test.ts diff --git a/clients/privacy-center/__tests__/common/location.test.ts b/clients/privacy-center/__tests__/common/location.test.ts new file mode 100644 index 0000000000..9bfddf64f5 --- /dev/null +++ b/clients/privacy-center/__tests__/common/location.test.ts @@ -0,0 +1,108 @@ +import { createRequest } from "node-mocks-http"; + +import { getLocation } from "~/common/location"; + +describe("getLocation", () => { + describe("when using location headers", () => { + it("returns location data from country & region headers", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js", + headers: { + "CloudFront-Viewer-Country": "US", + "CloudFront-Viewer-Country-Region": "NY", + }, + }); + const location = getLocation(req); + expect(location).toEqual({ + country: "US", + location: "US-NY", + region: "NY", + }); + }); + + it("returns location data from country header", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js", + headers: { + "CloudFront-Viewer-Country": "FR", + }, + }); + const location = getLocation(req); + expect(location).toEqual({ + country: "FR", + location: "FR", + }); + }); + + it("ignores only region headers", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js", + headers: { + "CloudFront-Viewer-Country-Region": "NY", + }, + }); + const location = getLocation(req); + expect(location).toBeUndefined(); + }); + + it("handles invalid location headers", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js", + headers: { + "CloudFront-Viewer-Country": "Magicland", + }, + }); + const location = getLocation(req); + expect(location).toBeUndefined(); + }); + }); + + describe("when using ?location query param", () => { + it("returns location data from query param", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js?location=FR-IDF", + }); + const location = getLocation(req); + expect(location).toEqual({ + country: "FR", + location: "FR-IDF", + region: "IDF", + }); + }); + + it("handles invalid location query param", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js?location=America", + }); + const location = getLocation(req); + expect(location).toBeUndefined(); + }); + }); + + describe("when using both headers and query param", () => { + it("overrides headers with explicit location query param", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js?location=US-CA", + headers: { + "CloudFront-Viewer-Country": "FR", + }, + }); + const location = getLocation(req); + expect(location).toEqual({ + country: "US", + location: "US-CA", + region: "CA", + }); + }); + }); + + describe("when using neither headers nor query param", () => { + it("returns undefined location", () => { + const req = createRequest({ + url: "https://privacy.example.com/fides.js", + }); + const location = getLocation(req); + expect(location).toBeUndefined(); + }); + }); +}); diff --git a/clients/privacy-center/common/location.ts b/clients/privacy-center/common/location.ts new file mode 100644 index 0000000000..34886dafd1 --- /dev/null +++ b/clients/privacy-center/common/location.ts @@ -0,0 +1,59 @@ +import type { UserGeolocation } from "fides-js"; +import type { NextApiRequest } from "next"; + +// Regex to validate a location string, which must: +// 1) Start with a 2-3 character country code (e.g. "US") +// 2) Optionally end with a 2-3 character region code (e.g. "CA") +// 3) Separated by a dash (e.g. "US-CA") +const VALID_ISO_3166_LOCATION_REGEX = /^\w{2,3}(-\w{2,3})?$/ + +// Constants for the supported CloudFront geolocation headers +// (see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html#cloudfront-headers-viewer-location) +const CLOUDFRONT_HEADER_COUNTRY = "cloudfront-viewer-country"; +const CLOUDFRONT_HEADER_REGION = "cloudfront-viewer-country-region"; +export const LOCATION_HEADERS = [CLOUDFRONT_HEADER_COUNTRY, CLOUDFRONT_HEADER_REGION]; + +/** + * Lookup the "location" (ie country and region) for the given request by looking for either: + * 1) Supported geolocation headers (e.g. "Cloudfront-Viewer-Country: US") + * 2) An explicit "location" query param (e.g. https://privacy.example.com/some/path?location=US-CA) + * + * If neither of these are found, return an undefined location. + * + * NOTE: This specifically *does not* include performing a geo-IP lookup... yet! + */ +export const getLocation = ( + req: NextApiRequest +): UserGeolocation | undefined => { + // DEFER: read headers to determine & return the request's IP address + + // Check for a provided "location" query param + const { location } = req.query; + if (typeof location === "string" && VALID_ISO_3166_LOCATION_REGEX.test(location)) { + const [country, region] = location.split("-"); + return { + location, + country, + region, + }; + } + + // Check for CloudFront viewer location headers + if (typeof req.headers[CLOUDFRONT_HEADER_COUNTRY] === "string") { + let location, country, region; + country = req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; + location = country; + if (typeof req.headers[CLOUDFRONT_HEADER_REGION] === "string") { + region = req.headers[CLOUDFRONT_HEADER_REGION].split(",")[0]; + location += "-" + region; + } + if (VALID_ISO_3166_LOCATION_REGEX.test(location)) { + return { + location, + country, + region, + } + } + } + return undefined; +}; diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts index 5e39247061..60a38a01ba 100644 --- a/clients/privacy-center/cypress/e2e/fides-js.cy.ts +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -1,6 +1,6 @@ -describe("fides.js", () => { +describe("fides.js API route", () => { it("returns the fides.js package bundled with the global config", () => { - cy.request("/fides.js").then(response => { + cy.request("/fides.js").then((response) => { expect(response.status).to.eq(200); expect(response) .to.have.property("headers") @@ -17,21 +17,54 @@ describe("fides.js", () => { expect(response.body) .to.match(/var fidesConfig = \{/, "should bundle Fides.init") .to.match(/Fides.init\(fidesConfig\);/, "should bundle Fides.init"); - const matches = response.body.match(/var fidesConfig = (?\{.*?\});/); - expect(matches).to.have.nested.property("groups.json") + const matches = response.body.match( + /var fidesConfig = (?\{.*?\});/ + ); + expect(matches).to.have.nested.property("groups.json"); expect(JSON.parse(matches.groups.json)) .to.have.nested.property("consent.options") - .to.have.length(3) + .to.have.length(3); }); }); - describe("when location is provided to the fides.js API", () => { - it("returns location provided as a ?location query param"); - it("returns error for an invalid ?location query param"); - it("returns location provided as ?country®ion query params"); - it("returns error for invalid ?country®ion query params"); - it("returns location provided as Cloudfront location headers"); - it("returns error for invalid Cloudfront location headers"); + describe("when pre-fetching location", () => { + it("returns location if provided as a '?location' query param", () => { + cy.request("/fides.js?location=US-CA").then((response) => { + expect(response.body).to.match(/var fidesConfig = \{/); + const matches = response.body.match( + /var fidesConfig = (?\{.*?\});/ + ); + expect(JSON.parse(matches.groups.json)) + .to.have.nested.property("location") + .to.deep.equal({ + location: "US-CA", + country: "US", + region: "CA", + }); + }); + }); + + it("returns location if provided as CloudFront location headers", () => { + cy.request({ + url: "/fides.js", + headers: { + "CloudFront-Viewer-Country": "FR", + "CloudFront-Viewer-Country-Region": "IDF", + } + }).then((response) => { + expect(response.body).to.match(/var fidesConfig = \{/); + const matches = response.body.match( + /var fidesConfig = (?\{.*?\});/ + ); + expect(JSON.parse(matches.groups.json)) + .to.have.nested.property("location") + .to.deep.equal({ + location: "FR-IDF", + country: "FR", + region: "IDF", + }); + }); + }); }); it("caches in the browser", () => { @@ -48,11 +81,13 @@ describe("fides.js", () => { describe("when generating cache-control headers", () => { beforeEach(() => { - cy.request("/fides.js").then(response => { + cy.request("/fides.js").then((response) => { expect(response.status).to.eq(200); cy.wrap(response.headers).as("headers"); cy.get("@headers").should("have.property", "etag").as("etag"); - cy.get("@headers").should("have.property", "cache-control").as("cacheHeaders"); + cy.get("@headers") + .should("have.property", "cache-control") + .as("cacheHeaders"); }); }); @@ -66,21 +101,28 @@ describe("fides.js", () => { }); it("generates 'etag' that is consistent when re-requested", () => { - cy.request("/fides.js").should("have.nested.property", "headers.etag").then((etag) => { - cy.get("@etag").should("eq", etag); - }); + cy.request("/fides.js") + .should("have.nested.property", "headers.etag") + .then((etag) => { + cy.get("@etag").should("eq", etag); + }); }); it("generates 'etag' that varies based on location query params", () => { - cy.request("/fides.js?location=US-CA").should("have.nested.property", "headers.etag").as("USCATag").then((etag) => { - cy.get("@etag").should("not.eq", etag); - }); + cy.request("/fides.js?location=US-CA") + .should("have.nested.property", "headers.etag") + .as("USCATag") + .then((etag) => { + cy.get("@etag").should("not.eq", etag); + }); // Fetch a second time with a different location param - cy.request("/fides.js?location=FR").should("have.nested.property", "headers.etag").then((etag) => { - cy.get("@etag").should("not.eq", etag); - cy.get("@USCATag").should("not.eq", etag); - }); + cy.request("/fides.js?location=FR") + .should("have.nested.property", "headers.etag") + .then((etag) => { + cy.get("@etag").should("not.eq", etag); + cy.get("@USCATag").should("not.eq", etag); + }); }); it("generates 'etag' that varies based on Cloudfront location headers", () => { @@ -89,33 +131,40 @@ describe("fides.js", () => { headers: { "Cloudfront-Viewer-Country": "US", "Cloudfront-Viewer-Country-Region": "CA", - } - }).should("have.nested.property", "headers.etag").as("USCATag").then((etag) => { - cy.get("@etag").should("not.eq", etag); - }); + }, + }) + .should("have.nested.property", "headers.etag") + .as("USCATag") + .then((etag) => { + cy.get("@etag").should("not.eq", etag); + }); // Fetch a second time with different location headers cy.request({ url: "/fides.js", headers: { "Cloudfront-Viewer-Country": "FR", - } - }).should("have.nested.property", "headers.etag").as("headersTag").then((etag) => { - cy.get("@etag").should("not.eq", etag); - cy.get("@USCATag").should("not.eq", etag); - }); + }, + }) + .should("have.nested.property", "headers.etag") + .as("headersTag") + .then((etag) => { + cy.get("@etag").should("not.eq", etag); + cy.get("@USCATag").should("not.eq", etag); + }); }); it("returns 'vary' header for supported Cloudfront location headers", () => { const expected = [ - "Cloudfront-Viewer-Country", - "Cloudfront-Viewer-Country-Region", + "cloudfront-viewer-country", + "cloudfront-viewer-country-region", ]; - cy.get("@headers").should("have.property", "vary").then((vary) => { - expected.forEach((header) => { - expect(vary).includes(header); + cy.get("@headers") + .should("have.property", "vary") + .then((vary: any) => { + const varyHeaders = (vary as string).replace(" ", "").split(","); + expect(varyHeaders).to.include.members(expected); }); - }); }); }); }); diff --git a/clients/privacy-center/package.json b/clients/privacy-center/package.json index 9fdbe55771..d76626fecb 100644 --- a/clients/privacy-center/package.json +++ b/clients/privacy-center/package.json @@ -70,6 +70,7 @@ "jest-environment-jsdom": "^29.5.0", "lint-staged": "^13.2.0", "msw": "^1.2.1", + "node-mocks-http": "^1.12.2", "openapi-typescript-codegen": "^0.23.0", "prettier": "^2.8.7", "typescript": "4.9.5", diff --git a/clients/privacy-center/pages/api/fides-js.ts b/clients/privacy-center/pages/api/fides-js.ts index b1467b70e1..9606b7c877 100644 --- a/clients/privacy-center/pages/api/fides-js.ts +++ b/clients/privacy-center/pages/api/fides-js.ts @@ -3,50 +3,22 @@ import { promises as fsPromises } from "fs"; import type { NextApiRequest, NextApiResponse } from "next"; import { CacheControl, stringify } from "cache-control-parser"; -import { ConsentOption, FidesConfig } from "fides-js"; +import { ConsentOption, FidesConfig, UserGeolocation } from "fides-js"; import { loadPrivacyCenterEnvironment } from "~/app/server-environment"; +import { getLocation, LOCATION_HEADERS } from "~/common/location"; const FIDES_JS_MAX_AGE_SECONDS = 60 * 60; // one hour -const CLOUDFRONT_HEADER_COUNTRY = "cloudfront-viewer-country"; -const CLOUDFRONT_HEADER_REGION = "cloudfront-viewer-region"; /** * Server-side API route to generate the customized "fides.js" script * based on the current configuration values. - * - * DEFER: Optimize this route, and ensure it is cacheable - * (see https://github.com/ethyca/fides/issues/3170) */ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { - // TODO: hoist these two "lookup location" blocks of logic to a helper function - // Optionally, the caller can request a customized "fides.js" bundle for a - // specific geo-location (e.g. "US-CA"). This location can be found in 3 ways: - // 1) Providing a supported geo-location header (e.g. "Cloudfront-Viewer-Country: US") - // 2) Providing an explicit "location" query param (e.g. /fides.js?location=US-CA) - // 3) (future) Performing a geo-ip lookup for the request IP address - let location: string | undefined; - if (typeof req.headers[CLOUDFRONT_HEADER_COUNTRY] === "string") { - location = req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; - if (typeof req.headers[CLOUDFRONT_HEADER_REGION] === "string") { - location += "-" + req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; - } - } - - // Check for any provided "geo-location" query params (optional) - const { location: locationParam, country, region } = req.query; - if (typeof locationParam === "string" && locationParam) { - location = locationParam; - } else if (typeof country === "string" && country) { - location = country; - if (typeof region === "string" && region) { - location += "-" + region; - } - } - - // TODO: validate the location, slightly + // Check if a location was provided via headers or query param; if so, inject into the bundle + const location = getLocation(req); // Load the configured consent options (data uses, defaults, etc.) from environment const environment = await loadPrivacyCenterEnvironment(); @@ -60,8 +32,9 @@ export default async function handler( })); } - // Create the FidesConfig object that will be used to initialize fides.js - const fidesConfig: FidesConfig & { location?: string } = { + // Create the FidesConfig JSON that will be used to initialize fides.js + // DEFER: update this to match what FidesConfig expects in the future for location + const fidesConfig: FidesConfig & { location?: UserGeolocation } = { consent: { options, }, @@ -69,12 +42,10 @@ export default async function handler( }; const fidesConfigJSON = JSON.stringify(fidesConfig); - // DEFER: Optimize this by loading from a vendored asset folder instead - // (see https://github.com/ethyca/fides/issues/3170) console.log( "Bundling generic fides.js & Privacy Center configuration together..." ); - const fidesJSBuffer = await fsPromises.readFile("../fides-js/dist/fides.js"); + const fidesJSBuffer = await fsPromises.readFile("public/lib/fides.js"); const fidesJS: string = fidesJSBuffer.toString(); if (!fidesJS || fidesJS === "") { throw new Error("Unable to load latest fides.js script from server!"); @@ -90,11 +61,10 @@ export default async function handler( })(); `; - // Calculate the cache-control headers - // TODO: ...configuration? + // Instruct any caches to store this response, since these bundles do not change often const cacheHeaders: CacheControl = { "max-age": FIDES_JS_MAX_AGE_SECONDS, - "public": true, + public: true, }; // Send the bundled script, ready to be loaded directly into a page! @@ -102,5 +72,6 @@ export default async function handler( .status(200) .setHeader("Content-Type", "application/javascript") .setHeader("Cache-Control", stringify(cacheHeaders)) + .setHeader("Vary", LOCATION_HEADERS) .send(script); } From 33e0c003b0ecb09a0ee3afb7dced6ec26986d391 Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Wed, 10 May 2023 09:38:31 -0400 Subject: [PATCH 06/10] Fix tests, lints, and formatting --- clients/privacy-center/common/location.ts | 41 +++++++++++-------- .../privacy-center/cypress/e2e/fides-js.cy.ts | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/clients/privacy-center/common/location.ts b/clients/privacy-center/common/location.ts index 34886dafd1..c2fe0007c0 100644 --- a/clients/privacy-center/common/location.ts +++ b/clients/privacy-center/common/location.ts @@ -5,13 +5,16 @@ import type { NextApiRequest } from "next"; // 1) Start with a 2-3 character country code (e.g. "US") // 2) Optionally end with a 2-3 character region code (e.g. "CA") // 3) Separated by a dash (e.g. "US-CA") -const VALID_ISO_3166_LOCATION_REGEX = /^\w{2,3}(-\w{2,3})?$/ +const VALID_ISO_3166_LOCATION_REGEX = /^\w{2,3}(-\w{2,3})?$/; // Constants for the supported CloudFront geolocation headers // (see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html#cloudfront-headers-viewer-location) const CLOUDFRONT_HEADER_COUNTRY = "cloudfront-viewer-country"; const CLOUDFRONT_HEADER_REGION = "cloudfront-viewer-country-region"; -export const LOCATION_HEADERS = [CLOUDFRONT_HEADER_COUNTRY, CLOUDFRONT_HEADER_REGION]; +export const LOCATION_HEADERS = [ + CLOUDFRONT_HEADER_COUNTRY, + CLOUDFRONT_HEADER_REGION, +]; /** * Lookup the "location" (ie country and region) for the given request by looking for either: @@ -28,31 +31,35 @@ export const getLocation = ( // DEFER: read headers to determine & return the request's IP address // Check for a provided "location" query param - const { location } = req.query; - if (typeof location === "string" && VALID_ISO_3166_LOCATION_REGEX.test(location)) { - const [country, region] = location.split("-"); + const { location: locationQuery } = req.query; + if ( + typeof locationQuery === "string" && + VALID_ISO_3166_LOCATION_REGEX.test(locationQuery) + ) { + const [country, region] = locationQuery.split("-"); return { - location, - country, - region, + location: locationQuery, + country, + region, }; } // Check for CloudFront viewer location headers if (typeof req.headers[CLOUDFRONT_HEADER_COUNTRY] === "string") { - let location, country, region; - country = req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; + let location; + let region; + const country = req.headers[CLOUDFRONT_HEADER_COUNTRY].split(",")[0]; location = country; if (typeof req.headers[CLOUDFRONT_HEADER_REGION] === "string") { - region = req.headers[CLOUDFRONT_HEADER_REGION].split(",")[0]; - location += "-" + region; + [region] = req.headers[CLOUDFRONT_HEADER_REGION].split(","); + location = `${country}-${region}`; } if (VALID_ISO_3166_LOCATION_REGEX.test(location)) { - return { - location, - country, - region, - } + return { + location, + country, + region, + }; } } return undefined; diff --git a/clients/privacy-center/cypress/e2e/fides-js.cy.ts b/clients/privacy-center/cypress/e2e/fides-js.cy.ts index 60a38a01ba..193ccac918 100644 --- a/clients/privacy-center/cypress/e2e/fides-js.cy.ts +++ b/clients/privacy-center/cypress/e2e/fides-js.cy.ts @@ -50,7 +50,7 @@ describe("fides.js API route", () => { headers: { "CloudFront-Viewer-Country": "FR", "CloudFront-Viewer-Country-Region": "IDF", - } + }, }).then((response) => { expect(response.body).to.match(/var fidesConfig = \{/); const matches = response.body.match( From 5d280bfda9d763d0530b922b42cd11a2558b6830 Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Wed, 10 May 2023 09:51:16 -0400 Subject: [PATCH 07/10] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b56e1a55d4..83ea91de27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The types of changes are: - Privacy Experience Bulk Create, Bulk Update, and Detail Endpoints [#3185](https://github.com/ethyca/fides/pull/3185) - Initial privacy experience UI [#3186](https://github.com/ethyca/fides/pull/3186) - Access and erasure support for OneSignal [#3199](https://github.com/ethyca/fides/pull/3199) +- Add the ability to "inject" location into `/fides.js` bundles and cache responses for one hour [#3272](https://github.com/ethyca/fides/pull/3272) ### Changed From 1c22430d4c70a50666d6bd02ddb350ac2fde61b0 Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Wed, 10 May 2023 10:23:21 -0400 Subject: [PATCH 08/10] Backport "UserGeolocation" type to privacy center (for now) --- clients/privacy-center/common/location.ts | 11 ++++++++++- clients/privacy-center/pages/api/fides-js.ts | 4 ++-- clients/turbo.json | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clients/privacy-center/common/location.ts b/clients/privacy-center/common/location.ts index c2fe0007c0..8227bdcb3b 100644 --- a/clients/privacy-center/common/location.ts +++ b/clients/privacy-center/common/location.ts @@ -1,6 +1,15 @@ -import type { UserGeolocation } from "fides-js"; import type { NextApiRequest } from "next"; +// DEFER: Import this type from fides-js when it exists! +// (see https://github.com/ethyca/fides/pull/3191) +// import type { UserGeolocation } from "fides-js"; +export type UserGeolocation = { + country?: string; // "US" + ip?: string; // "192.168.0.1:12345" + location?: string; // "US-NY" + region?: string; // "NY" +}; + // Regex to validate a location string, which must: // 1) Start with a 2-3 character country code (e.g. "US") // 2) Optionally end with a 2-3 character region code (e.g. "CA") diff --git a/clients/privacy-center/pages/api/fides-js.ts b/clients/privacy-center/pages/api/fides-js.ts index 9606b7c877..3920b1b72f 100644 --- a/clients/privacy-center/pages/api/fides-js.ts +++ b/clients/privacy-center/pages/api/fides-js.ts @@ -3,9 +3,9 @@ import { promises as fsPromises } from "fs"; import type { NextApiRequest, NextApiResponse } from "next"; import { CacheControl, stringify } from "cache-control-parser"; -import { ConsentOption, FidesConfig, UserGeolocation } from "fides-js"; +import { ConsentOption, FidesConfig } from "fides-js"; import { loadPrivacyCenterEnvironment } from "~/app/server-environment"; -import { getLocation, LOCATION_HEADERS } from "~/common/location"; +import { getLocation, LOCATION_HEADERS, UserGeolocation } from "~/common/location"; const FIDES_JS_MAX_AGE_SECONDS = 60 * 60; // one hour diff --git a/clients/turbo.json b/clients/turbo.json index 8f6bc5099d..bd120db9af 100644 --- a/clients/turbo.json +++ b/clients/turbo.json @@ -3,7 +3,7 @@ "pipeline": { "build": { "dependsOn": ["^build"], - "outputs": [".next/*", "!.next/cache/*"] + "outputs": [".next/*", "!.next/cache/*", "dist/*"] }, "dev": { "dependsOn": ["^build"], From ba275dac6100c3db164946bba8895e849ac7aec7 Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Wed, 10 May 2023 10:28:29 -0400 Subject: [PATCH 09/10] Fix formatting! --- clients/privacy-center/pages/api/fides-js.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clients/privacy-center/pages/api/fides-js.ts b/clients/privacy-center/pages/api/fides-js.ts index 3920b1b72f..f35434ecb5 100644 --- a/clients/privacy-center/pages/api/fides-js.ts +++ b/clients/privacy-center/pages/api/fides-js.ts @@ -5,7 +5,11 @@ import { CacheControl, stringify } from "cache-control-parser"; import { ConsentOption, FidesConfig } from "fides-js"; import { loadPrivacyCenterEnvironment } from "~/app/server-environment"; -import { getLocation, LOCATION_HEADERS, UserGeolocation } from "~/common/location"; +import { + getLocation, + LOCATION_HEADERS, + UserGeolocation, +} from "~/common/location"; const FIDES_JS_MAX_AGE_SECONDS = 60 * 60; // one hour From ec2f00f29a96fbeb56983158b7da170613e68cbc Mon Sep 17 00:00:00 2001 From: Neville Samuell Date: Wed, 10 May 2023 11:57:37 -0400 Subject: [PATCH 10/10] Fix comment for CR --- clients/privacy-center/common/location.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/privacy-center/common/location.ts b/clients/privacy-center/common/location.ts index 8227bdcb3b..a137e56875 100644 --- a/clients/privacy-center/common/location.ts +++ b/clients/privacy-center/common/location.ts @@ -27,8 +27,8 @@ export const LOCATION_HEADERS = [ /** * Lookup the "location" (ie country and region) for the given request by looking for either: - * 1) Supported geolocation headers (e.g. "Cloudfront-Viewer-Country: US") - * 2) An explicit "location" query param (e.g. https://privacy.example.com/some/path?location=US-CA) + * 1) An explicit "location" query param (e.g. https://privacy.example.com/some/path?location=US-CA) + * 2) Supported geolocation headers (e.g. "Cloudfront-Viewer-Country: US") * * If neither of these are found, return an undefined location. *