138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
import { ViewName, getQueryIndex, isRelationshipColumn } from "../utils"
|
|
import { FieldTypes } from "../../constants"
|
|
import { createLinkView } from "../views/staticViews"
|
|
import { context, logging } from "@budibase/backend-core"
|
|
import {
|
|
DatabaseQueryOpts,
|
|
LinkDocument,
|
|
LinkDocumentValue,
|
|
Table,
|
|
} from "@budibase/types"
|
|
import sdk from "../../sdk"
|
|
|
|
export { createLinkView } from "../views/staticViews"
|
|
|
|
/**
|
|
* Only needed so that boolean parameters are being used for includeDocs
|
|
* @type {{EXCLUDE: boolean, INCLUDE: boolean}}
|
|
*/
|
|
export const IncludeDocs = {
|
|
INCLUDE: true,
|
|
EXCLUDE: false,
|
|
}
|
|
|
|
/**
|
|
* Gets the linking documents, not the linked documents themselves.
|
|
* @param args.tableId The table which we are searching for linked rows against.
|
|
* @param args.fieldName The name of column/field which is being altered, only looking for
|
|
* linking documents that are related to it. If this is not specified then the table level will be assumed.
|
|
* @param args.rowId The ID of the row which we want to find linking documents for -
|
|
* if this is not specified then it will assume table or field level depending on whether the
|
|
* field name has been specified.
|
|
* @param args.includeDocs whether to include docs in the response call, this is considerably slower so only
|
|
* use this if actually interested in the docs themselves.
|
|
* @returns This will return an array of the linking documents that were found
|
|
* (if any).
|
|
*/
|
|
export async function getLinkDocuments(args: {
|
|
tableId?: string
|
|
rowId?: string
|
|
fieldName?: string
|
|
includeDocs?: boolean
|
|
}): Promise<LinkDocumentValue[] | LinkDocument[]> {
|
|
const { tableId, rowId, fieldName, includeDocs } = args
|
|
const db = context.getAppDB()
|
|
let params: DatabaseQueryOpts
|
|
if (rowId) {
|
|
params = { key: [tableId, rowId] }
|
|
}
|
|
// only table is known
|
|
else {
|
|
params = { startkey: [tableId], endkey: [tableId, {}] }
|
|
}
|
|
if (includeDocs) {
|
|
params.include_docs = true
|
|
}
|
|
try {
|
|
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
|
|
// filter to get unique entries
|
|
const foundIds: string[] = []
|
|
linkRows = linkRows.filter(link => {
|
|
// make sure anything unique is the correct key
|
|
if (
|
|
(tableId && link.key[0] !== tableId) ||
|
|
(rowId && link.key[1] !== rowId)
|
|
) {
|
|
return false
|
|
}
|
|
const unique = foundIds.indexOf(link.id) === -1
|
|
if (unique) {
|
|
foundIds.push(link.id)
|
|
}
|
|
return unique
|
|
})
|
|
|
|
// filter down to just the required field name
|
|
if (fieldName) {
|
|
linkRows = linkRows.filter(link => {
|
|
const value = link.value as LinkDocumentValue
|
|
return value.fieldName === fieldName
|
|
})
|
|
}
|
|
// return docs if docs requested, otherwise just the value information
|
|
if (includeDocs) {
|
|
return linkRows.map(row => row.doc) as LinkDocument[]
|
|
} else {
|
|
return linkRows.map(row => row.value) as LinkDocumentValue[]
|
|
}
|
|
} catch (err: any) {
|
|
// check if the view doesn't exist, it should for all new instances
|
|
if (err != null && err.name === "not_found") {
|
|
await createLinkView()
|
|
return getLinkDocuments(arguments[0])
|
|
} else {
|
|
/* istanbul ignore next */
|
|
logging.logAlert("Failed to get link documents", err)
|
|
throw err
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getUniqueByProp(array: any[], prop: string) {
|
|
return array.filter((obj, pos, arr) => {
|
|
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
|
|
})
|
|
}
|
|
|
|
export function getLinkedTableIDs(table: Table): string[] {
|
|
return Object.values(table.schema)
|
|
.filter(isRelationshipColumn)
|
|
.map(column => column.tableId)
|
|
}
|
|
|
|
export async function getLinkedTable(id: string, tables: Table[]) {
|
|
let linkedTable = tables.find(table => table._id === id)
|
|
if (linkedTable) {
|
|
return linkedTable
|
|
}
|
|
linkedTable = await sdk.tables.getTable(id)
|
|
if (linkedTable) {
|
|
tables.push(linkedTable)
|
|
}
|
|
return linkedTable
|
|
}
|
|
|
|
export function getRelatedTableForField(table: Table, fieldName: string) {
|
|
// look to see if its on the table, straight in the schema
|
|
const field = table.schema[fieldName]
|
|
if (field?.type === FieldTypes.LINK) {
|
|
return field.tableId
|
|
}
|
|
for (let column of Object.values(table.schema)) {
|
|
if (column.type === FieldTypes.LINK && column.fieldName === fieldName) {
|
|
return column.tableId
|
|
}
|
|
}
|
|
return null
|
|
}
|