1
0
Fork 0
mirror of synced 2024-07-20 05:35:58 +12:00

Merge pull request #11315 from Budibase/types/improve-field-schema

Table types improvement - split up FieldSchema
This commit is contained in:
Michael Drury 2023-07-21 13:29:24 +01:00 committed by GitHub
commit fe8c92be79
19 changed files with 230 additions and 193 deletions

View file

@ -18,7 +18,7 @@
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
import { import {
FIELDS, FIELDS,
RelationshipTypes, RelationshipType,
ALLOWABLE_STRING_OPTIONS, ALLOWABLE_STRING_OPTIONS,
ALLOWABLE_NUMBER_OPTIONS, ALLOWABLE_NUMBER_OPTIONS,
ALLOWABLE_STRING_TYPES, ALLOWABLE_STRING_TYPES,
@ -183,7 +183,7 @@
dispatch("updatecolumns") dispatch("updatecolumns")
if ( if (
saveColumn.type === LINK_TYPE && saveColumn.type === LINK_TYPE &&
saveColumn.relationshipType === RelationshipTypes.MANY_TO_MANY saveColumn.relationshipType === RelationshipType.MANY_TO_MANY
) { ) {
// Fetching the new tables // Fetching the new tables
tables.fetch() tables.fetch()
@ -237,7 +237,7 @@
// Default relationships many to many // Default relationships many to many
if (editableColumn.type === LINK_TYPE) { if (editableColumn.type === LINK_TYPE) {
editableColumn.relationshipType = RelationshipTypes.MANY_TO_MANY editableColumn.relationshipType = RelationshipType.MANY_TO_MANY
} }
if (editableColumn.type === FORMULA_TYPE) { if (editableColumn.type === FORMULA_TYPE) {
editableColumn.formulaType = "dynamic" editableColumn.formulaType = "dynamic"
@ -285,17 +285,17 @@
{ {
name: `Many ${thisName} rows → many ${linkName} rows`, name: `Many ${thisName} rows → many ${linkName} rows`,
alt: `Many ${table.name} rows → many ${linkTable.name} rows`, alt: `Many ${table.name} rows → many ${linkTable.name} rows`,
value: RelationshipTypes.MANY_TO_MANY, value: RelationshipType.MANY_TO_MANY,
}, },
{ {
name: `One ${linkName} row → many ${thisName} rows`, name: `One ${linkName} row → many ${thisName} rows`,
alt: `One ${linkTable.name} rows → many ${table.name} rows`, alt: `One ${linkTable.name} rows → many ${table.name} rows`,
value: RelationshipTypes.ONE_TO_MANY, value: RelationshipType.ONE_TO_MANY,
}, },
{ {
name: `One ${thisName} row → many ${linkName} rows`, name: `One ${thisName} row → many ${linkName} rows`,
alt: `One ${table.name} rows → many ${linkTable.name} rows`, alt: `One ${table.name} rows → many ${linkTable.name} rows`,
value: RelationshipTypes.MANY_TO_ONE, value: RelationshipType.MANY_TO_ONE,
}, },
] ]
} }

View file

@ -1,5 +1,5 @@
<script> <script>
import { RelationshipTypes } from "constants/backend" import { RelationshipType } from "constants/backend"
import { import {
keepOpen, keepOpen,
Button, Button,
@ -25,11 +25,11 @@
const relationshipTypes = [ const relationshipTypes = [
{ {
label: "One to Many", label: "One to Many",
value: RelationshipTypes.MANY_TO_ONE, value: RelationshipType.MANY_TO_ONE,
}, },
{ {
label: "Many to Many", label: "Many to Many",
value: RelationshipTypes.MANY_TO_MANY, value: RelationshipType.MANY_TO_MANY,
}, },
] ]
@ -58,8 +58,8 @@
value: table._id, value: table._id,
})) }))
$: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet() $: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet()
$: isManyToMany = relationshipType === RelationshipTypes.MANY_TO_MANY $: isManyToMany = relationshipType === RelationshipType.MANY_TO_MANY
$: isManyToOne = relationshipType === RelationshipTypes.MANY_TO_ONE $: isManyToOne = relationshipType === RelationshipType.MANY_TO_ONE
function getTable(id) { function getTable(id) {
return plusTables.find(table => table._id === id) return plusTables.find(table => table._id === id)
@ -116,7 +116,7 @@
function allRequiredAttributesSet() { function allRequiredAttributesSet() {
const base = getTable(fromId) && getTable(toId) && fromColumn && toColumn const base = getTable(fromId) && getTable(toId) && fromColumn && toColumn
if (relationshipType === RelationshipTypes.MANY_TO_ONE) { if (relationshipType === RelationshipType.MANY_TO_ONE) {
return base && fromPrimary && fromForeign return base && fromPrimary && fromForeign
} else { } else {
return base && getTable(throughId) && throughFromKey && throughToKey return base && getTable(throughId) && throughFromKey && throughToKey
@ -181,12 +181,12 @@
} }
function otherRelationshipType(type) { function otherRelationshipType(type) {
if (type === RelationshipTypes.MANY_TO_ONE) { if (type === RelationshipType.MANY_TO_ONE) {
return RelationshipTypes.ONE_TO_MANY return RelationshipType.ONE_TO_MANY
} else if (type === RelationshipTypes.ONE_TO_MANY) { } else if (type === RelationshipType.ONE_TO_MANY) {
return RelationshipTypes.MANY_TO_ONE return RelationshipType.MANY_TO_ONE
} else if (type === RelationshipTypes.MANY_TO_MANY) { } else if (type === RelationshipType.MANY_TO_MANY) {
return RelationshipTypes.MANY_TO_MANY return RelationshipType.MANY_TO_MANY
} }
} }
@ -218,7 +218,7 @@
// if any to many only need to check from // if any to many only need to check from
const manyToMany = const manyToMany =
relateFrom.relationshipType === RelationshipTypes.MANY_TO_MANY relateFrom.relationshipType === RelationshipType.MANY_TO_MANY
if (!manyToMany) { if (!manyToMany) {
delete relateFrom.through delete relateFrom.through
@ -253,7 +253,7 @@
} }
relateTo = { relateTo = {
...relateTo, ...relateTo,
relationshipType: RelationshipTypes.ONE_TO_MANY, relationshipType: RelationshipType.ONE_TO_MANY,
foreignKey: relateFrom.fieldName, foreignKey: relateFrom.fieldName,
fieldName: fromPrimary, fieldName: fromPrimary,
} }
@ -321,7 +321,7 @@
fromColumn = toRelationship.name fromColumn = toRelationship.name
} }
relationshipType = relationshipType =
fromRelationship.relationshipType || RelationshipTypes.MANY_TO_ONE fromRelationship.relationshipType || RelationshipType.MANY_TO_ONE
if (selectedFromTable) { if (selectedFromTable) {
fromId = selectedFromTable._id fromId = selectedFromTable._id
fromColumn = selectedFromTable.name fromColumn = selectedFromTable.name

View file

@ -1,4 +1,4 @@
import { RelationshipTypes } from "constants/backend" import { RelationshipType } from "constants/backend"
const typeMismatch = "Column type of the foreign key must match the primary key" const typeMismatch = "Column type of the foreign key must match the primary key"
const columnBeingUsed = "Column name cannot be an existing column" const columnBeingUsed = "Column name cannot be an existing column"
@ -40,7 +40,7 @@ export class RelationshipErrorChecker {
} }
isMany() { isMany() {
return this.type === RelationshipTypes.MANY_TO_MANY return this.type === RelationshipType.MANY_TO_MANY
} }
relationshipTypeSet(type) { relationshipTypeSet(type) {

View file

@ -151,7 +151,7 @@ export function isAutoColumnUserRelationship(subtype) {
) )
} }
export const RelationshipTypes = { export const RelationshipType = {
MANY_TO_MANY: "many-to-many", MANY_TO_MANY: "many-to-many",
ONE_TO_MANY: "one-to-many", ONE_TO_MANY: "one-to-many",
MANY_TO_ONE: "many-to-one", MANY_TO_ONE: "many-to-one",

View file

@ -1,8 +1,4 @@
import { import { FieldTypes, RelationshipType, FormulaTypes } from "../../src/constants"
FieldTypes,
RelationshipTypes,
FormulaTypes,
} from "../../src/constants"
import { object } from "./utils" import { object } from "./utils"
import Resource from "./utils/Resource" import Resource from "./utils/Resource"
@ -100,7 +96,7 @@ const tableSchema = {
}, },
relationshipType: { relationshipType: {
type: "string", type: "string",
enum: Object.values(RelationshipTypes), enum: Object.values(RelationshipType),
description: description:
"Defines the type of relationship that this column will be used for.", "Defines the type of relationship that this column will be used for.",
}, },

View file

@ -7,7 +7,7 @@ import {
Operation, Operation,
PaginationJson, PaginationJson,
RelationshipsJson, RelationshipsJson,
RelationshipTypes, RelationshipType,
Row, Row,
SearchFilters, SearchFilters,
SortJson, SortJson,
@ -577,7 +577,7 @@ export class ExternalRequest {
) { ) {
continue continue
} }
const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY const isMany = field.relationshipType === RelationshipType.MANY_TO_MANY
const tableId = isMany ? field.through : field.tableId const tableId = isMany ? field.through : field.tableId
const { tableName: relatedTableName } = breakExternalTableId(tableId) const { tableName: relatedTableName } = breakExternalTableId(tableId)
// @ts-ignore // @ts-ignore

View file

@ -20,7 +20,7 @@ import {
FieldSchema, FieldSchema,
Operation, Operation,
QueryJson, QueryJson,
RelationshipTypes, RelationshipType,
RenameColumn, RenameColumn,
Table, Table,
TableRequest, TableRequest,
@ -103,12 +103,12 @@ function getDatasourceId(table: Table) {
} }
function otherRelationshipType(type?: string) { function otherRelationshipType(type?: string) {
if (type === RelationshipTypes.MANY_TO_MANY) { if (type === RelationshipType.MANY_TO_MANY) {
return RelationshipTypes.MANY_TO_MANY return RelationshipType.MANY_TO_MANY
} }
return type === RelationshipTypes.ONE_TO_MANY return type === RelationshipType.ONE_TO_MANY
? RelationshipTypes.MANY_TO_ONE ? RelationshipType.MANY_TO_ONE
: RelationshipTypes.ONE_TO_MANY : RelationshipType.ONE_TO_MANY
} }
function generateManyLinkSchema( function generateManyLinkSchema(
@ -151,12 +151,12 @@ function generateLinkSchema(
column: FieldSchema, column: FieldSchema,
table: Table, table: Table,
relatedTable: Table, relatedTable: Table,
type: RelationshipTypes type: RelationshipType
) { ) {
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")
} }
const isOneSide = type === RelationshipTypes.ONE_TO_MANY const isOneSide = type === RelationshipType.ONE_TO_MANY
const primary = isOneSide ? relatedTable.primary[0] : table.primary[0] const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
// generate a foreign key // generate a foreign key
const foreignKey = generateForeignKey(column, relatedTable) const foreignKey = generateForeignKey(column, relatedTable)
@ -251,7 +251,7 @@ export async function save(ctx: UserCtx) {
} }
const relatedColumnName = schema.fieldName! const relatedColumnName = schema.fieldName!
const relationType = schema.relationshipType! const relationType = schema.relationshipType!
if (relationType === RelationshipTypes.MANY_TO_MANY) { if (relationType === RelationshipType.MANY_TO_MANY) {
const junctionTable = generateManyLinkSchema( const junctionTable = generateManyLinkSchema(
datasource, datasource,
schema, schema,
@ -265,7 +265,7 @@ export async function save(ctx: UserCtx) {
extraTablesToUpdate.push(junctionTable) extraTablesToUpdate.push(junctionTable)
} else { } else {
const fkTable = const fkTable =
relationType === RelationshipTypes.ONE_TO_MANY relationType === RelationshipType.ONE_TO_MANY
? tableToSave ? tableToSave
: relatedTable : relatedTable
const foreignKey = generateLinkSchema( const foreignKey = generateLinkSchema(

View file

@ -1,6 +1,6 @@
import { objectStore, roles, constants } from "@budibase/backend-core" import { objectStore, roles, constants } from "@budibase/backend-core"
import { FieldType as FieldTypes } from "@budibase/types" import { FieldType as FieldTypes } from "@budibase/types"
export { FieldType as FieldTypes, RelationshipTypes } from "@budibase/types" export { FieldType as FieldTypes, RelationshipType } from "@budibase/types"
export enum FilterTypes { export enum FilterTypes {
STRING = "string", STRING = "string",

View file

@ -7,7 +7,7 @@ import { employeeImport } from "./employeeImport"
import { jobsImport } from "./jobsImport" import { jobsImport } from "./jobsImport"
import { expensesImport } from "./expensesImport" import { expensesImport } from "./expensesImport"
import { db as dbCore } from "@budibase/backend-core" import { db as dbCore } from "@budibase/backend-core"
import { Table, Row, RelationshipTypes } from "@budibase/types" import { Table, Row, RelationshipType } from "@budibase/types"
export const DEFAULT_JOBS_TABLE_ID = "ta_bb_jobs" export const DEFAULT_JOBS_TABLE_ID = "ta_bb_jobs"
export const DEFAULT_INVENTORY_TABLE_ID = "ta_bb_inventory" export const DEFAULT_INVENTORY_TABLE_ID = "ta_bb_inventory"
@ -299,7 +299,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
}, },
fieldName: "Assigned", fieldName: "Assigned",
name: "Jobs", name: "Jobs",
relationshipType: RelationshipTypes.MANY_TO_MANY, relationshipType: RelationshipType.MANY_TO_MANY,
tableId: DEFAULT_JOBS_TABLE_ID, tableId: DEFAULT_JOBS_TABLE_ID,
}, },
"Start Date": { "Start Date": {
@ -458,7 +458,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
type: FieldTypes.LINK, type: FieldTypes.LINK,
tableId: DEFAULT_EMPLOYEE_TABLE_ID, tableId: DEFAULT_EMPLOYEE_TABLE_ID,
fieldName: "Jobs", fieldName: "Jobs",
relationshipType: RelationshipTypes.MANY_TO_MANY, relationshipType: RelationshipType.MANY_TO_MANY,
// sortable: true, // sortable: true,
}, },
"Works End": { "Works End": {

View file

@ -8,7 +8,7 @@ import {
Database, Database,
FieldSchema, FieldSchema,
LinkDocumentValue, LinkDocumentValue,
RelationshipTypes, RelationshipType,
Row, Row,
Table, Table,
} from "@budibase/types" } from "@budibase/types"
@ -136,16 +136,16 @@ class LinkController {
handleRelationshipType(linkerField: FieldSchema, linkedField: FieldSchema) { handleRelationshipType(linkerField: FieldSchema, linkedField: FieldSchema) {
if ( if (
!linkerField.relationshipType || !linkerField.relationshipType ||
linkerField.relationshipType === RelationshipTypes.MANY_TO_MANY linkerField.relationshipType === RelationshipType.MANY_TO_MANY
) { ) {
linkedField.relationshipType = RelationshipTypes.MANY_TO_MANY linkedField.relationshipType = RelationshipType.MANY_TO_MANY
// make sure by default all are many to many (if not specified) // make sure by default all are many to many (if not specified)
linkerField.relationshipType = RelationshipTypes.MANY_TO_MANY linkerField.relationshipType = RelationshipType.MANY_TO_MANY
} else if (linkerField.relationshipType === RelationshipTypes.MANY_TO_ONE) { } else if (linkerField.relationshipType === RelationshipType.MANY_TO_ONE) {
// Ensure that the other side of the relationship is locked to one record // Ensure that the other side of the relationship is locked to one record
linkedField.relationshipType = RelationshipTypes.ONE_TO_MANY linkedField.relationshipType = RelationshipType.ONE_TO_MANY
} else if (linkerField.relationshipType === RelationshipTypes.ONE_TO_MANY) { } else if (linkerField.relationshipType === RelationshipType.ONE_TO_MANY) {
linkedField.relationshipType = RelationshipTypes.MANY_TO_ONE linkedField.relationshipType = RelationshipType.MANY_TO_ONE
} }
return { linkerField, linkedField } return { linkerField, linkedField }
} }
@ -200,9 +200,7 @@ class LinkController {
// iterate through the link IDs in the row field, see if any don't exist already // iterate through the link IDs in the row field, see if any don't exist already
for (let linkId of rowField) { for (let linkId of rowField) {
if ( if (linkedSchema?.relationshipType === RelationshipType.ONE_TO_MANY) {
linkedSchema?.relationshipType === RelationshipTypes.ONE_TO_MANY
) {
let links = ( let links = (
(await getLinkDocuments({ (await getLinkDocuments({
tableId: field.tableId, tableId: field.tableId,

View file

@ -2,7 +2,7 @@ const TestConfig = require("../../tests/utilities/TestConfiguration")
const { basicRow, basicLinkedRow, basicTable } = require("../../tests/utilities/structures") const { basicRow, basicLinkedRow, basicTable } = require("../../tests/utilities/structures")
const LinkController = require("../linkedRows/LinkController").default const LinkController = require("../linkedRows/LinkController").default
const { context } = require("@budibase/backend-core") const { context } = require("@budibase/backend-core")
const { RelationshipTypes } = require("../../constants") const { RelationshipType } = require("../../constants")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
describe("test the link controller", () => { describe("test the link controller", () => {
@ -16,7 +16,7 @@ describe("test the link controller", () => {
beforeEach(async () => { beforeEach(async () => {
const { _id } = await config.createTable() const { _id } = await config.createTable()
table2 = await config.createLinkedTable(RelationshipTypes.MANY_TO_MANY, ["link", "link2"]) table2 = await config.createLinkedTable(RelationshipType.MANY_TO_MANY, ["link", "link2"])
// update table after creating link // update table after creating link
table1 = await config.getTable(_id) table1 = await config.getTable(_id)
}) })
@ -57,17 +57,17 @@ describe("test the link controller", () => {
const controller = await createLinkController(table1) const controller = await createLinkController(table1)
// empty case // empty case
let output = controller.handleRelationshipType({}, {}) let output = controller.handleRelationshipType({}, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) expect(output.linkedField.relationshipType).toEqual(RelationshipType.MANY_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) expect(output.linkerField.relationshipType).toEqual(RelationshipType.MANY_TO_MANY)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_MANY }, {}) output = controller.handleRelationshipType({ relationshipType: RelationshipType.MANY_TO_MANY }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) expect(output.linkedField.relationshipType).toEqual(RelationshipType.MANY_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) expect(output.linkerField.relationshipType).toEqual(RelationshipType.MANY_TO_MANY)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_ONE }, {}) output = controller.handleRelationshipType({ relationshipType: RelationshipType.MANY_TO_ONE }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY) expect(output.linkedField.relationshipType).toEqual(RelationshipType.ONE_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE) expect(output.linkerField.relationshipType).toEqual(RelationshipType.MANY_TO_ONE)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.ONE_TO_MANY }, {}) output = controller.handleRelationshipType({ relationshipType: RelationshipType.ONE_TO_MANY }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE) expect(output.linkedField.relationshipType).toEqual(RelationshipType.MANY_TO_ONE)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY) expect(output.linkerField.relationshipType).toEqual(RelationshipType.ONE_TO_MANY)
}) })
it("should be able to delete a row", async () => { it("should be able to delete a row", async () => {
@ -157,7 +157,7 @@ describe("test the link controller", () => {
it("should throw an error when overwriting a link column", async () => { it("should throw an error when overwriting a link column", async () => {
const update = cloneDeep(table1) const update = cloneDeep(table1)
update.schema.link.relationshipType = RelationshipTypes.MANY_TO_ONE update.schema.link.relationshipType = RelationshipType.MANY_TO_ONE
let error let error
try { try {
const controller = await createLinkController(update) const controller = await createLinkController(update)
@ -183,7 +183,7 @@ describe("test the link controller", () => {
it("shouldn't allow one to many having many relationships against it", async () => { it("shouldn't allow one to many having many relationships against it", async () => {
const firstTable = await config.createTable() const firstTable = await config.createTable()
const { _id } = await config.createLinkedTable(RelationshipTypes.MANY_TO_ONE, ["link"]) const { _id } = await config.createLinkedTable(RelationshipType.MANY_TO_ONE, ["link"])
const linkTable = await config.getTable(_id) const linkTable = await config.getTable(_id)
// an initial row to link around // an initial row to link around
const row = await createLinkedRow("link", linkTable, firstTable) const row = await createLinkedRow("link", linkTable, firstTable)

View file

@ -10,7 +10,7 @@ import * as setup from "../api/routes/tests/utilities"
import { import {
Datasource, Datasource,
FieldType, FieldType,
RelationshipTypes, RelationshipType,
Row, Row,
SourceName, SourceName,
Table, Table,
@ -101,17 +101,17 @@ describe("postgres integrations", () => {
oneToManyRelationshipInfo = { oneToManyRelationshipInfo = {
table: await createAuxTable("o2m"), table: await createAuxTable("o2m"),
fieldName: "oneToManyRelation", fieldName: "oneToManyRelation",
relationshipType: RelationshipTypes.ONE_TO_MANY, relationshipType: RelationshipType.ONE_TO_MANY,
} }
manyToOneRelationshipInfo = { manyToOneRelationshipInfo = {
table: await createAuxTable("m2o"), table: await createAuxTable("m2o"),
fieldName: "manyToOneRelation", fieldName: "manyToOneRelation",
relationshipType: RelationshipTypes.MANY_TO_ONE, relationshipType: RelationshipType.MANY_TO_ONE,
} }
manyToManyRelationshipInfo = { manyToManyRelationshipInfo = {
table: await createAuxTable("m2m"), table: await createAuxTable("m2m"),
fieldName: "manyToManyRelation", fieldName: "manyToManyRelation",
relationshipType: RelationshipTypes.MANY_TO_MANY, relationshipType: RelationshipType.MANY_TO_MANY,
} }
primaryPostgresTable = await config.createTable({ primaryPostgresTable = await config.createTable({
@ -143,7 +143,7 @@ describe("postgres integrations", () => {
}, },
fieldName: oneToManyRelationshipInfo.fieldName, fieldName: oneToManyRelationshipInfo.fieldName,
name: "oneToManyRelation", name: "oneToManyRelation",
relationshipType: RelationshipTypes.ONE_TO_MANY, relationshipType: RelationshipType.ONE_TO_MANY,
tableId: oneToManyRelationshipInfo.table._id, tableId: oneToManyRelationshipInfo.table._id,
main: true, main: true,
}, },
@ -154,7 +154,7 @@ describe("postgres integrations", () => {
}, },
fieldName: manyToOneRelationshipInfo.fieldName, fieldName: manyToOneRelationshipInfo.fieldName,
name: "manyToOneRelation", name: "manyToOneRelation",
relationshipType: RelationshipTypes.MANY_TO_ONE, relationshipType: RelationshipType.MANY_TO_ONE,
tableId: manyToOneRelationshipInfo.table._id, tableId: manyToOneRelationshipInfo.table._id,
main: true, main: true,
}, },
@ -165,7 +165,7 @@ describe("postgres integrations", () => {
}, },
fieldName: manyToManyRelationshipInfo.fieldName, fieldName: manyToManyRelationshipInfo.fieldName,
name: "manyToManyRelation", name: "manyToManyRelation",
relationshipType: RelationshipTypes.MANY_TO_MANY, relationshipType: RelationshipType.MANY_TO_MANY,
tableId: manyToManyRelationshipInfo.table._id, tableId: manyToManyRelationshipInfo.table._id,
main: true, main: true,
}, },
@ -193,12 +193,12 @@ describe("postgres integrations", () => {
type ForeignTableInfo = { type ForeignTableInfo = {
table: Table table: Table
fieldName: string fieldName: string
relationshipType: RelationshipTypes relationshipType: RelationshipType
} }
type ForeignRowsInfo = { type ForeignRowsInfo = {
row: Row row: Row
relationshipType: RelationshipTypes relationshipType: RelationshipType
} }
async function createPrimaryRow(opts: { async function createPrimaryRow(opts: {
@ -263,7 +263,7 @@ describe("postgres integrations", () => {
rowData[manyToOneRelationshipInfo.fieldName].push(foreignRow._id) rowData[manyToOneRelationshipInfo.fieldName].push(foreignRow._id)
foreignRows.push({ foreignRows.push({
row: foreignRow, row: foreignRow,
relationshipType: RelationshipTypes.MANY_TO_ONE, relationshipType: RelationshipType.MANY_TO_ONE,
}) })
} }
@ -281,7 +281,7 @@ describe("postgres integrations", () => {
rowData[manyToManyRelationshipInfo.fieldName].push(foreignRow._id) rowData[manyToManyRelationshipInfo.fieldName].push(foreignRow._id)
foreignRows.push({ foreignRows.push({
row: foreignRow, row: foreignRow,
relationshipType: RelationshipTypes.MANY_TO_MANY, relationshipType: RelationshipType.MANY_TO_MANY,
}) })
} }
@ -559,7 +559,7 @@ describe("postgres integrations", () => {
expect(res.status).toBe(200) expect(res.status).toBe(200)
const one2ManyForeignRows = foreignRows.filter( const one2ManyForeignRows = foreignRows.filter(
x => x.relationshipType === RelationshipTypes.ONE_TO_MANY x => x.relationshipType === RelationshipType.ONE_TO_MANY
) )
expect(one2ManyForeignRows).toHaveLength(1) expect(one2ManyForeignRows).toHaveLength(1)
@ -921,7 +921,7 @@ describe("postgres integrations", () => {
(row: Row) => row.id === 2 (row: Row) => row.id === 2
) )
expect(m2mRow1).toEqual({ expect(m2mRow1).toEqual({
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row, ...foreignRowsByType[RelationshipType.MANY_TO_MANY][0].row,
[m2mFieldName]: [ [m2mFieldName]: [
{ {
_id: row._id, _id: row._id,
@ -930,7 +930,7 @@ describe("postgres integrations", () => {
], ],
}) })
expect(m2mRow2).toEqual({ expect(m2mRow2).toEqual({
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row, ...foreignRowsByType[RelationshipType.MANY_TO_MANY][1].row,
[m2mFieldName]: [ [m2mFieldName]: [
{ {
_id: row._id, _id: row._id,
@ -940,24 +940,24 @@ describe("postgres integrations", () => {
}) })
expect(res.body[m2oFieldName]).toEqual([ expect(res.body[m2oFieldName]).toEqual([
{ {
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row, ...foreignRowsByType[RelationshipType.MANY_TO_ONE][0].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id, row.id,
}, },
{ {
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row, ...foreignRowsByType[RelationshipType.MANY_TO_ONE][1].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id, row.id,
}, },
{ {
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row, ...foreignRowsByType[RelationshipType.MANY_TO_ONE][2].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id, row.id,
}, },
]) ])
expect(res.body[o2mFieldName]).toEqual([ expect(res.body[o2mFieldName]).toEqual([
{ {
...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row, ...foreignRowsByType[RelationshipType.ONE_TO_MANY][0].row,
_id: expect.any(String), _id: expect.any(String),
_rev: expect.any(String), _rev: expect.any(String),
}, },

View file

@ -3,7 +3,7 @@ import { 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
import { FieldTypes, RelationshipTypes } from "../../constants" import { FieldTypes, RelationshipType } from "../../constants"
function generateSchema( function generateSchema(
schema: CreateTableBuilder, schema: CreateTableBuilder,
@ -70,8 +70,8 @@ function generateSchema(
case FieldTypes.LINK: case FieldTypes.LINK:
// this side of the relationship doesn't need any SQL work // this side of the relationship doesn't need any SQL work
if ( if (
column.relationshipType !== RelationshipTypes.MANY_TO_ONE && column.relationshipType !== RelationshipType.MANY_TO_ONE &&
column.relationshipType !== RelationshipTypes.MANY_TO_MANY column.relationshipType !== RelationshipType.MANY_TO_MANY
) { ) {
if (!column.foreignKey || !column.tableId) { if (!column.foreignKey || !column.tableId) {
throw "Invalid relationship schema" throw "Invalid relationship schema"

View file

@ -3,7 +3,7 @@ import {
Datasource, Datasource,
FieldSchema, FieldSchema,
FieldType, FieldType,
RelationshipTypes, RelationshipType,
} from "@budibase/types" } from "@budibase/types"
import { FieldTypes } from "../../../constants" import { FieldTypes } from "../../../constants"
@ -19,14 +19,14 @@ function checkForeignKeysAreAutoColumns(datasource: Datasource) {
column => column.type === FieldType.LINK column => column.type === FieldType.LINK
) )
relationships.forEach(relationship => { relationships.forEach(relationship => {
if (relationship.relationshipType === RelationshipTypes.MANY_TO_MANY) { if (relationship.relationshipType === RelationshipType.MANY_TO_MANY) {
const tableId = relationship.through! const tableId = relationship.through!
foreignKeys.push({ key: relationship.throughTo!, tableId }) foreignKeys.push({ key: relationship.throughTo!, tableId })
foreignKeys.push({ key: relationship.throughFrom!, tableId }) foreignKeys.push({ key: relationship.throughFrom!, tableId })
} else { } else {
const fk = relationship.foreignKey! const fk = relationship.foreignKey!
const oneSide = const oneSide =
relationship.relationshipType === RelationshipTypes.ONE_TO_MANY relationship.relationshipType === RelationshipType.ONE_TO_MANY
foreignKeys.push({ foreignKeys.push({
tableId: oneSide ? table._id! : relationship.tableId!, tableId: oneSide ? table._id! : relationship.tableId!,
key: fk, key: fk,

View file

@ -1,97 +0,0 @@
import { Document } from "../document"
import { View } from "./view"
import { RenameColumn } from "../../sdk"
import { FieldType } from "./row"
export enum RelationshipTypes {
ONE_TO_MANY = "one-to-many",
MANY_TO_ONE = "many-to-one",
MANY_TO_MANY = "many-to-many",
}
export enum AutoReason {
FOREIGN_KEY = "foreign_key",
}
export interface FieldSchema {
type: FieldType
externalType?: string
fieldName?: string
name: string
sortable?: boolean
tableId?: string
relationshipType?: RelationshipTypes
through?: string
foreignKey?: string
icon?: string
autocolumn?: boolean
autoReason?: AutoReason
subtype?: string
throughFrom?: string
throughTo?: string
formula?: string
formulaType?: string
main?: boolean
ignoreTimezones?: boolean
timeOnly?: boolean
lastID?: number
useRichText?: boolean | null
order?: number
width?: number
meta?: {
toTable: string
toKey: string
}
constraints?: {
type?: string
email?: boolean
inclusion?: string[]
length?: {
minimum?: string | number | null
maximum?: string | number | null
}
numericality?: {
greaterThanOrEqualTo: string | null
lessThanOrEqualTo: string | null
}
presence?:
| boolean
| {
allowEmpty?: boolean
}
datetime?: {
latest: string
earliest: string
}
}
}
export interface TableSchema {
[key: string]: FieldSchema
}
export interface Table extends Document {
type?: string
views?: { [key: string]: View }
name: string
primary?: string[]
schema: TableSchema
primaryDisplay?: string
sourceId?: string
relatedFormula?: string[]
constrained?: string[]
sql?: boolean
indexes?: { [key: string]: any }
rows?: { [key: string]: any }
created?: boolean
rowHeight?: number
}
export interface ExternalTable extends Table {
sourceId: string
}
export interface TableRequest extends Table {
_rename?: RenameColumn
created?: boolean
}

View file

@ -0,0 +1,9 @@
export enum RelationshipType {
ONE_TO_MANY = "one-to-many",
MANY_TO_ONE = "many-to-one",
MANY_TO_MANY = "many-to-many",
}
export enum AutoReason {
FOREIGN_KEY = "foreign_key",
}

View file

@ -0,0 +1,3 @@
export * from "./table"
export * from "./schema"
export * from "./constants"

View file

@ -0,0 +1,98 @@
// all added by grid/table when defining the
// column size, position and whether it can be viewed
import { FieldType } from "../row"
import { AutoReason, RelationshipType } from "./constants"
export interface UIFieldMetadata {
order?: number
width?: number
visible?: boolean
icon?: string
}
export interface RelationshipFieldMetadata {
main?: boolean
fieldName?: string
tableId?: string
// below is used for SQL relationships, needed to define the foreign keys
// or the tables used for many-to-many relationships (through)
relationshipType?: RelationshipType
through?: string
foreignKey?: string
throughFrom?: string
throughTo?: string
}
export interface AutoColumnFieldMetadata {
autocolumn?: boolean
subtype?: string
lastID?: number
// if the column was turned to an auto-column for SQL, explains why (primary, foreign etc)
autoReason?: AutoReason
}
export interface NumberFieldMetadata {
// used specifically when Budibase generates external tables, this denotes if a number field
// is a foreign key used for a many-to-many relationship
meta?: {
toTable: string
toKey: string
}
}
export interface DateFieldMetadata {
ignoreTimezones?: boolean
timeOnly?: boolean
}
export interface StringFieldMetadata {
useRichText?: boolean | null
}
export interface FormulaFieldMetadata {
formula?: string
formulaType?: string
}
export interface FieldConstraints {
type?: string
email?: boolean
inclusion?: string[]
length?: {
minimum?: string | number | null
maximum?: string | number | null
}
numericality?: {
greaterThanOrEqualTo: string | null
lessThanOrEqualTo: string | null
}
presence?:
| boolean
| {
allowEmpty?: boolean
}
datetime?: {
latest: string
earliest: string
}
}
export interface FieldSchema
extends UIFieldMetadata,
DateFieldMetadata,
RelationshipFieldMetadata,
AutoColumnFieldMetadata,
StringFieldMetadata,
FormulaFieldMetadata,
NumberFieldMetadata {
type: FieldType
name: string
sortable?: boolean
// only used by external databases, to denote the real type
externalType?: string
constraints?: FieldConstraints
}
export interface TableSchema {
[key: string]: FieldSchema
}

View file

@ -0,0 +1,30 @@
import { Document } from "../../document"
import { View } from "../view"
import { RenameColumn } from "../../../sdk"
import { TableSchema } from "./schema"
export interface Table extends Document {
type?: string
views?: { [key: string]: View }
name: string
primary?: string[]
schema: TableSchema
primaryDisplay?: string
sourceId?: string
relatedFormula?: string[]
constrained?: string[]
sql?: boolean
indexes?: { [key: string]: any }
rows?: { [key: string]: any }
created?: boolean
rowHeight?: number
}
export interface ExternalTable extends Table {
sourceId: string
}
export interface TableRequest extends Table {
_rename?: RenameColumn
created?: boolean
}