diff --git a/packages/builder/src/stores/backend/datasources.js b/packages/builder/src/stores/backend/datasources.js
index 0815f9d766..af914cbee7 100644
--- a/packages/builder/src/stores/backend/datasources.js
+++ b/packages/builder/src/stores/backend/datasources.js
@@ -113,6 +113,10 @@ export function createDatasourcesStore() {
...state,
list: [...state.list, datasource],
}))
+
+ // If this is a new datasource then we should refresh the tables list,
+ // because otherwise we'll never see the new tables
+ tables.fetch()
}
// Update existing datasource
diff --git a/packages/builder/src/stores/backend/tables.js b/packages/builder/src/stores/backend/tables.js
index f8796712a8..d79ed6f072 100644
--- a/packages/builder/src/stores/backend/tables.js
+++ b/packages/builder/src/stores/backend/tables.js
@@ -1,5 +1,4 @@
import { get, writable, derived } from "svelte/store"
-import { datasources } from "./"
import { cloneDeep } from "lodash/fp"
import { API } from "api"
import { SWITCHABLE_TYPES } from "constants/backend"
@@ -63,7 +62,6 @@ export function createTablesStore() {
const savedTable = await API.saveTable(updatedTable)
replaceTable(savedTable._id, savedTable)
- await datasources.fetch()
select(savedTable._id)
return savedTable
}
diff --git a/packages/builder/vite.config.js b/packages/builder/vite.config.js
index 7227551850..bf6b4ce17e 100644
--- a/packages/builder/vite.config.js
+++ b/packages/builder/vite.config.js
@@ -1,6 +1,7 @@
import { svelte } from "@sveltejs/vite-plugin-svelte"
import replace from "@rollup/plugin-replace"
import { defineConfig, loadEnv } from "vite"
+import { viteStaticCopy } from "vite-plugin-static-copy"
import path from "path"
const ignoredWarnings = [
@@ -59,6 +60,18 @@ export default defineConfig(({ mode }) => {
),
"process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN),
}),
+ viteStaticCopy({
+ targets: [
+ {
+ src: "../../node_modules/@fontsource/source-sans-pro",
+ dest: "fonts",
+ },
+ {
+ src: "../../node_modules/remixicon/fonts/*",
+ dest: "fonts",
+ },
+ ],
+ }),
],
optimizeDeps: {
exclude: ["@roxi/routify"],
diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte
index 244500f266..ba5bbedd2f 100644
--- a/packages/client/src/components/app/Layout.svelte
+++ b/packages/client/src/components/app/Layout.svelte
@@ -180,10 +180,7 @@
{/if}
{#if !hideLogo}
-
+
{/if}
{#if !hideTitle && title}
{title}
diff --git a/packages/client/src/components/app/deprecated/Navigation.svelte b/packages/client/src/components/app/deprecated/Navigation.svelte
index dbc847f269..d2c9b6986f 100644
--- a/packages/client/src/components/app/deprecated/Navigation.svelte
+++ b/packages/client/src/components/app/deprecated/Navigation.svelte
@@ -18,7 +18,7 @@
diff --git a/packages/client/src/components/app/forms/InnerForm.svelte b/packages/client/src/components/app/forms/InnerForm.svelte
index 27e854781c..9eb21b0bae 100644
--- a/packages/client/src/components/app/forms/InnerForm.svelte
+++ b/packages/client/src/components/app/forms/InnerForm.svelte
@@ -283,7 +283,7 @@
// Skip if the value is the same
if (!skipCheck && fieldState.value === value) {
- return true
+ return false
}
// Update field state
@@ -295,7 +295,7 @@
return state
})
- return !error
+ return true
}
// Clears the value of a certain field back to the default value
@@ -376,8 +376,9 @@
deregister,
validate: () => {
// Validate the field by force setting the same value again
- const { fieldState } = get(getField(field))
- return setValue(fieldState.value, true)
+ const fieldInfo = getField(field)
+ setValue(get(fieldInfo).fieldState.value, true)
+ return !get(fieldInfo).fieldState.error
},
}
}
diff --git a/packages/client/src/licensing/utils.js b/packages/client/src/licensing/utils.js
index effed6867f..544fa0f526 100644
--- a/packages/client/src/licensing/utils.js
+++ b/packages/client/src/licensing/utils.js
@@ -1,18 +1,30 @@
import { authStore } from "../stores/auth.js"
+import { appStore } from "../stores/app.js"
import { get } from "svelte/store"
import { Constants } from "@budibase/frontend-core"
-const getLicense = () => {
+const getUserLicense = () => {
const user = get(authStore)
if (user) {
return user.license
}
}
+const getAppLicenseType = () => {
+ const appDef = get(appStore)
+ if (appDef?.licenseType) {
+ return appDef.licenseType
+ }
+}
+
export const isFreePlan = () => {
- const license = getLicense()
- if (license) {
- return license.plan.type === Constants.PlanType.FREE
+ let licenseType = getAppLicenseType()
+ if (!licenseType) {
+ const license = getUserLicense()
+ licenseType = license?.plan?.type
+ }
+ if (licenseType) {
+ return licenseType === Constants.PlanType.FREE
} else {
// safety net - no license means free plan
return true
diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js
index 024fdc29bc..82a26d923c 100644
--- a/packages/frontend-core/src/components/grid/stores/columns.js
+++ b/packages/frontend-core/src/components/grid/stores/columns.js
@@ -90,12 +90,12 @@ export const deriveStores = context => {
// Update local state
table.set(newTable)
+ // Update server
+ await API.saveTable(newTable)
+
// Broadcast change to external state can be updated, as this change
// will not be received by the builder websocket because we caused it ourselves
dispatch("updatetable", newTable)
-
- // Update server
- await API.saveTable(newTable)
}
return {
diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js
index 198c05025c..2020708fa8 100644
--- a/packages/frontend-core/src/components/grid/stores/rows.js
+++ b/packages/frontend-core/src/components/grid/stores/rows.js
@@ -2,6 +2,7 @@ import { writable, derived, get } from "svelte/store"
import { fetchData } from "../../../fetch/fetchData"
import { notifications } from "@budibase/bbui"
import { NewRowID, RowPageSize } from "../lib/constants"
+import { tick } from "svelte"
const initialSortState = {
column: null,
@@ -124,13 +125,22 @@ export const deriveStores = context => {
})
// Subscribe to changes of this fetch model
- unsubscribe = newFetch.subscribe($fetch => {
+ unsubscribe = newFetch.subscribe(async $fetch => {
if ($fetch.loaded && !$fetch.loading) {
hasNextPage.set($fetch.hasNextPage)
const $instanceLoaded = get(instanceLoaded)
const resetRows = $fetch.resetKey !== lastResetKey
+ const previousResetKey = lastResetKey
lastResetKey = $fetch.resetKey
+ // If resetting rows due to a table change, wipe data and wait for
+ // derived stores to compute. This prevents stale data being passed
+ // to cells when we save the new schema.
+ if (!$instanceLoaded && previousResetKey) {
+ rows.set([])
+ await tick()
+ }
+
// Reset state properties when dataset changes
if (!$instanceLoaded || resetRows) {
table.set($fetch.definition)
diff --git a/packages/pro b/packages/pro
index 01fbc86700..f4b8449aac 160000
--- a/packages/pro
+++ b/packages/pro
@@ -1 +1 @@
-Subproject commit 01fbc8670021c5a275c2a1a36ee18b984eeafad5
+Subproject commit f4b8449aac9bd265214396afbdce7ff984a2ae34
diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts
index 9c89b48b8a..451b3e366c 100644
--- a/packages/server/src/api/controllers/application.ts
+++ b/packages/server/src/api/controllers/application.ts
@@ -1,53 +1,54 @@
import env from "../../environment"
import {
+ createAllSearchIndex,
createLinkView,
createRoutingView,
- createAllSearchIndex,
} from "../../db/views/staticViews"
-import { createApp, deleteApp } from "../../utilities/fileSystem"
import {
+ backupClientLibrary,
+ createApp,
+ deleteApp,
+ revertClientLibrary,
+ updateClientLibrary,
+} from "../../utilities/fileSystem"
+import {
+ AppStatus,
+ DocumentType,
generateAppID,
+ generateDevAppID,
getLayoutParams,
getScreenParams,
- generateDevAppID,
- DocumentType,
- AppStatus,
} from "../../db/utils"
import {
- db as dbCore,
- roles,
cache,
- tenancy,
context,
+ db as dbCore,
+ env as envCore,
+ ErrorCode,
events,
migrations,
objectStore,
- ErrorCode,
- env as envCore,
+ roles,
+ tenancy,
} from "@budibase/backend-core"
import { USERS_TABLE_SCHEMA } from "../../constants"
import {
- DEFAULT_BB_DATASOURCE_ID,
buildDefaultDocs,
+ DEFAULT_BB_DATASOURCE_ID,
} from "../../db/defaultData/datasource_bb_default"
import { removeAppFromUserRoles } from "../../utilities/workerRequests"
-import { stringToReadStream, isQsTrue } from "../../utilities"
-import { getLocksById, doesUserHaveLock } from "../../utilities/redis"
-import {
- updateClientLibrary,
- backupClientLibrary,
- revertClientLibrary,
-} from "../../utilities/fileSystem"
+import { stringToReadStream } from "../../utilities"
+import { doesUserHaveLock, getLocksById } from "../../utilities/redis"
import { cleanupAutomations } from "../../automations/utils"
import { checkAppMetadata } from "../../automations/logging"
import { getUniqueRows } from "../../utilities/usageQuota/rows"
-import { quotas, groups } from "@budibase/pro"
+import { groups, licensing, quotas } from "@budibase/pro"
import {
App,
Layout,
- Screen,
MigrationType,
- Database,
+ PlanType,
+ Screen,
UserCtx,
} from "@budibase/types"
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
@@ -207,6 +208,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
let application = await db.get(DocumentType.APP_METADATA)
const layouts = await getLayouts()
let screens = await getScreens()
+ const license = await licensing.cache.getCachedLicense()
// Enrich plugin URLs
application.usedPlugins = objectStore.enrichPluginURLs(
@@ -227,6 +229,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
ctx.body = {
application: { ...application, upgradableVersion: envCore.VERSION },
+ licenseType: license?.plan.type || PlanType.FREE,
screens,
layouts,
clientLibPath,
diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts
index b13ec22cfd..f6b75aca05 100644
--- a/packages/server/src/api/controllers/row/ExternalRequest.ts
+++ b/packages/server/src/api/controllers/row/ExternalRequest.ts
@@ -19,6 +19,7 @@ import {
breakRowIdField,
convertRowId,
generateRowIdField,
+ getPrimaryDisplay,
isRowId,
isSQL,
} from "../../../integrations/utils"
@@ -391,7 +392,10 @@ export class ExternalRequest {
}
}
relatedRow = processFormulas(linkedTable, relatedRow)
- const relatedDisplay = display ? relatedRow[display] : undefined
+ let relatedDisplay
+ if (display) {
+ relatedDisplay = getPrimaryDisplay(relatedRow[display])
+ }
row[relationship.column][key] = {
primaryDisplay: relatedDisplay || "Invalid display column",
_id: relatedRow._id,
diff --git a/packages/server/src/api/controllers/row/utils.ts b/packages/server/src/api/controllers/row/utils.ts
index f1edbf538b..a213f14a08 100644
--- a/packages/server/src/api/controllers/row/utils.ts
+++ b/packages/server/src/api/controllers/row/utils.ts
@@ -3,10 +3,10 @@ import * as userController from "../user"
import { FieldTypes } from "../../../constants"
import { context } from "@budibase/backend-core"
import { makeExternalQuery } from "../../../integrations/base/query"
-import { Row, Table } from "@budibase/types"
+import { FieldType, Row, Table, UserCtx } from "@budibase/types"
import { Format } from "../view/exporters"
-import { UserCtx } from "@budibase/types"
import sdk from "../../../sdk"
+
const validateJs = require("validate.js")
const { cloneDeep } = require("lodash/fp")
@@ -20,6 +20,13 @@ validateJs.extend(validateJs.validators.datetime, {
},
})
+function isForeignKey(key: string, table: Table) {
+ const relationships = Object.values(table.schema).filter(
+ column => column.type === FieldType.LINK
+ )
+ return relationships.some(relationship => relationship.foreignKey === key)
+}
+
export async function getDatasourceAndQuery(json: any) {
const datasourceId = json.endpoint.datasourceId
const datasource = await sdk.datasources.get(datasourceId)
@@ -65,6 +72,10 @@ export async function validate({
const column = fetchedTable.schema[fieldName]
const constraints = cloneDeep(column.constraints)
const type = column.type
+ // foreign keys are likely to be enriched
+ if (isForeignKey(fieldName, fetchedTable)) {
+ continue
+ }
// formulas shouldn't validated, data will be deleted anyway
if (type === FieldTypes.FORMULA || column.autocolumn) {
continue
diff --git a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
index 5df821f453..32edb6dc7b 100644
--- a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
+++ b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
@@ -40,19 +40,14 @@
{#if favicon !== ""}
{:else}
-
+
{/if}
-
-
-
-
+
+
+
+
+