Merge pull request #4090 from Budibase/fix/3721

Fixing issue with existing SQL relationships and deleting tables externally to Budibase
This commit is contained in:
Michael Drury 2022-01-19 15:02:15 +00:00 committed by GitHub
commit 84f7405f02
3 changed files with 57 additions and 9 deletions

View File

@ -6,9 +6,12 @@
export let datasource export let datasource
let name = "" let name = ""
let submitted = false
$: valid = name && name.length > 0 && !datasource?.entities[name] $: valid = name && name.length > 0 && !datasource?.entities[name]
$: error = $: error =
name && datasource?.entities[name] ? "Table name already in use." : null !submitted && name && datasource?.entities[name]
? "Table name already in use."
: null
function buildDefaultTable(tableName, datasourceId) { function buildDefaultTable(tableName, datasourceId) {
return { return {
@ -26,6 +29,7 @@
} }
async function saveTable() { async function saveTable() {
submitted = true
const table = await tables.save(buildDefaultTable(name, datasource._id)) const table = await tables.save(buildDefaultTable(name, datasource._id))
await datasources.fetch() await datasources.fetch()
$goto(`../../table/${table._id}`) $goto(`../../table/${table._id}`)

View File

@ -143,11 +143,46 @@ export function isIsoDateString(str: string) {
return d.toISOString() === str return d.toISOString() === str
} }
// add the existing relationships from the entities if they exist, to prevent them from being overridden /**
* This function will determine whether a column is a relationship and whether it
* is currently valid. The reason for the validity check is that tables can be deleted
* outside of Budibase control and if this is the case it will break Budibase relationships.
* The tableIds is a list passed down from the main finalise tables function, which is
* based on the tables that have just been fetched. This will only really be used on subsequent
* fetches to the first one - if the user is periodically refreshing Budibase knowledge of tables.
* @param column The column to check, to see if it is a valid relationship.
* @param tableIds The IDs of the tables which currently exist.
*/
function shouldCopyRelationship(column: { type: string, tableId?: string }, tableIds: [string]) {
return column.type === FieldTypes.LINK && column.tableId && tableIds.includes(column.tableId)
}
/**
* Similar function to the shouldCopyRelationship function, but instead this looks for options and boolean
* types. It is possible to switch a string -> options and a number -> boolean (and vice versus) need to make
* sure that these get copied over when tables are fetched. Also checks whether they are still valid, if a
* column has changed type in the external database then copying it over may not be possible.
* @param column The column to check for options or boolean type.
* @param fetchedColumn The fetched column to check for the type in the external database.
*/
function shouldCopySpecialColumn(column: { type: string }, fetchedColumn: { type: string } | undefined) {
return column.type === FieldTypes.OPTIONS ||
((!fetchedColumn || fetchedColumn.type === FieldTypes.NUMBER) && column.type === FieldTypes.BOOLEAN)
}
/**
* Looks for columns which need to be copied over into the new table definitions, like relationships
* and options types.
* @param tableName The name of the table which is being checked.
* @param table The specific table which is being checked.
* @param entities All the tables that existed before - the old table definitions.
* @param tableIds The IDs of the tables which exist now, to check if anything has been removed.
*/
function copyExistingPropsOver( function copyExistingPropsOver(
tableName: string, tableName: string,
table: Table, table: Table,
entities: { [key: string]: any } entities: { [key: string]: any },
tableIds: [string]
) { ) {
if (entities && entities[tableName]) { if (entities && entities[tableName]) {
if (entities[tableName].primaryDisplay) { if (entities[tableName].primaryDisplay) {
@ -158,11 +193,10 @@ function copyExistingPropsOver(
if (!existingTableSchema.hasOwnProperty(key)) { if (!existingTableSchema.hasOwnProperty(key)) {
continue continue
} }
const column = existingTableSchema[key]
if ( if (
existingTableSchema[key].type === FieldTypes.LINK || shouldCopyRelationship(column, tableIds) ||
existingTableSchema[key].type === FieldTypes.OPTIONS || shouldCopySpecialColumn(column, table.schema[key])
((!table.schema[key] || table.schema[key].type === FieldTypes.NUMBER) &&
existingTableSchema[key].type === FieldTypes.BOOLEAN)
) { ) {
table.schema[key] = existingTableSchema[key] table.schema[key] = existingTableSchema[key]
} }
@ -171,6 +205,13 @@ function copyExistingPropsOver(
return table return table
} }
/**
* Look through the final table definitions to see if anything needs to be
* copied over from the old and if any errors have occurred mark them so
* that the user can be made aware.
* @param tables The list of tables that have been retrieved from the external database.
* @param entities The old list of tables, if there was any to look for definitions in.
*/
export function finaliseExternalTables( export function finaliseExternalTables(
tables: { [key: string]: any }, tables: { [key: string]: any },
entities: { [key: string]: any } entities: { [key: string]: any }
@ -178,6 +219,8 @@ export function finaliseExternalTables(
const invalidColumns = Object.values(InvalidColumns) const invalidColumns = Object.values(InvalidColumns)
let finalTables: { [key: string]: any } = {} let finalTables: { [key: string]: any } = {}
const errors: { [key: string]: string } = {} const errors: { [key: string]: string } = {}
// @ts-ignore
const tableIds: [string] = Object.values(tables).map(table => table._id)
for (let [name, table] of Object.entries(tables)) { for (let [name, table] of Object.entries(tables)) {
const schemaFields = Object.keys(table.schema) const schemaFields = Object.keys(table.schema)
// make sure every table has a key // make sure every table has a key
@ -189,7 +232,7 @@ export function finaliseExternalTables(
continue continue
} }
// make sure all previous props have been added back // make sure all previous props have been added back
finalTables[name] = copyExistingPropsOver(name, table, entities) finalTables[name] = copyExistingPropsOver(name, table, entities, tableIds)
} }
// sort the tables by name // sort the tables by name
finalTables = Object.entries(finalTables) finalTables = Object.entries(finalTables)

View File

@ -6,6 +6,7 @@ exports.fetch = async ctx => {
cloud: !env.SELF_HOSTED, cloud: !env.SELF_HOSTED,
accountPortalUrl: env.ACCOUNT_PORTAL_URL, accountPortalUrl: env.ACCOUNT_PORTAL_URL,
disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL, disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
isDev: env.isDev(), // in test need to pretend its in production for the UI (Cypress)
isDev: env.isDev() && !env.isTest(),
} }
} }