2023-01-17 16:07:52 +01:00
|
|
|
import { parse, isSchema, isRows } from "../../../utilities/schema"
|
2022-10-20 16:05:50 +02:00
|
|
|
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
2023-07-27 21:36:32 +02:00
|
|
|
import isEqual from "lodash/isEqual"
|
2023-03-14 13:11:01 +01:00
|
|
|
import {
|
|
|
|
AutoFieldSubTypes,
|
|
|
|
FieldTypes,
|
|
|
|
GOOGLE_SHEETS_PRIMARY_KEY,
|
|
|
|
} from "../../../constants"
|
2022-03-09 22:16:22 +01:00
|
|
|
import {
|
2022-01-24 17:32:41 +01:00
|
|
|
inputProcessing,
|
|
|
|
cleanupAttachments,
|
2022-03-09 22:16:22 +01:00
|
|
|
} from "../../../utilities/rowProcessor"
|
|
|
|
import {
|
2022-01-26 19:50:13 +01:00
|
|
|
USERS_TABLE_SCHEMA,
|
|
|
|
SwitchableTypes,
|
|
|
|
CanSwitchTypes,
|
2022-03-09 22:16:22 +01:00
|
|
|
} from "../../../constants"
|
|
|
|
import { getViews, saveView } from "../view/utils"
|
|
|
|
import viewTemplate from "../view/viewBuilder"
|
|
|
|
import { cloneDeep } from "lodash/fp"
|
2022-05-26 17:01:10 +02:00
|
|
|
import { quotas } from "@budibase/pro"
|
2022-11-22 14:56:01 +01:00
|
|
|
import { events, context } from "@budibase/backend-core"
|
2023-10-10 12:58:51 +02:00
|
|
|
import {
|
|
|
|
ContextUser,
|
|
|
|
Datasource,
|
|
|
|
Row,
|
|
|
|
SourceName,
|
|
|
|
Table,
|
2023-10-19 13:26:29 +02:00
|
|
|
Database,
|
|
|
|
RenameColumn,
|
|
|
|
NumberFieldMetadata,
|
|
|
|
FieldSchema,
|
|
|
|
View,
|
|
|
|
RelationshipFieldMetadata,
|
|
|
|
FieldType,
|
2023-10-10 12:58:51 +02:00
|
|
|
} from "@budibase/types"
|
2021-02-22 12:39:58 +01:00
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export async function clearColumns(table: Table, columnNames: string[]) {
|
2023-07-18 12:00:02 +02:00
|
|
|
const db = context.getAppDB()
|
2022-01-24 17:32:41 +01:00
|
|
|
const rows = await db.allDocs(
|
|
|
|
getRowParams(table._id, null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2022-11-22 14:56:01 +01:00
|
|
|
return (await db.bulkDocs(
|
2022-03-09 22:16:22 +01:00
|
|
|
rows.rows.map(({ doc }: any) => {
|
|
|
|
columnNames.forEach((colName: any) => delete doc[colName])
|
2022-01-24 17:32:41 +01:00
|
|
|
return doc
|
|
|
|
})
|
2022-11-22 14:56:01 +01:00
|
|
|
)) as { id: string; _rev?: string }[]
|
2022-01-24 17:32:41 +01:00
|
|
|
}
|
2021-02-22 12:39:58 +01:00
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export async function checkForColumnUpdates(
|
|
|
|
updatedTable: Table,
|
|
|
|
oldTable?: Table,
|
|
|
|
columnRename?: RenameColumn
|
|
|
|
) {
|
2022-11-22 14:56:01 +01:00
|
|
|
const db = context.getAppDB()
|
2021-02-22 13:05:59 +01:00
|
|
|
let updatedRows = []
|
2022-03-09 22:16:22 +01:00
|
|
|
let deletedColumns: any = []
|
2021-02-22 12:39:58 +01:00
|
|
|
if (oldTable && oldTable.schema && updatedTable.schema) {
|
|
|
|
deletedColumns = Object.keys(oldTable.schema).filter(
|
2021-05-04 12:32:22 +02:00
|
|
|
colName => updatedTable.schema[colName] == null
|
2021-02-22 12:39:58 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
// check for renaming of columns or deleted columns
|
2023-10-19 13:26:29 +02:00
|
|
|
if (columnRename || deletedColumns.length !== 0) {
|
2021-10-20 21:01:49 +02:00
|
|
|
// Update all rows
|
2021-02-22 12:39:58 +01:00
|
|
|
const rows = await db.allDocs(
|
|
|
|
getRowParams(updatedTable._id, null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2022-03-09 22:16:22 +01:00
|
|
|
const rawRows = rows.rows.map(({ doc }: any) => doc)
|
|
|
|
updatedRows = rawRows.map((row: any) => {
|
2022-01-24 17:32:41 +01:00
|
|
|
row = cloneDeep(row)
|
2023-10-19 13:26:29 +02:00
|
|
|
if (columnRename) {
|
|
|
|
row[columnRename.updated] = row[columnRename.old]
|
|
|
|
delete row[columnRename.old]
|
2021-02-22 12:39:58 +01:00
|
|
|
} else if (deletedColumns.length !== 0) {
|
2022-03-09 22:16:22 +01:00
|
|
|
deletedColumns.forEach((colName: any) => delete row[colName])
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
2022-01-24 17:32:41 +01:00
|
|
|
return row
|
2021-02-22 12:39:58 +01:00
|
|
|
})
|
2021-10-20 21:01:49 +02:00
|
|
|
|
2022-01-24 17:32:41 +01:00
|
|
|
// cleanup any attachments from object storage for deleted attachment columns
|
2022-01-31 18:00:22 +01:00
|
|
|
await cleanupAttachments(updatedTable, { oldTable, rows: rawRows })
|
2021-10-20 21:01:49 +02:00
|
|
|
// Update views
|
2023-10-19 13:26:29 +02:00
|
|
|
await checkForViewUpdates(updatedTable, deletedColumns, columnRename)
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
|
|
|
return { rows: updatedRows, table: updatedTable }
|
|
|
|
}
|
|
|
|
|
|
|
|
// makes sure the passed in table isn't going to reset the auto ID
|
2023-10-19 13:26:29 +02:00
|
|
|
export function makeSureTableUpToDate(table: Table, tableToSave: Table) {
|
2021-02-22 12:39:58 +01:00
|
|
|
if (!table) {
|
|
|
|
return tableToSave
|
|
|
|
}
|
|
|
|
// sure sure rev is up to date
|
|
|
|
tableToSave._rev = table._rev
|
|
|
|
// make sure auto IDs are always updated - these are internal
|
|
|
|
// so the client may not know they have changed
|
2022-03-09 22:16:22 +01:00
|
|
|
let field: any
|
|
|
|
let column: any
|
|
|
|
for ([field, column] of Object.entries(table.schema)) {
|
2021-02-22 12:39:58 +01:00
|
|
|
if (
|
|
|
|
column.autocolumn &&
|
|
|
|
column.subtype === AutoFieldSubTypes.AUTO_ID &&
|
|
|
|
tableToSave.schema[field]
|
|
|
|
) {
|
2023-10-19 13:26:29 +02:00
|
|
|
const tableCol = tableToSave.schema[field] as NumberFieldMetadata
|
|
|
|
tableCol.lastID = column.lastID
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return tableToSave
|
|
|
|
}
|
|
|
|
|
2023-09-15 10:31:54 +02:00
|
|
|
export async function importToRows(
|
2023-10-19 13:26:29 +02:00
|
|
|
data: Row[],
|
2023-03-30 12:30:35 +02:00
|
|
|
table: Table,
|
2023-10-19 13:26:29 +02:00
|
|
|
user?: ContextUser
|
2023-03-30 12:30:35 +02:00
|
|
|
) {
|
2023-04-20 21:10:30 +02:00
|
|
|
let originalTable = table
|
2022-03-18 09:01:31 +01:00
|
|
|
let finalData: any = []
|
2021-11-12 19:26:57 +01:00
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
|
|
let row = data[i]
|
2023-03-30 11:12:50 +02:00
|
|
|
row._id = generateRowID(table._id!)
|
2021-11-12 19:26:57 +01:00
|
|
|
row.tableId = table._id
|
2023-04-20 21:10:30 +02:00
|
|
|
|
|
|
|
// We use a reference to table here and update it after input processing,
|
|
|
|
// so that we can auto increment auto IDs in imported data properly
|
2023-09-15 10:31:54 +02:00
|
|
|
const processed = await inputProcessing(user?._id, table, row, {
|
2021-11-12 19:26:57 +01:00
|
|
|
noAutoRelationships: true,
|
|
|
|
})
|
|
|
|
row = processed.row
|
2023-03-30 11:12:50 +02:00
|
|
|
table = processed.table
|
2021-08-26 15:13:30 +02:00
|
|
|
|
2023-04-20 21:10:30 +02:00
|
|
|
// However here we must reference the original table, as we want to mutate
|
|
|
|
// the real schema of the table passed in, not the clone used for
|
|
|
|
// incrementing auto IDs
|
|
|
|
for (const [fieldName, schema] of Object.entries(originalTable.schema)) {
|
2023-05-04 13:12:47 +02:00
|
|
|
const rowVal = Array.isArray(row[fieldName])
|
|
|
|
? row[fieldName]
|
|
|
|
: [row[fieldName]]
|
2021-11-12 19:26:57 +01:00
|
|
|
if (
|
2023-02-20 19:46:06 +01:00
|
|
|
(schema.type === FieldTypes.OPTIONS ||
|
2023-05-04 13:12:47 +02:00
|
|
|
schema.type === FieldTypes.ARRAY) &&
|
2023-05-04 12:21:24 +02:00
|
|
|
row[fieldName]
|
2021-11-12 19:26:57 +01:00
|
|
|
) {
|
2023-05-04 13:12:47 +02:00
|
|
|
let merged = [...schema.constraints!.inclusion!, ...rowVal]
|
|
|
|
let superSet = new Set(merged)
|
|
|
|
schema.constraints!.inclusion = Array.from(superSet)
|
2023-03-30 11:21:50 +02:00
|
|
|
schema.constraints!.inclusion.sort()
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 19:26:57 +01:00
|
|
|
finalData.push(row)
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
2022-11-13 22:37:50 +01:00
|
|
|
return finalData
|
|
|
|
}
|
|
|
|
|
2023-05-25 19:05:07 +02:00
|
|
|
export async function handleDataImport(
|
2023-10-10 12:58:51 +02:00
|
|
|
table: Table,
|
2023-10-19 13:26:29 +02:00
|
|
|
opts?: { identifierFields?: string[]; user?: ContextUser; importRows?: Row[] }
|
2023-05-25 19:05:07 +02:00
|
|
|
) {
|
2023-10-10 12:58:51 +02:00
|
|
|
const schema = table.schema
|
2023-10-19 13:26:29 +02:00
|
|
|
const identifierFields = opts?.identifierFields || []
|
|
|
|
const user = opts?.user
|
|
|
|
const importRows = opts?.importRows
|
2023-01-17 16:07:52 +01:00
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
if (!importRows || !isRows(importRows) || !isSchema(schema)) {
|
2022-11-13 22:37:50 +01:00
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
2022-11-22 14:56:01 +01:00
|
|
|
const db = context.getAppDB()
|
2023-10-19 13:26:29 +02:00
|
|
|
const data = parse(importRows, schema)
|
2022-11-13 22:37:50 +01:00
|
|
|
|
2023-09-15 10:31:54 +02:00
|
|
|
let finalData: any = await importToRows(data, table, user)
|
2021-11-12 19:26:57 +01:00
|
|
|
|
2023-05-25 19:05:07 +02:00
|
|
|
//Set IDs of finalData to match existing row if an update is expected
|
|
|
|
if (identifierFields.length > 0) {
|
|
|
|
const allDocs = await db.allDocs(
|
|
|
|
getRowParams(table._id, null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
allDocs.rows
|
|
|
|
.map(existingRow => existingRow.doc)
|
|
|
|
.forEach((doc: any) => {
|
|
|
|
finalData.forEach((finalItem: any) => {
|
|
|
|
let match = true
|
|
|
|
for (const field of identifierFields) {
|
|
|
|
if (finalItem[field] !== doc[field]) {
|
|
|
|
match = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (match) {
|
|
|
|
finalItem._id = doc._id
|
|
|
|
finalItem._rev = doc._rev
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-05-26 17:01:10 +02:00
|
|
|
await quotas.addRows(finalData.length, () => db.bulkDocs(finalData), {
|
|
|
|
tableId: table._id,
|
|
|
|
})
|
2023-01-17 16:07:52 +01:00
|
|
|
|
|
|
|
await events.rows.imported(table, finalData.length)
|
2021-02-22 12:39:58 +01:00
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export async function handleSearchIndexes(table: Table) {
|
2022-11-22 14:56:01 +01:00
|
|
|
const db = context.getAppDB()
|
2021-02-22 12:39:58 +01:00
|
|
|
// create relevant search indexes
|
|
|
|
if (table.indexes && table.indexes.length > 0) {
|
|
|
|
const currentIndexes = await db.getIndexes()
|
|
|
|
const indexName = `search:${table._id}`
|
|
|
|
|
|
|
|
const existingIndex = currentIndexes.indexes.find(
|
2022-03-09 22:16:22 +01:00
|
|
|
(existing: any) => existing.name === indexName
|
2021-02-22 12:39:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
if (existingIndex) {
|
|
|
|
const currentFields = existingIndex.def.fields.map(
|
2022-03-09 22:16:22 +01:00
|
|
|
(field: any) => Object.keys(field)[0]
|
2021-02-22 12:39:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// if index fields have changed, delete the original index
|
|
|
|
if (!isEqual(currentFields, table.indexes)) {
|
|
|
|
await db.deleteIndex(existingIndex)
|
|
|
|
// create/recreate the index with fields
|
|
|
|
await db.createIndex({
|
|
|
|
index: {
|
|
|
|
fields: table.indexes,
|
|
|
|
name: indexName,
|
|
|
|
ddoc: "search_ddoc",
|
|
|
|
type: "json",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// create/recreate the index with fields
|
|
|
|
await db.createIndex({
|
|
|
|
index: {
|
|
|
|
fields: table.indexes,
|
|
|
|
name: indexName,
|
|
|
|
ddoc: "search_ddoc",
|
|
|
|
type: "json",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export function checkStaticTables(table: Table) {
|
2021-02-22 12:39:58 +01:00
|
|
|
// check user schema has all required elements
|
2021-04-09 18:33:21 +02:00
|
|
|
if (table._id === InternalTables.USER_METADATA) {
|
2021-02-22 12:39:58 +01:00
|
|
|
for (let [key, schema] of Object.entries(USERS_TABLE_SCHEMA.schema)) {
|
|
|
|
// check if the schema exists on the table to be created/updated
|
|
|
|
if (table.schema[key] == null) {
|
2023-10-19 13:26:29 +02:00
|
|
|
table.schema[key] = schema as FieldSchema
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
|
|
|
class TableSaveFunctions {
|
2023-10-19 13:26:29 +02:00
|
|
|
db: Database
|
|
|
|
user?: ContextUser
|
|
|
|
oldTable?: Table
|
|
|
|
importRows?: Row[]
|
|
|
|
rows: Row[]
|
|
|
|
|
|
|
|
constructor({
|
|
|
|
user,
|
|
|
|
oldTable,
|
|
|
|
importRows,
|
|
|
|
}: {
|
|
|
|
user?: ContextUser
|
|
|
|
oldTable?: Table
|
|
|
|
importRows?: Row[]
|
|
|
|
}) {
|
2022-11-22 14:56:01 +01:00
|
|
|
this.db = context.getAppDB()
|
2022-01-27 19:18:31 +01:00
|
|
|
this.user = user
|
2021-02-22 12:39:58 +01:00
|
|
|
this.oldTable = oldTable
|
2023-01-17 16:07:52 +01:00
|
|
|
this.importRows = importRows
|
2021-02-22 12:39:58 +01:00
|
|
|
// any rows that need updated
|
|
|
|
this.rows = []
|
|
|
|
}
|
|
|
|
|
|
|
|
// before anything is done
|
2023-10-19 13:26:29 +02:00
|
|
|
async before(table: Table) {
|
2021-02-22 12:39:58 +01:00
|
|
|
if (this.oldTable) {
|
2022-03-09 22:16:22 +01:00
|
|
|
table = makeSureTableUpToDate(this.oldTable, table)
|
2021-02-22 12:39:58 +01:00
|
|
|
}
|
2022-03-09 22:16:22 +01:00
|
|
|
table = checkStaticTables(table)
|
2021-02-22 12:39:58 +01:00
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
|
|
|
// when confirmed valid
|
2023-10-19 13:26:29 +02:00
|
|
|
async mid(table: Table, columnRename?: RenameColumn) {
|
|
|
|
let response = await checkForColumnUpdates(
|
|
|
|
table,
|
|
|
|
this.oldTable,
|
|
|
|
columnRename
|
|
|
|
)
|
2021-02-22 13:05:59 +01:00
|
|
|
this.rows = this.rows.concat(response.rows)
|
2021-02-22 12:39:58 +01:00
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
|
|
|
// after saving
|
2023-10-19 13:26:29 +02:00
|
|
|
async after(table: Table) {
|
2022-03-09 22:16:22 +01:00
|
|
|
table = await handleSearchIndexes(table)
|
2023-10-19 13:26:29 +02:00
|
|
|
table = await handleDataImport(table, {
|
|
|
|
importRows: this.importRows,
|
|
|
|
user: this.user,
|
|
|
|
})
|
2021-02-22 12:39:58 +01:00
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
|
|
|
getUpdatedRows() {
|
|
|
|
return this.rows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:16:22 +01:00
|
|
|
export async function checkForViewUpdates(
|
2023-10-19 13:26:29 +02:00
|
|
|
table: Table,
|
|
|
|
deletedColumns: string[],
|
|
|
|
columnRename?: RenameColumn
|
2022-03-09 22:16:22 +01:00
|
|
|
) {
|
2022-01-27 19:18:31 +01:00
|
|
|
const views = await getViews()
|
2023-11-08 13:46:00 +01:00
|
|
|
const tableViews = views.filter(view => view.meta?.tableId === table._id)
|
2021-10-20 21:01:49 +02:00
|
|
|
|
|
|
|
// Check each table view to see if impacted by this table action
|
|
|
|
for (let view of tableViews) {
|
|
|
|
let needsUpdated = false
|
2023-11-08 13:46:00 +01:00
|
|
|
const viewMetadata = view.meta as any
|
|
|
|
if (!viewMetadata) {
|
|
|
|
continue
|
|
|
|
}
|
2021-10-20 21:01:49 +02:00
|
|
|
|
|
|
|
// First check for renames, otherwise check for deletions
|
2023-10-19 13:26:29 +02:00
|
|
|
if (columnRename) {
|
2021-10-20 21:01:49 +02:00
|
|
|
// Update calculation field if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.field === columnRename.old) {
|
|
|
|
viewMetadata.field = columnRename.updated
|
2021-10-20 21:01:49 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update group by field if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.groupBy === columnRename.old) {
|
|
|
|
viewMetadata.groupBy = columnRename.updated
|
2021-10-20 21:01:49 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update filters if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.filters) {
|
|
|
|
viewMetadata.filters.forEach((filter: any) => {
|
2023-10-19 13:26:29 +02:00
|
|
|
if (filter.key === columnRename.old) {
|
|
|
|
filter.key = columnRename.updated
|
2021-10-21 11:24:41 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else if (deletedColumns) {
|
2023-10-19 13:26:29 +02:00
|
|
|
deletedColumns.forEach((column: string) => {
|
2021-10-20 21:01:49 +02:00
|
|
|
// Remove calculation statement if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.field === column) {
|
|
|
|
delete viewMetadata.field
|
|
|
|
delete viewMetadata.calculation
|
|
|
|
delete viewMetadata.groupBy
|
2021-10-20 21:01:49 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove group by field if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.groupBy === column) {
|
|
|
|
delete viewMetadata.groupBy
|
2021-10-20 21:01:49 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove filters referencing deleted field if required
|
2023-11-08 13:46:00 +01:00
|
|
|
if (viewMetadata.filters && viewMetadata.filters.length) {
|
|
|
|
const initialLength = viewMetadata.filters.length
|
|
|
|
viewMetadata.filters = viewMetadata.filters.filter((filter: any) => {
|
2021-10-20 21:01:49 +02:00
|
|
|
return filter.key !== column
|
|
|
|
})
|
2023-11-08 13:46:00 +01:00
|
|
|
if (initialLength !== viewMetadata.filters.length) {
|
2021-10-20 21:01:49 +02:00
|
|
|
needsUpdated = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update view if required
|
|
|
|
if (needsUpdated) {
|
2023-01-10 17:25:23 +01:00
|
|
|
const groupByField: any = Object.values(table.schema).find(
|
|
|
|
(field: any) => field.name == view.groupBy
|
|
|
|
)
|
|
|
|
const newViewTemplate = viewTemplate(
|
2023-11-08 13:46:00 +01:00
|
|
|
viewMetadata,
|
2023-01-10 17:25:23 +01:00
|
|
|
groupByField?.type === FieldTypes.ARRAY
|
|
|
|
)
|
2023-11-08 13:46:00 +01:00
|
|
|
const viewName = view.name!
|
|
|
|
await saveView(null, viewName, newViewTemplate)
|
|
|
|
if (!newViewTemplate.meta?.schema) {
|
|
|
|
newViewTemplate.meta!.schema = table.schema
|
2021-10-20 21:01:49 +02:00
|
|
|
}
|
2023-11-08 13:46:00 +01:00
|
|
|
if (table.views?.[viewName]) {
|
|
|
|
table.views[viewName] = newViewTemplate.meta as View
|
2023-10-19 13:26:29 +02:00
|
|
|
}
|
2021-10-20 21:01:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export function generateForeignKey(
|
|
|
|
column: RelationshipFieldMetadata,
|
|
|
|
relatedTable: Table
|
|
|
|
) {
|
2021-10-29 19:37:29 +02:00
|
|
|
return `fk_${relatedTable.name}_${column.fieldName}`
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:16:22 +01:00
|
|
|
export function generateJunctionTableName(
|
2023-10-19 13:26:29 +02:00
|
|
|
column: RelationshipFieldMetadata,
|
|
|
|
table: Table,
|
|
|
|
relatedTable: Table
|
2022-03-09 22:16:22 +01:00
|
|
|
) {
|
2021-10-29 19:37:29 +02:00
|
|
|
return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
|
|
|
|
}
|
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export function foreignKeyStructure(keyName: string, meta?: any) {
|
2022-03-09 22:16:22 +01:00
|
|
|
const structure: any = {
|
2021-10-29 19:37:29 +02:00
|
|
|
type: FieldTypes.NUMBER,
|
|
|
|
constraints: {},
|
|
|
|
name: keyName,
|
|
|
|
}
|
|
|
|
if (meta) {
|
|
|
|
structure.meta = meta
|
|
|
|
}
|
|
|
|
return structure
|
|
|
|
}
|
|
|
|
|
2023-10-19 13:26:29 +02:00
|
|
|
export function areSwitchableTypes(type1: FieldType, type2: FieldType) {
|
2022-01-26 19:50:13 +01:00
|
|
|
if (
|
|
|
|
SwitchableTypes.indexOf(type1) === -1 &&
|
|
|
|
SwitchableTypes.indexOf(type2) === -1
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for (let option of CanSwitchTypes) {
|
|
|
|
const index1 = option.indexOf(type1),
|
|
|
|
index2 = option.indexOf(type2)
|
|
|
|
if (index1 !== -1 && index2 !== -1 && index1 !== index2) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-08-18 15:33:21 +02:00
|
|
|
export function hasTypeChanged(table: Table, oldTable: Table | undefined) {
|
2021-11-10 16:01:44 +01:00
|
|
|
if (!oldTable) {
|
|
|
|
return false
|
|
|
|
}
|
2023-09-14 15:18:54 +02:00
|
|
|
for (let [key, field] of Object.entries(oldTable.schema)) {
|
2021-11-10 16:01:44 +01:00
|
|
|
if (!table.schema[key]) {
|
|
|
|
continue
|
|
|
|
}
|
2023-09-14 15:18:54 +02:00
|
|
|
const oldType = field.type
|
2021-11-10 16:01:44 +01:00
|
|
|
const newType = table.schema[key].type
|
2022-03-09 22:16:22 +01:00
|
|
|
if (oldType !== newType && !areSwitchableTypes(oldType, newType)) {
|
2021-11-10 16:01:44 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-03-14 13:11:01 +01:00
|
|
|
// used for external tables, some of them will have static schemas that need
|
|
|
|
// to be hard set
|
|
|
|
export function setStaticSchemas(datasource: Datasource, table: Table) {
|
|
|
|
// GSheets is a specific case - only ever has a static primary key
|
|
|
|
if (table && datasource.source === SourceName.GOOGLE_SHEETS) {
|
|
|
|
table.primary = [GOOGLE_SHEETS_PRIMARY_KEY]
|
|
|
|
// if there is an id column, remove it, should never exist in GSheets
|
|
|
|
delete table.schema?.id
|
|
|
|
}
|
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:16:22 +01:00
|
|
|
const _TableSaveFunctions = TableSaveFunctions
|
|
|
|
export { _TableSaveFunctions as TableSaveFunctions }
|