2023-01-27 03:22:44 -08:00
import type {
2023-03-09 09:13:15 -08:00
IPollFunctions ,
2020-11-06 17:18:10 -08:00
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeOperationError } from 'n8n-workflow' ;
2020-11-06 17:18:10 -08:00
2024-01-15 06:45:33 -08:00
import moment from 'moment-timezone' ;
2023-07-17 09:42:30 -07:00
import type { IRecord } from './v1/GenericFunctions' ;
import { apiRequestAllItems , downloadRecordAttachments } from './v1/GenericFunctions' ;
2020-11-06 17:18:10 -08:00
export class AirtableTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'Airtable Trigger' ,
name : 'airtableTrigger' ,
2020-11-25 05:09:29 -08:00
icon : 'file:airtable.svg' ,
2020-11-06 17:18:10 -08:00
group : [ 'trigger' ] ,
version : 1 ,
description : 'Starts the workflow when Airtable events occur' ,
subtitle : '={{$parameter["event"]}}' ,
defaults : {
name : 'Airtable Trigger' ,
} ,
credentials : [
{
name : 'airtableApi' ,
required : true ,
2023-05-04 03:17:22 -07:00
displayOptions : {
show : {
authentication : [ 'airtableApi' ] ,
} ,
} ,
} ,
{
name : 'airtableTokenApi' ,
required : true ,
displayOptions : {
show : {
authentication : [ 'airtableTokenApi' ] ,
} ,
} ,
2020-11-06 17:18:10 -08:00
} ,
2023-07-17 09:42:30 -07:00
{
name : 'airtableOAuth2Api' ,
required : true ,
displayOptions : {
show : {
authentication : [ 'airtableOAuth2Api' ] ,
} ,
} ,
} ,
2020-11-06 17:18:10 -08:00
] ,
polling : true ,
inputs : [ ] ,
outputs : [ 'main' ] ,
properties : [
2023-05-04 03:17:22 -07:00
{
displayName : 'Authentication' ,
name : 'authentication' ,
type : 'options' ,
options : [
{
name : 'API Key' ,
value : 'airtableApi' ,
} ,
{
name : 'Access Token' ,
value : 'airtableTokenApi' ,
} ,
2023-07-17 09:42:30 -07:00
{
name : 'OAuth2' ,
value : 'airtableOAuth2Api' ,
} ,
2023-05-04 03:17:22 -07:00
] ,
default : 'airtableApi' ,
} ,
2020-11-06 17:18:10 -08:00
{
2022-10-27 01:51:32 -07:00
displayName : 'Base' ,
2020-11-06 17:18:10 -08:00
name : 'baseId' ,
2022-10-27 01:51:32 -07:00
type : 'resourceLocator' ,
default : { mode : 'url' , value : '' } ,
2020-11-06 17:18:10 -08:00
required : true ,
2022-10-27 01:51:32 -07:00
description : 'The Airtable Base in which to operate on' ,
modes : [
{
displayName : 'By URL' ,
name : 'url' ,
type : 'string' ,
placeholder : 'https://airtable.com/app12DiScdfes/tblAAAAAAAAAAAAA/viwHdfasdfeieg5p' ,
validation : [
{
type : 'regex' ,
properties : {
regex : 'https://airtable.com/([a-zA-Z0-9]{2,})/.*' ,
errorMessage : 'Not a valid Airtable Base URL' ,
} ,
} ,
] ,
extractValue : {
type : 'regex' ,
regex : 'https://airtable.com/([a-zA-Z0-9]{2,})' ,
} ,
} ,
{
displayName : 'ID' ,
name : 'id' ,
type : 'string' ,
validation : [
{
type : 'regex' ,
properties : {
regex : '[a-zA-Z0-9]{2,}' ,
errorMessage : 'Not a valid Airtable Base ID' ,
} ,
} ,
] ,
placeholder : 'appD3dfaeidke' ,
url : '=https://airtable.com/{{$value}}' ,
} ,
] ,
2020-11-06 17:18:10 -08:00
} ,
{
2022-10-27 01:51:32 -07:00
displayName : 'Table' ,
2020-11-06 17:18:10 -08:00
name : 'tableId' ,
2022-10-27 01:51:32 -07:00
type : 'resourceLocator' ,
default : { mode : 'url' , value : '' } ,
2020-11-06 17:18:10 -08:00
required : true ,
2022-10-27 01:51:32 -07:00
modes : [
{
displayName : 'By URL' ,
name : 'url' ,
type : 'string' ,
placeholder : 'https://airtable.com/app12DiScdfes/tblAAAAAAAAAAAAA/viwHdfasdfeieg5p' ,
validation : [
{
type : 'regex' ,
properties : {
regex : 'https://airtable.com/[a-zA-Z0-9]{2,}/([a-zA-Z0-9]{2,})/.*' ,
errorMessage : 'Not a valid Airtable Table URL' ,
} ,
} ,
] ,
extractValue : {
type : 'regex' ,
regex : 'https://airtable.com/[a-zA-Z0-9]{2,}/([a-zA-Z0-9]{2,})' ,
} ,
} ,
{
displayName : 'ID' ,
name : 'id' ,
type : 'string' ,
validation : [
{
type : 'regex' ,
properties : {
regex : '[a-zA-Z0-9]{2,}' ,
errorMessage : 'Not a valid Airtable Table ID' ,
} ,
} ,
] ,
placeholder : 'tbl3dirwqeidke' ,
} ,
] ,
2020-11-10 13:40:44 -08:00
} ,
{
2020-11-06 17:18:10 -08:00
displayName : 'Trigger Field' ,
name : 'triggerField' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
'A Created Time or Last Modified Time field that will be used to sort records. If you do not have a Created Time or Last Modified Time field in your schema, please create one, because without this field trigger will not work correctly.' ,
2020-11-06 17:18:10 -08:00
required : true ,
} ,
2021-02-04 06:43:48 -08:00
{
displayName : 'Download Attachments' ,
name : 'downloadAttachments' ,
type : 'boolean' ,
default : false ,
2022-08-01 13:47:55 -07:00
description : "Whether the attachment fields define in 'Download Fields' will be downloaded" ,
2021-02-04 06:43:48 -08:00
} ,
{
displayName : 'Download Fields' ,
name : 'downloadFieldNames' ,
type : 'string' ,
required : true ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
downloadAttachments : [ true ] ,
2021-02-04 06:43:48 -08:00
} ,
} ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
"Name of the fields of type 'attachment' that should be downloaded. Multiple ones can be defined separated by comma. Case sensitive." ,
2021-02-04 06:43:48 -08:00
} ,
2020-11-06 17:18:10 -08:00
{
displayName : 'Additional Fields' ,
name : 'additionalFields' ,
type : 'collection' ,
placeholder : 'Add Field' ,
default : { } ,
options : [
{
displayName : 'Fields' ,
name : 'fields' ,
type : 'string' ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'multiple' ,
2020-11-06 17:18:10 -08:00
default : '' ,
2022-05-06 14:01:25 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-description-miscased-id
2022-08-01 13:47:55 -07:00
description :
'Fields to be included in the response. Multiple ones can be set separated by comma. Example: <code>name, id</code>. By default just the trigger field will be included.' ,
2020-11-06 17:18:10 -08:00
} ,
{
displayName : 'Formula' ,
name : 'formula' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
'Formulas may involve functions, numeric operations, logical operations, and text operations that operate on fields. More info <a href="https://support.airtable.com/hc/en-us/articles/203255215-Formula-Field-Reference">here</a>.' ,
2020-11-06 17:18:10 -08:00
} ,
{
displayName : 'View ID' ,
name : 'viewId' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
'The name or ID of a view in the table. If set, only the records in that view will be returned.' ,
2020-11-06 17:18:10 -08:00
} ,
] ,
} ,
] ,
} ;
async poll ( this : IPollFunctions ) : Promise < INodeExecutionData [ ] [ ] | null > {
2021-02-04 06:43:48 -08:00
const downloadAttachments = this . getNodeParameter ( 'downloadAttachments' , 0 ) as boolean ;
2020-11-06 17:18:10 -08:00
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
const additionalFields = this . getNodeParameter ( 'additionalFields' ) as IDataObject ;
2022-10-27 01:51:32 -07:00
const base = this . getNodeParameter ( 'baseId' , '' , { extractValue : true } ) as string ;
const table = this . getNodeParameter ( 'tableId' , '' , { extractValue : true } ) as string ;
2020-11-06 17:18:10 -08:00
const triggerField = this . getNodeParameter ( 'triggerField' ) as string ;
2023-05-04 03:17:22 -07:00
const qs : IDataObject = { } ;
2020-11-06 17:18:10 -08:00
const endpoint = ` ${ base } / ${ table } ` ;
const now = moment ( ) . utc ( ) . format ( ) ;
2022-08-01 13:47:55 -07:00
const startDate = ( webhookData . lastTimeChecked as string ) || now ;
2020-11-06 17:18:10 -08:00
const endDate = now ;
if ( additionalFields . viewId ) {
qs . view = additionalFields . viewId ;
}
if ( additionalFields . fields ) {
qs [ 'fields[]' ] = ( additionalFields . fields as string ) . split ( ',' ) ;
}
qs . filterByFormula = ` IS_AFTER({ ${ triggerField } }, DATETIME_PARSE(" ${ startDate } ", "YYYY-MM-DD HH:mm:ss")) ` ;
if ( additionalFields . formula ) {
qs . filterByFormula = ` AND( ${ qs . filterByFormula } , ${ additionalFields . formula } ) ` ;
}
2020-11-14 17:21:41 -08:00
if ( this . getMode ( ) === 'manual' ) {
delete qs . filterByFormula ;
qs . maxRecords = 1 ;
}
2020-11-06 17:18:10 -08:00
const { records } = await apiRequestAllItems . call ( this , 'GET' , endpoint , { } , qs ) ;
webhookData . lastTimeChecked = endDate ;
if ( Array . isArray ( records ) && records . length ) {
2020-11-24 10:58:49 -08:00
if ( this . getMode ( ) === 'manual' && records [ 0 ] . fields [ triggerField ] === undefined ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` The Field " ${ triggerField } " does not exist. ` ) ;
2020-11-24 10:58:49 -08:00
}
2022-12-02 12:54:28 -08:00
if ( downloadAttachments ) {
2022-08-01 13:47:55 -07:00
const downloadFieldNames = ( this . getNodeParameter ( 'downloadFieldNames' , 0 ) as string ) . split (
',' ,
) ;
2023-02-27 19:39:43 -08:00
const data = await downloadRecordAttachments . call (
this ,
records as IRecord [ ] ,
downloadFieldNames ,
) ;
2021-02-04 06:43:48 -08:00
return [ data ] ;
}
2020-11-06 17:18:10 -08:00
return [ this . helpers . returnJsonArray ( records ) ] ;
}
return null ;
}
}