diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts index 399d5f1d0c..76c29be538 100644 --- a/packages/server/src/api/controllers/datasource.ts +++ b/packages/server/src/api/controllers/datasource.ts @@ -79,7 +79,6 @@ async function buildSchemaHelper(datasource: Datasource) { let error = null if (errors && Object.keys(errors).length > 0) { const noKey = getErrorTables(errors, BuildSchemaErrors.NO_KEY) - const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN) if (noKey.length) { error = updateError( error, @@ -87,6 +86,8 @@ async function buildSchemaHelper(datasource: Datasource) { noKey ) } + + const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN) if (invalidCol.length) { const invalidCols = Object.values(InvalidColumns).join(", ") error = updateError( @@ -95,6 +96,15 @@ async function buildSchemaHelper(datasource: Datasource) { invalidCol ) } + + const noHeader = getErrorTables(errors, BuildSchemaErrors.NO_HEADER_ROW) + if (noHeader.length) { + error = updateError( + error, + `No header row found in the following:`, + noHeader + ) + } } return { tables: connector.tables, error } } diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 326389996d..ccd4aaf485 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -162,6 +162,7 @@ export enum InvalidColumns { export enum BuildSchemaErrors { NO_KEY = "no_key", INVALID_COLUMN = "invalid_column", + NO_HEADER_ROW = "no_header_row", } export enum AutomationErrors { diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index 5360d6b319..0ee3df12df 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -21,7 +21,7 @@ import { GoogleSpreadsheet, GoogleSpreadsheetRow } from "google-spreadsheet" import fetch from "node-fetch" import { cache, configs, context, HTTPError } from "@budibase/backend-core" import { dataFilters, utils } from "@budibase/shared-core" -import { GOOGLE_SHEETS_PRIMARY_KEY } from "../constants" +import { BuildSchemaErrors, GOOGLE_SHEETS_PRIMARY_KEY } from "../constants" interface GoogleSheetsConfig { spreadsheetId: string @@ -289,11 +289,29 @@ class GoogleSheetsIntegration implements DatasourcePlus { await this.connect() const sheets = this.client.sheetsByIndex const tables: Record = {} + const errors: Record = {} await utils.parallelForeach( sheets, async sheet => { // must fetch rows to determine schema - await sheet.getRows() + try { + await sheet.getRows() + } catch (err) { + // We expect this to always be an Error so if it's not, rethrow it to + // make sure we don't fail quietly. + if (!(err instanceof Error)) { + throw err; + } + + if (err.message.startsWith("No values in the header row")) { + errors[sheet.title] = BuildSchemaErrors.NO_HEADER_ROW + } else { + // If we get an error we don't have a BuildSchemaErrors enum variant + // for, rethrow to avoid failing quietly. + throw err; + } + return + } const id = buildExternalTableId(datasourceId, sheet.title) tables[sheet.title] = this.getTableSchema( @@ -307,7 +325,7 @@ class GoogleSheetsIntegration implements DatasourcePlus { ) const final = finaliseExternalTables(tables, entities) this.tables = final.tables - this.schemaErrors = final.errors + this.schemaErrors = { ...final.errors, ...errors } } async query(json: QueryJson) {