This commit is contained in:
Adria Navarro 2023-10-05 19:47:00 +02:00
parent ea86ded912
commit 6c328109b6
8 changed files with 69 additions and 45 deletions

View File

@ -15,11 +15,14 @@ import { handleRequest } from "../row/external"
import { context, events } from "@budibase/backend-core" import { context, events } from "@budibase/backend-core"
import { isRows, isSchema, parse } from "../../../utilities/schema" import { isRows, isSchema, parse } from "../../../utilities/schema"
import { import {
AutoReason,
Datasource, Datasource,
FieldSchema, FieldSchema,
ManyToManyRelationshipFieldMetadata,
ManyToOneRelationshipFieldMetadata,
OneToManyRelationshipFieldMetadata,
Operation, Operation,
QueryJson, QueryJson,
RelationshipFieldMetadata,
RelationshipType, RelationshipType,
RenameColumn, RenameColumn,
SaveTableRequest, SaveTableRequest,
@ -74,10 +77,11 @@ function cleanupRelationships(
schema.type === FieldTypes.LINK && schema.type === FieldTypes.LINK &&
(!oldTable || table.schema[key] == null) (!oldTable || table.schema[key] == null)
) { ) {
const schemaTableId = schema.tableId
const relatedTable = Object.values(tables).find( const relatedTable = Object.values(tables).find(
table => table._id === schema.tableId table => table._id === schemaTableId
) )
const foreignKey = schema.foreignKey const foreignKey = (schema as any).foreignKey
if (!relatedTable || !foreignKey) { if (!relatedTable || !foreignKey) {
continue continue
} }
@ -116,7 +120,7 @@ function otherRelationshipType(type?: string) {
function generateManyLinkSchema( function generateManyLinkSchema(
datasource: Datasource, datasource: Datasource,
column: FieldSchema, column: ManyToManyRelationshipFieldMetadata,
table: Table, table: Table,
relatedTable: Table relatedTable: Table
): Table { ): Table {
@ -151,10 +155,12 @@ function generateManyLinkSchema(
} }
function generateLinkSchema( function generateLinkSchema(
column: FieldSchema, column:
| OneToManyRelationshipFieldMetadata
| ManyToOneRelationshipFieldMetadata,
table: Table, table: Table,
relatedTable: Table, relatedTable: Table,
type: RelationshipType type: RelationshipType.ONE_TO_MANY | RelationshipType.MANY_TO_ONE
) { ) {
if (!table.primary || !relatedTable.primary) { if (!table.primary || !relatedTable.primary) {
throw new Error("Unable to generate link schema, no primary keys") throw new Error("Unable to generate link schema, no primary keys")
@ -170,7 +176,7 @@ function generateLinkSchema(
} }
function generateRelatedSchema( function generateRelatedSchema(
linkColumn: FieldSchema, linkColumn: RelationshipFieldMetadata,
table: Table, table: Table,
relatedTable: Table, relatedTable: Table,
columnName: string columnName: string
@ -178,16 +184,16 @@ function generateRelatedSchema(
// generate column for other table // generate column for other table
const relatedSchema = cloneDeep(linkColumn) const relatedSchema = cloneDeep(linkColumn)
// swap them from the main link // swap them from the main link
if (linkColumn.foreignKey) { if ((linkColumn as any).foreignKey) {
relatedSchema.fieldName = linkColumn.foreignKey relatedSchema.fieldName = (linkColumn as any).foreignKey
relatedSchema.foreignKey = linkColumn.fieldName relatedSchema.foreignKey = linkColumn.fieldName
} }
// is many to many // is many to many
else { else {
// don't need to copy through, already got it // don't need to copy through, already got it
relatedSchema.fieldName = linkColumn.throughTo relatedSchema.fieldName = (linkColumn as any).throughTo
relatedSchema.throughTo = linkColumn.throughFrom relatedSchema.throughTo = (linkColumn as any).throughFrom
relatedSchema.throughFrom = linkColumn.throughTo relatedSchema.throughFrom = (linkColumn as any).throughTo
} }
relatedSchema.relationshipType = otherRelationshipType( relatedSchema.relationshipType = otherRelationshipType(
linkColumn.relationshipType linkColumn.relationshipType
@ -198,7 +204,7 @@ function generateRelatedSchema(
} }
function isRelationshipSetup(column: FieldSchema) { function isRelationshipSetup(column: FieldSchema) {
return column.foreignKey || column.through return (column as any).foreignKey || (column as any).through
} }
export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) { export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
@ -257,14 +263,15 @@ export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) { if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
continue continue
} }
const schemaTableId = schema.tableId
const relatedTable = Object.values(tables).find( const relatedTable = Object.values(tables).find(
table => table._id === schema.tableId table => table._id === schemaTableId
) )
if (!relatedTable) { if (!relatedTable) {
continue continue
} }
const relatedColumnName = schema.fieldName! const relatedColumnName = schema.fieldName!
const relationType = schema.relationshipType! const relationType = schema.relationshipType
if (relationType === RelationshipType.MANY_TO_MANY) { if (relationType === RelationshipType.MANY_TO_MANY) {
const junctionTable = generateManyLinkSchema( const junctionTable = generateManyLinkSchema(
datasource, datasource,

View File

@ -354,7 +354,7 @@ describe("/tables", () => {
type: FieldType.LINK, type: FieldType.LINK,
name: "TestTable", name: "TestTable",
fieldName: "TestTable", fieldName: "TestTable",
tableId: testTable._id, tableId: testTable._id!,
constraints: { constraints: {
type: "array", type: "array",
}, },

View File

@ -29,7 +29,7 @@ function syncLastIds(table: Table, rowCount: number) {
Object.keys(table.schema).forEach(key => { Object.keys(table.schema).forEach(key => {
const entry = table.schema[key] const entry = table.schema[key]
if (entry.autocolumn && entry.subtype == "autoID") { if (entry.autocolumn && entry.subtype == "autoID") {
entry.lastID = rowCount ;(entry as any).lastID = rowCount
} }
}) })
} }
@ -184,7 +184,7 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
}, },
...AUTO_COLUMNS, ...AUTO_COLUMNS,
}, },
} } as any
export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = { export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
_id: DEFAULT_EMPLOYEE_TABLE_ID, _id: DEFAULT_EMPLOYEE_TABLE_ID,
@ -332,7 +332,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
}, },
...AUTO_COLUMNS, ...AUTO_COLUMNS,
}, },
} } as any
export const DEFAULT_JOBS_TABLE_SCHEMA: Table = { export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
_id: DEFAULT_JOBS_TABLE_ID, _id: DEFAULT_JOBS_TABLE_ID,
@ -489,7 +489,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
}, },
...AUTO_COLUMNS, ...AUTO_COLUMNS,
}, },
} } as any
export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = { export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
_id: DEFAULT_EXPENSES_TABLE_ID, _id: DEFAULT_EXPENSES_TABLE_ID,
@ -599,7 +599,7 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
}, },
...AUTO_COLUMNS, ...AUTO_COLUMNS,
}, },
} } as any
export async function buildDefaultDocs() { export async function buildDefaultDocs() {
const inventoryData = await tableImport( const inventoryData = await tableImport(

View File

@ -8,6 +8,7 @@ import {
Database, Database,
FieldSchema, FieldSchema,
LinkDocumentValue, LinkDocumentValue,
RelationshipFieldMetadata,
RelationshipType, RelationshipType,
Row, Row,
Table, Table,
@ -133,7 +134,10 @@ class LinkController {
* Given the link field of this table, and the link field of the linked table, this makes sure * Given the link field of this table, and the link field of the linked table, this makes sure
* the state of relationship type is accurate on both. * the state of relationship type is accurate on both.
*/ */
handleRelationshipType(linkerField: FieldSchema, linkedField: FieldSchema) { handleRelationshipType(
linkerField: RelationshipFieldMetadata,
linkedField: RelationshipFieldMetadata
) {
if ( if (
!linkerField.relationshipType || !linkerField.relationshipType ||
linkerField.relationshipType === RelationshipType.MANY_TO_MANY linkerField.relationshipType === RelationshipType.MANY_TO_MANY
@ -183,7 +187,9 @@ class LinkController {
// if 1:N, ensure that this ID is not already attached to another record // if 1:N, ensure that this ID is not already attached to another record
const linkedTable = await this._db.get<Table>(field.tableId) const linkedTable = await this._db.get<Table>(field.tableId)
const linkedSchema = linkedTable.schema[field.fieldName!] const linkedSchema = linkedTable.schema[
field.fieldName!
] as RelationshipFieldMetadata
// We need to map the global users to metadata in each app for relationships // We need to map the global users to metadata in each app for relationships
if (field.tableId === InternalTables.USER_METADATA) { if (field.tableId === InternalTables.USER_METADATA) {
@ -291,7 +297,7 @@ class LinkController {
*/ */
async removeFieldFromTable(fieldName: string) { async removeFieldFromTable(fieldName: string) {
let oldTable = this._oldTable let oldTable = this._oldTable
let field = oldTable?.schema[fieldName] as FieldSchema let field = oldTable?.schema[fieldName] as RelationshipFieldMetadata
const linkDocs = await this.getTableLinkDocs() const linkDocs = await this.getTableLinkDocs()
let toDelete = linkDocs.filter(linkDoc => { let toDelete = linkDocs.filter(linkDoc => {
let correctFieldName = let correctFieldName =
@ -351,9 +357,9 @@ class LinkController {
name: field.fieldName, name: field.fieldName,
type: FieldTypes.LINK, type: FieldTypes.LINK,
// these are the props of the table that initiated the link // these are the props of the table that initiated the link
tableId: table._id, tableId: table._id!,
fieldName: fieldName, fieldName: fieldName,
}) } as any)
// update table schema after checking relationship types // update table schema after checking relationship types
schema[fieldName] = fields.linkerField schema[fieldName] = fields.linkerField

View File

@ -1,5 +1,11 @@
import { Knex, knex } from "knex" import { Knex, knex } from "knex"
import { Operation, QueryJson, RenameColumn, Table } from "@budibase/types" import {
NumberFieldMetadata,
Operation,
QueryJson,
RenameColumn,
Table,
} from "@budibase/types"
import { breakExternalTableId } from "../utils" import { breakExternalTableId } from "../utils"
import SchemaBuilder = Knex.SchemaBuilder import SchemaBuilder = Knex.SchemaBuilder
import CreateTableBuilder = Knex.CreateTableBuilder import CreateTableBuilder = Knex.CreateTableBuilder
@ -15,7 +21,7 @@ function generateSchema(
let primaryKey = table && table.primary ? table.primary[0] : null let primaryKey = table && table.primary ? table.primary[0] : null
const columns = Object.values(table.schema) const columns = Object.values(table.schema)
// all columns in a junction table will be meta // all columns in a junction table will be meta
let metaCols = columns.filter(col => col.meta) let metaCols = columns.filter(col => (col as NumberFieldMetadata).meta)
let isJunction = metaCols.length === columns.length let isJunction = metaCols.length === columns.length
// can't change primary once its set for now // can't change primary once its set for now
if (primaryKey && !oldTable && !isJunction) { if (primaryKey && !oldTable && !isJunction) {
@ -25,7 +31,9 @@ function generateSchema(
} }
// check if any columns need added // check if any columns need added
const foreignKeys = Object.values(table.schema).map(col => col.foreignKey) const foreignKeys = Object.values(table.schema).map(
col => (col as any).foreignKey
)
for (let [key, column] of Object.entries(table.schema)) { for (let [key, column] of Object.entries(table.schema)) {
// skip things that are already correct // skip things that are already correct
const oldColumn = oldTable ? oldTable.schema[key] : null const oldColumn = oldTable ? oldTable.schema[key] : null

View File

@ -1,10 +1,11 @@
import cloneDeep from "lodash/cloneDeep" import cloneDeep from "lodash/cloneDeep"
import validateJs from "validate.js" import validateJs from "validate.js"
import { FieldType, Row, Table, TableSchema } from "@budibase/types" import { Row, Table, TableSchema } from "@budibase/types"
import { FieldTypes } from "../../../constants" import { FieldTypes } from "../../../constants"
import { makeExternalQuery } from "../../../integrations/base/query" import { makeExternalQuery } from "../../../integrations/base/query"
import { Format } from "../../../api/controllers/view/exporters" import { Format } from "../../../api/controllers/view/exporters"
import sdk from "../.." import sdk from "../.."
import { isRelationshipColumn } from "../../../db/utils"
export async function getDatasourceAndQuery(json: any) { export async function getDatasourceAndQuery(json: any) {
const datasourceId = json.endpoint.datasourceId const datasourceId = json.endpoint.datasourceId
@ -50,10 +51,10 @@ export function cleanExportRows(
} }
function isForeignKey(key: string, table: Table) { function isForeignKey(key: string, table: Table) {
const relationships = Object.values(table.schema).filter( const relationships = Object.values(table.schema).filter(isRelationshipColumn)
column => column.type === FieldType.LINK return relationships.some(
relationship => (relationship as any).foreignKey === key
) )
return relationships.some(relationship => relationship.foreignKey === key)
} }
export async function validate({ export async function validate({

View File

@ -4,7 +4,7 @@ import { FieldSchema, FieldType, RelationshipType } from "@budibase/types"
describe("rowProcessor utility", () => { describe("rowProcessor utility", () => {
describe("fixAutoColumnSubType", () => { describe("fixAutoColumnSubType", () => {
let schema: FieldSchema = { const schema: FieldSchema = {
name: "", name: "",
type: FieldType.LINK, type: FieldType.LINK,
subtype: "", // missing subtype subtype: "", // missing subtype

View File

@ -36,7 +36,7 @@ export interface OneToManyRelationshipFieldMetadata
export interface ManyToOneRelationshipFieldMetadata export interface ManyToOneRelationshipFieldMetadata
extends BaseRelationshipFieldMetadata { extends BaseRelationshipFieldMetadata {
relationshipType: RelationshipType.MANY_TO_ONE relationshipType: RelationshipType.MANY_TO_ONE
foreignKey: string foreignKey?: string
} }
export type RelationshipFieldMetadata = export type RelationshipFieldMetadata =
| ManyToManyRelationshipFieldMetadata | ManyToManyRelationshipFieldMetadata
@ -52,10 +52,11 @@ export interface AutoColumnFieldMetadata extends BaseFieldSchema {
autoReason?: AutoReason autoReason?: AutoReason
} }
interface NumberForeignKeyMetadata { export interface NumberFieldMetadata extends BaseFieldSchema {
subtype: AutoFieldSubTypes.AUTO_ID type: FieldType.NUMBER
autoReason: AutoReason.FOREIGN_KEY autocolumn?: boolean
autocolumn: true subtype?: AutoFieldSubTypes.AUTO_ID
autoReason?: AutoReason.FOREIGN_KEY
// used specifically when Budibase generates external tables, this denotes if a number field // used specifically when Budibase generates external tables, this denotes if a number field
// is a foreign key used for a many-to-many relationship // is a foreign key used for a many-to-many relationship
meta?: { meta?: {
@ -64,11 +65,6 @@ interface NumberForeignKeyMetadata {
} }
} }
export type NumberFieldMetadata = BaseFieldSchema & {
type: FieldType.NUMBER
autocolumn?: boolean
} & (NumberForeignKeyMetadata | {})
export interface DateFieldMetadata extends BaseFieldSchema { export interface DateFieldMetadata extends BaseFieldSchema {
type: FieldType.DATETIME type: FieldType.DATETIME
ignoreTimezones?: boolean ignoreTimezones?: boolean
@ -77,6 +73,10 @@ export interface DateFieldMetadata extends BaseFieldSchema {
export interface StringFieldMetadata extends BaseFieldSchema { export interface StringFieldMetadata extends BaseFieldSchema {
type: FieldType.STRING type: FieldType.STRING
}
export interface LongFormFieldMetadata extends BaseFieldSchema {
type: FieldType.LONGFORM
useRichText?: boolean | null useRichText?: boolean | null
} }
@ -117,7 +117,7 @@ interface BaseFieldSchema extends UIFieldMetadata {
externalType?: string externalType?: string
constraints?: FieldConstraints constraints?: FieldConstraints
autocolumn?: boolean autocolumn?: boolean
autoReason?: AutoReason autoReason?: AutoReason.FOREIGN_KEY
subtype?: string subtype?: string
} }
@ -130,6 +130,7 @@ interface OtherFieldMetadata extends BaseFieldSchema {
| FieldType.STRING | FieldType.STRING
| FieldType.FORMULA | FieldType.FORMULA
| FieldType.NUMBER | FieldType.NUMBER
| FieldType.LONGFORM
> >
} }
@ -141,6 +142,7 @@ export type FieldSchema =
| StringFieldMetadata | StringFieldMetadata
| FormulaFieldMetadata | FormulaFieldMetadata
| NumberFieldMetadata | NumberFieldMetadata
| LongFormFieldMetadata
export interface TableSchema { export interface TableSchema {
[key: string]: FieldSchema [key: string]: FieldSchema