diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index 483e895e98..b44395d2fe 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -13,7 +13,6 @@ on: options: - patch - minor - - major required: true jobs: diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index d9b8719f0f..e06a197ad5 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -81,11 +81,17 @@ mkdir -p ${DATA_DIR}/minio mkdir -p ${DATA_DIR}/redis chown -R couchdb:couchdb ${DATA_DIR}/couch -sed -i "s#DATA_DIR#${DATA_DIR}#g" /etc/redis/redis.conf +REDIS_CONFIG="/etc/redis/redis.conf" +sed -i "s#DATA_DIR#${DATA_DIR}#g" "${REDIS_CONFIG}" + +if [[ -n "${USE_DEFAULT_REDIS_CONFIG}" ]]; then + REDIS_CONFIG="" +fi + if [[ -n "${REDIS_PASSWORD}" ]]; then - redis-server /etc/redis/redis.conf --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 & + redis-server "${REDIS_CONFIG}" --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 & else - redis-server /etc/redis/redis.conf > /dev/stdout 2>&1 & + redis-server "${REDIS_CONFIG}" > /dev/stdout 2>&1 & fi /bbcouch-runner.sh & diff --git a/lerna.json b/lerna.json index e4b72979b9..8e53ea97a9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.0.1", + "version": "3.0.3", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/packages/backend-core/src/features/features.ts b/packages/backend-core/src/features/features.ts index e2f8d9b6a1..b9302f9bce 100644 --- a/packages/backend-core/src/features/features.ts +++ b/packages/backend-core/src/features/features.ts @@ -272,7 +272,6 @@ export const flags = new FlagSet({ [FeatureFlag.SQS]: Flag.boolean(true), [FeatureFlag.AI_CUSTOM_CONFIGS]: Flag.boolean(env.isDev()), [FeatureFlag.ENRICHED_RELATIONSHIPS]: Flag.boolean(env.isDev()), - [FeatureFlag.TABLES_DEFAULT_ADMIN]: Flag.boolean(env.isDev()), [FeatureFlag.BUDIBASE_AI]: Flag.boolean(env.isDev()), }) diff --git a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte index 51538944f4..fd235a70f2 100644 --- a/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte +++ b/packages/builder/src/components/automation/SetupPanel/CronBuilder.svelte @@ -9,7 +9,7 @@ } from "@budibase/bbui" import { onMount, createEventDispatcher } from "svelte" import { flags } from "stores/builder" - import { featureFlags } from "stores/portal" + import { featureFlags, licensing } from "stores/portal" import { API } from "api" import MagicWand from "../../../../assets/MagicWand.svelte" @@ -26,7 +26,9 @@ let aiCronPrompt = "" let loadingAICronExpression = false - $: aiEnabled = $featureFlags.AI_CUSTOM_CONFIGS || $featureFlags.BUDIBASE_AI + $: aiEnabled = + ($featureFlags.AI_CUSTOM_CONFIGS && $licensing.customAIConfigsEnabled) || + ($featureFlags.BUDIBASE_AI && $licensing.budibaseAIEnabled) $: { if (cronExpression) { try { diff --git a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte index b54ecbf9fd..7e11c98768 100644 --- a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte +++ b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte @@ -65,7 +65,7 @@ let tableOptions let errorChecker = new RelationshipErrorChecker( invalidThroughTable, - relationshipExists + manyToManyRelationshipExistsFn ) let errors = {} let fromPrimary, fromForeign, fromColumn, toColumn @@ -125,7 +125,7 @@ } return false } - function relationshipExists() { + function manyToManyRelationshipExistsFn() { if ( originalFromTable && originalToTable && @@ -141,16 +141,14 @@ datasource.entities[getTable(toId).name].schema ).filter(value => value.through) - const matchAgainstUserInput = (fromTableId, toTableId) => - (fromTableId === fromId && toTableId === toId) || - (fromTableId === toId && toTableId === fromId) + const matchAgainstUserInput = link => + (link.throughTo === throughToKey && + link.throughFrom === throughFromKey) || + (link.throughTo === throughFromKey && link.throughFrom === throughToKey) - return !!fromThroughLinks.find(from => - toThroughLinks.find( - to => - from.through === to.through && - matchAgainstUserInput(from.tableId, to.tableId) - ) + const allLinks = [...fromThroughLinks, ...toThroughLinks] + return !!allLinks.find( + link => link.through === throughId && matchAgainstUserInput(link) ) } @@ -181,16 +179,15 @@ relationshipType: errorChecker.relationshipTypeSet(relationshipType), fromTable: errorChecker.tableSet(fromTable) || - errorChecker.doesRelationshipExists() || errorChecker.differentTables(fromId, toId, throughId), toTable: errorChecker.tableSet(toTable) || - errorChecker.doesRelationshipExists() || errorChecker.differentTables(toId, fromId, throughId), throughTable: errorChecker.throughTableSet(throughTable) || errorChecker.throughIsNullable() || - errorChecker.differentTables(throughId, fromId, toId), + errorChecker.differentTables(throughId, fromId, toId) || + errorChecker.doesRelationshipExists(), throughFromKey: errorChecker.manyForeignKeySet(throughFromKey) || errorChecker.manyTypeMismatch( @@ -198,7 +195,8 @@ throughTable, fromTable.primary[0], throughToKey - ), + ) || + errorChecker.differentColumns(throughFromKey, throughToKey), throughToKey: errorChecker.manyForeignKeySet(throughToKey) || errorChecker.manyTypeMismatch( @@ -372,6 +370,16 @@ fromColumn = selectedFromTable.name fromPrimary = selectedFromTable?.primary[0] || null } + if (relationshipType === RelationshipType.MANY_TO_MANY) { + relationshipPart1 = PrettyRelationshipDefinitions.MANY + relationshipPart2 = PrettyRelationshipDefinitions.MANY + } else if (relationshipType === RelationshipType.MANY_TO_ONE) { + relationshipPart1 = PrettyRelationshipDefinitions.ONE + relationshipPart2 = PrettyRelationshipDefinitions.MANY + } else { + relationshipPart1 = PrettyRelationshipDefinitions.MANY + relationshipPart2 = PrettyRelationshipDefinitions.ONE + } }) diff --git a/packages/builder/src/components/backend/Datasources/relationshipErrors.js b/packages/builder/src/components/backend/Datasources/relationshipErrors.js index 610ff9f1fe..9fd30eaea2 100644 --- a/packages/builder/src/components/backend/Datasources/relationshipErrors.js +++ b/packages/builder/src/components/backend/Datasources/relationshipErrors.js @@ -3,6 +3,7 @@ import { RelationshipType } from "@budibase/types" const typeMismatch = "Column type of the foreign key must match the primary key" const columnBeingUsed = "Column name cannot be an existing column" const mustBeDifferentTables = "From/to/through tables must be different" +const mustBeDifferentColumns = "Foreign keys must be different" const primaryKeyNotSet = "Please pick the primary key" const throughNotNullable = "Ensure non-key columns are nullable or auto-generated" @@ -30,9 +31,9 @@ function typeMismatchCheck(fromTable, toTable, primary, foreign) { } export class RelationshipErrorChecker { - constructor(invalidThroughTableFn, relationshipExistsFn) { + constructor(invalidThroughTableFn, manyToManyRelationshipExistsFn) { this.invalidThroughTable = invalidThroughTableFn - this.relationshipExists = relationshipExistsFn + this.manyToManyRelationshipExists = manyToManyRelationshipExistsFn } setType(type) { @@ -72,7 +73,7 @@ export class RelationshipErrorChecker { } doesRelationshipExists() { - return this.isMany() && this.relationshipExists() + return this.isMany() && this.manyToManyRelationshipExists() ? relationshipAlreadyExists : null } @@ -83,6 +84,11 @@ export class RelationshipErrorChecker { return error ? mustBeDifferentTables : null } + differentColumns(columnA, columnB) { + const error = columnA && columnB && columnA === columnB + return error ? mustBeDifferentColumns : null + } + columnBeingUsed(table, column, ogName) { return isColumnNameBeingUsed(table, column, ogName) ? columnBeingUsed : null } diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte index 34c317e865..c48cc3b8ce 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte @@ -5,13 +5,13 @@ Button, Drawer, DrawerContent, - Helpers, } from "@budibase/bbui" import { createEventDispatcher } from "svelte" import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import FilterBuilder from "./FilterBuilder.svelte" import { tables, selectedScreen } from "stores/builder" import { search } from "@budibase/frontend-core" + import { utils } from "@budibase/shared-core" const dispatch = createEventDispatcher() @@ -22,7 +22,7 @@ let drawer - $: localFilters = Helpers.cloneDeep(value) + $: localFilters = value $: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: dsSchema = getSchemaForDatasource($selectedScreen, datasource)?.schema $: schemaFields = search.getFields( @@ -30,8 +30,7 @@ Object.values(schema || dsSchema || {}), { allowLinks: true } ) - - $: text = getText(value?.groups) + $: text = getText(value) async function saveFilter() { dispatch("change", localFilters) @@ -39,11 +38,14 @@ drawer.hide() } - const getText = (filterGroups = []) => { - const allFilters = filterGroups.reduce((acc, group) => { + const getText = filters => { + if (Array.isArray(filters)) { + filters = utils.processSearchFilters(filters) + } + const groups = filters?.groups || [] + const allFilters = groups.reduce((acc, group) => { return (acc += group.filters.filter(filter => filter.field).length) }, 0) - if (allFilters === 0) { return "No filters set" } else { @@ -62,7 +64,7 @@ on:drawerShow on:drawerShow={() => { // Reset to the currently available value. - localFilters = Helpers.cloneDeep(value) + localFilters = value }} > diff --git a/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js b/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js index 300d5d5369..e797a1c5bd 100644 --- a/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js +++ b/packages/builder/src/pages/builder/portal/settings/ai/AISettings.spec.js @@ -1,7 +1,7 @@ import { it, expect, describe, vi } from "vitest" import AISettings from "./index.svelte" import { render, fireEvent } from "@testing-library/svelte" -import { admin, licensing } from "stores/portal" +import { admin, licensing, featureFlags } from "stores/portal" import { notifications } from "@budibase/bbui" vi.spyOn(notifications, "error").mockImplementation(vi.fn) @@ -12,12 +12,17 @@ const Hosting = { Self: "self", } -function setupEnv(hosting, features = {}) { +function setupEnv(hosting, features = {}, flags = {}) { const defaultFeatures = { budibaseAIEnabled: false, customAIConfigsEnabled: false, ...features, } + const defaultFlags = { + BUDIBASE_AI: false, + AI_CUSTOM_CONFIGS: false, + ...flags, + } admin.subscribe = vi.fn().mockImplementation(callback => { callback({ cloud: hosting === Hosting.Cloud }) return () => {} @@ -26,6 +31,10 @@ function setupEnv(hosting, features = {}) { callback(defaultFeatures) return () => {} }) + featureFlags.subscribe = vi.fn().mockImplementation(callback => { + callback(defaultFlags) + return () => {} + }) } describe("AISettings", () => { @@ -72,7 +81,11 @@ describe("AISettings", () => { let addConfigurationButton let configModal - setupEnv(Hosting.Cloud, { customAIConfigsEnabled: true }) + setupEnv( + Hosting.Cloud, + { customAIConfigsEnabled: true }, + { AI_CUSTOM_CONFIGS: true } + ) instance = render(AISettings) addConfigurationButton = instance.queryByText("Add configuration") expect(addConfigurationButton).toBeInTheDocument() @@ -85,7 +98,11 @@ describe("AISettings", () => { let addConfigurationButton let configModal - setupEnv(Hosting.Self, { customAIConfigsEnabled: true }) + setupEnv( + Hosting.Self, + { customAIConfigsEnabled: true }, + { AI_CUSTOM_CONFIGS: true } + ) instance = render(AISettings) addConfigurationButton = instance.queryByText("Add configuration") expect(addConfigurationButton).toBeInTheDocument() diff --git a/packages/builder/src/pages/builder/portal/settings/ai/index.svelte b/packages/builder/src/pages/builder/portal/settings/ai/index.svelte index bbdf46a24e..ec0ff31e58 100644 --- a/packages/builder/src/pages/builder/portal/settings/ai/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/ai/index.svelte @@ -12,7 +12,7 @@ Tags, Tag, } from "@budibase/bbui" - import { admin, licensing } from "stores/portal" + import { admin, licensing, featureFlags } from "stores/portal" import { API } from "api" import AIConfigModal from "./ConfigModal.svelte" import AIConfigTile from "./AIConfigTile.svelte" @@ -27,7 +27,8 @@ let editingUuid $: isCloud = $admin.cloud - $: customAIConfigsEnabled = $licensing.customAIConfigsEnabled + $: customAIConfigsEnabled = + $featureFlags.AI_CUSTOM_CONFIGS && $licensing.customAIConfigsEnabled async function fetchAIConfig() { try { diff --git a/packages/builder/src/pages/builder/portal/settings/index.svelte b/packages/builder/src/pages/builder/portal/settings/index.svelte index 1448b43ec4..9ab8436f94 100644 --- a/packages/builder/src/pages/builder/portal/settings/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/index.svelte @@ -1,8 +1,8 @@