diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
index bb38c5094f..530595fe40 100644
--- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
@@ -386,7 +386,7 @@
>
Hide column
- {#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === TableNames.USERS}
+ {#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === TableNames.USERS && !column.schema.autocolumn}
diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts
index be6ac885df..bd92413851 100644
--- a/packages/server/src/api/controllers/row/ExternalRequest.ts
+++ b/packages/server/src/api/controllers/row/ExternalRequest.ts
@@ -265,7 +265,10 @@ export class ExternalRequest {
}
}
- inputProcessing(row: Row | undefined, table: Table) {
+ inputProcessing(
+ row: T,
+ table: Table
+ ): { row: T; manyRelationships: ManyRelationship[] } {
if (!row) {
return { row, manyRelationships: [] }
}
@@ -346,7 +349,7 @@ export class ExternalRequest {
// we return the relationships that may need to be created in the through table
// we do this so that if the ID is generated by the DB it can be inserted
// after the fact
- return { row: newRow, manyRelationships }
+ return { row: newRow as T, manyRelationships }
}
/**
@@ -598,6 +601,18 @@ export class ExternalRequest {
// clean up row on ingress using schema
const processed = this.inputProcessing(row, table)
row = processed.row
+ let manyRelationships = processed.manyRelationships
+
+ if (!row && rows) {
+ manyRelationships = []
+ for (let i = 0; i < rows.length; i++) {
+ const processed = this.inputProcessing(rows[i], table)
+ rows[i] = processed.row
+ if (processed.manyRelationships.length) {
+ manyRelationships.push(...processed.manyRelationships)
+ }
+ }
+ }
if (
operation === Operation.DELETE &&
(filters == null || Object.keys(filters).length === 0)
diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts
index e526af4ecb..bd674d7d38 100644
--- a/packages/server/src/api/controllers/table/external.ts
+++ b/packages/server/src/api/controllers/table/external.ts
@@ -15,6 +15,7 @@ import {
} from "@budibase/types"
import sdk from "../../../sdk"
import { builderSocket } from "../../../websockets"
+import { inputProcessing } from "../../../utilities/rowProcessor"
function getDatasourceId(table: Table) {
if (!table) {
@@ -80,7 +81,7 @@ export async function destroy(ctx: UserCtx) {
export async function bulkImport(
ctx: UserCtx
) {
- const table = await sdk.tables.getTable(ctx.params.tableId)
+ let table = await sdk.tables.getTable(ctx.params.tableId)
const { rows } = ctx.request.body
const schema = table.schema
@@ -88,7 +89,15 @@ export async function bulkImport(
ctx.throw(400, "Provided data import information is invalid.")
}
- const parsedRows = parse(rows, schema)
+ const parsedRows = []
+ for (const row of parse(rows, schema)) {
+ const processed = await inputProcessing(ctx.user?._id, table, row, {
+ noAutoRelationships: true,
+ })
+ parsedRows.push(processed.row)
+ table = processed.table
+ }
+
await handleRequest(Operation.BULK_CREATE, table._id!, {
rows: parsedRows,
})
diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts
index 2d194ab196..e347a8657d 100644
--- a/packages/server/src/sdk/app/rows/search.ts
+++ b/packages/server/src/sdk/app/rows/search.ts
@@ -79,9 +79,7 @@ export async function search(
}
const table = await sdk.tables.getTable(options.tableId)
- options = searchInputMapping(table, options, {
- isSql: !!table.sql || !!env.SQS_SEARCH_ENABLE,
- })
+ options = searchInputMapping(table, options)
if (isExternalTable) {
return external.search(options, table)
diff --git a/packages/server/src/sdk/app/rows/search/utils.ts b/packages/server/src/sdk/app/rows/search/utils.ts
index 62f5af2b70..797383eff0 100644
--- a/packages/server/src/sdk/app/rows/search/utils.ts
+++ b/packages/server/src/sdk/app/rows/search/utils.ts
@@ -11,7 +11,7 @@ import {
RowSearchParams,
} from "@budibase/types"
import { db as dbCore, context } from "@budibase/backend-core"
-import { helpers, utils } from "@budibase/shared-core"
+import { utils } from "@budibase/shared-core"
export async function paginatedSearch(
query: SearchFilters,
@@ -49,12 +49,7 @@ function findColumnInQueries(
}
}
-function userColumnMapping(
- column: string,
- options: RowSearchParams,
- isDeprecatedSingleUserColumn: boolean = false,
- isSql: boolean = false
-) {
+function userColumnMapping(column: string, options: RowSearchParams) {
findColumnInQueries(column, options, (filterValue: any): any => {
const isArray = Array.isArray(filterValue),
isString = typeof filterValue === "string"
@@ -71,33 +66,23 @@ function userColumnMapping(
}
}
- let wrapper = (s: string) => s
- if (isDeprecatedSingleUserColumn && filterValue && isSql) {
- // Decreated single users are stored as stringified arrays of a single value
- wrapper = (s: string) => JSON.stringify([s])
- }
-
if (isArray) {
return filterValue.map(el => {
if (typeof el === "string") {
- return wrapper(processString(el))
+ return processString(el)
} else {
return el
}
})
} else {
- return wrapper(processString(filterValue))
+ return processString(filterValue)
}
})
}
// maps through the search parameters to check if any of the inputs are invalid
// based on the table schema, converts them to something that is valid.
-export function searchInputMapping(
- table: Table,
- options: RowSearchParams,
- datasourceOptions: { isSql?: boolean } = {}
-) {
+export function searchInputMapping(table: Table, options: RowSearchParams) {
if (!table?.schema) {
return options
}
@@ -116,12 +101,7 @@ export function searchInputMapping(
break
}
case FieldType.BB_REFERENCE: {
- userColumnMapping(
- key,
- options,
- helpers.schema.isDeprecatedSingleUserColumn(column),
- datasourceOptions.isSql
- )
+ userColumnMapping(key, options)
break
}
}
diff --git a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts
index d69fe73052..874113f6f1 100644
--- a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts
+++ b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts
@@ -14,7 +14,13 @@ export async function processInputBBReference(
subtype: BBReferenceFieldSubType.USER
): Promise {
if (value && Array.isArray(value)) {
- throw "BB_REFERENCE_SINGLE cannot be an array"
+ if (value.length > 1) {
+ throw new InvalidBBRefError(
+ JSON.stringify(value),
+ BBReferenceFieldSubType.USER
+ )
+ }
+ value = value[0]
}
let id = typeof value === "string" ? value : value?._id
diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts
index e7bc725285..59237be5f3 100644
--- a/packages/server/src/utilities/rowProcessor/index.ts
+++ b/packages/server/src/utilities/rowProcessor/index.ts
@@ -18,6 +18,7 @@ import {
processOutputBBReferences,
} from "./bbReferenceProcessor"
import { isExternalTableID } from "../../integrations/utils"
+import { helpers } from "@budibase/shared-core"
export * from "./utils"
export * from "./attachments"
@@ -162,10 +163,14 @@ export async function inputProcessing(
if (attachment?.url) {
delete clonedRow[key].url
}
- } else if (field.type === FieldType.BB_REFERENCE && value) {
- clonedRow[key] = await processInputBBReferences(value, field.subtype)
- } else if (field.type === FieldType.BB_REFERENCE_SINGLE && value) {
+ } else if (
+ value &&
+ (field.type === FieldType.BB_REFERENCE_SINGLE ||
+ helpers.schema.isDeprecatedSingleUserColumn(field))
+ ) {
clonedRow[key] = await processInputBBReference(value, field.subtype)
+ } else if (value && field.type === FieldType.BB_REFERENCE) {
+ clonedRow[key] = await processInputBBReferences(value, field.subtype)
}
}
diff --git a/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts
index b1928b696b..81094583e2 100644
--- a/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts
+++ b/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts
@@ -102,7 +102,7 @@ describe("rowProcessor - inputProcessing", () => {
name: "user",
constraints: {
presence: true,
- type: "string",
+ type: "array",
},
},
},
@@ -154,7 +154,7 @@ describe("rowProcessor - inputProcessing", () => {
name: "user",
constraints: {
presence: false,
- type: "string",
+ type: "array",
},
},
},
@@ -196,7 +196,7 @@ describe("rowProcessor - inputProcessing", () => {
name: "user",
constraints: {
presence: false,
- type: "string",
+ type: "array",
},
},
},
diff --git a/packages/shared-core/src/helpers/schema.ts b/packages/shared-core/src/helpers/schema.ts
index 985e068be0..ad4c237247 100644
--- a/packages/shared-core/src/helpers/schema.ts
+++ b/packages/shared-core/src/helpers/schema.ts
@@ -6,7 +6,10 @@ import {
export function isDeprecatedSingleUserColumn(
schema: Pick
-) {
+): schema is {
+ type: FieldType.BB_REFERENCE
+ subtype: BBReferenceFieldSubType.USER
+} {
const result =
schema.type === FieldType.BB_REFERENCE &&
schema.subtype === BBReferenceFieldSubType.USER &&