feature flag support
This commit is contained in:
parent
b4a4f81308
commit
96fbc8fff0
|
@ -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()),
|
||||||
})
|
})
|
||||||
|
|
|
@ -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: {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue