Fixing a lot of issues around dropping columns, updating columns, relationships and bi-directionality, display columns now default to something for SQL tables as well.
This commit is contained in:
parent
949c6b8653
commit
a94376ce43
|
@ -16,8 +16,8 @@
|
|||
export let value = defaultValue || (meta.type === "boolean" ? false : "")
|
||||
export let readonly
|
||||
|
||||
$: type = meta.type
|
||||
$: label = capitalise(meta.name)
|
||||
$: type = meta?.type
|
||||
$: label = meta.name ? capitalise(meta.name) : ""
|
||||
</script>
|
||||
|
||||
{#if type === "options"}
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
const AUTO_TYPE = "auto"
|
||||
const FORMULA_TYPE = FIELDS.FORMULA.type
|
||||
const LINK_TYPE = FIELDS.LINK.type
|
||||
const STRING_TYPE = FIELDS.STRING.type
|
||||
const NUMBER_TYPE = FIELDS.NUMBER.type
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
||||
const { hide } = getContext(Context.Modal)
|
||||
|
@ -55,6 +58,7 @@
|
|||
let confirmDeleteDialog
|
||||
let deletion
|
||||
|
||||
$: checkConstraints(field)
|
||||
$: tableOptions = $tables.list.filter(
|
||||
opt => opt._id !== $tables.draft._id && opt.type === table.type
|
||||
)
|
||||
|
@ -180,23 +184,26 @@
|
|||
}
|
||||
const thisName = truncate(table.name, { length: 14 }),
|
||||
linkName = truncate(linkTable.name, { length: 14 })
|
||||
return [
|
||||
const options = [
|
||||
{
|
||||
name: `Many ${thisName} rows → many ${linkName} rows`,
|
||||
alt: `Many ${table.name} rows → many ${linkTable.name} rows`,
|
||||
value: RelationshipTypes.MANY_TO_MANY,
|
||||
},
|
||||
{
|
||||
name: `One ${linkName} row → many ${thisName} rows`,
|
||||
alt: `One ${linkTable.name} rows → many ${table.name} rows`,
|
||||
value: RelationshipTypes.ONE_TO_MANY,
|
||||
},
|
||||
{
|
||||
name: `One ${thisName} row → many ${linkName} rows`,
|
||||
alt: `One ${table.name} rows → many ${linkTable.name} rows`,
|
||||
value: RelationshipTypes.MANY_TO_ONE,
|
||||
},
|
||||
]
|
||||
if (!external) {
|
||||
options.push({
|
||||
name: `One ${linkName} row → many ${thisName} rows`,
|
||||
alt: `One ${linkTable.name} rows → many ${table.name} rows`,
|
||||
value: RelationshipTypes.ONE_TO_MANY,
|
||||
})
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function getAllowedTypes() {
|
||||
|
@ -219,6 +226,24 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
function checkConstraints(fieldToCheck) {
|
||||
// most types need this, just make sure its always present
|
||||
if (fieldToCheck && !fieldToCheck.constraints) {
|
||||
fieldToCheck.constraints = {}
|
||||
}
|
||||
// some string types may have been built by server, may not always have constraints
|
||||
if (fieldToCheck.type === STRING_TYPE && !fieldToCheck.constraints.length) {
|
||||
fieldToCheck.constraints.length = {}
|
||||
}
|
||||
// some number types made server-side will be missing constraints
|
||||
if (
|
||||
fieldToCheck.type === NUMBER_TYPE &&
|
||||
!fieldToCheck.constraints.numericality
|
||||
) {
|
||||
fieldToCheck.constraints.numericality = {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModalContent
|
||||
|
@ -268,7 +293,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if canBeSearched}
|
||||
{#if canBeSearched && !external}
|
||||
<div>
|
||||
<Label grey small>Search Indexes</Label>
|
||||
<Toggle
|
||||
|
@ -328,7 +353,7 @@
|
|||
getOptionLabel={table => table.name}
|
||||
getOptionValue={table => table._id}
|
||||
/>
|
||||
{#if relationshipOptions && relationshipOptions.length > 0 && !external}
|
||||
{#if relationshipOptions && relationshipOptions.length > 0}
|
||||
<RadioGroup
|
||||
disabled={linkEditDisabled}
|
||||
label="Define the relationship"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
Layout,
|
||||
Modal,
|
||||
InlineAlert,
|
||||
ActionButton,
|
||||
} from "@budibase/bbui"
|
||||
import { datasources, integrations, queries, tables } from "stores/backend"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
|
@ -200,11 +199,6 @@
|
|||
/>
|
||||
{/if}
|
||||
<div class="query-list">
|
||||
<div class="add-table">
|
||||
<ActionButton quiet icon="TableAdd" on:click={createNewTable}>
|
||||
New table
|
||||
</ActionButton>
|
||||
</div>
|
||||
{#each plusTables as table}
|
||||
<div class="query-list-item" on:click={() => onClickTable(table)}>
|
||||
<p class="query-name">{table.name}</p>
|
||||
|
@ -212,6 +206,9 @@
|
|||
<p>→</p>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="add-table">
|
||||
<Button cta on:click={createNewTable}>Create new table</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#if plusTables?.length !== 0}
|
||||
<Divider />
|
||||
|
@ -343,7 +340,6 @@
|
|||
}
|
||||
|
||||
.add-table {
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
margin-top: var(--spacing-m);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -152,6 +152,16 @@ const buildSchemaHelper = async datasource => {
|
|||
await connector.buildSchema(datasource._id, datasource.entities)
|
||||
datasource.entities = connector.tables
|
||||
|
||||
// make sure they all have a display name selected
|
||||
for (let entity of Object.values(datasource.entities)) {
|
||||
if (entity.primaryDisplay) {
|
||||
continue
|
||||
}
|
||||
entity.primaryDisplay = Object.values(entity.schema).find(
|
||||
schema => !schema.autocolumn
|
||||
).name
|
||||
}
|
||||
|
||||
const errors = connector.schemaErrors
|
||||
let error = null
|
||||
if (errors && Object.keys(errors).length > 0) {
|
||||
|
|
|
@ -36,6 +36,35 @@ async function makeTableRequest(
|
|||
return makeExternalQuery(datasource, json)
|
||||
}
|
||||
|
||||
function cleanupRelationships(table, tables, oldTable = null) {
|
||||
const tableToIterate = oldTable ? oldTable : table
|
||||
// clean up relationships in couch table schemas
|
||||
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
||||
if (
|
||||
schema.type === FieldTypes.LINK &&
|
||||
(!oldTable || table.schema[key] == null)
|
||||
) {
|
||||
const relatedTable = Object.values(tables).find(
|
||||
table => table._id === schema.tableId
|
||||
)
|
||||
const foreignKey = schema.foreignKey
|
||||
if (!relatedTable || !foreignKey) {
|
||||
continue
|
||||
}
|
||||
for (let [relatedKey, relatedSchema] of Object.entries(
|
||||
relatedTable.schema
|
||||
)) {
|
||||
if (
|
||||
relatedSchema.type === FieldTypes.LINK &&
|
||||
relatedSchema.fieldName === foreignKey
|
||||
) {
|
||||
delete relatedSchema[relatedKey]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDatasourceId(table) {
|
||||
if (!table) {
|
||||
throw "No table supplied"
|
||||
|
@ -57,6 +86,14 @@ function generateRelatedSchema(linkColumn, table) {
|
|||
return relatedSchema
|
||||
}
|
||||
|
||||
function oneToManyRelationshipNeedsSetup(column) {
|
||||
return (
|
||||
column.type === FieldTypes.LINK &&
|
||||
column.relationshipType === RelationshipTypes.ONE_TO_MANY &&
|
||||
!column.foreignKey
|
||||
)
|
||||
}
|
||||
|
||||
exports.save = async function (ctx) {
|
||||
const appId = ctx.appId
|
||||
const table = ctx.request.body
|
||||
|
@ -81,7 +118,7 @@ exports.save = async function (ctx) {
|
|||
// check if relations need setup
|
||||
for (let schema of Object.values(tableToSave.schema)) {
|
||||
// TODO: many to many handling
|
||||
if (schema.type === FieldTypes.LINK) {
|
||||
if (oneToManyRelationshipNeedsSetup(schema)) {
|
||||
const relatedTable = Object.values(tables).find(
|
||||
table => table._id === schema.tableId
|
||||
)
|
||||
|
@ -104,6 +141,8 @@ exports.save = async function (ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
cleanupRelationships(tableToSave, tables, oldTable)
|
||||
|
||||
const operation = oldTable
|
||||
? DataSourceOperation.UPDATE_TABLE
|
||||
: DataSourceOperation.CREATE_TABLE
|
||||
|
@ -128,6 +167,8 @@ exports.destroy = async function (ctx) {
|
|||
const operation = DataSourceOperation.DELETE_TABLE
|
||||
await makeTableRequest(datasource, operation, tableToDelete, tables)
|
||||
|
||||
cleanupRelationships(tableToDelete, tables)
|
||||
|
||||
delete datasource.entities[tableToDelete.name]
|
||||
await db.put(datasource)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Operation, QueryJson } from "../../definitions/datasource"
|
|||
import { breakExternalTableId } from "../utils"
|
||||
import SchemaBuilder = Knex.SchemaBuilder
|
||||
import CreateTableBuilder = Knex.CreateTableBuilder
|
||||
const { FieldTypes } = require("../../constants")
|
||||
const { FieldTypes, RelationshipTypes } = require("../../constants")
|
||||
|
||||
function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record<string, Table>, oldTable: null | Table = null) {
|
||||
let primaryKey = table && table.primary ? table.primary[0] : null
|
||||
|
@ -12,6 +12,8 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record
|
|||
if (primaryKey && !oldTable) {
|
||||
schema.increments(primaryKey).primary()
|
||||
}
|
||||
|
||||
// check if any columns need added
|
||||
const foreignKeys = Object.values(table.schema).map(col => col.foreignKey)
|
||||
for (let [key, column] of Object.entries(table.schema)) {
|
||||
// skip things that are already correct
|
||||
|
@ -38,6 +40,10 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record
|
|||
schema.json(key)
|
||||
break
|
||||
case FieldTypes.LINK:
|
||||
// this side of the relationship doesn't need any SQL work
|
||||
if (column.relationshipType === RelationshipTypes.MANY_TO_ONE) {
|
||||
break
|
||||
}
|
||||
if (!column.foreignKey || !column.tableId) {
|
||||
throw "Invalid relationship schema"
|
||||
}
|
||||
|
@ -51,6 +57,17 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record
|
|||
schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`)
|
||||
}
|
||||
}
|
||||
|
||||
// need to check if any columns have been deleted
|
||||
if (oldTable) {
|
||||
const deletedColumns = Object.entries(oldTable.schema)
|
||||
.filter(([key, schema]) => schema.type !== FieldTypes.LINK && table.schema[key] == null)
|
||||
.map(([key]) => key)
|
||||
deletedColumns.forEach(key => {
|
||||
schema.dropColumn(key)
|
||||
})
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue