import { cache, db as dbCore } from "@budibase/backend-core" import { utils } from "@budibase/shared-core" import { FieldType, BBReferenceFieldSubType, DocumentType, SEPARATOR, } from "@budibase/types" import { InvalidBBRefError } from "./errors" const ROW_PREFIX = DocumentType.ROW + SEPARATOR export async function processInputBBReference( value: string | { _id: string }, subtype: BBReferenceFieldSubType.USER ): Promise { if (value && Array.isArray(value)) { throw "BB_REFERENCE_SINGLE cannot be an array" } let id = typeof value === "string" ? value : value?._id if (!id) { return null } switch (subtype) { case BBReferenceFieldSubType.USER: { if (id.startsWith(ROW_PREFIX)) { id = dbCore.getGlobalIDFromUserMetadataID(id) } try { await cache.user.getUser(id) return id } catch (e: any) { if (e.statusCode === 404) { throw new InvalidBBRefError(id, BBReferenceFieldSubType.USER) } throw e } } default: throw utils.unreachable(subtype) } } export async function processInputBBReferences( value: string | string[] | { _id: string }[], subtype: BBReferenceFieldSubType ): Promise { if (!value || !value[0]) { return null } let referenceIds if (typeof value === "string") { referenceIds = value .split(",") .map(u => u.trim()) .filter(u => !!u) } else { referenceIds = value.map(idOrDoc => typeof idOrDoc === "string" ? idOrDoc : idOrDoc._id ) } // make sure all reference IDs are correct global user IDs // they may be user metadata references (start with row prefix) // and these need to be converted to global IDs referenceIds = referenceIds.map(id => { if (id?.startsWith(ROW_PREFIX)) { return dbCore.getGlobalIDFromUserMetadataID(id) } else { return id } }) switch (subtype) { case undefined: throw "Subtype must be defined" case BBReferenceFieldSubType.USER: case BBReferenceFieldSubType.USERS: { const { notFoundIds } = await cache.user.getUsers(referenceIds) if (notFoundIds?.length) { throw new InvalidBBRefError( notFoundIds[0], BBReferenceFieldSubType.USER ) } if (!referenceIds?.length) { return null } return referenceIds } default: throw utils.unreachable(subtype) } } interface UserReferenceInfo { _id: string primaryDisplay: string email: string firstName: string lastName: string } export function processOutputBBReferences( value: string, type: FieldType.BB_REFERENCE_SINGLE ): Promise export function processOutputBBReferences( value: string, type: FieldType.BB_REFERENCE, subtype: BBReferenceFieldSubType ): Promise export async function processOutputBBReferences( value: string | string[], type: FieldType.BB_REFERENCE | FieldType.BB_REFERENCE_SINGLE, subtype?: BBReferenceFieldSubType ) { if (value === null || value === undefined) { // Already processed or nothing to process return value || undefined } switch (type) { case FieldType.BB_REFERENCE: { const ids = typeof value === "string" ? value.split(",").filter(id => !!id) : value switch (subtype) { case undefined: throw "Subtype must be defined" case BBReferenceFieldSubType.USER: case BBReferenceFieldSubType.USERS: { const { users } = await cache.user.getUsers(ids) if (!users.length) { return undefined } return users.map(u => ({ _id: u._id, primaryDisplay: u.email, email: u.email, firstName: u.firstName, lastName: u.lastName, })) } default: throw utils.unreachable(subtype) } } case FieldType.BB_REFERENCE_SINGLE: { if (!value) { return undefined } let user try { user = await cache.user.getUser(value as string) } catch (err: any) { if (err.code !== 404) { throw err } } if (!user) { return undefined } return { _id: user._id, primaryDisplay: user.email, email: user.email, firstName: user.firstName, lastName: user.lastName, } } default: throw utils.unreachable(type) } }