budibase/packages/client/src/utils/schema.ts

130 lines
3.5 KiB
TypeScript

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<string, any>
) => {
if (!schema) {
return null
}
let relationshipAdditions: Record<string, any> = {}
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
}