99 lines
2.9 KiB
TypeScript
99 lines
2.9 KiB
TypeScript
import { db } from "@budibase/backend-core"
|
|
import {
|
|
FieldType,
|
|
isLogicalSearchOperator,
|
|
SearchFilters,
|
|
Table,
|
|
} from "@budibase/types"
|
|
import { cloneDeep } from "lodash/fp"
|
|
import sdk from "../../../sdk"
|
|
|
|
export const removeInvalidFilters = (
|
|
filters: SearchFilters,
|
|
validFields: string[]
|
|
) => {
|
|
const result = cloneDeep(filters)
|
|
|
|
validFields = validFields.map(f => f.toLowerCase())
|
|
for (const filterKey of Object.keys(result) as (keyof SearchFilters)[]) {
|
|
if (typeof result[filterKey] !== "object") {
|
|
continue
|
|
}
|
|
if (isLogicalSearchOperator(filterKey)) {
|
|
const resultingConditions: SearchFilters[] = []
|
|
for (const condition of result[filterKey].conditions) {
|
|
const resultingCondition = removeInvalidFilters(condition, validFields)
|
|
if (Object.keys(resultingCondition).length) {
|
|
resultingConditions.push(resultingCondition)
|
|
}
|
|
}
|
|
if (resultingConditions.length) {
|
|
result[filterKey].conditions = resultingConditions
|
|
} else {
|
|
delete result[filterKey]
|
|
}
|
|
continue
|
|
}
|
|
|
|
const filter = result[filterKey]
|
|
for (const columnKey of Object.keys(filter)) {
|
|
const possibleKeys = [columnKey, db.removeKeyNumbering(columnKey)].map(
|
|
c => c.toLowerCase()
|
|
)
|
|
if (!validFields.some(f => possibleKeys.includes(f.toLowerCase()))) {
|
|
delete filter[columnKey]
|
|
}
|
|
}
|
|
if (!Object.keys(filter).length) {
|
|
delete result[filterKey]
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
export const getQueryableFields = async (
|
|
fields: string[],
|
|
table: Table
|
|
): Promise<string[]> => {
|
|
const handledTables = new Set<string>([table._id!])
|
|
const extractTableFields = async (
|
|
table: Table,
|
|
allowedFields: string[]
|
|
): Promise<string[]> => {
|
|
const result = []
|
|
for (const field of Object.keys(table.schema).filter(
|
|
f => allowedFields.includes(f) && table.schema[f].visible !== false
|
|
)) {
|
|
const subSchema = table.schema[field]
|
|
if (subSchema.type === FieldType.LINK) {
|
|
if (handledTables.has(`${table._id}_${subSchema.tableId}`)) {
|
|
// avoid circular loops
|
|
continue
|
|
}
|
|
handledTables.add(`${table._id}_${subSchema.tableId}`)
|
|
const relatedTable = await sdk.tables.getTable(subSchema.tableId)
|
|
const relatedFields = await extractTableFields(
|
|
relatedTable,
|
|
Object.keys(relatedTable.schema)
|
|
)
|
|
|
|
result.push(...relatedFields.map(f => `${subSchema.name}.${f}`))
|
|
// should be able to filter by relationship using table name
|
|
result.push(...relatedFields.map(f => `${relatedTable.name}.${f}`))
|
|
} else {
|
|
result.push(field)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
const result = [
|
|
"_id", // Querying by _id is always allowed, even if it's never part of the schema
|
|
]
|
|
|
|
result.push(...(await extractTableFields(table, fields)))
|
|
|
|
return result
|
|
}
|