diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts index 8fe0ab70da..062a185a23 100644 --- a/packages/server/src/api/controllers/datasource.ts +++ b/packages/server/src/api/controllers/datasource.ts @@ -183,9 +183,7 @@ export async function buildSchemaFromDb(ctx: UserCtx) { let { tables, error } = await buildSchemaHelper(datasource) if (tablesFilter) { - if (!datasource.entities) { - datasource.entities = {} - } + datasource.entities = {} for (let key in tables) { if ( tablesFilter.some( @@ -200,7 +198,7 @@ export async function buildSchemaFromDb(ctx: UserCtx) { } setDefaultDisplayColumns(datasource) - const dbResp = await db.put(datasource) + const dbResp = await db.put(sdk.tables.checkExternalTableSchemas(datasource)) datasource._rev = dbResp.rev const cleanedDatasource = await sdk.datasources.removeSecretSingle(datasource) @@ -286,7 +284,9 @@ export async function update(ctx: UserCtx) { datasource.config!.auth = auth } - const response = await db.put(datasource) + const response = await db.put( + sdk.tables.checkExternalTableSchemas(datasource) + ) await events.datasource.updated(datasource) datasource._rev = response.rev @@ -327,7 +327,7 @@ export async function save( setDefaultDisplayColumns(datasource) } - const dbResp = await db.put(datasource) + const dbResp = await db.put(sdk.tables.checkExternalTableSchemas(datasource)) await events.datasource.created(datasource) datasource._rev = dbResp.rev diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index 24d4242478..8877f0f8d0 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -1,32 +1,34 @@ import { - buildExternalTableId, breakExternalTableId, + buildExternalTableId, } from "../../../integrations/utils" import { + foreignKeyStructure, generateForeignKey, generateJunctionTableName, - foreignKeyStructure, hasTypeChanged, setStaticSchemas, } from "./utils" import { FieldTypes } from "../../../constants" import { makeExternalQuery } from "../../../integrations/base/query" import { handleRequest } from "../row/external" -import { events, context } from "@budibase/backend-core" -import { parse, isRows, isSchema } from "../../../utilities/schema" +import { context, events } from "@budibase/backend-core" +import { isRows, isSchema, parse } from "../../../utilities/schema" import { + AutoReason, Datasource, - Table, - QueryJson, - Operation, - RenameColumn, FieldSchema, - UserCtx, - TableRequest, + Operation, + QueryJson, RelationshipTypes, + RenameColumn, + Table, + TableRequest, + UserCtx, } from "@budibase/types" import sdk from "../../../sdk" import { builderSocket } from "../../../websockets" + const { cloneDeep } = require("lodash/fp") async function makeTableRequest( @@ -317,7 +319,7 @@ export async function save(ctx: UserCtx) { delete tableToSave._rename // store it into couch now for budibase reference datasource.entities[tableToSave.name] = tableToSave - await db.put(datasource) + await db.put(sdk.tables.checkExternalTableSchemas(datasource)) // Since tables are stored inside datasources, we need to notify clients // that the datasource definition changed @@ -348,7 +350,7 @@ export async function destroy(ctx: UserCtx) { datasource.entities = tables } - await db.put(datasource) + await db.put(sdk.tables.checkExternalTableSchemas(datasource)) // Since tables are stored inside datasources, we need to notify clients // that the datasource definition changed diff --git a/packages/server/src/sdk/app/tables/index.ts b/packages/server/src/sdk/app/tables/index.ts index 6bb09ae845..92ef3f3291 100644 --- a/packages/server/src/sdk/app/tables/index.ts +++ b/packages/server/src/sdk/app/tables/index.ts @@ -7,6 +7,7 @@ import { } from "../../../integrations/utils" import { Table, Database } from "@budibase/types" import datasources from "../datasources" +import { checkExternalTableSchemas } from "./validation" async function getAllInternalTables(db?: Database): Promise { if (!db) { @@ -60,4 +61,5 @@ export default { getAllExternalTables, getExternalTable, getTable, + checkExternalTableSchemas, } diff --git a/packages/server/src/sdk/app/tables/validation.ts b/packages/server/src/sdk/app/tables/validation.ts new file mode 100644 index 0000000000..98ad72ea3d --- /dev/null +++ b/packages/server/src/sdk/app/tables/validation.ts @@ -0,0 +1,57 @@ +import { + Datasource, + FieldType, + RelationshipTypes, + AutoReason, +} from "@budibase/types" + +function checkForeignKeysAreAutoColumns(datasource: Datasource) { + if (!datasource.entities) { + return datasource + } + const tables = Object.values(datasource.entities) + // make sure all foreign key columns are marked as auto columns + const foreignKeys: { tableId: string; key: string }[] = [] + for (let table of tables) { + const relationships = Object.values(table.schema).filter( + column => column.type === FieldType.LINK + ) + relationships.forEach(relationship => { + if (relationship.relationshipType === RelationshipTypes.MANY_TO_MANY) { + const tableId = relationship.through! + foreignKeys.push({ key: relationship.throughTo!, tableId }) + foreignKeys.push({ key: relationship.throughFrom!, tableId }) + } else { + const fk = relationship.foreignKey! + const oneSide = + relationship.relationshipType === RelationshipTypes.ONE_TO_MANY + foreignKeys.push({ + tableId: oneSide ? table._id! : relationship.tableId!, + key: fk, + }) + } + }) + } + + // now make sure schemas are all accurate + for (let table of tables) { + for (let column of Object.values(table.schema)) { + const shouldBeForeign = foreignKeys.find( + options => options.tableId === table._id && options.key === column.name + ) + if (shouldBeForeign) { + column.autocolumn = true + column.autoReason = AutoReason.FOREIGN_KEY + } else if (column.autoReason === AutoReason.FOREIGN_KEY) { + delete column.autocolumn + delete column.autoReason + } + } + } + + return datasource +} + +export function checkExternalTableSchemas(datasource: Datasource) { + return checkForeignKeysAreAutoColumns(datasource) +} diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 7089709808..18b415da5f 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -9,6 +9,10 @@ export enum RelationshipTypes { MANY_TO_MANY = "many-to-many", } +export enum AutoReason { + FOREIGN_KEY = "foreign_key", +} + export interface FieldSchema { type: FieldType externalType?: string @@ -21,6 +25,7 @@ export interface FieldSchema { foreignKey?: string icon?: string autocolumn?: boolean + autoReason?: AutoReason subtype?: string throughFrom?: string throughTo?: string