diff --git a/src/featureFlagging/common.ts b/src/featureFlagging/common.ts index bf8f81bb..febaf25f 100644 --- a/src/featureFlagging/common.ts +++ b/src/featureFlagging/common.ts @@ -7,13 +7,46 @@ export type IFlags = { }; -import {flags as server} from './server'; +import {flags as server, getIsProd} from './server'; import {flags as frontend} from './frontend'; -// TODO: if the flags from the server side are relevant to the client, we might needto add a function here that mutates the 'servers' object and is called from +layout.svelte? + + +type DefaultFlagOverrides = {dev:boolean}|{prod:boolean}|{dev:boolean, prod:boolean} +const DEFAULTFLAGS: DefaultFlagOverrides = { + dev: true, + prod: false +}; + +/** Flag-value parsing, taking care of override logic and possible values of flagValue, + * which can come from env variables or URI parameters. + * + * If flagValue is set, parses true/"true" or positive integers to true, and anything else to false - and returns it. + * If flagValue is not set, returns a default value that depends on the environment - and can be overridden. + * @param flagValue + */ +export const parseFlag = (flagValue: boolean|string|number|undefined|null, defaultValues?: DefaultFlagOverrides): boolean => { + if (null == flagValue) { + const defaults = { + ...DEFAULTFLAGS, + ...defaultValues + }; + return (getIsProd() ? defaults.prod : defaults.dev); + } + + const flagString = (flagValue + "").trim().toLowerCase(); + return "true" === flagString || parseInt(flagString) > 0; +}; + // Because lifecycles, this must be called from the script section of consuming svelte components. // Also note, frontend flags will overwrite backend ones: export const featureIsToggledOn = (flagName: FlagName): boolean => { // @ts-ignore - return frontend[flagName] || server[flagName]; + return ( + frontend[flagName] ?? ( + server[flagName] ?? ( + getIsProd() ? DEFAULTFLAGS.prod : DEFAULTFLAGS.dev + ) + ) + ); } diff --git a/src/featureFlagging/frontend.ts b/src/featureFlagging/frontend.ts index 7933351d..d2f8ac11 100644 --- a/src/featureFlagging/frontend.ts +++ b/src/featureFlagging/frontend.ts @@ -5,8 +5,7 @@ */ - -import {getIsProd} from "./server"; +import {parseFlag} from "./common"; /** IMPORTANT! Over time, different feature flags may be scattered around the code base. Typescript help will come * really handy for maintaining this, especially when it's time for cleanup. So: @@ -24,13 +23,7 @@ export const flags: FrontendFeatureToggles = { performances: false } -const getTruthy = (paramValue: any): boolean => ( - !!paramValue && - "false" !== (paramValue + "").toLowerCase() && - 0 !== parseInt(paramValue) -); -// Adding ' || !getIsProd()' turns the toggle on in non-prod environments. export const toggleByURIParams = (searchParams: URLSearchParams) => { - flags.performances = getTruthy(searchParams.get("performances")) || !getIsProd(); + flags.performances = parseFlag(searchParams.get("performances")); } diff --git a/src/featureFlagging/server.ts b/src/featureFlagging/server.ts index 07846460..6d4ef19c 100644 --- a/src/featureFlagging/server.ts +++ b/src/featureFlagging/server.ts @@ -4,11 +4,11 @@ * - configs? */ -let isProd = false; +let isProd: boolean|undefined = undefined; let prodIsLogged = false; export function parseIsProd(env: {PUBLIC_SANITY_DATASET: string}) { - isProd = (env.PUBLIC_SANITY_DATASET === 'prod' || env.PUBLIC_SANITY_DATASET === 'production'); + isProd = (!!env.PUBLIC_SANITY_DATASET && !!env.PUBLIC_SANITY_DATASET.startsWith('prod')); if (!prodIsLogged) { console.log("isProd:", isProd); prodIsLogged = true; @@ -33,11 +33,13 @@ export function getIsProd() { * sending it in through an object). */ export interface ServersideFeatureToggles { - strictAlphaNumericSlug: boolean + strictAlphaNumericSlug: boolean, + forceNavigate: boolean, }; export type ServersideFeatureToggleName = keyof ServersideFeatureToggles; export const flags: ServersideFeatureToggles = { - strictAlphaNumericSlug: false + strictAlphaNumericSlug: false, + forceNavigate: false, } export const setServersideToggle = (name: ServersideFeatureToggleName, flag: boolean) => { @@ -47,7 +49,7 @@ export const setServersideToggle = (name: ServersideFeatureToggleName, flag: boo // @ts-ignore // If environment is prod, then use the 'flag' arg to set the toggle value. // If environment is not prod, just flag it as true / toggle on. - flags[name] = flag || !getIsProd(); + flags[name] = flag; console.log("Serverside feature flag:", name, "=", JSON.stringify(flags[name])); } } diff --git a/src/lib/server/sanityClient.ts b/src/lib/server/sanityClient.ts index d2ce40f7..338cce0a 100644 --- a/src/lib/server/sanityClient.ts +++ b/src/lib/server/sanityClient.ts @@ -2,9 +2,8 @@ import sanityClient from '@sanity/client'; import type { SanityClient, SanityDocument, SanityImageAssetDocument } from '@sanity/client'; import type { Author } from '../types/author'; import type { ConferenceType } from '../types/conference'; -// @ts-ignore + import { env as private_env } from '$env/dynamic/private'; -// @ts-ignore import { env as public_env } from '$env/dynamic/public'; import { makeid } from '../../utils/conference-utils'; import type {IConference} from "../../model/conference"; @@ -205,6 +204,7 @@ async function addAuthor(author: BaseAuthor): Promise { interface ISubmission { title: string, + slug?: string, submissionType: string, description: IDescription[], duration: number, diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 891790cf..7cd5d11e 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -4,13 +4,15 @@ import {parseIsProd, setServersideToggle, getIsProd} from "../featureFlagging/se $: { parseIsProd(env); - setServersideToggle("strictAlphaNumericSlug", env.PUBLIC_FLAG_ALPHANUMERICSLUG === "true"); + setServersideToggle("strictAlphaNumericSlug", parseFlag(env.PUBLIC_FLAG_ALPHANUMERICSLUG)); + setServersideToggle("forceNavigate", parseFlag(env.PUBLIC_FLAG_FORCENAVIGATE)); } import { fetchSiteSettings } from '$lib/sanityClient'; import type { User } from '$lib/types/user.js'; import type { ISiteSetting } from '../model/site-setting.js'; import type { LayoutServerLoad } from './$types'; +import {parseFlag} from "../featureFlagging/common"; export interface ILayoutPageLoadData { settings: ISiteSetting; diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 5463a449..fc055988 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -3,10 +3,12 @@ import { fetchEvents } from '$lib/sanityClient'; import { getUserFromCookie } from '$lib/server/auth.js'; import type { User } from '$lib/types/user'; import type { IEvent } from '../model/event'; +import {featureIsToggledOn} from "../featureFlagging/common"; export interface IPageLoadData { events: IEvent[]; user: User; + forceNavigate: string|undefined, } export async function load({ cookies }) { @@ -19,12 +21,18 @@ export async function load({ cookies }) { }; } + let forceNavigate = undefined; if (user.isAuthenticated) { - throw redirect(308, '/konferanser'); + const redirectTarget = '/konferanser'; + if (!featureIsToggledOn("forceNavigate")) { + throw redirect(308, redirectTarget); + } + forceNavigate = redirectTarget; } return { user, events, + forceNavigate }; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 24aeb9f1..3e5b6e5a 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -6,6 +6,11 @@ export let data: IPageLoadData; export let events = data.events; export let user = data.user; + + if (data.forceNavigate) { + console.log("(Navigating to " + data.forceNavigate + ")") + window.location.href = data.forceNavigate; + } @@ -20,3 +25,4 @@ {/if} +