budibase/packages/server/src/db/linkedRows/linkUtils.js

117 lines
3.6 KiB
JavaScript

const Sentry = require("@sentry/node")
const { ViewName, getQueryIndex } = require("../utils")
const { FieldTypes } = require("../../constants")
const { createLinkView } = require("../views/staticViews")
const { getAppDB } = require("@budibase/backend-core/context")
/**
* Only needed so that boolean parameters are being used for includeDocs
* @type {{EXCLUDE: boolean, INCLUDE: boolean}}
*/
exports.IncludeDocs = {
INCLUDE: true,
EXCLUDE: false,
}
exports.createLinkView = createLinkView
/**
* Gets the linking documents, not the linked documents themselves.
* @param {string} args.tableId The table which we are searching for linked rows against.
* @param {string|null} 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 {string|null} 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 {boolean|null} 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 {Promise<object[]>} This will return an array of the linking documents that were found
* (if any).
*/
exports.getLinkDocuments = async function (args) {
const { tableId, rowId, includeDocs } = args
const db = getAppDB()
let params
if (rowId != null) {
params = { key: [tableId, rowId] }
}
// only table is known
else {
params = { startKey: [tableId], endKey: [tableId, {}] }
}
params.include_docs = !!includeDocs
try {
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
// filter to get unique entries
const foundIds = []
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
})
if (includeDocs) {
return linkRows.map(row => row.doc)
} else {
return linkRows.map(row => row.value)
}
} catch (err) {
// check if the view doesn't exist, it should for all new instances
if (err != null && err.name === "not_found") {
await exports.createLinkView()
return exports.getLinkDocuments(arguments[0])
} else {
/* istanbul ignore next */
Sentry.captureException(err)
}
}
}
exports.getUniqueByProp = (array, prop) => {
return array.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
})
}
exports.getLinkedTableIDs = table => {
return Object.values(table.schema)
.filter(column => column.type === FieldTypes.LINK)
.map(column => column.tableId)
}
exports.getLinkedTable = async (id, tables) => {
const db = getAppDB()
let linkedTable = tables.find(table => table._id === id)
if (linkedTable) {
return linkedTable
}
linkedTable = await db.get(id)
if (linkedTable) {
tables.push(linkedTable)
}
return linkedTable
}
exports.getRelatedTableForField = (table, fieldName) => {
// look to see if its on the table, straight in the schema
const field = table.schema[fieldName]
if (field != null) {
return field.tableId
}
for (let column of Object.values(table.schema)) {
if (column.type === FieldTypes.LINK && column.fieldName === fieldName) {
return column.tableId
}
}
return null
}