Merge branch 'master' into chore/datasource-store-switch-to-budistore

This commit is contained in:
Michael Drury 2024-12-18 14:32:00 +00:00 committed by GitHub
commit 1f724789ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 130 additions and 21 deletions

View File

@ -70,6 +70,10 @@ export function encodeTableId(tableId: string) {
}
}
export function encodeViewId(viewId: string) {
return encodeURIComponent(viewId)
}
export function breakExternalTableId(tableId: string) {
const parts = tableId.split(DOUBLE_SEPARATOR)
let datasourceId = parts.shift()

View File

@ -7,7 +7,7 @@
"build": "routify -b && NODE_OPTIONS=\"--max_old_space_size=4096\" vite build --emptyOutDir",
"start": "routify -c rollup",
"dev": "routify -c dev:vite",
"dev:vite": "vite --host 0.0.0.0",
"dev:vite": "vite --host 0.0.0.0 --mode=dev",
"rollup": "rollup -c -w",
"test": "vitest run",
"test:watch": "vitest",

View File

@ -102,9 +102,8 @@
lastSearchId = Math.random()
searching = true
const thisSearchId = lastSearchId
const results = await searchFunction({
const results = await searchFunction(schema.tableId, {
paginate: false,
tableId: schema.tableId,
limit: 20,
query: {
string: {

View File

@ -52,10 +52,22 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
const table = await utils.getTableFromSource(source)
const { _id, ...rowData } = ctx.request.body
const dataToUpdate = await inputProcessing(
const beforeRow = await sdk.rows.external.getRow(table._id!, _id, {
relationships: true,
})
let dataToUpdate = cloneDeep(beforeRow)
const allowedField = utils.getSourceFields(source)
for (const key of Object.keys(rowData)) {
if (!allowedField.includes(key)) continue
dataToUpdate[key] = rowData[key]
}
dataToUpdate = await inputProcessing(
ctx.user?._id,
cloneDeep(source),
rowData
dataToUpdate
)
const validateResult = await sdk.rows.utils.validate({
@ -66,10 +78,6 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
throw { validation: validateResult.errors }
}
const beforeRow = await sdk.rows.external.getRow(table._id!, _id, {
relationships: true,
})
const response = await handleRequest(Operation.UPDATE, source, {
id: breakRowIdField(_id),
row: dataToUpdate,

View File

@ -66,7 +66,7 @@ export function getSourceId(ctx: Ctx): { tableId: string; viewId?: string } {
if (docIds.isViewId(sourceId)) {
return {
tableId: utils.extractViewInfoFromID(sourceId).tableId,
viewId: sourceId,
viewId: sql.utils.encodeViewId(sourceId),
}
}
return { tableId: sql.utils.encodeTableId(ctx.params.sourceId) }
@ -110,6 +110,21 @@ function fixBooleanFields(row: Row, table: Table) {
return row
}
export function getSourceFields(source: Table | ViewV2): string[] {
const isView = sdk.views.isView(source)
if (isView) {
const fields = Object.keys(
helpers.views.basicFields(source, { visible: true })
)
return fields
}
const fields = Object.entries(source.schema)
.filter(([_, field]) => field.visible !== false)
.map(([columnName]) => columnName)
return fields
}
export async function sqlOutputProcessing(
rows: DatasourcePlusQueryResponse,
source: Table | ViewV2,

View File

@ -1333,6 +1333,62 @@ if (descriptions.length) {
expect(resp.relationship.length).toBe(1)
})
it("should be able to keep linked data when updating from views that trims links from the main table", async () => {
let row = await config.api.row.save(table._id!, {
name: "main",
description: "main description",
})
const row2 = await config.api.row.save(otherTable._id!, {
name: "link",
description: "link description",
relationship: [row._id],
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: "view",
schema: {
name: { visible: true },
},
})
const resp = await config.api.row.patch(view.id, {
_id: row._id!,
_rev: row._rev!,
tableId: row.tableId!,
name: "test2",
relationship: [row2._id],
})
expect(resp.relationship).toBeUndefined()
const updatedRow = await config.api.row.get(table._id!, row._id!)
expect(updatedRow.relationship.length).toBe(1)
})
it("should be able to keep linked data when updating from views that trims links from the foreign table", async () => {
let row = await config.api.row.save(table._id!, {
name: "main",
description: "main description",
})
const row2 = await config.api.row.save(otherTable._id!, {
name: "link",
description: "link description",
relationship: [row._id],
})
const view = await config.api.viewV2.create({
tableId: otherTable._id!,
name: "view",
})
await config.api.row.patch(view.id, {
_id: row2._id!,
_rev: row2._rev!,
tableId: row2.tableId!,
})
const updatedRow = await config.api.row.get(table._id!, row._id!)
expect(updatedRow.relationship.length).toBe(1)
})
!isInternal &&
// MSSQL needs a setting called IDENTITY_INSERT to be set to ON to allow writing
// to identity columns. This is not something Budibase does currently.

View File

@ -55,7 +55,7 @@ if (descriptions.length) {
let datasource: Datasource | undefined
function saveTableRequest(
...overrides: Partial<Omit<SaveTableRequest, "name">>[]
...overrides: Partial<SaveTableRequest>[]
): SaveTableRequest {
const req: SaveTableRequest = {
name: generator.guid().replaceAll("-", "").substring(0, 16),
@ -1898,6 +1898,36 @@ if (descriptions.length) {
}
expect(view.queryUI).toEqual(expected)
})
it("tables and views can contain whitespaces", async () => {
const table = await config.api.table.save(
saveTableRequest({
name: `table with spaces ${generator.hash()}`,
schema: {
name: {
type: FieldType.STRING,
name: "name",
},
},
})
)
const view = await config.api.viewV2.create({
tableId: table._id!,
name: `view name with spaces`,
schema: {
name: { visible: true },
},
})
expect(await getDelegate(view)).toEqual({
...view,
schema: {
id: { ...table.schema["id"], visible: false },
name: { ...table.schema["name"], visible: true },
},
})
})
})
describe("updating table schema", () => {

View File

@ -1,10 +1,4 @@
import {
context,
db as dbCore,
docIds,
utils,
sql,
} from "@budibase/backend-core"
import { context, db as dbCore, docIds, utils } from "@budibase/backend-core"
import {
DatabaseQueryOpts,
Datasource,
@ -334,7 +328,7 @@ export function extractViewInfoFromID(viewId: string) {
const regex = new RegExp(`^(?<tableId>.+)${SEPARATOR}([^${SEPARATOR}]+)$`)
const res = regex.exec(viewId)
return {
tableId: sql.utils.encodeTableId(res!.groups!["tableId"]),
tableId: res!.groups!["tableId"],
}
}

View File

@ -46,8 +46,11 @@ export class ViewV2API extends TestAPI {
}
get = async (viewId: string) => {
return (await this._get<ViewResponseEnriched>(`/api/v2/views/${viewId}`))
.data
return (
await this._get<ViewResponseEnriched>(
`/api/v2/views/${encodeURIComponent(viewId)}`
)
).data
}
fetch = async (expectations?: Expectations) => {