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 @@