diff --git a/globalSetup.ts b/globalSetup.ts
index ec4f38b388..0b0e276b49 100644
--- a/globalSetup.ts
+++ b/globalSetup.ts
@@ -4,8 +4,8 @@ import {
getContainerRuntimeClient,
} from "testcontainers"
import { ContainerInfo } from "dockerode"
-import path from "path"
-import lockfile from "proper-lockfile"
+import * as path from "path"
+import * as lockfile from "proper-lockfile"
import { execSync } from "child_process"
interface DockerContext {
@@ -29,8 +29,8 @@ function getCurrentDockerContext(): DockerContext {
async function getBudibaseContainers() {
const client = await getContainerRuntimeClient()
- const conatiners = await client.container.list()
- return conatiners.filter(
+ const containers = await client.container.list()
+ return containers.filter(
container =>
container.Labels["com.budibase"] === "true" &&
container.Labels["org.testcontainers"] === "true"
diff --git a/packages/backend-core/src/sql/utils.ts b/packages/backend-core/src/sql/utils.ts
index 1b80ff337d..14127a189f 100644
--- a/packages/backend-core/src/sql/utils.ts
+++ b/packages/backend-core/src/sql/utils.ts
@@ -59,11 +59,15 @@ export function isExternalTable(table: Table) {
}
export function buildExternalTableId(datasourceId: string, tableName: string) {
- // encode spaces
- if (tableName.includes(" ")) {
- tableName = encodeURIComponent(tableName)
+ return `${datasourceId}${DOUBLE_SEPARATOR}${encodeURIComponent(tableName)}`
+}
+
+export function encodeTableId(tableId: string) {
+ if (isExternalTableID(tableId)) {
+ return encodeURIComponent(tableId)
+ } else {
+ return tableId
}
- return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}`
}
export function breakExternalTableId(tableId: string) {
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte
index 524e70e329..88201aa225 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte
@@ -49,7 +49,7 @@
const disabled = () => {
return {
SEND_EMAIL_SMTP: {
- disabled: !$admin.checklist.smtp.checked,
+ disabled: !$admin.checklist?.smtp?.checked,
message: "Please configure SMTP",
},
COLLECT: {
diff --git a/packages/builder/src/pages/builder/admin/_layout.svelte b/packages/builder/src/pages/builder/admin/_layout.svelte
index f03a7b8285..b304d91710 100644
--- a/packages/builder/src/pages/builder/admin/_layout.svelte
+++ b/packages/builder/src/pages/builder/admin/_layout.svelte
@@ -9,7 +9,7 @@
$: useAccountPortal = cloud && !$admin.disableAccountPortal
onMount(() => {
- if ($admin?.checklist?.adminUser.checked || useAccountPortal) {
+ if ($admin?.checklist?.adminUser?.checked || useAccountPortal) {
$redirect("../")
} else {
loaded = true
diff --git a/packages/builder/src/pages/builder/portal/settings/email/index.svelte b/packages/builder/src/pages/builder/portal/settings/email/index.svelte
index 0b9fb44bac..4d76c4f49c 100644
--- a/packages/builder/src/pages/builder/portal/settings/email/index.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/email/index.svelte
@@ -177,7 +177,7 @@
diff --git a/packages/builder/src/stores/portal/admin.test.js b/packages/builder/src/stores/portal/admin.test.js
index a3f1e645c3..a4adb4320a 100644
--- a/packages/builder/src/stores/portal/admin.test.js
+++ b/packages/builder/src/stores/portal/admin.test.js
@@ -1,5 +1,5 @@
import { it, expect, describe, beforeEach, vi } from "vitest"
-import { DEFAULT_CONFIG, createAdminStore } from "./admin"
+import { createAdminStore } from "./admin"
import { writable, get } from "svelte/store"
import { API } from "api"
@@ -45,11 +45,6 @@ describe("admin store", () => {
ctx.returnedStore = createAdminStore()
})
- it("inits the writable store with the default config", () => {
- expect(writable).toHaveBeenCalledTimes(1)
- expect(writable).toHaveBeenCalledWith(DEFAULT_CONFIG)
- })
-
it("returns the created store", ctx => {
expect(ctx.returnedStore).toEqual({
subscribe: expect.toBe(ctx.writableReturn.subscribe),
diff --git a/packages/builder/src/stores/portal/admin.js b/packages/builder/src/stores/portal/admin.ts
similarity index 80%
rename from packages/builder/src/stores/portal/admin.js
rename to packages/builder/src/stores/portal/admin.ts
index 54757fb314..2141cf1b9c 100644
--- a/packages/builder/src/stores/portal/admin.js
+++ b/packages/builder/src/stores/portal/admin.ts
@@ -2,27 +2,28 @@ import { writable, get } from "svelte/store"
import { API } from "api"
import { auth } from "stores/portal"
import { banner } from "@budibase/bbui"
+import {
+ ConfigChecklistResponse,
+ GetEnvironmentResponse,
+ SystemStatusResponse,
+} from "@budibase/types"
-export const DEFAULT_CONFIG = {
- loaded: false,
- multiTenancy: false,
- cloud: false,
- isDev: false,
- disableAccountPortal: false,
- accountPortalUrl: "",
- importComplete: false,
- checklist: {
- apps: { checked: false },
- smtp: { checked: false },
- adminUser: { checked: false },
- sso: { checked: false },
- },
- maintenance: [],
- offlineMode: false,
+interface PortalAdminStore extends GetEnvironmentResponse {
+ loaded: boolean
+ checklist?: ConfigChecklistResponse
+ status?: SystemStatusResponse
}
export function createAdminStore() {
- const admin = writable(DEFAULT_CONFIG)
+ const admin = writable({
+ loaded: false,
+ multiTenancy: false,
+ cloud: false,
+ isDev: false,
+ disableAccountPortal: false,
+ offlineMode: false,
+ maintenance: [],
+ })
async function init() {
await getChecklist()
diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts
index 0463c0a565..77c05abb95 100644
--- a/packages/server/src/api/controllers/row/index.ts
+++ b/packages/server/src/api/controllers/row/index.ts
@@ -288,19 +288,21 @@ function replaceTableNamesInFilters(
for (const key of Object.keys(filter)) {
const matches = key.match(`^(?.+)\\.(?.+)`)
- const relation = matches?.groups?.["relation"]
+ // this is the possible table name which we need to check if it needs to be converted
+ const relatedTableName = matches?.groups?.["relation"]
const field = matches?.groups?.["field"]
- if (!relation || !field) {
+ if (!relatedTableName || !field) {
continue
}
- const table = allTables.find(r => r._id === tableId)!
- if (Object.values(table.schema).some(f => f.name === relation)) {
+ const table = allTables.find(r => r._id === tableId)
+ const isColumnName = !!table?.schema[relatedTableName]
+ if (!table || isColumnName) {
continue
}
- const matchedTable = allTables.find(t => t.name === relation)
+ const matchedTable = allTables.find(t => t.name === relatedTableName)
const relationship = Object.values(table.schema).find(
f => isRelationshipField(f) && f.tableId === matchedTable?._id
)
diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts
index 5b60143792..baa811fe90 100644
--- a/packages/server/src/api/controllers/row/utils/utils.ts
+++ b/packages/server/src/api/controllers/row/utils/utils.ts
@@ -1,6 +1,6 @@
import * as utils from "../../../../db/utils"
-import { docIds } from "@budibase/backend-core"
+import { docIds, sql } from "@budibase/backend-core"
import {
Ctx,
DatasourcePlusQueryResponse,
@@ -69,15 +69,15 @@ export function getSourceId(ctx: Ctx): { tableId: string; viewId?: string } {
viewId: sourceId,
}
}
- return { tableId: ctx.params.sourceId }
+ return { tableId: sql.utils.encodeTableId(ctx.params.sourceId) }
}
// now check for old way of specifying table ID
if (ctx.params?.tableId) {
- return { tableId: ctx.params.tableId }
+ return { tableId: sql.utils.encodeTableId(ctx.params.tableId) }
}
// check body for a table ID
if (ctx.request.body?.tableId) {
- return { tableId: ctx.request.body.tableId }
+ return { tableId: sql.utils.encodeTableId(ctx.request.body.tableId) }
}
throw new Error("Unable to find table ID in request")
}
diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts
index 67f303aac3..e97f48afbe 100644
--- a/packages/server/src/api/routes/tests/search.spec.ts
+++ b/packages/server/src/api/routes/tests/search.spec.ts
@@ -71,18 +71,27 @@ if (descriptions.length) {
let tableOrViewId: string
let rows: Row[]
- async function basicRelationshipTables(type: RelationshipType) {
+ async function basicRelationshipTables(
+ type: RelationshipType,
+ opts?: {
+ tableName?: string
+ primaryColumn?: string
+ otherColumn?: string
+ }
+ ) {
const relatedTable = await createTable({
- name: { name: "name", type: FieldType.STRING },
+ name: { name: opts?.tableName || "name", type: FieldType.STRING },
})
+
+ const columnName = opts?.primaryColumn || "productCat"
+ //@ts-ignore - API accepts this structure, will build out rest of definition
const tableId = await createTable({
- name: { name: "name", type: FieldType.STRING },
- //@ts-ignore - API accepts this structure, will build out rest of definition
- productCat: {
+ name: { name: opts?.tableName || "name", type: FieldType.STRING },
+ [columnName]: {
type: FieldType.LINK,
relationshipType: type,
- name: "productCat",
- fieldName: "product",
+ name: columnName,
+ fieldName: opts?.otherColumn || "product",
tableId: relatedTable,
constraints: {
type: "array",
@@ -2776,6 +2785,42 @@ if (descriptions.length) {
})
})
+ isSql &&
+ describe("relationship - table with spaces", () => {
+ let primaryTable: Table, row: Row
+
+ beforeAll(async () => {
+ const { relatedTable, tableId } =
+ await basicRelationshipTables(
+ RelationshipType.ONE_TO_MANY,
+ {
+ tableName: "table with spaces",
+ primaryColumn: "related",
+ otherColumn: "related",
+ }
+ )
+ tableOrViewId = tableId
+ primaryTable = relatedTable
+
+ row = await config.api.row.save(primaryTable._id!, {
+ name: "foo",
+ })
+
+ await config.api.row.save(tableOrViewId, {
+ name: "foo",
+ related: [row._id],
+ })
+ })
+
+ it("should be able to search by table name with spaces", async () => {
+ await expectQuery({
+ equal: {
+ ["table with spaces.name"]: "foo",
+ },
+ }).toContain([{ name: "foo" }])
+ })
+ })
+
isSql &&
describe.each([
RelationshipType.MANY_TO_ONE,
diff --git a/packages/server/src/db/utils.ts b/packages/server/src/db/utils.ts
index 6c1065e847..70c69b3c60 100644
--- a/packages/server/src/db/utils.ts
+++ b/packages/server/src/db/utils.ts
@@ -1,4 +1,10 @@
-import { context, db as dbCore, docIds, utils } from "@budibase/backend-core"
+import {
+ context,
+ db as dbCore,
+ docIds,
+ utils,
+ sql,
+} from "@budibase/backend-core"
import {
DatabaseQueryOpts,
Datasource,
@@ -328,7 +334,7 @@ export function extractViewInfoFromID(viewId: string) {
const regex = new RegExp(`^(?.+)${SEPARATOR}([^${SEPARATOR}]+)$`)
const res = regex.exec(viewId)
return {
- tableId: res!.groups!["tableId"],
+ tableId: sql.utils.encodeTableId(res!.groups!["tableId"]),
}
}