import type { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-core'; import type { OptionsWithUri } from 'request'; import type { IDataObject, JsonObject } from 'n8n-workflow'; import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import type { GristCredentials, GristDefinedFields, GristFilterProperties, GristSortProperties, } from './types'; export async function gristApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: IDataObject | number[] = {}, qs: IDataObject = {}, ) { const { apiKey, planType, customSubdomain, selfHostedUrl } = (await this.getCredentials( 'gristApi', )) as GristCredentials; const gristapiurl = planType === 'free' ? `https://docs.getgrist.com/api${endpoint}` : planType === 'paid' ? `https://${customSubdomain}.getgrist.com/api${endpoint}` : `${selfHostedUrl}/api${endpoint}`; const options: OptionsWithUri = { headers: { Authorization: `Bearer ${apiKey}`, }, method, uri: gristapiurl, qs, body, json: true, }; if (!Object.keys(body).length) { delete options.body; } if (!Object.keys(qs).length) { delete options.qs; } try { return await this.helpers.request(options); } catch (error) { throw new NodeApiError(this.getNode(), error as JsonObject); } } export function parseSortProperties(sortProperties: GristSortProperties) { return sortProperties.reduce((acc, cur, curIdx) => { if (cur.direction === 'desc') acc += '-'; acc += cur.field; if (curIdx !== sortProperties.length - 1) acc += ','; return acc; }, ''); } export function parseFilterProperties(filterProperties: GristFilterProperties) { return filterProperties.reduce<{ [key: string]: Array }>((acc, cur) => { acc[cur.field] = acc[cur.field] ?? []; const values = isNaN(Number(cur.values)) ? cur.values : Number(cur.values); acc[cur.field].push(values); return acc; }, {}); } export function parseDefinedFields(fieldsToSendProperties: GristDefinedFields) { return fieldsToSendProperties.reduce<{ [key: string]: string }>((acc, cur) => { acc[cur.fieldId] = cur.fieldValue; return acc; }, {}); } export function parseAutoMappedInputs(incomingKeys: string[], inputsToIgnore: string[], item: any) { return incomingKeys.reduce<{ [key: string]: any }>((acc, curKey) => { if (inputsToIgnore.includes(curKey)) return acc; acc = { ...acc, [curKey]: item[curKey] }; return acc; }, {}); } export function throwOnZeroDefinedFields(this: IExecuteFunctions, fields: GristDefinedFields) { if (!fields?.length) { throw new NodeOperationError( this.getNode(), "No defined data found. Please specify the data to send in 'Fields to Send'.", ); } }