Skip to content

Commit 7ff24f1

Browse files
elsmrJoffcom
andauthored
fix(Google BigQuery Node): Better error messages, transform timestamps (#9255)
Co-authored-by: Jonathan Bennetts <[email protected]>
1 parent e896889 commit 7ff24f1

File tree

5 files changed

+39
-10
lines changed

5 files changed

+39
-10
lines changed

packages/nodes-base/nodes/Google/BigQuery/GoogleBigQuery.node.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ export class GoogleBigQuery extends VersionedNodeType {
1313
group: ['input'],
1414
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
1515
description: 'Consume Google BigQuery API',
16-
defaultVersion: 2,
16+
defaultVersion: 2.1,
1717
};
1818

1919
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
2020
1: new GoogleBigQueryV1(baseDescription),
2121
2: new GoogleBigQueryV2(baseDescription),
22+
2.1: new GoogleBigQueryV2(baseDescription),
2223
};
2324

2425
super(nodeVersions, baseDescription);

packages/nodes-base/nodes/Google/BigQuery/test/v2/utils/utils.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import type { IDataObject, IExecuteFunctions } from 'n8n-workflow';
1+
import type { IDataObject, IExecuteFunctions, INode } from 'n8n-workflow';
22
import { constructExecutionMetaData } from 'n8n-core';
33
import { mock } from 'jest-mock-extended';
44
import { prepareOutput } from '../../../v2/helpers/utils';
55

66
describe('Google BigQuery v2 Utils', () => {
77
it('should prepareOutput', () => {
8-
const thisArg = mock<IExecuteFunctions>({ helpers: mock({ constructExecutionMetaData }) });
8+
const thisArg = mock<IExecuteFunctions>({
9+
getNode: () => ({ typeVersion: 2.1 }) as INode,
10+
helpers: mock({ constructExecutionMetaData }),
11+
});
912
const response: IDataObject = {
1013
kind: 'bigquery#getQueryResultsResponse',
1114
etag: 'e_tag',

packages/nodes-base/nodes/Google/BigQuery/v2/actions/database/executeQuery.operation.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,16 @@ export async function execute(this: IExecuteFunctions): Promise<INodeExecutionDa
249249
returnData.push(...executionErrorData);
250250
continue;
251251
}
252-
if ((error.message as string).includes('location')) {
252+
if ((error.message as string).includes('location') || error.httpCode === '404') {
253253
error.description =
254254
"Are you sure your table is in that region? You can specify the region using the 'Location' parameter from options.";
255255
}
256+
257+
if (error.httpCode === '403' && error.message.includes('Drive')) {
258+
error.description =
259+
'If your table(s) pull from a document in Google Drive, make sure that document is shared with your user';
260+
}
261+
256262
throw new NodeOperationError(this.getNode(), error as Error, {
257263
itemIndex: i,
258264
description: error.description,

packages/nodes-base/nodes/Google/BigQuery/v2/actions/versionDescription.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const versionDescription: INodeTypeDescription = {
77
name: 'googleBigQuery',
88
icon: 'file:googleBigQuery.svg',
99
group: ['input'],
10-
version: 2,
10+
version: [2, 2.1],
1111
subtitle: '={{$parameter["operation"]}}',
1212
description: 'Consume Google BigQuery API',
1313
defaults: {

packages/nodes-base/nodes/Google/BigQuery/v2/helpers/utils.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { DateTime } from 'luxon';
12
import type { IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
23
import { jsonParse, NodeOperationError } from 'n8n-workflow';
34
import type { SchemaField, TableRawData, TableSchema } from './interfaces';
45

5-
function getFieldValue(schemaField: SchemaField, field: IDataObject) {
6+
function getFieldValue(schemaField: SchemaField, field: IDataObject, parseTimestamps = false) {
67
if (schemaField.type === 'RECORD') {
78
// eslint-disable-next-line @typescript-eslint/no-use-before-define
89
return simplify([field.v as TableRawData], schemaField.fields as unknown as SchemaField[]);
@@ -12,6 +13,9 @@ function getFieldValue(schemaField: SchemaField, field: IDataObject) {
1213
try {
1314
value = jsonParse(value as string);
1415
} catch (error) {}
16+
} else if (schemaField.type === 'TIMESTAMP' && parseTimestamps) {
17+
const dt = DateTime.fromSeconds(Number(value));
18+
value = dt.isValid ? dt.toISO() : value;
1519
}
1620
return value;
1721
}
@@ -26,18 +30,27 @@ export function wrapData(data: IDataObject | IDataObject[]): INodeExecutionData[
2630
}));
2731
}
2832

29-
export function simplify(data: TableRawData[], schema: SchemaField[], includeSchema = false) {
33+
export function simplify(
34+
data: TableRawData[],
35+
schema: SchemaField[],
36+
includeSchema = false,
37+
parseTimestamps = false,
38+
) {
3039
const returnData: IDataObject[] = [];
3140
for (const entry of data) {
3241
const record: IDataObject = {};
3342

3443
for (const [index, field] of entry.f.entries()) {
3544
if (schema[index].mode !== 'REPEATED') {
36-
record[schema[index].name] = getFieldValue(schema[index], field);
45+
record[schema[index].name] = getFieldValue(schema[index], field, parseTimestamps);
3746
} else {
3847
record[schema[index].name] = (field.v as unknown as IDataObject[]).flatMap(
3948
(repeatedField) => {
40-
return getFieldValue(schema[index], repeatedField as unknown as IDataObject);
49+
return getFieldValue(
50+
schema[index],
51+
repeatedField as unknown as IDataObject,
52+
parseTimestamps,
53+
);
4154
},
4255
);
4356
}
@@ -68,12 +81,18 @@ export function prepareOutput(
6881
responseData = response;
6982
} else {
7083
const { rows, schema } = response;
84+
const parseTimestamps = this.getNode().typeVersion >= 2.1;
7185

7286
if (rows !== undefined && schema !== undefined) {
7387
const fields = (schema as TableSchema).fields;
7488
responseData = rows;
7589

76-
responseData = simplify(responseData as TableRawData[], fields, includeSchema);
90+
responseData = simplify(
91+
responseData as TableRawData[],
92+
fields,
93+
includeSchema,
94+
parseTimestamps,
95+
);
7796
} else if (schema && includeSchema) {
7897
responseData = { success: true, _schema: schema };
7998
} else {

0 commit comments

Comments
 (0)