diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index 6a3015d916..a00e65687f 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -7,6 +7,7 @@ import { generateJunctionTableName, foreignKeyStructure, hasTypeChanged, + setStaticSchemas, } from "./utils" import { FieldTypes } from "../../../constants" import { makeExternalQuery } from "../../../integrations/base/query" @@ -195,19 +196,19 @@ function isRelationshipSetup(column: FieldSchema) { } export async function save(ctx: UserCtx) { - const table: TableRequest = ctx.request.body - const renamed = table?._rename + const inputs: TableRequest = ctx.request.body + const renamed = inputs?._rename // can't do this right now - delete table.rows + delete inputs.rows const datasourceId = getDatasourceId(ctx.request.body)! // table doesn't exist already, note that it is created - if (!table._id) { - table.created = true + if (!inputs._id) { + inputs.created = true } let tableToSave: TableRequest = { type: "table", - _id: buildExternalTableId(datasourceId, table.name), - ...table, + _id: buildExternalTableId(datasourceId, inputs.name), + ...inputs, } let oldTable @@ -224,6 +225,10 @@ export async function save(ctx: UserCtx) { if (!datasource.entities) { datasource.entities = {} } + + // GSheets is a specific case - only ever has a static primary key + tableToSave = setStaticSchemas(datasource, tableToSave) + const oldTables = cloneDeep(datasource.entities) const tables: Record = datasource.entities @@ -246,7 +251,7 @@ export async function save(ctx: UserCtx) { const junctionTable = generateManyLinkSchema( datasource, schema, - table, + tableToSave, relatedTable ) if (tables[junctionTable.name]) { @@ -256,10 +261,12 @@ export async function save(ctx: UserCtx) { extraTablesToUpdate.push(junctionTable) } else { const fkTable = - relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable + relationType === RelationshipTypes.ONE_TO_MANY + ? tableToSave + : relatedTable const foreignKey = generateLinkSchema( schema, - table, + tableToSave, relatedTable, relationType ) @@ -271,11 +278,11 @@ export async function save(ctx: UserCtx) { fkTable.constrained.push(foreignKey) } // foreign key is in other table, need to save it to external - if (fkTable._id !== table._id) { + if (fkTable._id !== tableToSave._id) { extraTablesToUpdate.push(fkTable) } } - generateRelatedSchema(schema, relatedTable, table, relatedColumnName) + generateRelatedSchema(schema, relatedTable, tableToSave, relatedColumnName) schema.main = true } diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index bbccde467b..7c5c81939a 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -1,7 +1,11 @@ import { parse, isSchema, isRows } from "../../../utilities/schema" import { getRowParams, generateRowID, InternalTables } from "../../../db/utils" import { isEqual } from "lodash" -import { AutoFieldSubTypes, FieldTypes } from "../../../constants" +import { + AutoFieldSubTypes, + FieldTypes, + GOOGLE_SHEETS_PRIMARY_KEY, +} from "../../../constants" import { inputProcessing, cleanupAttachments, @@ -16,7 +20,7 @@ import viewTemplate from "../view/viewBuilder" import { cloneDeep } from "lodash/fp" import { quotas } from "@budibase/pro" import { events, context } from "@budibase/backend-core" -import { Database } from "@budibase/types" +import { Database, Datasource, SourceName, Table } from "@budibase/types" export async function clearColumns(table: any, columnNames: any) { const db: Database = context.getAppDB() @@ -392,5 +396,17 @@ export function hasTypeChanged(table: any, oldTable: any) { return false } +// 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 +} + const _TableSaveFunctions = TableSaveFunctions export { _TableSaveFunctions as TableSaveFunctions } diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index e55ad09add..9d6a1c247a 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -180,3 +180,4 @@ export enum AutomationErrors { // pass through the list from the auth/core lib export const ObjectStoreBuckets = objectStore.ObjectStoreBuckets export const MAX_AUTOMATION_RECURRING_ERRORS = 5 +export const GOOGLE_SHEETS_PRIMARY_KEY = "rowNumber" diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index e9fde99a2b..f8bc84adea 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -19,6 +19,7 @@ import { GoogleSpreadsheet } from "google-spreadsheet" import fetch from "node-fetch" import { configs, HTTPError } from "@budibase/backend-core" import { dataFilters } from "@budibase/shared-core" +import { GOOGLE_SHEETS_PRIMARY_KEY } from "../constants" interface GoogleSheetsConfig { spreadsheetId: string @@ -227,7 +228,7 @@ class GoogleSheetsIntegration implements DatasourcePlus { // base table const table: Table = { name: title, - primary: ["rowNumber"], + primary: [GOOGLE_SHEETS_PRIMARY_KEY], schema: {}, } if (id) {