Fixes
This commit is contained in:
parent
ea86ded912
commit
6c328109b6
|
@ -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,
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue