Merge branch 'master' into chore/fix-oss
This commit is contained in:
commit
fe63c1c447
|
@ -10,7 +10,10 @@ import flatten from "lodash/flatten"
|
||||||
import { USER_METDATA_PREFIX } from "../utils"
|
import { USER_METDATA_PREFIX } from "../utils"
|
||||||
import partition from "lodash/partition"
|
import partition from "lodash/partition"
|
||||||
import { getGlobalUsersFromMetadata } from "../../utilities/global"
|
import { getGlobalUsersFromMetadata } from "../../utilities/global"
|
||||||
import { outputProcessing, processFormulas } from "../../utilities/rowProcessor"
|
import {
|
||||||
|
coreOutputProcessing,
|
||||||
|
processFormulas,
|
||||||
|
} from "../../utilities/rowProcessor"
|
||||||
import { context, features } from "@budibase/backend-core"
|
import { context, features } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
ContextUser,
|
ContextUser,
|
||||||
|
@ -156,9 +159,6 @@ export async function updateLinks(args: {
|
||||||
/**
|
/**
|
||||||
* Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
|
* Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
|
||||||
* This is required for formula fields, this may only be utilised internally (for now).
|
* This is required for formula fields, this may only be utilised internally (for now).
|
||||||
* @param table The table from which the rows originated.
|
|
||||||
* @param rows The rows which are to be enriched.
|
|
||||||
* @param opts optional - options like passing in a base row to use for enrichment.
|
|
||||||
* @return returns the rows with all of the enriched relationships on it.
|
* @return returns the rows with all of the enriched relationships on it.
|
||||||
*/
|
*/
|
||||||
export async function attachFullLinkedDocs(
|
export async function attachFullLinkedDocs(
|
||||||
|
@ -248,9 +248,6 @@ export type SquashTableFields = Record<string, { visibleFieldNames: string[] }>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will take the given enriched rows and squash the links to only contain the primary display field.
|
* This function will take the given enriched rows and squash the links to only contain the primary display field.
|
||||||
* @param table The table from which the rows originated.
|
|
||||||
* @param enriched The pre-enriched rows (full docs) which are to be squashed.
|
|
||||||
* @param squashFields Per link column (key) define which columns are allowed while squashing.
|
|
||||||
* @returns The rows after having their links squashed to only contain the ID and primary display.
|
* @returns The rows after having their links squashed to only contain the ID and primary display.
|
||||||
*/
|
*/
|
||||||
export async function squashLinks<T = Row[] | Row>(
|
export async function squashLinks<T = Row[] | Row>(
|
||||||
|
@ -283,21 +280,18 @@ export async function squashLinks<T = Row[] | Row>(
|
||||||
if (schema.type !== FieldType.LINK || !Array.isArray(row[column])) {
|
if (schema.type !== FieldType.LINK || !Array.isArray(row[column])) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const newLinks = []
|
const relatedTable = await getLinkedTable(schema.tableId, linkedTables)
|
||||||
for (const link of row[column]) {
|
if (viewSchema[column]?.columns) {
|
||||||
const linkTblId =
|
row[column] = await coreOutputProcessing(relatedTable, row[column])
|
||||||
link.tableId || getRelatedTableForField(table.schema, column)
|
}
|
||||||
const linkedTable = await getLinkedTable(linkTblId!, linkedTables)
|
row[column] = row[column].map((link: Row) => {
|
||||||
const obj: any = { _id: link._id }
|
const obj: any = { _id: link._id }
|
||||||
obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable)
|
obj.primaryDisplay = getPrimaryDisplayValue(link, relatedTable)
|
||||||
|
|
||||||
if (viewSchema[column]?.columns) {
|
if (viewSchema[column]?.columns) {
|
||||||
const enrichedLink = await outputProcessing(linkedTable, link, {
|
const squashFields = Object.entries(viewSchema[column].columns || {})
|
||||||
squash: false,
|
|
||||||
})
|
|
||||||
const squashFields = Object.entries(viewSchema[column].columns)
|
|
||||||
.filter(([columnName, viewColumnConfig]) => {
|
.filter(([columnName, viewColumnConfig]) => {
|
||||||
const tableColumn = linkedTable.schema[columnName]
|
const tableColumn = relatedTable.schema[columnName]
|
||||||
if (!tableColumn) {
|
if (!tableColumn) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -315,13 +309,14 @@ export async function squashLinks<T = Row[] | Row>(
|
||||||
.map(([columnName]) => columnName)
|
.map(([columnName]) => columnName)
|
||||||
|
|
||||||
for (const relField of squashFields) {
|
for (const relField of squashFields) {
|
||||||
obj[relField] = enrichedLink[relField]
|
if (link[relField] != null) {
|
||||||
|
obj[relField] = link[relField]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newLinks.push(obj)
|
return obj
|
||||||
}
|
})
|
||||||
row[column] = newLinks
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (isArray ? enrichedArray : enrichedArray[0]) as T
|
return (isArray ? enrichedArray : enrichedArray[0]) as T
|
||||||
|
|
|
@ -264,6 +264,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
} else {
|
} else {
|
||||||
safeRows = rows
|
safeRows = rows
|
||||||
}
|
}
|
||||||
|
// SQS returns the rows with full relationship contents
|
||||||
// attach any linked row information
|
// attach any linked row information
|
||||||
let enriched = !opts.preserveLinks
|
let enriched = !opts.preserveLinks
|
||||||
? await linkRows.attachFullLinkedDocs(table.schema, safeRows, {
|
? await linkRows.attachFullLinkedDocs(table.schema, safeRows, {
|
||||||
|
@ -271,11 +272,39 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
})
|
})
|
||||||
: safeRows
|
: safeRows
|
||||||
|
|
||||||
// make sure squash is enabled if needed
|
|
||||||
if (!opts.squash && utils.hasCircularStructure(rows)) {
|
if (!opts.squash && utils.hasCircularStructure(rows)) {
|
||||||
opts.squash = true
|
opts.squash = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enriched = await coreOutputProcessing(table, enriched, opts)
|
||||||
|
|
||||||
|
if (opts.squash) {
|
||||||
|
enriched = await linkRows.squashLinks(table, enriched, {
|
||||||
|
fromViewId: opts?.fromViewId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (wasArray ? enriched : enriched[0]) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is similar to the outputProcessing function above, it makes sure that all the provided
|
||||||
|
* rows are ready for output, but does not have enrichment for squash capabilities which can cause performance issues.
|
||||||
|
* outputProcessing should be used when responding from the API, while this should be used when internally processing
|
||||||
|
* rows for any reason (like part of view operations).
|
||||||
|
*/
|
||||||
|
export async function coreOutputProcessing(
|
||||||
|
table: Table,
|
||||||
|
rows: Row[],
|
||||||
|
opts: {
|
||||||
|
preserveLinks?: boolean
|
||||||
|
skipBBReferences?: boolean
|
||||||
|
fromViewId?: string
|
||||||
|
} = {
|
||||||
|
preserveLinks: false,
|
||||||
|
skipBBReferences: false,
|
||||||
|
}
|
||||||
|
): Promise<Row[]> {
|
||||||
// process complex types: attachments, bb references...
|
// process complex types: attachments, bb references...
|
||||||
for (const [property, column] of Object.entries(table.schema)) {
|
for (const [property, column] of Object.entries(table.schema)) {
|
||||||
if (
|
if (
|
||||||
|
@ -283,7 +312,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
column.type === FieldType.ATTACHMENT_SINGLE ||
|
column.type === FieldType.ATTACHMENT_SINGLE ||
|
||||||
column.type === FieldType.SIGNATURE_SINGLE
|
column.type === FieldType.SIGNATURE_SINGLE
|
||||||
) {
|
) {
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
if (row[property] == null) {
|
if (row[property] == null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -308,7 +337,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
!opts.skipBBReferences &&
|
!opts.skipBBReferences &&
|
||||||
column.type == FieldType.BB_REFERENCE
|
column.type == FieldType.BB_REFERENCE
|
||||||
) {
|
) {
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
row[property] = await processOutputBBReferences(
|
row[property] = await processOutputBBReferences(
|
||||||
row[property],
|
row[property],
|
||||||
column.subtype
|
column.subtype
|
||||||
|
@ -318,14 +347,14 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
!opts.skipBBReferences &&
|
!opts.skipBBReferences &&
|
||||||
column.type == FieldType.BB_REFERENCE_SINGLE
|
column.type == FieldType.BB_REFERENCE_SINGLE
|
||||||
) {
|
) {
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
row[property] = await processOutputBBReference(
|
row[property] = await processOutputBBReference(
|
||||||
row[property],
|
row[property],
|
||||||
column.subtype
|
column.subtype
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (column.type === FieldType.DATETIME && column.timeOnly) {
|
} else if (column.type === FieldType.DATETIME && column.timeOnly) {
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
if (row[property] instanceof Date) {
|
if (row[property] instanceof Date) {
|
||||||
const hours = row[property].getUTCHours().toString().padStart(2, "0")
|
const hours = row[property].getUTCHours().toString().padStart(2, "0")
|
||||||
const minutes = row[property]
|
const minutes = row[property]
|
||||||
|
@ -340,7 +369,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (column.type === FieldType.LINK) {
|
} else if (column.type === FieldType.LINK) {
|
||||||
for (let row of enriched) {
|
for (let row of rows) {
|
||||||
// if relationship is empty - remove the array, this has been part of the API for some time
|
// if relationship is empty - remove the array, this has been part of the API for some time
|
||||||
if (Array.isArray(row[property]) && row[property].length === 0) {
|
if (Array.isArray(row[property]) && row[property].length === 0) {
|
||||||
delete row[property]
|
delete row[property]
|
||||||
|
@ -350,17 +379,12 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// process formulas after the complex types had been processed
|
// process formulas after the complex types had been processed
|
||||||
enriched = await processFormulas(table, enriched, { dynamic: true })
|
rows = await processFormulas(table, rows, { dynamic: true })
|
||||||
|
|
||||||
if (opts.squash) {
|
|
||||||
enriched = await linkRows.squashLinks(table, enriched, {
|
|
||||||
fromViewId: opts?.fromViewId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// remove null properties to match internal API
|
// remove null properties to match internal API
|
||||||
const isExternal = isExternalTableID(table._id!)
|
const isExternal = isExternalTableID(table._id!)
|
||||||
if (isExternal || (await features.flags.isEnabled("SQS"))) {
|
if (isExternal || (await features.flags.isEnabled("SQS"))) {
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
for (const key of Object.keys(row)) {
|
for (const key of Object.keys(row)) {
|
||||||
if (row[key] === null) {
|
if (row[key] === null) {
|
||||||
delete row[key]
|
delete row[key]
|
||||||
|
@ -388,7 +412,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
const fields = [...tableFields, ...protectedColumns].map(f =>
|
const fields = [...tableFields, ...protectedColumns].map(f =>
|
||||||
f.toLowerCase()
|
f.toLowerCase()
|
||||||
)
|
)
|
||||||
for (const row of enriched) {
|
for (const row of rows) {
|
||||||
for (const key of Object.keys(row)) {
|
for (const key of Object.keys(row)) {
|
||||||
if (!fields.includes(key.toLowerCase())) {
|
if (!fields.includes(key.toLowerCase())) {
|
||||||
delete row[key]
|
delete row[key]
|
||||||
|
@ -397,5 +421,5 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (wasArray ? enriched : enriched[0]) as T
|
return rows
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue