feature flag support

This commit is contained in:
Martin McKeaveney 2024-09-09 18:07:47 +01:00
parent b4a4f81308
commit 96fbc8fff0
7 changed files with 52 additions and 8 deletions

View File

@ -268,4 +268,5 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
export const flags = new FlagSet({ export const flags = new FlagSet({
DEFAULT_VALUES: Flag.boolean(env.isDev()), DEFAULT_VALUES: Flag.boolean(env.isDev()),
SQS: Flag.boolean(env.isDev()), SQS: Flag.boolean(env.isDev()),
AI_CUSTOM_CONFIGS: Flag.boolean(env.isDev()),
}) })

View File

@ -17,6 +17,7 @@ export const usage = (users: number = 0, creators: number = 0): QuotaUsage => {
automations: 0, automations: 0,
dayPasses: 0, dayPasses: 0,
queries: 0, queries: 0,
budibaseAICredits: 0,
triggers: {}, triggers: {},
breakdown: { breakdown: {
rowQueries: { rowQueries: {
@ -46,12 +47,14 @@ export const usage = (users: number = 0, creators: number = 0): QuotaUsage => {
automations: 0, automations: 0,
dayPasses: 0, dayPasses: 0,
queries: 0, queries: 0,
budibaseAICredits: 0,
triggers: {}, triggers: {},
}, },
current: { current: {
automations: 0, automations: 0,
dayPasses: 0, dayPasses: 0,
queries: 0, queries: 0,
budibaseAICredits: 0,
triggers: {}, triggers: {},
}, },
}, },
@ -62,6 +65,7 @@ export const usage = (users: number = 0, creators: number = 0): QuotaUsage => {
creators, creators,
userGroups: 0, userGroups: 0,
rows: 0, rows: 0,
aiCustomConfigs: 0,
triggers: {}, triggers: {},
}, },
} }

View File

@ -80,7 +80,9 @@
} }
async function deleteConfig(key) { async function deleteConfig(key) {
// Delete a configuration // We don't store the default BB AI config in the DB
delete fullAIConfig.config.budibase_ai
// Delete the configuration
delete fullAIConfig.config[key] delete fullAIConfig.config[key]
try { try {

View File

@ -1,7 +1,9 @@
import { derived } from "svelte/store" import { derived } from "svelte/store"
import { admin } from "./admin" import { admin } from "./admin"
import { auth } from "./auth" import { auth } from "./auth"
import { isEnabled } from "helpers/featureFlags"
import { sdk } from "@budibase/shared-core" import { sdk } from "@budibase/shared-core"
import { FeatureFlag } from "@budibase/types";
export const menu = derived([admin, auth], ([$admin, $auth]) => { export const menu = derived([admin, auth], ([$admin, $auth]) => {
const user = $auth?.user const user = $auth?.user
@ -45,10 +47,6 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
title: "Auth", title: "Auth",
href: "/builder/portal/settings/auth", href: "/builder/portal/settings/auth",
}, },
{
title: "AI",
href: "/builder/portal/settings/ai",
},
{ {
title: "Email", title: "Email",
href: "/builder/portal/settings/email", href: "/builder/portal/settings/email",
@ -66,6 +64,15 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
href: "/builder/portal/settings/environment", href: "/builder/portal/settings/environment",
}, },
] ]
if (isEnabled(FeatureFlag.AI_CUSTOM_CONFIGS)) {
settingsSubPages.push(
{
title: "AI",
href: "/builder/portal/settings/ai",
}
)
}
if (!cloud) { if (!cloud) {
settingsSubPages.push({ settingsSubPages.push({
title: "Version", title: "Version",

View File

@ -1,6 +1,7 @@
export enum FeatureFlag { export enum FeatureFlag {
PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE", PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE",
PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT", PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT",
AI_CUSTOM_CONFIGS = "AI_CUSTOM_CONFIGS",
} }
export interface TenantFeatureFlags { export interface TenantFeatureFlags {

View File

@ -135,7 +135,6 @@ const getEventFns = async (config: Config, existing?: Config) => {
} }
} }
} }
return fns return fns
} }
@ -344,11 +343,12 @@ async function enrichAIConfig(aiConfig: AIConfig) {
// Return the Budibase AI data source as part of the response if licensing allows // Return the Budibase AI data source as part of the response if licensing allows
const budibaseAIEnabled = await pro.features.isBudibaseAIEnabled() const budibaseAIEnabled = await pro.features.isBudibaseAIEnabled()
const defaultConfigExists = Object.keys(aiConfig.config).some(key => aiConfig.config[key].isDefault)
if (budibaseAIEnabled) { if (budibaseAIEnabled) {
aiConfig.config["budibase_ai"] = { aiConfig.config["budibase_ai"] = {
provider: "OpenAI", provider: "OpenAI",
active: true, active: true,
isDefault: true, isDefault: !defaultConfigExists,
defaultModel: env.BUDIBASE_AI_DEFAULT_MODEL, defaultModel: env.BUDIBASE_AI_DEFAULT_MODEL,
name: "Budibase AI", name: "Budibase AI",
} }

View File

@ -1,4 +1,3 @@
import { expect } from "vitest"
import { configs } from "@budibase/backend-core" import { configs } from "@budibase/backend-core"
import { UserCtx } from "@budibase/types" import { UserCtx } from "@budibase/types"
import * as pro from "@budibase/pro" import * as pro from "@budibase/pro"
@ -81,6 +80,35 @@ describe("Global configs controller", () => {
}) })
}) })
it("Should not not return the default Budibase AI config when on self host", async () => {
pro.features.isBudibaseAIEnabled = jest.fn(() => false)
configs.getConfig.mockResolvedValue({
config: {
ai: {
apiKey: "abc123APIKey",
baseUrl: "https://api.example.com",
},
},
})
const ctx = {
params: {
type: "ai",
},
throw: jest.fn(),
} as UserCtx
await find(ctx)
expect(ctx.body).toEqual({
config: {
ai: {
apiKey: "--secret-value--",
baseUrl: "https://api.example.com",
},
},
})
})
it("Should not update existing secrets when updating an existing AI Config", async () => { it("Should not update existing secrets when updating an existing AI Config", async () => {
const newConfig = { const newConfig = {
type: "ai", type: "ai",
@ -114,4 +142,5 @@ describe("Global configs controller", () => {
// should be unchanged // should be unchanged
expect(newConfig.config.aiconfig.apiKey === "myapikey") expect(newConfig.config.aiconfig.apiKey === "myapikey")
}) })
}) })