Merge pull request #12862 from Budibase/refactor/remove-field-types
Refactor - fully replace FieldTypes with newer FieldType
This commit is contained in:
commit
94ec160ba3
37 changed files with 426 additions and 355 deletions
|
@ -13,7 +13,7 @@
|
||||||
import { getColumnIcon } from "../lib/utils"
|
import { getColumnIcon } from "../lib/utils"
|
||||||
import MigrationModal from "../controls/MigrationModal.svelte"
|
import MigrationModal from "../controls/MigrationModal.svelte"
|
||||||
import { debounce } from "../../../utils/utils"
|
import { debounce } from "../../../utils/utils"
|
||||||
import { FieldType, FormulaTypes } from "@budibase/types"
|
import { FieldType, FormulaType } from "@budibase/types"
|
||||||
import { TableNames } from "../../../constants"
|
import { TableNames } from "../../../constants"
|
||||||
|
|
||||||
export let column
|
export let column
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
const { type, formulaType } = col.schema
|
const { type, formulaType } = col.schema
|
||||||
return (
|
return (
|
||||||
searchableTypes.includes(type) ||
|
searchableTypes.includes(type) ||
|
||||||
(type === FieldType.FORMULA && formulaType === FormulaTypes.STATIC)
|
(type === FieldType.FORMULA && formulaType === FormulaType.STATIC)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FieldTypes, RelationshipType, FormulaTypes } from "../../src/constants"
|
import { FieldType, FormulaType, RelationshipType } from "@budibase/types"
|
||||||
import { object } from "./utils"
|
import { object } from "./utils"
|
||||||
import Resource from "./utils/Resource"
|
import Resource from "./utils/Resource"
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ const table = {
|
||||||
const baseColumnDef = {
|
const baseColumnDef = {
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: Object.values(FieldTypes),
|
enum: Object.values(FieldType),
|
||||||
description:
|
description:
|
||||||
"Defines the type of the column, most explain themselves, a link column is a relationship.",
|
"Defines the type of the column, most explain themselves, a link column is a relationship.",
|
||||||
},
|
},
|
||||||
|
@ -81,7 +81,7 @@ const tableSchema = {
|
||||||
...baseColumnDef,
|
...baseColumnDef,
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: [FieldTypes.LINK],
|
enum: [FieldType.LINK],
|
||||||
description: "A relationship column.",
|
description: "A relationship column.",
|
||||||
},
|
},
|
||||||
fieldName: {
|
fieldName: {
|
||||||
|
@ -128,7 +128,7 @@ const tableSchema = {
|
||||||
...baseColumnDef,
|
...baseColumnDef,
|
||||||
type: {
|
type: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: [FieldTypes.FORMULA],
|
enum: [FieldType.FORMULA],
|
||||||
description: "A formula column.",
|
description: "A formula column.",
|
||||||
},
|
},
|
||||||
formula: {
|
formula: {
|
||||||
|
@ -138,7 +138,7 @@ const tableSchema = {
|
||||||
},
|
},
|
||||||
formulaType: {
|
formulaType: {
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: Object.values(FormulaTypes),
|
enum: Object.values(FormulaType),
|
||||||
description:
|
description:
|
||||||
"Defines whether this is a static or dynamic formula.",
|
"Defines whether this is a static or dynamic formula.",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
AutoReason,
|
AutoReason,
|
||||||
Datasource,
|
Datasource,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
|
@ -27,7 +28,6 @@ import {
|
||||||
isSQL,
|
isSQL,
|
||||||
} from "../../../integrations/utils"
|
} from "../../../integrations/utils"
|
||||||
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
|
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
|
||||||
import { AutoFieldSubTypes, FieldTypes } from "../../../constants"
|
|
||||||
import { processObjectSync } from "@budibase/string-templates"
|
import { processObjectSync } from "@budibase/string-templates"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { processDates, processFormulas } from "../../../utilities/rowProcessor"
|
import { processDates, processFormulas } from "../../../utilities/rowProcessor"
|
||||||
|
@ -111,10 +111,10 @@ function buildFilters(
|
||||||
*/
|
*/
|
||||||
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
||||||
const primaryOptions = [
|
const primaryOptions = [
|
||||||
FieldTypes.STRING,
|
FieldType.STRING,
|
||||||
FieldTypes.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
FieldTypes.OPTIONS,
|
FieldType.OPTIONS,
|
||||||
FieldTypes.NUMBER,
|
FieldType.NUMBER,
|
||||||
]
|
]
|
||||||
// filter out fields which cannot be keys
|
// filter out fields which cannot be keys
|
||||||
const fieldNames = Object.entries(table.schema)
|
const fieldNames = Object.entries(table.schema)
|
||||||
|
@ -241,10 +241,7 @@ function basicProcessing({
|
||||||
|
|
||||||
function fixArrayTypes(row: Row, table: Table) {
|
function fixArrayTypes(row: Row, table: Table) {
|
||||||
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
||||||
if (
|
if (schema.type === FieldType.ARRAY && typeof row[fieldName] === "string") {
|
||||||
schema.type === FieldTypes.ARRAY &&
|
|
||||||
typeof row[fieldName] === "string"
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
row[fieldName] = JSON.parse(row[fieldName])
|
row[fieldName] = JSON.parse(row[fieldName])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -274,8 +271,8 @@ function isEditableColumn(column: FieldSchema) {
|
||||||
const isExternalAutoColumn =
|
const isExternalAutoColumn =
|
||||||
column.autocolumn &&
|
column.autocolumn &&
|
||||||
column.autoReason !== AutoReason.FOREIGN_KEY &&
|
column.autoReason !== AutoReason.FOREIGN_KEY &&
|
||||||
column.subtype !== AutoFieldSubTypes.AUTO_ID
|
column.subtype !== AutoFieldSubType.AUTO_ID
|
||||||
const isFormula = column.type === FieldTypes.FORMULA
|
const isFormula = column.type === FieldType.FORMULA
|
||||||
return !(isExternalAutoColumn || isFormula)
|
return !(isExternalAutoColumn || isFormula)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,11 +319,11 @@ export class ExternalRequest<T extends Operation> {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// parse floats/numbers
|
// parse floats/numbers
|
||||||
if (field.type === FieldTypes.NUMBER && !isNaN(parseFloat(row[key]))) {
|
if (field.type === FieldType.NUMBER && !isNaN(parseFloat(row[key]))) {
|
||||||
newRow[key] = parseFloat(row[key])
|
newRow[key] = parseFloat(row[key])
|
||||||
}
|
}
|
||||||
// if its not a link then just copy it over
|
// if its not a link then just copy it over
|
||||||
if (field.type !== FieldTypes.LINK) {
|
if (field.type !== FieldType.LINK) {
|
||||||
newRow[key] = row[key]
|
newRow[key] = row[key]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -532,7 +529,7 @@ export class ExternalRequest<T extends Operation> {
|
||||||
buildRelationships(table: Table): RelationshipsJson[] {
|
buildRelationships(table: Table): RelationshipsJson[] {
|
||||||
const relationships = []
|
const relationships = []
|
||||||
for (let [fieldName, field] of Object.entries(table.schema)) {
|
for (let [fieldName, field] of Object.entries(table.schema)) {
|
||||||
if (field.type !== FieldTypes.LINK) {
|
if (field.type !== FieldType.LINK) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
|
@ -586,7 +583,7 @@ export class ExternalRequest<T extends Operation> {
|
||||||
// we need this to work out if any relationships need removed
|
// we need this to work out if any relationships need removed
|
||||||
for (const field of Object.values(table.schema)) {
|
for (const field of Object.values(table.schema)) {
|
||||||
if (
|
if (
|
||||||
field.type !== FieldTypes.LINK ||
|
field.type !== FieldType.LINK ||
|
||||||
!field.fieldName ||
|
!field.fieldName ||
|
||||||
isOneSide(field)
|
isOneSide(field)
|
||||||
) {
|
) {
|
||||||
|
@ -730,15 +727,15 @@ export class ExternalRequest<T extends Operation> {
|
||||||
return Object.entries(table.schema)
|
return Object.entries(table.schema)
|
||||||
.filter(
|
.filter(
|
||||||
column =>
|
column =>
|
||||||
column[1].type !== FieldTypes.LINK &&
|
column[1].type !== FieldType.LINK &&
|
||||||
column[1].type !== FieldTypes.FORMULA &&
|
column[1].type !== FieldType.FORMULA &&
|
||||||
!existing.find((field: string) => field === column[0])
|
!existing.find((field: string) => field === column[0])
|
||||||
)
|
)
|
||||||
.map(column => `${table.name}.${column[0]}`)
|
.map(column => `${table.name}.${column[0]}`)
|
||||||
}
|
}
|
||||||
let fields = extractRealFields(table)
|
let fields = extractRealFields(table)
|
||||||
for (let field of Object.values(table.schema)) {
|
for (let field of Object.values(table.schema)) {
|
||||||
if (field.type !== FieldTypes.LINK || !includeRelations) {
|
if (field.type !== FieldType.LINK || !includeRelations) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import {
|
import {
|
||||||
breakExternalTableId,
|
breakExternalTableId,
|
||||||
breakRowIdField,
|
breakRowIdField,
|
||||||
|
@ -9,6 +8,7 @@ import {
|
||||||
RunConfig,
|
RunConfig,
|
||||||
} from "./ExternalRequest"
|
} from "./ExternalRequest"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Datasource,
|
Datasource,
|
||||||
IncludeRelationship,
|
IncludeRelationship,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -154,7 +154,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
// for a single row, there is probably a better way to do this with some smart multi-layer joins
|
// for a single row, there is probably a better way to do this with some smart multi-layer joins
|
||||||
for (let [fieldName, field] of Object.entries(table.schema)) {
|
for (let [fieldName, field] of Object.entries(table.schema)) {
|
||||||
if (
|
if (
|
||||||
field.type !== FieldTypes.LINK ||
|
field.type !== FieldType.LINK ||
|
||||||
!row[fieldName] ||
|
!row[fieldName] ||
|
||||||
row[fieldName].length === 0
|
row[fieldName].length === 0
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import {
|
||||||
inputProcessing,
|
inputProcessing,
|
||||||
outputProcessing,
|
outputProcessing,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import * as utils from "./utils"
|
import * as utils from "./utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { finaliseRow, updateRelatedFormula } from "./staticFormula"
|
import { finaliseRow, updateRelatedFormula } from "./staticFormula"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
PatchRowRequest,
|
PatchRowRequest,
|
||||||
PatchRowResponse,
|
PatchRowResponse,
|
||||||
|
@ -225,7 +225,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
// insert the link rows in the correct place throughout the main row
|
// insert the link rows in the correct place throughout the main row
|
||||||
for (let fieldName of Object.keys(table.schema)) {
|
for (let fieldName of Object.keys(table.schema)) {
|
||||||
let field = table.schema[fieldName]
|
let field = table.schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK) {
|
if (field.type === FieldType.LINK) {
|
||||||
// find the links that pertain to this field
|
// find the links that pertain to this field
|
||||||
const links = linkVals.filter(link => link.fieldName === fieldName)
|
const links = linkVals.filter(link => link.fieldName === fieldName)
|
||||||
// find the rows that the links state are linked to this field
|
// find the rows that the links state are linked to this field
|
||||||
|
|
|
@ -4,9 +4,15 @@ import {
|
||||||
processAutoColumn,
|
processAutoColumn,
|
||||||
processFormulas,
|
processFormulas,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { FieldTypes, FormulaTypes } from "../../../constants"
|
|
||||||
import { context, locks } from "@budibase/backend-core"
|
import { context, locks } from "@budibase/backend-core"
|
||||||
import { Table, Row, LockType, LockName } from "@budibase/types"
|
import {
|
||||||
|
Table,
|
||||||
|
Row,
|
||||||
|
LockType,
|
||||||
|
LockName,
|
||||||
|
FormulaType,
|
||||||
|
FieldType,
|
||||||
|
} from "@budibase/types"
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
|
@ -35,7 +41,7 @@ export async function updateRelatedFormula(
|
||||||
let relatedRows: Record<string, Row[]> = {}
|
let relatedRows: Record<string, Row[]> = {}
|
||||||
for (let [key, field] of Object.entries(enrichedRow)) {
|
for (let [key, field] of Object.entries(enrichedRow)) {
|
||||||
const columnDefinition = table.schema[key]
|
const columnDefinition = table.schema[key]
|
||||||
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
|
if (columnDefinition && columnDefinition.type === FieldType.LINK) {
|
||||||
const relatedTableId = columnDefinition.tableId!
|
const relatedTableId = columnDefinition.tableId!
|
||||||
if (!relatedRows[relatedTableId]) {
|
if (!relatedRows[relatedTableId]) {
|
||||||
relatedRows[relatedTableId] = []
|
relatedRows[relatedTableId] = []
|
||||||
|
@ -63,8 +69,8 @@ export async function updateRelatedFormula(
|
||||||
for (let column of Object.values(relatedTable!.schema)) {
|
for (let column of Object.values(relatedTable!.schema)) {
|
||||||
// needs updated in related rows
|
// needs updated in related rows
|
||||||
if (
|
if (
|
||||||
column.type === FieldTypes.FORMULA &&
|
column.type === FieldType.FORMULA &&
|
||||||
column.formulaType === FormulaTypes.STATIC
|
column.formulaType === FormulaType.STATIC
|
||||||
) {
|
) {
|
||||||
// re-enrich rows for all the related, don't update the related formula for them
|
// re-enrich rows for all the related, don't update the related formula for them
|
||||||
promises = promises.concat(
|
promises = promises.concat(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { FormulaTypes } from "../../../constants"
|
|
||||||
import { clearColumns } from "./utils"
|
import { clearColumns } from "./utils"
|
||||||
import { doesContainStrings } from "@budibase/string-templates"
|
import { doesContainStrings } from "@budibase/string-templates"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
@ -7,6 +6,7 @@ import uniq from "lodash/uniq"
|
||||||
import { updateAllFormulasInTable } from "../row/staticFormula"
|
import { updateAllFormulasInTable } from "../row/staticFormula"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FormulaType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
FormulaFieldMetadata,
|
FormulaFieldMetadata,
|
||||||
|
@ -17,10 +17,10 @@ import { isRelationshipColumn } from "../../../db/utils"
|
||||||
|
|
||||||
function isStaticFormula(
|
function isStaticFormula(
|
||||||
column: FieldSchema
|
column: FieldSchema
|
||||||
): column is FormulaFieldMetadata & { formulaType: FormulaTypes.STATIC } {
|
): column is FormulaFieldMetadata & { formulaType: FormulaType.STATIC } {
|
||||||
return (
|
return (
|
||||||
column.type === FieldType.FORMULA &&
|
column.type === FieldType.FORMULA &&
|
||||||
column.formulaType === FormulaTypes.STATIC
|
column.formulaType === FormulaType.STATIC
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { FieldType } from "@budibase/types"
|
import { AutoFieldSubType, FieldType } from "@budibase/types"
|
||||||
import { AutoFieldSubTypes } from "../../../../constants"
|
|
||||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||||
import { importToRows } from "../utils"
|
import { importToRows } from "../utils"
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ describe("utils", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "autoId",
|
name: "autoId",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
|
@ -69,7 +68,7 @@ describe("utils", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "autoId",
|
name: "autoId",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
|
|
|
@ -2,8 +2,6 @@ import { parse, isSchema, isRows } from "../../../utilities/schema"
|
||||||
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
GOOGLE_SHEETS_PRIMARY_KEY,
|
GOOGLE_SHEETS_PRIMARY_KEY,
|
||||||
USERS_TABLE_SCHEMA,
|
USERS_TABLE_SCHEMA,
|
||||||
SwitchableTypes,
|
SwitchableTypes,
|
||||||
|
@ -19,6 +17,7 @@ import { cloneDeep } from "lodash/fp"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import { events, context } from "@budibase/backend-core"
|
import { events, context } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
ContextUser,
|
ContextUser,
|
||||||
Datasource,
|
Datasource,
|
||||||
Row,
|
Row,
|
||||||
|
@ -106,7 +105,7 @@ export function makeSureTableUpToDate(table: Table, tableToSave: Table) {
|
||||||
for ([field, column] of Object.entries(table.schema)) {
|
for ([field, column] of Object.entries(table.schema)) {
|
||||||
if (
|
if (
|
||||||
column.autocolumn &&
|
column.autocolumn &&
|
||||||
column.subtype === AutoFieldSubTypes.AUTO_ID &&
|
column.subtype === AutoFieldSubType.AUTO_ID &&
|
||||||
tableToSave.schema[field]
|
tableToSave.schema[field]
|
||||||
) {
|
) {
|
||||||
const tableCol = tableToSave.schema[field] as NumberFieldMetadata
|
const tableCol = tableToSave.schema[field] as NumberFieldMetadata
|
||||||
|
@ -144,8 +143,8 @@ export async function importToRows(
|
||||||
? row[fieldName]
|
? row[fieldName]
|
||||||
: [row[fieldName]]
|
: [row[fieldName]]
|
||||||
if (
|
if (
|
||||||
(schema.type === FieldTypes.OPTIONS ||
|
(schema.type === FieldType.OPTIONS ||
|
||||||
schema.type === FieldTypes.ARRAY) &&
|
schema.type === FieldType.ARRAY) &&
|
||||||
row[fieldName]
|
row[fieldName]
|
||||||
) {
|
) {
|
||||||
let merged = [...schema.constraints!.inclusion!, ...rowVal]
|
let merged = [...schema.constraints!.inclusion!, ...rowVal]
|
||||||
|
@ -403,7 +402,7 @@ export async function checkForViewUpdates(
|
||||||
)
|
)
|
||||||
const newViewTemplate = viewTemplate(
|
const newViewTemplate = viewTemplate(
|
||||||
viewMetadata,
|
viewMetadata,
|
||||||
groupByField?.type === FieldTypes.ARRAY
|
groupByField?.type === FieldType.ARRAY
|
||||||
)
|
)
|
||||||
const viewName = view.name!
|
const viewName = view.name!
|
||||||
await saveView(null, viewName, newViewTemplate)
|
await saveView(null, viewName, newViewTemplate)
|
||||||
|
@ -434,7 +433,7 @@ export function generateJunctionTableName(
|
||||||
|
|
||||||
export function foreignKeyStructure(keyName: string, meta?: any) {
|
export function foreignKeyStructure(keyName: string, meta?: any) {
|
||||||
const structure: any = {
|
const structure: any = {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {},
|
constraints: {},
|
||||||
name: keyName,
|
name: keyName,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { fetchView } from "../row"
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import { DocumentType } from "../../../db/utils"
|
import { DocumentType } from "../../../db/utils"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Ctx,
|
Ctx,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
@ -37,7 +37,7 @@ export async function save(ctx: Ctx) {
|
||||||
(field: any) => field.name == viewToSave.groupBy
|
(field: any) => field.name == viewToSave.groupBy
|
||||||
)
|
)
|
||||||
|
|
||||||
const view = viewTemplate(viewToSave, groupByField?.type === FieldTypes.ARRAY)
|
const view = viewTemplate(viewToSave, groupByField?.type === FieldType.ARRAY)
|
||||||
const viewName = viewToSave.name
|
const viewName = viewToSave.name
|
||||||
|
|
||||||
if (!viewName) {
|
if (!viewName) {
|
||||||
|
|
|
@ -6,11 +6,11 @@ import * as setup from "./utilities"
|
||||||
import { context, InternalTable, roles, tenancy } from "@budibase/backend-core"
|
import { context, InternalTable, roles, tenancy } from "@budibase/backend-core"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
FieldTypeSubtypes,
|
FieldTypeSubtypes,
|
||||||
FormulaTypes,
|
FormulaType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
MonthlyQuotaName,
|
MonthlyQuotaName,
|
||||||
PermissionLevel,
|
PermissionLevel,
|
||||||
|
@ -192,7 +192,7 @@ describe.each([
|
||||||
"Row ID": {
|
"Row ID": {
|
||||||
name: "Row ID",
|
name: "Row ID",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -2032,7 +2032,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: "{{ links.0.name }}",
|
formula: "{{ links.0.name }}",
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2086,7 +2086,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: `{{ js "${js}"}}`,
|
formula: `{{ js "${js}"}}`,
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -2129,7 +2129,7 @@ describe.each([
|
||||||
name: "formula",
|
name: "formula",
|
||||||
type: FieldType.FORMULA,
|
type: FieldType.FORMULA,
|
||||||
formula: `{{ js "${js}"}}`,
|
formula: `{{ js "${js}"}}`,
|
||||||
formulaType: FormulaTypes.DYNAMIC,
|
formulaType: FormulaType.DYNAMIC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
FieldSubtype,
|
FieldSubtype,
|
||||||
FieldType,
|
FieldType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
@ -205,7 +205,7 @@ describe("/tables", () => {
|
||||||
autoId: {
|
autoId: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as rowController from "../../api/controllers/row"
|
import * as rowController from "../../api/controllers/row"
|
||||||
import * as tableController from "../../api/controllers/table"
|
import * as tableController from "../../api/controllers/table"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { buildCtx } from "./utils"
|
import { buildCtx } from "./utils"
|
||||||
import * as automationUtils from "../automationUtils"
|
import * as automationUtils from "../automationUtils"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
AutomationActionStepId,
|
AutomationActionStepId,
|
||||||
AutomationCustomIOType,
|
AutomationCustomIOType,
|
||||||
AutomationFeature,
|
AutomationFeature,
|
||||||
|
@ -115,7 +115,7 @@ function typeCoercion(filters: SearchFilters, table: Table) {
|
||||||
if (!column || typeof value !== "string") {
|
if (!column || typeof value !== "string") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (column.type === FieldTypes.NUMBER) {
|
if (column.type === FieldType.NUMBER) {
|
||||||
if (key === "oneOf") {
|
if (key === "oneOf") {
|
||||||
searchParam[property] = value
|
searchParam[property] = value
|
||||||
.split(",")
|
.split(",")
|
||||||
|
@ -148,11 +148,11 @@ export async function run({ inputs, appId }: AutomationStepInput) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const table = await getTable(appId, tableId)
|
const table = await getTable(appId, tableId)
|
||||||
let sortType = FieldTypes.STRING
|
let sortType = FieldType.STRING
|
||||||
if (table && table.schema && table.schema[sortColumn] && sortColumn) {
|
if (table && table.schema && table.schema[sortColumn] && sortColumn) {
|
||||||
const fieldType = table.schema[sortColumn].type
|
const fieldType = table.schema[sortColumn].type
|
||||||
sortType =
|
sortType =
|
||||||
fieldType === FieldTypes.NUMBER ? FieldTypes.NUMBER : FieldTypes.STRING
|
fieldType === FieldType.NUMBER ? FieldType.NUMBER : FieldType.STRING
|
||||||
}
|
}
|
||||||
const ctx: any = buildCtx(appId, null, {
|
const ctx: any = buildCtx(appId, null, {
|
||||||
params: {
|
params: {
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
import { constants, objectStore, roles } from "@budibase/backend-core"
|
import { constants, objectStore, roles } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
FieldType as FieldTypes,
|
FieldType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
export {
|
|
||||||
FieldType as FieldTypes,
|
|
||||||
RelationshipType,
|
|
||||||
AutoFieldSubTypes,
|
|
||||||
FormulaTypes,
|
|
||||||
} from "@budibase/types"
|
|
||||||
|
|
||||||
export enum FilterTypes {
|
export enum FilterTypes {
|
||||||
STRING = "string",
|
STRING = "string",
|
||||||
FUZZY = "fuzzy",
|
FUZZY = "fuzzy",
|
||||||
|
@ -36,14 +29,14 @@ export const NoEmptyFilterStrings = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export const CanSwitchTypes = [
|
export const CanSwitchTypes = [
|
||||||
[FieldTypes.JSON, FieldTypes.ARRAY],
|
[FieldType.JSON, FieldType.ARRAY],
|
||||||
[
|
[
|
||||||
FieldTypes.STRING,
|
FieldType.STRING,
|
||||||
FieldTypes.OPTIONS,
|
FieldType.OPTIONS,
|
||||||
FieldTypes.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
FieldTypes.BARCODEQR,
|
FieldType.BARCODEQR,
|
||||||
],
|
],
|
||||||
[FieldTypes.BOOLEAN, FieldTypes.NUMBER],
|
[FieldType.BOOLEAN, FieldType.NUMBER],
|
||||||
]
|
]
|
||||||
|
|
||||||
export const SwitchableTypes = CanSwitchTypes.reduce((prev, current) =>
|
export const SwitchableTypes = CanSwitchTypes.reduce((prev, current) =>
|
||||||
|
@ -86,9 +79,9 @@ export const USERS_TABLE_SCHEMA: Table = {
|
||||||
// TODO: ADMIN PANEL - when implemented this doesn't need to be carried out
|
// TODO: ADMIN PANEL - when implemented this doesn't need to be carried out
|
||||||
schema: {
|
schema: {
|
||||||
email: {
|
email: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
email: true,
|
email: true,
|
||||||
length: {
|
length: {
|
||||||
maximum: "",
|
maximum: "",
|
||||||
|
@ -99,34 +92,34 @@ export const USERS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
firstName: {
|
firstName: {
|
||||||
name: "firstName",
|
name: "firstName",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lastName: {
|
lastName: {
|
||||||
name: "lastName",
|
name: "lastName",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
roleId: {
|
roleId: {
|
||||||
name: "roleId",
|
name: "roleId",
|
||||||
type: FieldTypes.OPTIONS,
|
type: FieldType.OPTIONS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: Object.values(roles.BUILTIN_ROLE_IDS),
|
inclusion: Object.values(roles.BUILTIN_ROLE_IDS),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
name: "status",
|
name: "status",
|
||||||
type: FieldTypes.OPTIONS,
|
type: FieldType.OPTIONS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: Object.values(constants.UserStatus),
|
inclusion: Object.values(constants.UserStatus),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
DEFAULT_BB_DATASOURCE_ID,
|
DEFAULT_BB_DATASOURCE_ID,
|
||||||
DEFAULT_INVENTORY_TABLE_ID,
|
DEFAULT_INVENTORY_TABLE_ID,
|
||||||
DEFAULT_EMPLOYEE_TABLE_ID,
|
DEFAULT_EMPLOYEE_TABLE_ID,
|
||||||
|
@ -16,6 +14,7 @@ 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 {
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
FieldType,
|
FieldType,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
Row,
|
Row,
|
||||||
|
@ -40,7 +39,7 @@ function syncLastIds(table: Table, rowCount: number) {
|
||||||
if (
|
if (
|
||||||
entry.autocolumn &&
|
entry.autocolumn &&
|
||||||
entry.type === FieldType.NUMBER &&
|
entry.type === FieldType.NUMBER &&
|
||||||
entry.subtype == AutoFieldSubTypes.AUTO_ID
|
entry.subtype == AutoFieldSubType.AUTO_ID
|
||||||
) {
|
) {
|
||||||
entry.lastID = rowCount
|
entry.lastID = rowCount
|
||||||
}
|
}
|
||||||
|
@ -58,12 +57,12 @@ async function tableImport(table: Table, data: Row[]) {
|
||||||
const AUTO_COLUMNS: TableSchema = {
|
const AUTO_COLUMNS: TableSchema = {
|
||||||
"Created At": {
|
"Created At": {
|
||||||
name: "Created At",
|
name: "Created At",
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
subtype: AutoFieldSubTypes.CREATED_AT,
|
subtype: AutoFieldSubType.CREATED_AT,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -74,12 +73,12 @@ const AUTO_COLUMNS: TableSchema = {
|
||||||
},
|
},
|
||||||
"Updated At": {
|
"Updated At": {
|
||||||
name: "Updated At",
|
name: "Updated At",
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
subtype: AutoFieldSubTypes.UPDATED_AT,
|
subtype: AutoFieldSubType.UPDATED_AT,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -101,12 +100,12 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Item ID": {
|
"Item ID": {
|
||||||
name: "Item ID",
|
name: "Item ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -115,9 +114,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Item Name": {
|
"Item Name": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -128,9 +127,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
name: "Item Name",
|
name: "Item Name",
|
||||||
},
|
},
|
||||||
"Item Tags": {
|
"Item Tags": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -140,9 +139,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -150,9 +149,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
Status: {
|
Status: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -162,18 +161,18 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
SKU: {
|
SKU: {
|
||||||
type: FieldTypes.BARCODEQR,
|
type: FieldType.BARCODEQR,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "SKU",
|
name: "SKU",
|
||||||
},
|
},
|
||||||
"Purchase Date": {
|
"Purchase Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -185,9 +184,9 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Purchase Price": {
|
"Purchase Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: null,
|
greaterThanOrEqualTo: null,
|
||||||
|
@ -211,75 +210,75 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"First Name": {
|
"First Name": {
|
||||||
name: "First Name",
|
name: "First Name",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Last Name": {
|
"Last Name": {
|
||||||
name: "Last Name",
|
name: "Last Name",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Email: {
|
Email: {
|
||||||
name: "Email",
|
name: "Email",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Address: {
|
Address: {
|
||||||
name: "Address",
|
name: "Address",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
City: {
|
City: {
|
||||||
name: "City",
|
name: "City",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Postcode: {
|
Postcode: {
|
||||||
name: "Postcode",
|
name: "Postcode",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Phone: {
|
Phone: {
|
||||||
name: "Phone",
|
name: "Phone",
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"EMPLOYEE ID": {
|
"EMPLOYEE ID": {
|
||||||
name: "EMPLOYEE ID",
|
name: "EMPLOYEE ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -288,9 +287,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Employee Level": {
|
"Employee Level": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
inclusion: ["Manager", "Junior", "Senior", "Apprentice", "Contractor"],
|
inclusion: ["Manager", "Junior", "Senior", "Apprentice", "Contractor"],
|
||||||
},
|
},
|
||||||
|
@ -298,18 +297,18 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
"Badge Photo": {
|
"Badge Photo": {
|
||||||
type: FieldTypes.ATTACHMENT,
|
type: FieldType.ATTACHMENT,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "Badge Photo",
|
name: "Badge Photo",
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Jobs: {
|
Jobs: {
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
fieldName: "Assigned",
|
fieldName: "Assigned",
|
||||||
|
@ -318,9 +317,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
tableId: DEFAULT_JOBS_TABLE_ID,
|
tableId: DEFAULT_JOBS_TABLE_ID,
|
||||||
},
|
},
|
||||||
"Start Date": {
|
"Start Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -332,9 +331,9 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"End Date": {
|
"End Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -359,12 +358,12 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Job ID": {
|
"Job ID": {
|
||||||
name: "Job ID",
|
name: "Job ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -373,9 +372,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Quote Date": {
|
"Quote Date": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
|
@ -389,9 +388,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Quote Price": {
|
"Quote Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -403,9 +402,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Quote Price",
|
name: "Quote Price",
|
||||||
},
|
},
|
||||||
"Works Start": {
|
"Works Start": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -417,9 +416,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
Address: {
|
Address: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -427,9 +426,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Customer Name": {
|
"Customer Name": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -438,9 +437,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Customer Name",
|
name: "Customer Name",
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -448,9 +447,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Customer Phone": {
|
"Customer Phone": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -459,9 +458,9 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
name: "Customer Phone",
|
name: "Customer Phone",
|
||||||
},
|
},
|
||||||
"Customer Email": {
|
"Customer Email": {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {
|
length: {
|
||||||
maximum: null,
|
maximum: null,
|
||||||
},
|
},
|
||||||
|
@ -471,14 +470,14 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
Assigned: {
|
Assigned: {
|
||||||
name: "Assigned",
|
name: "Assigned",
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.LINK,
|
||||||
tableId: DEFAULT_EMPLOYEE_TABLE_ID,
|
tableId: DEFAULT_EMPLOYEE_TABLE_ID,
|
||||||
fieldName: "Jobs",
|
fieldName: "Jobs",
|
||||||
relationshipType: RelationshipType.MANY_TO_MANY,
|
relationshipType: RelationshipType.MANY_TO_MANY,
|
||||||
// sortable: true,
|
// sortable: true,
|
||||||
},
|
},
|
||||||
"Works End": {
|
"Works End": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -492,7 +491,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Updated Price": {
|
"Updated Price": {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -518,12 +517,12 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
schema: {
|
schema: {
|
||||||
"Expense ID": {
|
"Expense ID": {
|
||||||
name: "Expense ID",
|
name: "Expense ID",
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
icon: "ri-magic-line",
|
icon: "ri-magic-line",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: false,
|
presence: false,
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: "",
|
greaterThanOrEqualTo: "",
|
||||||
|
@ -532,9 +531,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Expense Tags": {
|
"Expense Tags": {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -554,9 +553,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
Cost: {
|
Cost: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
presence: {
|
presence: {
|
||||||
allowEmpty: false,
|
allowEmpty: false,
|
||||||
},
|
},
|
||||||
|
@ -568,9 +567,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
name: "Cost",
|
name: "Cost",
|
||||||
},
|
},
|
||||||
Notes: {
|
Notes: {
|
||||||
type: FieldTypes.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
|
@ -578,9 +577,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
useRichText: null,
|
useRichText: null,
|
||||||
},
|
},
|
||||||
"Payment Due": {
|
"Payment Due": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -592,9 +591,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
"Date Paid": {
|
"Date Paid": {
|
||||||
type: FieldTypes.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.STRING,
|
type: FieldType.STRING,
|
||||||
length: {},
|
length: {},
|
||||||
presence: false,
|
presence: false,
|
||||||
datetime: {
|
datetime: {
|
||||||
|
@ -606,9 +605,9 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
Attachment: {
|
Attachment: {
|
||||||
type: FieldTypes.ATTACHMENT,
|
type: FieldType.ATTACHMENT,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldTypes.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { IncludeDocs, getLinkDocuments } from "./linkUtils"
|
import { IncludeDocs, getLinkDocuments } from "./linkUtils"
|
||||||
import { InternalTables, getUserMetadataParams } from "../utils"
|
import { InternalTables, getUserMetadataParams } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { context, logging } from "@budibase/backend-core"
|
import { context, logging } from "@budibase/backend-core"
|
||||||
import LinkDocument from "./LinkDocument"
|
import LinkDocument from "./LinkDocument"
|
||||||
import {
|
import {
|
||||||
|
@ -62,7 +61,7 @@ class LinkController {
|
||||||
}
|
}
|
||||||
for (let fieldName of Object.keys(table.schema)) {
|
for (let fieldName of Object.keys(table.schema)) {
|
||||||
const { type } = table.schema[fieldName]
|
const { type } = table.schema[fieldName]
|
||||||
if (type === FieldTypes.LINK) {
|
if (type === FieldType.LINK) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,7 @@ class LinkController {
|
||||||
validateTable(table: Table) {
|
validateTable(table: Table) {
|
||||||
const usedAlready = []
|
const usedAlready = []
|
||||||
for (let schema of Object.values(table.schema)) {
|
for (let schema of Object.values(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.LINK) {
|
if (schema.type !== FieldType.LINK) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const unique = schema.tableId! + schema?.fieldName
|
const unique = schema.tableId! + schema?.fieldName
|
||||||
|
@ -172,7 +171,7 @@ class LinkController {
|
||||||
// get the links this row wants to make
|
// get the links this row wants to make
|
||||||
const rowField = row[fieldName]
|
const rowField = row[fieldName]
|
||||||
const field = table.schema[fieldName]
|
const field = table.schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK && rowField != null) {
|
if (field.type === FieldType.LINK && rowField != null) {
|
||||||
// check which links actual pertain to the update in this row
|
// check which links actual pertain to the update in this row
|
||||||
const thisFieldLinkDocs = linkDocs.filter(
|
const thisFieldLinkDocs = linkDocs.filter(
|
||||||
linkDoc =>
|
linkDoc =>
|
||||||
|
@ -353,7 +352,7 @@ class LinkController {
|
||||||
const schema = table.schema
|
const schema = table.schema
|
||||||
for (let fieldName of Object.keys(schema)) {
|
for (let fieldName of Object.keys(schema)) {
|
||||||
const field = schema[fieldName]
|
const field = schema[fieldName]
|
||||||
if (field.type === FieldTypes.LINK && field.fieldName) {
|
if (field.type === FieldType.LINK && field.fieldName) {
|
||||||
// handle this in a separate try catch, want
|
// handle this in a separate try catch, want
|
||||||
// the put to bubble up as an error, if can't update
|
// the put to bubble up as an error, if can't update
|
||||||
// table for some reason
|
// table for some reason
|
||||||
|
@ -366,7 +365,7 @@ class LinkController {
|
||||||
}
|
}
|
||||||
const fields = this.handleRelationshipType(field, {
|
const fields = this.handleRelationshipType(field, {
|
||||||
name: field.fieldName,
|
name: field.fieldName,
|
||||||
type: FieldTypes.LINK,
|
type: FieldType.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,
|
||||||
|
@ -413,10 +412,7 @@ class LinkController {
|
||||||
for (let fieldName of Object.keys(oldTable?.schema || {})) {
|
for (let fieldName of Object.keys(oldTable?.schema || {})) {
|
||||||
const field = oldTable?.schema[fieldName] as FieldSchema
|
const field = oldTable?.schema[fieldName] as FieldSchema
|
||||||
// this field has been removed from the table schema
|
// this field has been removed from the table schema
|
||||||
if (
|
if (field.type === FieldType.LINK && newTable.schema[fieldName] == null) {
|
||||||
field.type === FieldTypes.LINK &&
|
|
||||||
newTable.schema[fieldName] == null
|
|
||||||
) {
|
|
||||||
await this.removeFieldFromTable(fieldName)
|
await this.removeFieldFromTable(fieldName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,7 +433,7 @@ class LinkController {
|
||||||
for (let fieldName of Object.keys(schema)) {
|
for (let fieldName of Object.keys(schema)) {
|
||||||
const field = schema[fieldName]
|
const field = schema[fieldName]
|
||||||
try {
|
try {
|
||||||
if (field.type === FieldTypes.LINK && field.fieldName) {
|
if (field.type === FieldType.LINK && field.fieldName) {
|
||||||
const linkedTable = await this._db.get<Table>(field.tableId)
|
const linkedTable = await this._db.get<Table>(field.tableId)
|
||||||
delete linkedTable.schema[field.fieldName]
|
delete linkedTable.schema[field.fieldName]
|
||||||
field.tableRev = (await this._db.put(linkedTable)).rev
|
field.tableRev = (await this._db.put(linkedTable)).rev
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { generateLinkID } from "../utils"
|
import { generateLinkID } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
import { FieldType, LinkDocument } from "@budibase/types"
|
||||||
import { LinkDocument } from "@budibase/types"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new link document structure which can be put to the database. It is important to
|
* Creates a new link document structure which can be put to the database. It is important to
|
||||||
|
@ -43,7 +42,7 @@ class LinkDocumentImpl implements LinkDocument {
|
||||||
fieldName1,
|
fieldName1,
|
||||||
fieldName2
|
fieldName2
|
||||||
)
|
)
|
||||||
this.type = FieldTypes.LINK
|
this.type = FieldType.LINK
|
||||||
this.doc1 = {
|
this.doc1 = {
|
||||||
tableId: tableId1,
|
tableId: tableId1,
|
||||||
fieldName: fieldName1,
|
fieldName: fieldName1,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ViewName, getQueryIndex, isRelationshipColumn } from "../utils"
|
import { ViewName, getQueryIndex, isRelationshipColumn } from "../utils"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
import { createLinkView } from "../views/staticViews"
|
import { createLinkView } from "../views/staticViews"
|
||||||
import { context, logging } from "@budibase/backend-core"
|
import { context, logging } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DatabaseQueryOpts,
|
DatabaseQueryOpts,
|
||||||
LinkDocument,
|
LinkDocument,
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
|
@ -131,11 +131,11 @@ export async function getLinkedTable(id: string, tables: Table[]) {
|
||||||
export function getRelatedTableForField(table: Table, fieldName: string) {
|
export function getRelatedTableForField(table: Table, fieldName: string) {
|
||||||
// look to see if its on the table, straight in the schema
|
// look to see if its on the table, straight in the schema
|
||||||
const field = table.schema[fieldName]
|
const field = table.schema[fieldName]
|
||||||
if (field?.type === FieldTypes.LINK) {
|
if (field?.type === FieldType.LINK) {
|
||||||
return field.tableId
|
return field.tableId
|
||||||
}
|
}
|
||||||
for (let column of Object.values(table.schema)) {
|
for (let column of Object.values(table.schema)) {
|
||||||
if (column.type === FieldTypes.LINK && column.fieldName === fieldName) {
|
if (column.type === FieldType.LINK && column.fieldName === fieldName) {
|
||||||
return column.tableId
|
return column.tableId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,57 @@
|
||||||
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
import TestConfig from "../../tests/utilities/TestConfiguration"
|
||||||
const {
|
import {
|
||||||
basicRow,
|
|
||||||
basicLinkedRow,
|
basicLinkedRow,
|
||||||
|
basicRow,
|
||||||
basicTable,
|
basicTable,
|
||||||
} = require("../../tests/utilities/structures")
|
} from "../../tests/utilities/structures"
|
||||||
const LinkController = require("../linkedRows/LinkController").default
|
import LinkController from "../linkedRows/LinkController"
|
||||||
const { context } = require("@budibase/backend-core")
|
import { context } from "@budibase/backend-core"
|
||||||
const { RelationshipType } = require("../../constants")
|
import {
|
||||||
const { cloneDeep } = require("lodash/fp")
|
FieldType,
|
||||||
|
ManyToManyRelationshipFieldMetadata,
|
||||||
|
ManyToOneRelationshipFieldMetadata,
|
||||||
|
OneToManyRelationshipFieldMetadata,
|
||||||
|
RelationshipFieldMetadata,
|
||||||
|
RelationshipType,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { cloneDeep } from "lodash"
|
||||||
|
|
||||||
|
const baseColumn = {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
fieldName: "",
|
||||||
|
tableId: "",
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockManyToManyColumn(): ManyToManyRelationshipFieldMetadata {
|
||||||
|
return <ManyToManyRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
through: "",
|
||||||
|
throughFrom: "",
|
||||||
|
throughTo: "",
|
||||||
|
relationshipType: RelationshipType.MANY_TO_MANY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockManyToOneColumn(): ManyToOneRelationshipFieldMetadata {
|
||||||
|
return <ManyToOneRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
relationshipType: RelationshipType.MANY_TO_ONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockOneToManyColumn(): OneToManyRelationshipFieldMetadata {
|
||||||
|
return <OneToManyRelationshipFieldMetadata>{
|
||||||
|
...baseColumn,
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("test the link controller", () => {
|
describe("test the link controller", () => {
|
||||||
let config = new TestConfig()
|
let config = new TestConfig()
|
||||||
let table1, table2, appId
|
let table1: Table, table2: Table, appId: string
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const app = await config.init()
|
const app = await config.init()
|
||||||
|
@ -30,9 +70,18 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
afterAll(config.end)
|
afterAll(config.end)
|
||||||
|
|
||||||
async function createLinkController(table, row = null, oldTable = null) {
|
async function createLinkController(
|
||||||
|
table: Table,
|
||||||
|
row?: Row,
|
||||||
|
oldTable?: Table
|
||||||
|
) {
|
||||||
return context.doInAppContext(appId, () => {
|
return context.doInAppContext(appId, () => {
|
||||||
const linkConfig = {
|
const linkConfig: {
|
||||||
|
tableId?: string
|
||||||
|
table: Table
|
||||||
|
row?: Row
|
||||||
|
oldTable?: Table
|
||||||
|
} = {
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
table,
|
table,
|
||||||
}
|
}
|
||||||
|
@ -47,11 +96,11 @@ describe("test the link controller", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) {
|
async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) {
|
||||||
const row = await config.createRow(basicRow(t2._id))
|
const row = await config.createRow(basicRow(t2._id!))
|
||||||
const { _id } = await config.createRow(
|
const { _id } = await config.createRow(
|
||||||
basicLinkedRow(t1._id, row._id, linkField)
|
basicLinkedRow(t1._id!, row._id!, linkField)
|
||||||
)
|
)
|
||||||
return config.getRow(t1._id, _id)
|
return config.getRow(t1._id!, _id!)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should be able to confirm if two table schemas are equal", async () => {
|
it("should be able to confirm if two table schemas are equal", async () => {
|
||||||
|
@ -71,6 +120,7 @@ describe("test the link controller", () => {
|
||||||
it("should be able to check the relationship types across two fields", async () => {
|
it("should be able to check the relationship types across two fields", async () => {
|
||||||
const controller = await createLinkController(table1)
|
const controller = await createLinkController(table1)
|
||||||
// empty case
|
// empty case
|
||||||
|
//@ts-ignore
|
||||||
let output = controller.handleRelationshipType({}, {})
|
let output = controller.handleRelationshipType({}, {})
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
|
@ -79,8 +129,8 @@ describe("test the link controller", () => {
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(
|
||||||
{ relationshipType: RelationshipType.MANY_TO_MANY },
|
mockManyToManyColumn(),
|
||||||
{}
|
{} as any
|
||||||
)
|
)
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
|
@ -88,20 +138,14 @@ describe("test the link controller", () => {
|
||||||
expect(output.linkerField.relationshipType).toEqual(
|
expect(output.linkerField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_MANY
|
RelationshipType.MANY_TO_MANY
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(mockManyToOneColumn(), {} as any)
|
||||||
{ relationshipType: RelationshipType.MANY_TO_ONE },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.ONE_TO_MANY
|
RelationshipType.ONE_TO_MANY
|
||||||
)
|
)
|
||||||
expect(output.linkerField.relationshipType).toEqual(
|
expect(output.linkerField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_ONE
|
RelationshipType.MANY_TO_ONE
|
||||||
)
|
)
|
||||||
output = controller.handleRelationshipType(
|
output = controller.handleRelationshipType(mockOneToManyColumn(), {} as any)
|
||||||
{ relationshipType: RelationshipType.ONE_TO_MANY },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
expect(output.linkedField.relationshipType).toEqual(
|
expect(output.linkedField.relationshipType).toEqual(
|
||||||
RelationshipType.MANY_TO_ONE
|
RelationshipType.MANY_TO_ONE
|
||||||
)
|
)
|
||||||
|
@ -115,16 +159,16 @@ describe("test the link controller", () => {
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
// get initial count
|
// get initial count
|
||||||
const beforeLinks = await controller.getRowLinkDocs(row._id)
|
const beforeLinks = await controller.getRowLinkDocs(row._id!)
|
||||||
await controller.rowDeleted()
|
await controller.rowDeleted()
|
||||||
let afterLinks = await controller.getRowLinkDocs(row._id)
|
let afterLinks = await controller.getRowLinkDocs(row._id!)
|
||||||
expect(beforeLinks.length).toEqual(1)
|
expect(beforeLinks.length).toEqual(1)
|
||||||
expect(afterLinks.length).toEqual(0)
|
expect(afterLinks.length).toEqual(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("shouldn't throw an error when deleting a row with no links", async () => {
|
it("shouldn't throw an error when deleting a row with no links", async () => {
|
||||||
const row = await config.createRow(basicRow(table1._id))
|
const row = await config.createRow(basicRow(table1._id!))
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let error
|
let error
|
||||||
|
@ -142,12 +186,13 @@ describe("test the link controller", () => {
|
||||||
const copyTable = {
|
const copyTable = {
|
||||||
...table1,
|
...table1,
|
||||||
}
|
}
|
||||||
|
//@ts-ignore
|
||||||
copyTable.schema.otherTableLink = {
|
copyTable.schema.otherTableLink = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table2._id,
|
tableId: table2._id!,
|
||||||
}
|
}
|
||||||
let error
|
let error: any
|
||||||
try {
|
try {
|
||||||
controller.validateTable(copyTable)
|
controller.validateTable(copyTable)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -166,7 +211,7 @@ describe("test the link controller", () => {
|
||||||
const controller = await createLinkController(table1, row)
|
const controller = await createLinkController(table1, row)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
await controller.rowSaved()
|
await controller.rowSaved()
|
||||||
let links = await controller.getRowLinkDocs(row._id)
|
let links = await controller.getRowLinkDocs(row._id!)
|
||||||
expect(links.length).toEqual(0)
|
expect(links.length).toEqual(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -186,7 +231,7 @@ describe("test the link controller", () => {
|
||||||
it("should be able to remove a linked field from a table", async () => {
|
it("should be able to remove a linked field from a table", async () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
await createLinkedRow("link2")
|
await createLinkedRow("link2")
|
||||||
const controller = await createLinkController(table1, null, table1)
|
const controller = await createLinkController(table1, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let before = await controller.getTableLinkDocs()
|
let before = await controller.getTableLinkDocs()
|
||||||
await controller.removeFieldFromTable("link")
|
await controller.removeFieldFromTable("link")
|
||||||
|
@ -199,7 +244,8 @@ 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 = RelationshipType.MANY_TO_ONE
|
const linkSchema = update.schema.link as ManyToOneRelationshipFieldMetadata
|
||||||
|
linkSchema.relationshipType = RelationshipType.MANY_TO_ONE
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
const controller = await createLinkController(update)
|
const controller = await createLinkController(update)
|
||||||
|
@ -215,7 +261,7 @@ describe("test the link controller", () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
const newTable = cloneDeep(table1)
|
const newTable = cloneDeep(table1)
|
||||||
delete newTable.schema.link
|
delete newTable.schema.link
|
||||||
const controller = await createLinkController(newTable, null, table1)
|
const controller = await createLinkController(newTable, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
await controller.tableUpdated()
|
await controller.tableUpdated()
|
||||||
const links = await controller.getTableLinkDocs()
|
const links = await controller.getTableLinkDocs()
|
||||||
|
@ -235,7 +281,7 @@ describe("test the link controller", () => {
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
// create another row to initiate the error
|
// create another row to initiate the error
|
||||||
await config.createRow(basicLinkedRow(row.tableId, row.link[0]))
|
await config.createRow(basicLinkedRow(row.tableId!, row.link[0]))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err
|
error = err
|
||||||
}
|
}
|
||||||
|
@ -245,7 +291,7 @@ describe("test the link controller", () => {
|
||||||
it("should not error if a link being created doesn't exist", async () => {
|
it("should not error if a link being created doesn't exist", async () => {
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
await config.createRow(basicLinkedRow(table1._id, "invalid"))
|
await config.createRow(basicLinkedRow(table1._id!, "invalid"))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err
|
error = err
|
||||||
}
|
}
|
||||||
|
@ -255,10 +301,11 @@ describe("test the link controller", () => {
|
||||||
it("make sure auto column goes onto other row too", async () => {
|
it("make sure auto column goes onto other row too", async () => {
|
||||||
const table = await config.createTable()
|
const table = await config.createTable()
|
||||||
const tableCfg = basicTable()
|
const tableCfg = basicTable()
|
||||||
|
//@ts-ignore
|
||||||
tableCfg.schema.link = {
|
tableCfg.schema.link = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table._id,
|
tableId: table._id!,
|
||||||
name: "link",
|
name: "link",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
}
|
}
|
||||||
|
@ -269,10 +316,11 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
it("should be able to link to self", async () => {
|
it("should be able to link to self", async () => {
|
||||||
const table = await config.createTable()
|
const table = await config.createTable()
|
||||||
|
//@ts-ignore
|
||||||
table.schema.link = {
|
table.schema.link = {
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
fieldName: "link",
|
fieldName: "link",
|
||||||
tableId: table._id,
|
tableId: table._id!,
|
||||||
name: "link",
|
name: "link",
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
}
|
}
|
||||||
|
@ -282,8 +330,9 @@ describe("test the link controller", () => {
|
||||||
it("should be able to remove a linked field from a table, even if the linked table does not exist", async () => {
|
it("should be able to remove a linked field from a table, even if the linked table does not exist", async () => {
|
||||||
await createLinkedRow()
|
await createLinkedRow()
|
||||||
await createLinkedRow("link2")
|
await createLinkedRow("link2")
|
||||||
table1.schema["link"].tableId = "not_found"
|
const linkSchema = table1.schema["link"] as RelationshipFieldMetadata
|
||||||
const controller = await createLinkController(table1, null, table1)
|
linkSchema.tableId = "not_found"
|
||||||
|
const controller = await createLinkController(table1, undefined, table1)
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
let before = await controller.getTableLinkDocs()
|
let before = await controller.getTableLinkDocs()
|
||||||
await controller.removeFieldFromTable("link")
|
await controller.removeFieldFromTable("link")
|
|
@ -1,14 +1,15 @@
|
||||||
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
import TestConfig from "../../tests/utilities/TestConfiguration"
|
||||||
const { basicTable } = require("../../tests/utilities/structures")
|
import { basicTable } from "../../tests/utilities/structures"
|
||||||
const linkUtils = require("../linkedRows/linkUtils")
|
import * as linkUtils from "../linkedRows/linkUtils"
|
||||||
const { context } = require("@budibase/backend-core")
|
import { context } from "@budibase/backend-core"
|
||||||
|
import { FieldType, RelationshipType, Table } from "@budibase/types"
|
||||||
|
|
||||||
describe("test link functionality", () => {
|
describe("test link functionality", () => {
|
||||||
const config = new TestConfig()
|
const config = new TestConfig()
|
||||||
let appId
|
let appId: string
|
||||||
|
|
||||||
describe("getLinkedTable", () => {
|
describe("getLinkedTable", () => {
|
||||||
let table
|
let table: Table
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const app = await config.init()
|
const app = await config.init()
|
||||||
appId = app.appId
|
appId = app.appId
|
||||||
|
@ -17,15 +18,15 @@ describe("test link functionality", () => {
|
||||||
|
|
||||||
it("should be able to retrieve a linked table from a list", async () => {
|
it("should be able to retrieve a linked table from a list", async () => {
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const retrieved = await linkUtils.getLinkedTable(table._id, [table])
|
const retrieved = await linkUtils.getLinkedTable(table._id!, [table])
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to retrieve a table from DB and update list", async () => {
|
it("should be able to retrieve a table from DB and update list", async () => {
|
||||||
const tables = []
|
const tables: Table[] = []
|
||||||
await context.doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const retrieved = await linkUtils.getLinkedTable(table._id, tables)
|
const retrieved = await linkUtils.getLinkedTable(table._id!, tables)
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
expect(tables[0]).toBeDefined()
|
expect(tables[0]).toBeDefined()
|
||||||
})
|
})
|
||||||
|
@ -35,9 +36,11 @@ describe("test link functionality", () => {
|
||||||
describe("getRelatedTableForField", () => {
|
describe("getRelatedTableForField", () => {
|
||||||
let link = basicTable()
|
let link = basicTable()
|
||||||
link.schema.link = {
|
link.schema.link = {
|
||||||
|
name: "link",
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
fieldName: "otherLink",
|
fieldName: "otherLink",
|
||||||
tableId: "tableID",
|
tableId: "tableID",
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should get the field from the table directly", () => {
|
it("should get the field from the table directly", () => {
|
|
@ -1,6 +1,7 @@
|
||||||
import newid from "./newid"
|
import newid from "./newid"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
RelationshipFieldMetadata,
|
RelationshipFieldMetadata,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
DatabaseQueryOpts,
|
DatabaseQueryOpts,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
|
|
||||||
export { DocumentType, VirtualDocumentType } from "@budibase/types"
|
export { DocumentType, VirtualDocumentType } from "@budibase/types"
|
||||||
|
|
||||||
|
@ -315,5 +315,5 @@ export function extractViewInfoFromID(viewId: string) {
|
||||||
export function isRelationshipColumn(
|
export function isRelationshipColumn(
|
||||||
column: FieldSchema
|
column: FieldSchema
|
||||||
): column is RelationshipFieldMetadata {
|
): column is RelationshipFieldMetadata {
|
||||||
return column.type === FieldTypes.LINK
|
return column.type === FieldType.LINK
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Knex, knex } from "knex"
|
import { Knex, knex } from "knex"
|
||||||
import {
|
import {
|
||||||
|
RelationshipType,
|
||||||
FieldSubtype,
|
FieldSubtype,
|
||||||
NumberFieldMetadata,
|
NumberFieldMetadata,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -11,7 +12,6 @@ import {
|
||||||
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 { RelationshipType } from "../../constants"
|
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
function isIgnoredType(type: FieldType) {
|
function isIgnoredType(type: FieldType) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
DatasourceFieldType,
|
DatasourceFieldType,
|
||||||
Integration,
|
Integration,
|
||||||
Operation,
|
Operation,
|
||||||
|
@ -21,7 +22,6 @@ import {
|
||||||
SqlClient,
|
SqlClient,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import Sql from "./base/sql"
|
import Sql from "./base/sql"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
import {
|
import {
|
||||||
BindParameters,
|
BindParameters,
|
||||||
Connection,
|
Connection,
|
||||||
|
@ -302,7 +302,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.isBooleanType(oracleColumn)) {
|
if (this.isBooleanType(oracleColumn)) {
|
||||||
fieldSchema.type = FieldTypes.BOOLEAN
|
fieldSchema.type = FieldType.BOOLEAN
|
||||||
}
|
}
|
||||||
|
|
||||||
table.schema[columnName] = fieldSchema
|
table.schema[columnName] = fieldSchema
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { CouchFindOptions, Table, Row } from "@budibase/types"
|
import { FieldType, CouchFindOptions, Table, Row } from "@budibase/types"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import { DocumentType, SEPARATOR } from "../../../db/utils"
|
import { DocumentType, SEPARATOR } from "../../../db/utils"
|
||||||
import { FieldTypes } from "../../../constants"
|
|
||||||
|
|
||||||
// default limit - seems to work well for performance
|
// default limit - seems to work well for performance
|
||||||
export const FIND_LIMIT = 25
|
export const FIND_LIMIT = 25
|
||||||
|
@ -31,7 +30,7 @@ export async function getRowsWithAttachments(appId: string, table: Table) {
|
||||||
const db = dbCore.getDB(appId)
|
const db = dbCore.getDB(appId)
|
||||||
const attachmentCols: string[] = []
|
const attachmentCols: string[] = []
|
||||||
for (let [key, column] of Object.entries(table.schema)) {
|
for (let [key, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldTypes.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENT) {
|
||||||
attachmentCols.push(key)
|
attachmentCols.push(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
FieldType,
|
FieldType,
|
||||||
Table,
|
Table,
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||||
|
@ -117,7 +117,7 @@ describe("sdk >> rows >> internal", () => {
|
||||||
id: {
|
id: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
lastID: 0,
|
lastID: 0,
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,7 @@ describe("sdk >> rows >> internal", () => {
|
||||||
id: {
|
id: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
subtype: AutoFieldSubTypes.AUTO_ID,
|
subtype: AutoFieldSubType.AUTO_ID,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
lastID: 0,
|
lastID: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import cloneDeep from "lodash/cloneDeep"
|
import cloneDeep from "lodash/cloneDeep"
|
||||||
import validateJs from "validate.js"
|
import validateJs from "validate.js"
|
||||||
import { Row, Table, TableSchema } from "@budibase/types"
|
import { FieldType, Row, Table, TableSchema } from "@budibase/types"
|
||||||
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 "../.."
|
||||||
|
@ -22,7 +21,7 @@ export function cleanExportRows(
|
||||||
let cleanRows = [...rows]
|
let cleanRows = [...rows]
|
||||||
|
|
||||||
const relationships = Object.entries(schema)
|
const relationships = Object.entries(schema)
|
||||||
.filter((entry: any[]) => entry[1].type === FieldTypes.LINK)
|
.filter((entry: any[]) => entry[1].type === FieldType.LINK)
|
||||||
.map(entry => entry[0])
|
.map(entry => entry[0])
|
||||||
|
|
||||||
relationships.forEach(column => {
|
relationships.forEach(column => {
|
||||||
|
@ -88,17 +87,17 @@ export async function validate({
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// formulas shouldn't validated, data will be deleted anyway
|
// formulas shouldn't validated, data will be deleted anyway
|
||||||
if (type === FieldTypes.FORMULA || column.autocolumn) {
|
if (type === FieldType.FORMULA || column.autocolumn) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// special case for options, need to always allow unselected (empty)
|
// special case for options, need to always allow unselected (empty)
|
||||||
if (type === FieldTypes.OPTIONS && constraints?.inclusion) {
|
if (type === FieldType.OPTIONS && constraints?.inclusion) {
|
||||||
constraints.inclusion.push(null as any, "")
|
constraints.inclusion.push(null as any, "")
|
||||||
}
|
}
|
||||||
let res
|
let res
|
||||||
|
|
||||||
// Validate.js doesn't seem to handle array
|
// Validate.js doesn't seem to handle array
|
||||||
if (type === FieldTypes.ARRAY && row[fieldName]) {
|
if (type === FieldType.ARRAY && row[fieldName]) {
|
||||||
if (row[fieldName].length) {
|
if (row[fieldName].length) {
|
||||||
if (!Array.isArray(row[fieldName])) {
|
if (!Array.isArray(row[fieldName])) {
|
||||||
row[fieldName] = row[fieldName].split(",")
|
row[fieldName] = row[fieldName].split(",")
|
||||||
|
@ -116,13 +115,13 @@ export async function validate({
|
||||||
errors[fieldName] = [`${fieldName} is required`]
|
errors[fieldName] = [`${fieldName} is required`]
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
(type === FieldTypes.ATTACHMENT || type === FieldTypes.JSON) &&
|
(type === FieldType.ATTACHMENT || type === FieldType.JSON) &&
|
||||||
typeof row[fieldName] === "string"
|
typeof row[fieldName] === "string"
|
||||||
) {
|
) {
|
||||||
// this should only happen if there is an error
|
// this should only happen if there is an error
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(row[fieldName])
|
const json = JSON.parse(row[fieldName])
|
||||||
if (type === FieldTypes.ATTACHMENT) {
|
if (type === FieldType.ATTACHMENT) {
|
||||||
if (Array.isArray(json)) {
|
if (Array.isArray(json)) {
|
||||||
row[fieldName] = json
|
row[fieldName] = json
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
Operation,
|
Operation,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
RenameColumn,
|
RenameColumn,
|
||||||
|
@ -14,7 +15,6 @@ import {
|
||||||
setStaticSchemas,
|
setStaticSchemas,
|
||||||
} from "../../../../api/controllers/table/utils"
|
} from "../../../../api/controllers/table/utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import { makeTableRequest } from "../../../../api/controllers/table/ExternalRequest"
|
import { makeTableRequest } from "../../../../api/controllers/table/ExternalRequest"
|
||||||
import {
|
import {
|
||||||
isRelationshipSetup,
|
isRelationshipSetup,
|
||||||
|
@ -78,7 +78,7 @@ export async function save(
|
||||||
|
|
||||||
// check if relations need setup
|
// check if relations need setup
|
||||||
for (let schema of Object.values(tableToSave.schema)) {
|
for (let schema of Object.values(tableToSave.schema)) {
|
||||||
if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
|
if (schema.type !== FieldType.LINK || isRelationshipSetup(schema)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const schemaTableId = schema.tableId
|
const schemaTableId = schema.tableId
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import {
|
import {
|
||||||
foreignKeyStructure,
|
foreignKeyStructure,
|
||||||
generateForeignKey,
|
generateForeignKey,
|
||||||
|
@ -27,7 +26,7 @@ export function cleanupRelationships(
|
||||||
// clean up relationships in couch table schemas
|
// clean up relationships in couch table schemas
|
||||||
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
||||||
if (
|
if (
|
||||||
schema.type === FieldTypes.LINK &&
|
schema.type === FieldType.LINK &&
|
||||||
(!oldTable || table.schema[key] == null)
|
(!oldTable || table.schema[key] == null)
|
||||||
) {
|
) {
|
||||||
const schemaTableId = schema.tableId
|
const schemaTableId = schema.tableId
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
FieldType,
|
||||||
RenameColumn,
|
RenameColumn,
|
||||||
Table,
|
Table,
|
||||||
ViewStatisticsSchema,
|
ViewStatisticsSchema,
|
||||||
|
@ -10,7 +11,6 @@ import {
|
||||||
hasTypeChanged,
|
hasTypeChanged,
|
||||||
TableSaveFunctions,
|
TableSaveFunctions,
|
||||||
} from "../../../../api/controllers/table/utils"
|
} from "../../../../api/controllers/table/utils"
|
||||||
import { FieldTypes } from "../../../../constants"
|
|
||||||
import { EventType, updateLinks } from "../../../../db/linkedRows"
|
import { EventType, updateLinks } from "../../../../db/linkedRows"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
|
@ -63,7 +63,7 @@ export async function save(
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename row fields when table column is renamed
|
// rename row fields when table column is renamed
|
||||||
if (renaming && table.schema[renaming.updated].type === FieldTypes.LINK) {
|
if (renaming && table.schema[renaming.updated].type === FieldType.LINK) {
|
||||||
throw new Error("Cannot rename a linked column.")
|
throw new Error("Cannot rename a linked column.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { FieldTypes, ObjectStoreBuckets } from "../../constants"
|
import { ObjectStoreBuckets } from "../../constants"
|
||||||
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
import { RenameColumn, Row, RowAttachment, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldType,
|
||||||
|
RenameColumn,
|
||||||
|
Row,
|
||||||
|
RowAttachment,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
export class AttachmentCleanup {
|
export class AttachmentCleanup {
|
||||||
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
||||||
|
@ -28,7 +34,7 @@ export class AttachmentCleanup {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
const tableSchema = opts.oldTable?.schema || table.schema
|
const tableSchema = opts.oldTable?.schema || table.schema
|
||||||
for (let [key, schema] of Object.entries(tableSchema)) {
|
for (let [key, schema] of Object.entries(tableSchema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const columnRemoved = opts.oldTable && !table.schema[key]
|
const columnRemoved = opts.oldTable && !table.schema[key]
|
||||||
|
@ -62,7 +68,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
|
@ -79,7 +85,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENT) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const oldKeys =
|
const oldKeys =
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import * as linkRows from "../../db/linkedRows"
|
import * as linkRows from "../../db/linkedRows"
|
||||||
import { FieldTypes, AutoFieldSubTypes } from "../../constants"
|
|
||||||
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
||||||
import { objectStore, utils } from "@budibase/backend-core"
|
import { objectStore, utils } from "@budibase/backend-core"
|
||||||
import { InternalTables } from "../../db/utils"
|
import { InternalTables } from "../../db/utils"
|
||||||
import { TYPE_TRANSFORM_MAP } from "./map"
|
import { TYPE_TRANSFORM_MAP } from "./map"
|
||||||
import { FieldSubtype, Row, RowAttachment, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldType,
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldSubtype,
|
||||||
|
Row,
|
||||||
|
RowAttachment,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import {
|
import {
|
||||||
processInputBBReferences,
|
processInputBBReferences,
|
||||||
|
@ -54,25 +60,25 @@ export function processAutoColumn(
|
||||||
schema = fixAutoColumnSubType(schema)
|
schema = fixAutoColumnSubType(schema)
|
||||||
}
|
}
|
||||||
switch (schema.subtype) {
|
switch (schema.subtype) {
|
||||||
case AutoFieldSubTypes.CREATED_BY:
|
case AutoFieldSubType.CREATED_BY:
|
||||||
if (creating && shouldUpdateUserFields && userId) {
|
if (creating && shouldUpdateUserFields && userId) {
|
||||||
row[key] = [userId]
|
row[key] = [userId]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.CREATED_AT:
|
case AutoFieldSubType.CREATED_AT:
|
||||||
if (creating) {
|
if (creating) {
|
||||||
row[key] = now
|
row[key] = now
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.UPDATED_BY:
|
case AutoFieldSubType.UPDATED_BY:
|
||||||
if (shouldUpdateUserFields && userId) {
|
if (shouldUpdateUserFields && userId) {
|
||||||
row[key] = [userId]
|
row[key] = [userId]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.UPDATED_AT:
|
case AutoFieldSubType.UPDATED_AT:
|
||||||
row[key] = now
|
row[key] = now
|
||||||
break
|
break
|
||||||
case AutoFieldSubTypes.AUTO_ID:
|
case AutoFieldSubType.AUTO_ID:
|
||||||
if (creating) {
|
if (creating) {
|
||||||
schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1
|
schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1
|
||||||
row[key] = schema.lastID
|
row[key] = schema.lastID
|
||||||
|
@ -134,7 +140,7 @@ export async function inputProcessing(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// remove any formula values, they are to be generated
|
// remove any formula values, they are to be generated
|
||||||
if (field.type === FieldTypes.FORMULA) {
|
if (field.type === FieldType.FORMULA) {
|
||||||
delete clonedRow[key]
|
delete clonedRow[key]
|
||||||
}
|
}
|
||||||
// otherwise coerce what is there to correct types
|
// otherwise coerce what is there to correct types
|
||||||
|
@ -143,7 +149,7 @@ export async function inputProcessing(
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any attachment urls, they are generated on read
|
// remove any attachment urls, they are generated on read
|
||||||
if (field.type === FieldTypes.ATTACHMENT) {
|
if (field.type === FieldType.ATTACHMENT) {
|
||||||
const attachments = clonedRow[key]
|
const attachments = clonedRow[key]
|
||||||
if (attachments?.length) {
|
if (attachments?.length) {
|
||||||
attachments.forEach((attachment: RowAttachment) => {
|
attachments.forEach((attachment: RowAttachment) => {
|
||||||
|
@ -152,7 +158,7 @@ export async function inputProcessing(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === FieldTypes.BB_REFERENCE && value) {
|
if (field.type === FieldType.BB_REFERENCE && value) {
|
||||||
clonedRow[key] = await processInputBBReferences(
|
clonedRow[key] = await processInputBBReferences(
|
||||||
value,
|
value,
|
||||||
field.subtype as FieldSubtype
|
field.subtype as FieldSubtype
|
||||||
|
@ -214,7 +220,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
|
|
||||||
// process complex types: attachements, bb references...
|
// process complex types: attachements, bb references...
|
||||||
for (let [property, column] of Object.entries(table.schema)) {
|
for (let [property, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldTypes.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENT) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
if (row[property] == null || !Array.isArray(row[property])) {
|
if (row[property] == null || !Array.isArray(row[property])) {
|
||||||
continue
|
continue
|
||||||
|
@ -227,7 +233,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
!opts.skipBBReferences &&
|
!opts.skipBBReferences &&
|
||||||
column.type == FieldTypes.BB_REFERENCE
|
column.type == FieldType.BB_REFERENCE
|
||||||
) {
|
) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
row[property] = await processOutputBBReferences(
|
row[property] = await processOutputBBReferences(
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// @ts-nocheck
|
import { FieldType } from "@budibase/types"
|
||||||
import { FieldTypes } from "../../constants"
|
|
||||||
|
|
||||||
const parseArrayString = value => {
|
const parseArrayString = (value: any) => {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
return []
|
return []
|
||||||
|
@ -21,11 +20,13 @@ const parseArrayString = value => {
|
||||||
* A map of how we convert various properties in rows to each other based on the row type.
|
* A map of how we convert various properties in rows to each other based on the row type.
|
||||||
*/
|
*/
|
||||||
export const TYPE_TRANSFORM_MAP: any = {
|
export const TYPE_TRANSFORM_MAP: any = {
|
||||||
[FieldTypes.LINK]: {
|
[FieldType.LINK]: {
|
||||||
"": [],
|
"": [],
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: link => {
|
parse: (link: any) => {
|
||||||
if (Array.isArray(link) && typeof link[0] === "object") {
|
if (Array.isArray(link) && typeof link[0] === "object") {
|
||||||
return link.map(el => (el && el._id ? el._id : el))
|
return link.map(el => (el && el._id ? el._id : el))
|
||||||
}
|
}
|
||||||
|
@ -35,75 +36,97 @@ export const TYPE_TRANSFORM_MAP: any = {
|
||||||
return link
|
return link
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[FieldTypes.OPTIONS]: {
|
[FieldType.OPTIONS]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.ARRAY]: {
|
[FieldType.ARRAY]: {
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: parseArrayString,
|
parse: parseArrayString,
|
||||||
},
|
},
|
||||||
[FieldTypes.STRING]: {
|
[FieldType.STRING]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.BARCODEQR]: {
|
[FieldType.BARCODEQR]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.FORMULA]: {
|
[FieldType.FORMULA]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.LONGFORM]: {
|
[FieldType.LONGFORM]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.NUMBER]: {
|
[FieldType.NUMBER]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: n => parseFloat(n),
|
parse: (n: any) => parseFloat(n),
|
||||||
},
|
},
|
||||||
[FieldTypes.BIGINT]: {
|
[FieldType.BIGINT]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.DATETIME]: {
|
[FieldType.DATETIME]: {
|
||||||
"": null,
|
"": null,
|
||||||
[undefined]: undefined,
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
parse: date => {
|
//@ts-ignore
|
||||||
|
[undefined]: undefined,
|
||||||
|
parse: (date: any) => {
|
||||||
if (date instanceof Date) {
|
if (date instanceof Date) {
|
||||||
return date.toISOString()
|
return date.toISOString()
|
||||||
}
|
}
|
||||||
return date
|
return date
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[FieldTypes.ATTACHMENT]: {
|
[FieldType.ATTACHMENT]: {
|
||||||
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
parse: parseArrayString,
|
parse: parseArrayString,
|
||||||
},
|
},
|
||||||
[FieldTypes.BOOLEAN]: {
|
[FieldType.BOOLEAN]: {
|
||||||
"": null,
|
"": null,
|
||||||
|
//@ts-ignore
|
||||||
[null]: null,
|
[null]: null,
|
||||||
|
//@ts-ignore
|
||||||
[undefined]: undefined,
|
[undefined]: undefined,
|
||||||
true: true,
|
true: true,
|
||||||
false: false,
|
false: false,
|
||||||
},
|
},
|
||||||
[FieldTypes.AUTO]: {
|
[FieldType.AUTO]: {
|
||||||
parse: () => undefined,
|
parse: () => undefined,
|
||||||
},
|
},
|
||||||
[FieldTypes.JSON]: {
|
[FieldType.JSON]: {
|
||||||
parse: input => {
|
parse: (input: any) => {
|
||||||
try {
|
try {
|
||||||
if (input === "") {
|
if (input === "") {
|
||||||
return undefined
|
return undefined
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { fixAutoColumnSubType } from "../utils"
|
import { fixAutoColumnSubType } from "../utils"
|
||||||
import { AutoFieldDefaultNames, AutoFieldSubTypes } from "../../../constants"
|
import { AutoFieldDefaultNames } from "../../../constants"
|
||||||
import { FieldSchema, FieldType, RelationshipType } from "@budibase/types"
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldSchema,
|
||||||
|
FieldType,
|
||||||
|
RelationshipType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
describe("rowProcessor utility", () => {
|
describe("rowProcessor utility", () => {
|
||||||
describe("fixAutoColumnSubType", () => {
|
describe("fixAutoColumnSubType", () => {
|
||||||
|
@ -20,37 +25,37 @@ describe("rowProcessor utility", () => {
|
||||||
it("updates the schema with the correct subtype", async () => {
|
it("updates the schema with the correct subtype", async () => {
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_BY
|
schema.name = AutoFieldDefaultNames.CREATED_BY
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.CREATED_BY
|
AutoFieldSubType.CREATED_BY
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.UPDATED_BY
|
schema.name = AutoFieldDefaultNames.UPDATED_BY
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.UPDATED_BY
|
AutoFieldSubType.UPDATED_BY
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.CREATED_AT
|
AutoFieldSubType.CREATED_AT
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.UPDATED_AT
|
schema.name = AutoFieldDefaultNames.UPDATED_AT
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.UPDATED_AT
|
AutoFieldSubType.UPDATED_AT
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
|
|
||||||
schema.name = AutoFieldDefaultNames.AUTO_ID
|
schema.name = AutoFieldDefaultNames.AUTO_ID
|
||||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||||
AutoFieldSubTypes.AUTO_ID
|
AutoFieldSubType.AUTO_ID
|
||||||
)
|
)
|
||||||
schema.subtype = undefined
|
schema.subtype = undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns the column if subtype exists", async () => {
|
it("returns the column if subtype exists", async () => {
|
||||||
schema.subtype = AutoFieldSubTypes.CREATED_BY
|
schema.subtype = AutoFieldSubType.CREATED_BY
|
||||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||||
expect(fixAutoColumnSubType(schema)).toEqual(schema)
|
expect(fixAutoColumnSubType(schema)).toEqual(schema)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import {
|
import { AutoFieldDefaultNames } from "../../constants"
|
||||||
AutoFieldDefaultNames,
|
|
||||||
AutoFieldSubTypes,
|
|
||||||
FieldTypes,
|
|
||||||
FormulaTypes,
|
|
||||||
} from "../../constants"
|
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import {
|
import {
|
||||||
AutoColumnFieldMetadata,
|
AutoColumnFieldMetadata,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
FormulaType,
|
||||||
|
AutoFieldSubType,
|
||||||
|
FieldType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import tracer from "dd-trace"
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
|
@ -30,15 +28,15 @@ export function fixAutoColumnSubType(
|
||||||
}
|
}
|
||||||
// the columns which get auto generated
|
// the columns which get auto generated
|
||||||
if (column.name.endsWith(AutoFieldDefaultNames.CREATED_BY)) {
|
if (column.name.endsWith(AutoFieldDefaultNames.CREATED_BY)) {
|
||||||
column.subtype = AutoFieldSubTypes.CREATED_BY
|
column.subtype = AutoFieldSubType.CREATED_BY
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_BY)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_BY)) {
|
||||||
column.subtype = AutoFieldSubTypes.UPDATED_BY
|
column.subtype = AutoFieldSubType.UPDATED_BY
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.CREATED_AT)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.CREATED_AT)) {
|
||||||
column.subtype = AutoFieldSubTypes.CREATED_AT
|
column.subtype = AutoFieldSubType.CREATED_AT
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_AT)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.UPDATED_AT)) {
|
||||||
column.subtype = AutoFieldSubTypes.UPDATED_AT
|
column.subtype = AutoFieldSubType.UPDATED_AT
|
||||||
} else if (column.name.endsWith(AutoFieldDefaultNames.AUTO_ID)) {
|
} else if (column.name.endsWith(AutoFieldDefaultNames.AUTO_ID)) {
|
||||||
column.subtype = AutoFieldSubTypes.AUTO_ID
|
column.subtype = AutoFieldSubType.AUTO_ID
|
||||||
}
|
}
|
||||||
return column
|
return column
|
||||||
}
|
}
|
||||||
|
@ -57,11 +55,11 @@ export function processFormulas<T extends Row | Row[]>(
|
||||||
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
||||||
if (rows) {
|
if (rows) {
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let [column, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.FORMULA) {
|
if (schema.type !== FieldType.FORMULA) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const isStatic = schema.formulaType === FormulaTypes.STATIC
|
const isStatic = schema.formulaType === FormulaType.STATIC
|
||||||
|
|
||||||
if (
|
if (
|
||||||
schema.formula == null ||
|
schema.formula == null ||
|
||||||
|
@ -100,7 +98,7 @@ export function processDates<T extends Row | Row[]>(
|
||||||
let rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
let rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
||||||
let datesWithTZ: string[] = []
|
let datesWithTZ: string[] = []
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
for (let [column, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldTypes.DATETIME) {
|
if (schema.type !== FieldType.DATETIME) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { FieldSubtype } from "@budibase/types"
|
import { FieldType, FieldSubtype } from "@budibase/types"
|
||||||
import { FieldTypes } from "../constants"
|
|
||||||
import { ValidColumnNameRegex, utils } from "@budibase/shared-core"
|
import { ValidColumnNameRegex, utils } from "@budibase/shared-core"
|
||||||
import { db } from "@budibase/backend-core"
|
import { db } from "@budibase/backend-core"
|
||||||
import { parseCsvExport } from "../api/controllers/view/exporters"
|
import { parseCsvExport } from "../api/controllers/view/exporters"
|
||||||
|
|
||||||
interface SchemaColumn {
|
interface SchemaColumn {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly type: FieldTypes
|
readonly type: FieldType
|
||||||
readonly subtype: FieldSubtype
|
readonly subtype: FieldSubtype
|
||||||
readonly autocolumn?: boolean
|
readonly autocolumn?: boolean
|
||||||
readonly constraints?: {
|
readonly constraints?: {
|
||||||
|
@ -36,13 +35,13 @@ interface ValidationResults {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARSERS: any = {
|
const PARSERS: any = {
|
||||||
[FieldTypes.NUMBER]: (attribute?: string) => {
|
[FieldType.NUMBER]: (attribute?: string) => {
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
return attribute
|
return attribute
|
||||||
}
|
}
|
||||||
return Number(attribute)
|
return Number(attribute)
|
||||||
},
|
},
|
||||||
[FieldTypes.DATETIME]: (attribute?: string) => {
|
[FieldType.DATETIME]: (attribute?: string) => {
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
return attribute
|
return attribute
|
||||||
}
|
}
|
||||||
|
@ -60,7 +59,7 @@ export function isSchema(schema: any): schema is Schema {
|
||||||
column !== null &&
|
column !== null &&
|
||||||
typeof column === "object" &&
|
typeof column === "object" &&
|
||||||
typeof column.type === "string" &&
|
typeof column.type === "string" &&
|
||||||
Object.values(FieldTypes).includes(column.type as FieldTypes)
|
Object.values(FieldType).includes(column.type as FieldType)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -110,20 +109,17 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
|
||||||
isAutoColumn
|
isAutoColumn
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
} else if (
|
} else if (columnType === FieldType.NUMBER && isNaN(Number(columnData))) {
|
||||||
columnType === FieldTypes.NUMBER &&
|
|
||||||
isNaN(Number(columnData))
|
|
||||||
) {
|
|
||||||
// If provided must be a valid number
|
// If provided must be a valid number
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
} else if (
|
} else if (
|
||||||
// If provided must be a valid date
|
// If provided must be a valid date
|
||||||
columnType === FieldTypes.DATETIME &&
|
columnType === FieldType.DATETIME &&
|
||||||
isNaN(new Date(columnData).getTime())
|
isNaN(new Date(columnData).getTime())
|
||||||
) {
|
) {
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
} else if (
|
} else if (
|
||||||
columnType === FieldTypes.BB_REFERENCE &&
|
columnType === FieldType.BB_REFERENCE &&
|
||||||
!isValidBBReference(columnData, columnSubtype)
|
!isValidBBReference(columnData, columnSubtype)
|
||||||
) {
|
) {
|
||||||
results.schemaValidation[columnName] = false
|
results.schemaValidation[columnName] = false
|
||||||
|
@ -155,15 +151,15 @@ export function parse(rows: Rows, schema: Schema): Rows {
|
||||||
const columnType = schema[columnName].type
|
const columnType = schema[columnName].type
|
||||||
const columnSubtype = schema[columnName].subtype
|
const columnSubtype = schema[columnName].subtype
|
||||||
|
|
||||||
if (columnType === FieldTypes.NUMBER) {
|
if (columnType === FieldType.NUMBER) {
|
||||||
// If provided must be a valid number
|
// If provided must be a valid number
|
||||||
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
||||||
} else if (columnType === FieldTypes.DATETIME) {
|
} else if (columnType === FieldType.DATETIME) {
|
||||||
// If provided must be a valid date
|
// If provided must be a valid date
|
||||||
parsedRow[columnName] = columnData
|
parsedRow[columnName] = columnData
|
||||||
? new Date(columnData).toISOString()
|
? new Date(columnData).toISOString()
|
||||||
: columnData
|
: columnData
|
||||||
} else if (columnType === FieldTypes.BB_REFERENCE) {
|
} else if (columnType === FieldType.BB_REFERENCE) {
|
||||||
const parsedValues =
|
const parsedValues =
|
||||||
!!columnData && parseCsvExport<{ _id: string }[]>(columnData)
|
!!columnData && parseCsvExport<{ _id: string }[]>(columnData)
|
||||||
if (!parsedValues) {
|
if (!parsedValues) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ export enum AutoReason {
|
||||||
FOREIGN_KEY = "foreign_key",
|
FOREIGN_KEY = "foreign_key",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AutoFieldSubTypes {
|
export enum AutoFieldSubType {
|
||||||
CREATED_BY = "createdBy",
|
CREATED_BY = "createdBy",
|
||||||
CREATED_AT = "createdAt",
|
CREATED_AT = "createdAt",
|
||||||
UPDATED_BY = "updatedBy",
|
UPDATED_BY = "updatedBy",
|
||||||
|
@ -16,7 +16,7 @@ export enum AutoFieldSubTypes {
|
||||||
AUTO_ID = "autoID",
|
AUTO_ID = "autoID",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FormulaTypes {
|
export enum FormulaType {
|
||||||
STATIC = "static",
|
STATIC = "static",
|
||||||
DYNAMIC = "dynamic",
|
DYNAMIC = "dynamic",
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
// column size, position and whether it can be viewed
|
// column size, position and whether it can be viewed
|
||||||
import { FieldSubtype, FieldType } from "../row"
|
import { FieldSubtype, FieldType } from "../row"
|
||||||
import {
|
import {
|
||||||
AutoFieldSubTypes,
|
AutoFieldSubType,
|
||||||
AutoReason,
|
AutoReason,
|
||||||
FormulaTypes,
|
FormulaType,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
} from "./constants"
|
} from "./constants"
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ interface BaseRelationshipFieldMetadata
|
||||||
fieldName: string
|
fieldName: string
|
||||||
tableId: string
|
tableId: string
|
||||||
tableRev?: string
|
tableRev?: string
|
||||||
subtype?: AutoFieldSubTypes.CREATED_BY | AutoFieldSubTypes.UPDATED_BY
|
subtype?: AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY
|
||||||
}
|
}
|
||||||
|
|
||||||
// External tables use junction tables, internal tables don't require them
|
// External tables use junction tables, internal tables don't require them
|
||||||
|
@ -62,7 +62,7 @@ export interface AutoColumnFieldMetadata
|
||||||
extends Omit<BaseFieldSchema, "subtype"> {
|
extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.AUTO
|
type: FieldType.AUTO
|
||||||
autocolumn: true
|
autocolumn: true
|
||||||
subtype?: AutoFieldSubTypes
|
subtype?: AutoFieldSubType
|
||||||
lastID?: number
|
lastID?: number
|
||||||
// if the column was turned to an auto-column for SQL, explains why (primary, foreign etc)
|
// if the column was turned to an auto-column for SQL, explains why (primary, foreign etc)
|
||||||
autoReason?: AutoReason
|
autoReason?: AutoReason
|
||||||
|
@ -70,7 +70,7 @@ export interface AutoColumnFieldMetadata
|
||||||
|
|
||||||
export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
export interface NumberFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.NUMBER
|
type: FieldType.NUMBER
|
||||||
subtype?: AutoFieldSubTypes.AUTO_ID
|
subtype?: AutoFieldSubType.AUTO_ID
|
||||||
lastID?: number
|
lastID?: number
|
||||||
autoReason?: AutoReason.FOREIGN_KEY
|
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
|
||||||
|
@ -85,7 +85,7 @@ export interface DateFieldMetadata extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.DATETIME
|
type: FieldType.DATETIME
|
||||||
ignoreTimezones?: boolean
|
ignoreTimezones?: boolean
|
||||||
timeOnly?: boolean
|
timeOnly?: boolean
|
||||||
subtype?: AutoFieldSubTypes.CREATED_AT | AutoFieldSubTypes.UPDATED_AT
|
subtype?: AutoFieldSubType.CREATED_AT | AutoFieldSubType.UPDATED_AT
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LongFormFieldMetadata extends BaseFieldSchema {
|
export interface LongFormFieldMetadata extends BaseFieldSchema {
|
||||||
|
@ -96,7 +96,7 @@ export interface LongFormFieldMetadata extends BaseFieldSchema {
|
||||||
export interface FormulaFieldMetadata extends BaseFieldSchema {
|
export interface FormulaFieldMetadata extends BaseFieldSchema {
|
||||||
type: FieldType.FORMULA
|
type: FieldType.FORMULA
|
||||||
formula: string
|
formula: string
|
||||||
formulaType?: FormulaTypes
|
formulaType?: FormulaType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BBReferenceFieldMetadata
|
export interface BBReferenceFieldMetadata
|
||||||
|
|
Loading…
Reference in a new issue