New endpoint to set feature flag overrides.

This commit is contained in:
Sam Rose 2025-04-24 12:32:26 +01:00
parent 39021dc485
commit d261b91a65
No known key found for this signature in database
12 changed files with 54 additions and 0 deletions

View File

@ -9,6 +9,7 @@ export enum Cookie {
ACCOUNT_RETURN_URL = "budibase:account:returnurl",
DatasourceAuth = "budibase:datasourceauth",
OIDC_CONFIG = "budibase:oidc:config",
FeatureFlags = "budibase:featureflags",
}
export { Header } from "@budibase/shared-core"

View File

@ -460,6 +460,17 @@ export function setFeatureFlags(key: string, value: Record<string, boolean>) {
context.featureFlagCache[key] = value
}
export function getFeatureFlagOverrides(): Record<string, boolean> {
return getCurrentContext()?.featureFlagOverrides || {}
}
export function doInFeatureFlagOverrideContext<T>(
value: Record<string, boolean>,
callback: () => Promise<T>
) {
return newContext({ featureFlagOverrides: value }, callback)
}
export function getTableForView(viewId: string): Table | undefined {
const context = getCurrentContext()
if (!context) {

View File

@ -24,5 +24,6 @@ export type ContextMap = {
featureFlagCache?: {
[key: string]: Record<string, boolean>
}
featureFlagOverrides?: Record<string, boolean>
viewToTableCache?: Record<string, Table>
}

View File

@ -0,0 +1,13 @@
import { Ctx, FeatureFlagCookie } from "@budibase/types"
import { Middleware, Next } from "koa"
import { getCookie } from "../utils"
import { Cookie } from "../constants"
import { doInFeatureFlagOverrideContext } from "../context"
export default (async (ctx: Ctx, next: Next) => {
const cookie = getCookie<FeatureFlagCookie>(ctx, Cookie.FeatureFlags)
const flags = cookie?.flags || {}
await doInFeatureFlagOverrideContext(flags, async () => {
await next()
})
}) as Middleware

View File

@ -20,5 +20,6 @@ export { default as correlation } from "../logging/correlation/middleware"
export { default as errorHandling } from "./errorHandling"
export { default as querystringToBody } from "./querystringToBody"
export { default as csp } from "./contentSecurityPolicy"
export { default as featureFlagCookie } from "./featureFlagCookie"
export * as joiValidator from "./joi-validator"
export { default as ip } from "./ip"

View File

@ -0,0 +1,7 @@
import { UserCtx, OverrideFeatureFlagRequest } from "@budibase/types"
import { Cookie, utils } from "@budibase/backend-core"
export async function override(ctx: UserCtx<OverrideFeatureFlagRequest, void>) {
const { flags = {} } = ctx.request.body
utils.setCookie(ctx, flags, Cookie.FeatureFlags)
}

View File

@ -71,6 +71,7 @@ if (apiEnabled()) {
)
.use(pro.licensing())
.use(currentApp)
.use(middleware.featureFlagCookie)
// Add CSP as soon as possible - depends on licensing and currentApp
if (!coreEnv.DISABLE_CONTENT_SECURITY_POLICY) {

View File

@ -0,0 +1,8 @@
import Router from "@koa/router"
import * as controller from "../controllers/features"
const router: Router = new Router()
router.patch("/api/features", controller.override)
export default router

View File

@ -0,0 +1,3 @@
export interface OverrideFeatureFlagRequest {
flags: Record<string, boolean>
}

View File

@ -5,6 +5,7 @@ export * from "./backup"
export * from "./component"
export * from "./datasource"
export * from "./deployment"
export * from "./features"
export * from "./integration"
export * from "./layout"
export * from "./metadata"

View File

@ -7,3 +7,7 @@ export interface SessionCookie {
sessionId: string
userId: string
}
export interface FeatureFlagCookie {
flags: Record<string, boolean>
}

View File

@ -1,4 +1,5 @@
export enum FeatureFlag {
DEBUG_UI = "DEBUG_UI",
USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR",
AI_JS_GENERATION = "AI_JS_GENERATION",
AI_TABLE_GENERATION = "AI_TABLE_GENERATION",
@ -14,6 +15,8 @@ export const FeatureFlagDefaults: Record<FeatureFlag, boolean> = {
// Account-portal
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
[FeatureFlag.DEBUG_UI]: false,
}
export type FeatureFlags = typeof FeatureFlagDefaults