Merge pull request #15172 from Budibase/fix/external-table-name-spaces

Relationship searching on tables with spaces
This commit is contained in:
Michael Drury 2024-12-12 18:20:45 +00:00 committed by GitHub
commit 8144f04e05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 83 additions and 26 deletions

View File

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

View File

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

View File

@ -288,19 +288,21 @@ function replaceTableNamesInFilters(
for (const key of Object.keys(filter)) {
const matches = key.match(`^(?<relation>.+)\\.(?<field>.+)`)
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
)

View File

@ -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")
}

View File

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

View File

@ -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(`^(?<tableId>.+)${SEPARATOR}([^${SEPARATOR}]+)$`)
const res = regex.exec(viewId)
return {
tableId: res!.groups!["tableId"],
tableId: sql.utils.encodeTableId(res!.groups!["tableId"]),
}
}