import { API } from "@/api" import { DataFetchMap, DataFetchType } from "@budibase/frontend-core" import { FieldType, TableSchema } from "@budibase/types" /** * Constructs a fetch instance for a given datasource. * All datasource fetch classes implement their own functionality to get the * schema of a datasource of their respective types. * @param datasource the datasource * @returns */ const getDatasourceFetchInstance = < TDatasource extends { type: DataFetchType } >( datasource: TDatasource ) => { const handler = DataFetchMap[datasource?.type] if (!handler) { return null } return new handler({ API, datasource: datasource as never, query: null as any, }) } /** * Fetches the schema of any kind of datasource. * @param datasource the datasource to fetch the schema for * @param options options for enriching the schema */ export const fetchDatasourceSchema = async < TDatasource extends { type: DataFetchType } >( datasource: TDatasource, options = { enrichRelationships: false, formSchema: false } ) => { const instance = getDatasourceFetchInstance(datasource) const definition = await instance?.getDefinition() if (!instance || !definition) { return null } // Get the normal schema as long as we aren't wanting a form schema let schema: TableSchema | undefined if (datasource?.type !== "query" || !options?.formSchema) { schema = instance.getSchema(definition as any) as TableSchema } else if ("parameters" in definition && definition.parameters?.length) { schema = {} for (const param of definition.parameters) { schema[param.name] = { ...param, type: FieldType.STRING } } } if (!schema) { return null } // Strip hidden fields from views if (datasource.type === "viewV2") { for (const field of Object.keys(schema)) { if (!schema[field].visible) { delete schema[field] } } } // Enrich schema with relationships if required if ( definition && "sql" in definition && definition.sql && options?.enrichRelationships ) { const relationshipAdditions = await getRelationshipSchemaAdditions(schema) schema = { ...schema, ...relationshipAdditions, } } // Ensure schema is in the correct structure return instance.enrichSchema(schema) } /** * Fetches the definition of any kind of datasource. * @param datasource the datasource to fetch the schema for */ export const fetchDatasourceDefinition = async < TDatasource extends { type: DataFetchType } >( datasource: TDatasource ) => { const instance = getDatasourceFetchInstance(datasource) return await instance?.getDefinition() } /** * Fetches the schema of relationship fields for a SQL table schema * @param schema the schema to enrich */ export const getRelationshipSchemaAdditions = async ( schema: Record ) => { if (!schema) { return null } let relationshipAdditions: Record = {} for (let fieldKey of Object.keys(schema)) { const fieldSchema = schema[fieldKey] if (fieldSchema?.type === "link") { const linkSchema = await fetchDatasourceSchema({ type: "table", tableId: fieldSchema?.tableId, }) if (!linkSchema) { continue } Object.keys(linkSchema).forEach(linkKey => { relationshipAdditions[`${fieldKey}.${linkKey}`] = { type: linkSchema[linkKey].type, externalType: linkSchema[linkKey].externalType, } }) } } return relationshipAdditions }