/* eslint-disable n8n-nodes-base/node-filename-against-convention */
import type {
INodeType,
INodeTypeDescription,
IExecuteFunctions,
INodeExecutionData,
} from 'n8n-workflow';
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
import { document, sheet } from '../../Google/Sheet/GoogleSheetsTrigger.node';
import { readFilter } from '../../Google/Sheet/v2/actions/sheet/read.operation';
import { authentication } from '../../Google/Sheet/v2/actions/versionDescription';
import type { ILookupValues } from '../../Google/Sheet/v2/helpers/GoogleSheets.types';
import { listSearch, loadOptions } from '../methods';
import {
getGoogleSheet,
getResults,
getRowsLeft,
getNumberOfRowsLeftFiltered,
getSheet,
} from '../utils/evaluationTriggerUtils';
export const DEFAULT_STARTING_ROW = 2;
export class EvaluationTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Evaluation Trigger',
icon: 'fa:check-double',
name: 'evaluationTrigger',
group: ['trigger'],
version: 4.6,
description: 'Run a test dataset through your workflow to check performance',
eventTriggerDescription: '',
defaults: {
name: 'When fetching a dataset row',
color: '#c3c9d5',
},
inputs: [],
outputs: [NodeConnectionTypes.Main],
properties: [
{
displayName:
'Pulls a test dataset from a Google Sheet. The workflow will run once for each row, in sequence. Tips for wiring this node up here.',
name: 'notice',
type: 'notice',
default: '',
},
{
displayName: 'Credentials',
name: 'credentials',
type: 'credentials',
default: '',
},
authentication,
{
...document,
displayName: 'Document Containing Dataset',
hint: 'Example dataset format here',
},
{ ...sheet, displayName: 'Sheet Containing Dataset' },
{
displayName: 'Limit Rows',
name: 'limitRows',
type: 'boolean',
default: false,
noDataExpression: true,
description: 'Whether to limit number of rows to process',
},
{
displayName: 'Max Rows to Process',
name: 'maxRows',
type: 'number',
default: 10,
description: 'Maximum number of rows to process',
noDataExpression: false,
displayOptions: { show: { limitRows: [true] } },
},
readFilter,
],
codex: {
alias: ['Test', 'Metrics', 'Evals', 'Set Output', 'Set Metrics'],
},
credentials: [
{
name: 'googleApi',
required: true,
displayOptions: {
show: {
authentication: ['serviceAccount'],
},
},
testedBy: 'googleApiCredentialTest',
},
{
name: 'googleSheetsOAuth2Api',
required: true,
displayOptions: {
show: {
authentication: ['oAuth2'],
},
},
},
],
};
methods = { loadOptions, listSearch };
async execute(this: IExecuteFunctions): Promise {
const inputData = this.getInputData();
const MAX_ROWS = 1000;
const maxRows = this.getNodeParameter('limitRows', 0)
? (this.getNodeParameter('maxRows', 0) as number) + 1
: MAX_ROWS;
const previousRunRowNumber = inputData?.[0]?.json?.row_number;
const previousRunRowsLeft = inputData?.[0]?.json?._rowsLeft;
const firstDataRow =
typeof previousRunRowNumber === 'number' && previousRunRowsLeft !== 0
? previousRunRowNumber + 1
: DEFAULT_STARTING_ROW;
const rangeOptions = {
rangeDefinition: 'specifyRange',
headerRow: 1,
firstDataRow,
};
const googleSheetInstance = getGoogleSheet.call(this);
const googleSheet = await getSheet.call(this, googleSheetInstance);
const allRows = await getResults.call(this, [], googleSheetInstance, googleSheet, rangeOptions);
// This is for test runner which requires a different return format
if (inputData[0].json.requestDataset) {
const testRunnerResult = await getResults.call(
this,
[],
googleSheetInstance,
googleSheet,
{},
);
const result = testRunnerResult.slice(0, maxRows - 1);
return [result];
}
const hasFilter = this.getNodeParameter('filtersUI.values', 0, []) as ILookupValues[];
if (hasFilter.length > 0) {
const currentRow = allRows[0];
const currentRowNumber = currentRow.json?.row_number as number;
if (currentRow === undefined) {
throw new NodeOperationError(this.getNode(), 'No row found');
}
const rowsLeft = await getNumberOfRowsLeftFiltered.call(
this,
googleSheetInstance,
googleSheet.title,
currentRowNumber + 1,
maxRows,
);
currentRow.json._rowsLeft = rowsLeft;
return [[currentRow]];
} else {
const currentRow = allRows.find((row) => (row?.json?.row_number as number) === firstDataRow);
const rowsLeft = await getRowsLeft.call(
this,
googleSheetInstance,
googleSheet.title,
`${googleSheet.title}!${firstDataRow}:${maxRows}`,
);
if (currentRow === undefined) {
throw new NodeOperationError(this.getNode(), 'No row found');
}
currentRow.json._rowsLeft = rowsLeft;
return [[currentRow]];
}
}
}