Skip to content

Commit

Permalink
#194: feature-flagged force-navigate instead of redirect.
Browse files Browse the repository at this point in the history
  • Loading branch information
Espen Norderud committed Oct 24, 2023
1 parent c734cce commit 79021a0
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 21 deletions.
39 changes: 36 additions & 3 deletions src/featureFlagging/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)
);
}
11 changes: 2 additions & 9 deletions src/featureFlagging/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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"));
}
12 changes: 7 additions & 5 deletions src/featureFlagging/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) => {
Expand All @@ -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]));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/sanityClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -205,6 +204,7 @@ async function addAuthor(author: BaseAuthor): Promise<string> {

interface ISubmission {
title: string,
slug?: string,
submissionType: string,
description: IDescription[],
duration: number,
Expand Down
4 changes: 3 additions & 1 deletion src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion src/routes/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand All @@ -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
};
}
6 changes: 6 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
</script>

<svelte:head>
Expand All @@ -20,3 +25,4 @@
<ExternalContent {events} />
{/if}
</div>

0 comments on commit 79021a0

Please sign in to comment.