Skip to content

Commit

Permalink
Persisterer language og context valg til cookies (#451)
Browse files Browse the repository at this point in the history
\+ litt diverse rydding, refactoring av typer, fjerner ubrukte
funksjoner, etc
  • Loading branch information
anders-nom authored Sep 26, 2024
1 parent 22c564c commit 4fec939
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 98 deletions.
6 changes: 2 additions & 4 deletions packages/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import { initHistoryEvents, initScrollToEvents } from "./events";
import { addFaroMetaData } from "./faro";
import { refreshAuthData } from "./helpers/auth";
import { buildHtmlElement } from "./helpers/html-element-builder";
import { param, initParams } from "./params";
import "./main.css";
import { param, updateDecoratorParams } from "./params";

import.meta.glob("./styles/*.css", { eager: true });
import.meta.glob(["./views/**/*.ts", "!./views/**/*.test.ts"], { eager: true });

updateDecoratorParams({});

// @TODO: Refactor loaders
window.addEventListener("load", () => {
addFaroMetaData();
});
Expand All @@ -35,6 +32,7 @@ const injectHeadAssets = () => {
};

const init = () => {
initParams();
injectHeadAssets();
initHistoryEvents();
initScrollToEvents();
Expand Down
67 changes: 50 additions & 17 deletions packages/client/src/params.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,70 @@
import { ClientParams, Environment } from "decorator-shared/params";
import Cookies from "js-cookie";
import {
ClientParams,
Context,
Environment,
Language,
} from "decorator-shared/params";
import { createEvent } from "./events";

export const hasParam = (paramKey: keyof ClientParams): boolean => {
return window.__DECORATOR_DATA__.params[paramKey] !== undefined;
};
type ParamKey = keyof ClientParams;
type EnvKey = keyof Environment;

const CONTEXT_COOKIE = "decorator-context";
const LANGUAGE_COOKIE = "decorator-language";

export const param = <TKey extends keyof ClientParams>(paramKey: TKey) => {
return window.__DECORATOR_DATA__.params[paramKey] as ClientParams[TKey];
export const param = <TKey extends ParamKey>(paramKey: TKey) => {
return window.__DECORATOR_DATA__.params[paramKey];
};

export const env = <TKey extends keyof Environment>(envKey: TKey): string => {
return window.__DECORATOR_DATA__.env[envKey] as Environment[TKey];
export const env = <TKey extends EnvKey>(envKey: TKey) => {
return window.__DECORATOR_DATA__.env[envKey];
};

export const updateDecoratorParams = (params: Partial<ClientParams>) => {
const updatedParams = params;
const updatedParams = { ...params };

Object.entries(params).map(([key, value]) => {
if (param(key as keyof ClientParams) === value) {
delete updatedParams[key as keyof ClientParams];
Object.entries(params).forEach(([key, value]) => {
if (param(key as ParamKey) === value) {
delete updatedParams[key as ParamKey];
}
});

if (Object.keys(updatedParams).length > 0) {
window.__DECORATOR_DATA__.params = {
...window.__DECORATOR_DATA__.params,
...updatedParams,
};
window.__DECORATOR_DATA__.params = {
...window.__DECORATOR_DATA__.params,
...updatedParams,
};

Cookies.set(CONTEXT_COOKIE, param("context"));
Cookies.set(LANGUAGE_COOKIE, param("language"));

if (Object.keys(updatedParams).length > 0) {
window.dispatchEvent(
createEvent("paramsupdated", {
detail: { params: updatedParams },
}),
);
}
};

export const initParams = () => {
const rawParams = window.__DECORATOR_DATA__.rawParams;

const initialParams: Partial<ClientParams> = {};

const language =
rawParams?.language ||
(Cookies.get(LANGUAGE_COOKIE) as Language | undefined);
if (language) {
initialParams.language = language;
}

const context =
rawParams?.context ||
(Cookies.get(CONTEXT_COOKIE) as Context | undefined);
if (context) {
initialParams.context = context;
}

updateDecoratorParams(initialParams);
};
9 changes: 6 additions & 3 deletions packages/server/src/handlers/ssr-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { validParams } from "../validateParams";
import { parseAndValidateParams } from "../validateParams";
import { getFeatures } from "../unleash";
import { HeaderTemplate } from "../views/header/header";
import { FooterTemplate } from "../views/footer/footer";
Expand All @@ -16,7 +16,8 @@ type SsrPayload = {
};

export const ssrApiHandler: Handler = async ({ req, json }) => {
const params = validParams(req.query());
const query = req.query();
const params = parseAndValidateParams(query);
const features = getFeatures();

return json({
Expand All @@ -33,7 +34,9 @@ export const ssrApiHandler: Handler = async ({ req, json }) => {
withContainers: true,
})
).render(params),
scripts: ScriptsTemplate({ features, params }).render(params),
scripts: ScriptsTemplate({ features, params, rawParams: query }).render(
params,
),
headAssets: HeadAssetsTemplate().render(),
versionId: env.VERSION_ID,
} satisfies SsrPayload);
Expand Down
45 changes: 23 additions & 22 deletions packages/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { fetchOpsMessages } from "./ops-msgs";
import { getTaskAnalyticsSurveys } from "./task-analytics-config";
import { getFeatures } from "./unleash";
import { isLocalhost } from "./urls";
import { validParams } from "./validateParams";
import { IndexTemplate } from "./views";
import { parseAndValidateParams } from "./validateParams";
import { IndexHtml } from "./views";
import { HeaderTemplate } from "./views/header/header";
import { FooterTemplate } from "./views/footer/footer";
import { buildDecoratorData } from "./views/scripts";
Expand Down Expand Up @@ -79,14 +79,14 @@ app.post("/api/notifications/:id/archive", async ({ req, json }) => {
app.get("/api/search", async ({ req, html }) =>
html(
await searchHandler({
...validParams(req.query()),
...parseAndValidateParams(req.query()),
query: req.query("q") ?? "",
}),
),
);
app.get("/api/csp", ({ json }) => json(cspDirectives));
app.get("/main-menu", async ({ req, html }) => {
const data = validParams(req.query());
const data = parseAndValidateParams(req.query());

return html(
(
Expand All @@ -99,14 +99,14 @@ app.get("/main-menu", async ({ req, html }) => {
app.get("/auth", async ({ req, json }) =>
json(
await authHandler({
params: validParams(req.query()),
params: parseAndValidateParams(req.query()),
cookie: req.header("Cookie") ?? "",
}),
),
);
app.get("/ops-messages", async ({ json }) => json(await fetchOpsMessages()));
app.get("/header", async ({ req, html }) => {
const params = validParams(req.query());
const params = parseAndValidateParams(req.query());

return html(
(await HeaderTemplate({ params, withContainers: false })).render(
Expand All @@ -115,7 +115,7 @@ app.get("/header", async ({ req, html }) => {
);
});
app.get("/footer", async ({ req, html }) => {
const params = validParams(req.query());
const params = parseAndValidateParams(req.query());

return html(
(
Expand All @@ -130,7 +130,8 @@ app.get("/footer", async ({ req, html }) => {
app.get("/ssr", ssrApiHandler);
// TODO: The CSR implementation can probably be tweaked to use the same data as /ssr
app.on("GET", ["/env", "/csr"], async ({ req, json }) => {
const params = validParams(req.query());
const query = req.query();
const params = parseAndValidateParams(query);
const features = getFeatures();

return json({
Expand All @@ -147,9 +148,13 @@ app.on("GET", ["/env", "/csr"], async ({ req, json }) => {
withContainers: true,
})
).render(params),
data: buildDecoratorData({ params, features, headAssets }),
data: buildDecoratorData({
params,
rawParams: query,
features,
headAssets,
}),
scripts: csrAssets.mainScripts,
//TODO: Add css?
} satisfies CsrPayload);
});
app.get("/:clientWithId{client(.*).js}", async ({ redirect }) =>
Expand All @@ -158,18 +163,14 @@ app.get("/:clientWithId{client(.*).js}", async ({ redirect }) =>
app.get("/css/:clientWithId{client(.*).css}", async ({ redirect }) =>
redirect(csrAssets.cssUrl),
);
app.get("/", async ({ req, html }) => {
const params = validParams(req.query());

return html(
(
await IndexTemplate({
params,
url: req.url,
})
).render(params),
);
});
app.get("/", async ({ req, html }) =>
html(
IndexHtml({
rawParams: req.query(),
url: req.url,
}),
),
);

app.route("/decorator-next", app);
app.route("/dekoratoren", app);
Expand Down
18 changes: 9 additions & 9 deletions packages/server/src/validateParams.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { describe, expect, it } from "bun:test";
import {
parseBooleanParam,
validateParams,
validParams,
parseAndValidateParams,
} from "./validateParams";
import { formatParams } from "decorator-shared/json";
import { Params } from "decorator-shared/params";

describe("Validating urls", () => {
it("Should validate nav.no urls", () => {
const params = validParams({
const params = parseAndValidateParams({
redirectToUrl: "https://myapp.nav.no/foo",
redirectToUrlLogout: "https://my.app.nav.no/bar",
logoutUrl: "https://www.nav.no/qwer",
Expand Down Expand Up @@ -47,7 +47,7 @@ describe("Validating urls", () => {
});

it("Should validate paths", () => {
const params = validParams({
const params = parseAndValidateParams({
redirectToUrl: "/foo",
redirectToUrlLogout: "/bar",
logoutUrl: "/qwer",
Expand Down Expand Up @@ -85,31 +85,31 @@ describe("Validating urls", () => {
});

it("Should not validate redirectToUrl with non-nav origin", () => {
const params = validParams({
const params = parseAndValidateParams({
redirectToUrl: "https://www.vg.no",
} satisfies Partial<Record<keyof Params, unknown>>);

expect(params.redirectToUrl).toBeUndefined();
});

it("Should not validate redirectToUrlLogout with non-nav origins", () => {
const params = validParams({
const params = parseAndValidateParams({
redirectToUrlLogout: "https://navv.no",
} satisfies Partial<Record<keyof Params, unknown>>);

expect(params.redirectToUrlLogout).toBeUndefined();
});

it("Should not validate logoutUrl with non-nav origins", () => {
const params = validParams({
const params = parseAndValidateParams({
logoutUrl: "https://www.notevilatall.no",
} satisfies Partial<Record<keyof Params, unknown>>);

expect(params.logoutUrl).toBeUndefined();
});

it("Should not validate breadcrumbs with non-nav origins", () => {
const params = validParams({
const params = parseAndValidateParams({
breadcrumbs: JSON.stringify([
{
title: "test",
Expand All @@ -123,7 +123,7 @@ describe("Validating urls", () => {

it("Should not validate availableLanguages with non-nav origins", () => {
const validateAvailableLanguage = () =>
validParams({
parseAndValidateParams({
availableLanguages: JSON.stringify([
{
handleInApp: false,
Expand All @@ -136,7 +136,7 @@ describe("Validating urls", () => {
});

it("Should not validate logoutUrl without protocol prefix", () => {
const params = validParams({
const params = parseAndValidateParams({
logoutUrl: "www.nav.no",
} satisfies Partial<Record<keyof Params, unknown>>);

Expand Down
Loading

0 comments on commit 4fec939

Please sign in to comment.