2023-01-27 03:22:44 -08:00
import type {
2023-03-09 09:13:15 -08:00
IExecuteFunctions ,
2019-07-07 10:00:05 -07:00
IDataObject ,
INodeExecutionData ,
INodeType ,
2020-10-01 05:01:39 -07:00
INodeTypeDescription ,
2019-07-07 10:00:05 -07:00
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeOperationError } from 'n8n-workflow' ;
2019-07-07 10:00:05 -07:00
2023-02-27 19:39:43 -08:00
import type { IRecord } from './GenericFunctions' ;
2022-08-01 13:47:55 -07:00
import { apiRequest , apiRequestAllItems , downloadRecordAttachments } from './GenericFunctions' ;
2019-07-07 10:00:05 -07:00
export class Airtable implements INodeType {
description : INodeTypeDescription = {
displayName : 'Airtable' ,
name : 'airtable' ,
2020-11-24 01:53:39 -08:00
icon : 'file:airtable.svg' ,
2019-07-07 10:00:05 -07:00
group : [ 'input' ] ,
version : 1 ,
2019-08-26 13:05:20 -07:00
description : 'Read, update, write and delete data from Airtable' ,
2019-07-07 10:00:05 -07:00
defaults : {
name : 'Airtable' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
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-10-22 06:46:03 -07:00
} ,
2023-06-21 01:54:32 -07:00
{
name : 'airtableOAuth2Api' ,
required : true ,
displayOptions : {
show : {
authentication : [ 'airtableOAuth2Api' ] ,
} ,
} ,
} ,
2019-07-07 10:00:05 -07:00
] ,
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-06-21 01:54:32 -07:00
{
name : 'OAuth2' ,
value : 'airtableOAuth2Api' ,
} ,
2023-05-04 03:17:22 -07:00
] ,
default : 'airtableApi' ,
} ,
2019-07-07 10:00:05 -07:00
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2019-07-07 10:00:05 -07:00
options : [
{
name : 'Append' ,
value : 'append' ,
2020-07-24 03:56:41 -07:00
description : 'Append the data to a table' ,
2022-07-10 13:50:51 -07:00
action : 'Append data to a table' ,
2019-07-07 10:00:05 -07:00
} ,
{
name : 'Delete' ,
value : 'delete' ,
2020-10-22 06:46:03 -07:00
description : 'Delete data from a table' ,
2022-07-10 13:50:51 -07:00
action : 'Delete data from a table' ,
2019-07-07 10:00:05 -07:00
} ,
{
name : 'List' ,
value : 'list' ,
2020-10-22 06:46:03 -07:00
description : 'List data from a table' ,
2022-07-10 13:50:51 -07:00
action : 'List data from a table' ,
2019-07-07 10:00:05 -07:00
} ,
{
name : 'Read' ,
value : 'read' ,
2020-10-22 06:46:03 -07:00
description : 'Read data from a table' ,
2022-07-10 13:50:51 -07:00
action : 'Read data from a table' ,
2019-07-07 10:00:05 -07:00
} ,
{
name : 'Update' ,
value : 'update' ,
2020-10-22 06:46:03 -07:00
description : 'Update data in a table' ,
2022-07-10 13:50:51 -07:00
action : 'Update data in a table' ,
2019-07-07 10:00:05 -07:00
} ,
] ,
default : 'read' ,
} ,
// ----------------------------------
// All
// ----------------------------------
2022-09-21 06:44:45 -07:00
2019-07-07 10:00:05 -07:00
{
2022-09-21 06:44:45 -07:00
displayName : 'Base' ,
2019-07-07 10:00:05 -07:00
name : 'application' ,
2022-09-21 06:44:45 -07:00
type : 'resourceLocator' ,
default : { mode : 'url' , value : '' } ,
2019-07-07 10:00:05 -07:00
required : true ,
2022-09-21 06:44:45 -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}}' ,
} ,
] ,
2019-07-07 10:00:05 -07:00
} ,
{
2022-09-21 06:44:45 -07:00
displayName : 'Table' ,
2019-07-07 10:00:05 -07:00
name : 'table' ,
2022-09-21 06:44:45 -07:00
type : 'resourceLocator' ,
default : { mode : 'url' , value : '' } ,
2019-07-07 10:00:05 -07:00
required : true ,
2022-09-21 06:44:45 -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' ,
} ,
] ,
2019-07-07 10:00:05 -07:00
} ,
// ----------------------------------
// append
// ----------------------------------
{
displayName : 'Add All Fields' ,
name : 'addAllFields' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'append' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether all fields should be sent to Airtable or only specific ones' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Fields' ,
name : 'fields' ,
type : 'string' ,
typeOptions : {
multipleValues : true ,
multipleValueButtonText : 'Add Field' ,
} ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'single' ,
2019-07-07 10:00:05 -07:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
addAllFields : [ false ] ,
operation : [ 'append' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
2019-10-20 08:38:11 -07:00
default : [ ] ,
2019-07-07 10:00:05 -07:00
placeholder : 'Name' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'The name of fields for which data should be sent to Airtable' ,
2019-07-07 10:00:05 -07:00
} ,
// ----------------------------------
// delete
// ----------------------------------
{
2021-03-12 01:23:35 -08:00
displayName : 'ID' ,
2019-07-07 10:00:05 -07:00
name : 'id' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'delete' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : '' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'ID of the record to delete' ,
2019-07-07 10:00:05 -07:00
} ,
// ----------------------------------
// list
// ----------------------------------
{
displayName : 'Return All' ,
name : 'returnAll' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'list' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : true ,
2022-05-06 14:01:25 -07:00
description : 'Whether to return all results or only up to a given limit' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Limit' ,
name : 'limit' ,
type : 'number' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'list' ] ,
returnAll : [ false ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
typeOptions : {
minValue : 1 ,
maxValue : 100 ,
} ,
default : 100 ,
2022-05-06 14:01:25 -07:00
description : 'Max number of results to return' ,
2019-07-07 10:00:05 -07:00
} ,
2020-11-24 01:53:39 -08:00
{
displayName : 'Download Attachments' ,
name : 'downloadAttachments' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'list' ] ,
2020-11-24 01:53:39 -08:00
} ,
} ,
default : false ,
2022-08-01 13:47:55 -07:00
description : "Whether the attachment fields define in 'Download Fields' will be downloaded" ,
2020-11-24 01:53:39 -08:00
} ,
{
displayName : 'Download Fields' ,
name : 'downloadFieldNames' ,
type : 'string' ,
required : true ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'multiple' ,
2020-11-24 01:53:39 -08:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'list' ] ,
downloadAttachments : [ true ] ,
2020-11-24 01:53:39 -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 and cannot include spaces after a comma." ,
2020-11-24 01:53:39 -08:00
} ,
2019-07-07 10:00:05 -07:00
{
displayName : 'Additional Options' ,
name : 'additionalOptions' ,
type : 'collection' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'list' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : { } ,
description : 'Additional options which decide which records should be returned' ,
placeholder : 'Add Option' ,
options : [
{
displayName : 'Fields' ,
name : 'fields' ,
type : 'string' ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'single' ,
2019-07-07 10:00:05 -07:00
typeOptions : {
multipleValues : true ,
multipleValueButtonText : 'Add Field' ,
} ,
2019-10-20 08:38:11 -07:00
default : [ ] ,
2019-07-07 10:00:05 -07:00
placeholder : 'Name' ,
2022-08-01 13:47:55 -07:00
description :
'Only data for fields whose names are in this list will be included in the records' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Filter By Formula' ,
name : 'filterByFormula' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
placeholder : "NOT({Name} = '')" ,
description :
'A formula used to filter records. The formula will be evaluated for each record, and if the result is not 0, false, "", NaN, [], or #Error! the record will be included in the response.' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Sort' ,
name : 'sort' ,
placeholder : 'Add Sort Rule' ,
2022-05-06 14:01:25 -07:00
description : 'Defines how the returned records should be ordered' ,
2019-07-07 10:00:05 -07:00
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
default : { } ,
options : [
{
name : 'property' ,
displayName : 'Property' ,
values : [
{
displayName : 'Field' ,
name : 'field' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Name of the field to sort on' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Direction' ,
name : 'direction' ,
type : 'options' ,
options : [
{
name : 'ASC' ,
value : 'asc' ,
description : 'Sort in ascending order (small -> large)' ,
} ,
{
name : 'DESC' ,
value : 'desc' ,
2020-10-22 06:46:03 -07:00
description : 'Sort in descending order (large -> small)' ,
2019-07-07 10:00:05 -07:00
} ,
] ,
default : 'asc' ,
2022-05-06 14:01:25 -07:00
description : 'The sort direction' ,
2019-07-07 10:00:05 -07:00
} ,
2020-10-22 06:46:03 -07:00
] ,
2019-07-07 10:00:05 -07:00
} ,
] ,
} ,
{
displayName : 'View' ,
name : 'view' ,
type : 'string' ,
default : '' ,
placeholder : 'All Stories' ,
2022-08-01 13:47:55 -07:00
description :
'The name or ID of a view in the Stories table. If set, only the records in that view will be returned. The records will be sorted according to the order of the view.' ,
2019-07-07 10:00:05 -07:00
} ,
] ,
} ,
// ----------------------------------
// read
// ----------------------------------
{
2021-03-12 01:23:35 -08:00
displayName : 'ID' ,
2019-07-07 10:00:05 -07:00
name : 'id' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'read' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : '' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'ID of the record to return' ,
2019-07-07 10:00:05 -07:00
} ,
// ----------------------------------
// update
// ----------------------------------
{
2021-03-12 01:23:35 -08:00
displayName : 'ID' ,
2019-07-07 10:00:05 -07:00
name : 'id' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'update' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : '' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'ID of the record to update' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Update All Fields' ,
name : 'updateAllFields' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'update' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
default : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether all fields should be sent to Airtable or only specific ones' ,
2019-07-07 10:00:05 -07:00
} ,
{
displayName : 'Fields' ,
name : 'fields' ,
type : 'string' ,
typeOptions : {
multipleValues : true ,
multipleValueButtonText : 'Add Field' ,
} ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'single' ,
2019-07-07 10:00:05 -07:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
updateAllFields : [ false ] ,
operation : [ 'update' ] ,
2019-07-07 10:00:05 -07:00
} ,
} ,
2019-10-20 08:38:11 -07:00
default : [ ] ,
2019-07-07 10:00:05 -07:00
placeholder : 'Name' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'The name of fields for which data should be sent to Airtable' ,
2019-07-07 10:00:05 -07:00
} ,
2020-09-17 22:58:49 -07:00
// ----------------------------------
2021-07-03 05:07:48 -07:00
// append + delete + update
2020-09-17 22:58:49 -07:00
// ----------------------------------
{
2020-09-17 22:59:10 -07:00
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
2020-09-17 22:58:49 -07:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
operation : [ 'append' , 'delete' , 'update' ] ,
2020-09-17 22:58:49 -07:00
} ,
} ,
2020-09-17 22:59:10 -07:00
default : { } ,
options : [
2021-07-03 05:07:48 -07:00
{
displayName : 'Bulk Size' ,
name : 'bulkSize' ,
type : 'number' ,
typeOptions : {
minValue : 1 ,
maxValue : 10 ,
} ,
default : 10 ,
2022-05-06 14:01:25 -07:00
description : 'Number of records to process at once' ,
2021-07-03 05:07:48 -07:00
} ,
2020-10-22 14:36:30 -07:00
{
displayName : 'Ignore Fields' ,
name : 'ignoreFields' ,
type : 'string' ,
2023-02-21 05:49:59 -08:00
requiresDataPath : 'multiple' ,
2020-10-22 14:36:30 -07:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
'/operation' : [ 'update' ] ,
'/updateAllFields' : [ true ] ,
2020-10-22 14:36:30 -07:00
} ,
} ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Comma-separated list of fields to ignore' ,
2020-10-22 14:36:30 -07:00
} ,
2020-09-17 22:59:10 -07:00
{
displayName : 'Typecast' ,
name : 'typecast' ,
type : 'boolean' ,
2021-07-03 05:07:48 -07:00
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
'/operation' : [ 'append' , 'update' ] ,
2021-07-03 05:07:48 -07:00
} ,
} ,
2020-09-17 22:59:10 -07:00
default : false ,
2022-08-01 13:47:55 -07:00
description :
'Whether the Airtable API should attempt mapping of string values for linked records & select options' ,
2020-09-17 22:59:10 -07:00
} ,
] ,
2020-09-17 22:58:49 -07:00
} ,
2019-07-07 10:00:05 -07:00
] ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
2022-08-30 08:55:33 -07:00
const returnData : INodeExecutionData [ ] = [ ] ;
2019-07-07 10:00:05 -07:00
let responseData ;
2022-12-02 03:53:59 -08:00
const operation = this . getNodeParameter ( 'operation' , 0 ) ;
2019-07-07 10:00:05 -07:00
2022-09-23 02:56:57 -07:00
const application = this . getNodeParameter ( 'application' , 0 , undefined , {
extractValue : true ,
} ) as string ;
const table = encodeURI (
this . getNodeParameter ( 'table' , 0 , undefined , {
extractValue : true ,
} ) as string ,
) ;
2019-07-07 10:00:05 -07:00
let returnAll = false ;
let endpoint = '' ;
let requestMethod = '' ;
const body : IDataObject = { } ;
const qs : IDataObject = { } ;
if ( operation === 'append' ) {
// ----------------------------------
// append
// ----------------------------------
requestMethod = 'POST' ;
endpoint = ` ${ application } / ${ table } ` ;
let addAllFields : boolean ;
let fields : string [ ] ;
2020-09-17 22:59:10 -07:00
let options : IDataObject ;
2021-07-03 05:07:48 -07:00
const rows : IDataObject [ ] = [ ] ;
let bulkSize = 10 ;
2019-07-07 10:00:05 -07:00
for ( let i = 0 ; i < items . length ; i ++ ) {
2021-07-19 23:58:54 -07:00
try {
addAllFields = this . getNodeParameter ( 'addAllFields' , i ) as boolean ;
2022-12-02 12:54:28 -08:00
options = this . getNodeParameter ( 'options' , i , { } ) ;
2022-08-01 13:47:55 -07:00
bulkSize = ( options . bulkSize as number ) || bulkSize ;
2021-07-19 23:58:54 -07:00
const row : IDataObject = { } ;
2022-12-02 12:54:28 -08:00
if ( addAllFields ) {
2021-07-19 23:58:54 -07:00
// Add all the fields the item has
row . fields = { . . . items [ i ] . json } ;
2023-01-27 02:01:31 -08:00
delete ( row . fields as any ) . id ;
2021-07-19 23:58:54 -07:00
} else {
// Add only the specified fields
2023-05-04 03:17:22 -07:00
const rowFields : IDataObject = { } ;
2021-07-19 23:58:54 -07:00
fields = this . getNodeParameter ( 'fields' , i , [ ] ) as string [ ] ;
for ( const fieldName of fields ) {
2023-05-04 03:17:22 -07:00
rowFields [ fieldName ] = items [ i ] . json [ fieldName ] ;
2021-07-19 23:58:54 -07:00
}
2023-05-04 03:17:22 -07:00
row . fields = rowFields ;
2019-07-07 10:00:05 -07:00
}
2020-09-17 22:59:10 -07:00
2021-07-19 23:58:54 -07:00
rows . push ( row ) ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
if ( rows . length === bulkSize || i === items . length - 1 ) {
if ( options . typecast === true ) {
2022-12-02 12:54:28 -08:00
body . typecast = true ;
2021-07-19 23:58:54 -07:00
}
2019-07-07 10:00:05 -07:00
2022-12-02 12:54:28 -08:00
body . records = rows ;
2021-07-03 05:07:48 -07:00
2021-07-19 23:58:54 -07:00
responseData = await apiRequest . call ( this , requestMethod , endpoint , body , qs ) ;
2022-08-30 08:55:33 -07:00
const executionData = this . helpers . constructExecutionMetaData (
2023-02-27 19:39:43 -08:00
this . helpers . returnJsonArray ( responseData . records as IDataObject [ ] ) ,
2022-08-30 08:55:33 -07:00
{ itemData : { item : i } } ,
) ;
returnData . push ( . . . executionData ) ;
2021-07-19 23:58:54 -07:00
// empty rows
rows . length = 0 ;
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-09-21 06:44:45 -07:00
returnData . push ( { json : { error : error.message } } ) ;
2021-07-19 23:58:54 -07:00
continue ;
}
throw error ;
2021-07-03 05:07:48 -07:00
}
2019-07-07 10:00:05 -07:00
}
} else if ( operation === 'delete' ) {
requestMethod = 'DELETE' ;
2021-07-03 05:07:48 -07:00
const rows : string [ ] = [ ] ;
2022-12-02 12:54:28 -08:00
const options = this . getNodeParameter ( 'options' , 0 , { } ) ;
2022-08-01 13:47:55 -07:00
const bulkSize = ( options . bulkSize as number ) || 10 ;
2021-07-03 05:07:48 -07:00
2019-07-07 10:00:05 -07:00
for ( let i = 0 ; i < items . length ; i ++ ) {
2021-07-19 23:58:54 -07:00
try {
2022-12-02 12:54:28 -08:00
const id = this . getNodeParameter ( 'id' , i ) as string ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
rows . push ( id ) ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
if ( rows . length === bulkSize || i === items . length - 1 ) {
endpoint = ` ${ application } / ${ table } ` ;
2021-03-12 01:23:35 -08:00
2021-07-19 23:58:54 -07:00
// Make one request after another. This is slower but makes
// sure that we do not run into the rate limit they have in
// place and so block for 30 seconds. Later some global
// functionality in core should make it easy to make requests
// according to specific rules like not more than 5 requests
// per seconds.
qs . records = rows ;
2021-07-03 05:07:48 -07:00
2021-07-19 23:58:54 -07:00
responseData = await apiRequest . call ( this , requestMethod , endpoint , body , qs ) ;
2019-07-07 10:00:05 -07:00
2022-08-30 08:55:33 -07:00
const executionData = this . helpers . constructExecutionMetaData (
2023-02-27 19:39:43 -08:00
this . helpers . returnJsonArray ( responseData . records as IDataObject [ ] ) ,
2022-08-30 08:55:33 -07:00
{ itemData : { item : i } } ,
) ;
returnData . push ( . . . executionData ) ;
2021-07-19 23:58:54 -07:00
// empty rows
rows . length = 0 ;
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-09-21 06:44:45 -07:00
returnData . push ( { json : { error : error.message } } ) ;
2021-07-19 23:58:54 -07:00
continue ;
}
throw error ;
2021-07-03 05:07:48 -07:00
}
2019-07-07 10:00:05 -07:00
}
} else if ( operation === 'list' ) {
// ----------------------------------
// list
// ----------------------------------
2021-07-19 23:58:54 -07:00
try {
requestMethod = 'GET' ;
endpoint = ` ${ application } / ${ table } ` ;
2019-07-07 10:00:05 -07:00
2022-11-18 05:31:38 -08:00
returnAll = this . getNodeParameter ( 'returnAll' , 0 ) ;
2019-07-07 10:00:05 -07:00
2022-11-18 05:31:38 -08:00
const downloadAttachments = this . getNodeParameter ( 'downloadAttachments' , 0 ) ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
const additionalOptions = this . getNodeParameter ( 'additionalOptions' , 0 , { } ) as IDataObject ;
2020-11-24 01:53:39 -08:00
2021-07-19 23:58:54 -07:00
for ( const key of Object . keys ( additionalOptions ) ) {
if ( key === 'sort' && ( additionalOptions . sort as IDataObject ) . property !== undefined ) {
qs [ key ] = ( additionalOptions [ key ] as IDataObject ) . property ;
} else {
qs [ key ] = additionalOptions [ key ] ;
}
}
2019-07-07 10:00:05 -07:00
2022-12-02 12:54:28 -08:00
if ( returnAll ) {
2021-07-19 23:58:54 -07:00
responseData = await apiRequestAllItems . call ( this , requestMethod , endpoint , body , qs ) ;
2019-07-07 10:00:05 -07:00
} else {
2022-11-18 06:26:22 -08:00
qs . maxRecords = this . getNodeParameter ( 'limit' , 0 ) ;
2021-07-19 23:58:54 -07:00
responseData = await apiRequest . call ( this , requestMethod , endpoint , body , qs ) ;
2019-07-07 10:00:05 -07:00
}
2023-02-27 19:39:43 -08:00
returnData . push . apply ( returnData , responseData . records as INodeExecutionData [ ] ) ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
if ( downloadAttachments === true ) {
2022-08-01 13:47:55 -07:00
const downloadFieldNames = (
this . getNodeParameter ( 'downloadFieldNames' , 0 ) as string
) . split ( ',' ) ;
const data = await downloadRecordAttachments . call (
this ,
2023-02-27 19:39:43 -08:00
responseData . records as IRecord [ ] ,
2022-08-01 13:47:55 -07:00
downloadFieldNames ,
) ;
2021-07-19 23:58:54 -07:00
return [ data ] ;
}
2022-08-30 08:55:33 -07:00
// We can return from here
return [
2022-09-21 06:44:45 -07:00
this . helpers . constructExecutionMetaData ( this . helpers . returnJsonArray ( returnData ) , {
itemData : { item : 0 } ,
} ) ,
2022-08-30 08:55:33 -07:00
] ;
2021-07-19 23:58:54 -07:00
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-09-21 06:44:45 -07:00
returnData . push ( { json : { error : error.message } } ) ;
2021-07-19 23:58:54 -07:00
} else {
throw error ;
}
2020-11-24 01:53:39 -08:00
}
2019-07-07 10:00:05 -07:00
} else if ( operation === 'read' ) {
// ----------------------------------
// read
// ----------------------------------
requestMethod = 'GET' ;
let id : string ;
for ( let i = 0 ; i < items . length ; i ++ ) {
id = this . getNodeParameter ( 'id' , i ) as string ;
endpoint = ` ${ application } / ${ table } / ${ id } ` ;
// Make one request after another. This is slower but makes
// sure that we do not run into the rate limit they have in
// place and so block for 30 seconds. Later some global
// functionality in core should make it easy to make requests
// according to specific rules like not more than 5 requests
// per seconds.
2021-07-19 23:58:54 -07:00
try {
responseData = await apiRequest . call ( this , requestMethod , endpoint , body , qs ) ;
2019-07-07 10:00:05 -07:00
2022-08-30 08:55:33 -07:00
const executionData = this . helpers . constructExecutionMetaData (
2023-02-27 19:39:43 -08:00
this . helpers . returnJsonArray ( responseData as IDataObject [ ] ) ,
2022-08-30 08:55:33 -07:00
{ itemData : { item : i } } ,
) ;
returnData . push ( . . . executionData ) ;
2021-07-19 23:58:54 -07:00
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-09-21 06:44:45 -07:00
returnData . push ( { json : { error : error.message } } ) ;
2021-07-19 23:58:54 -07:00
continue ;
}
throw error ;
}
2019-07-07 10:00:05 -07:00
}
} else if ( operation === 'update' ) {
// ----------------------------------
// update
// ----------------------------------
requestMethod = 'PATCH' ;
let updateAllFields : boolean ;
let fields : string [ ] ;
2020-09-17 22:59:10 -07:00
let options : IDataObject ;
2021-07-03 05:07:48 -07:00
const rows : IDataObject [ ] = [ ] ;
let bulkSize = 10 ;
2019-07-07 10:00:05 -07:00
for ( let i = 0 ; i < items . length ; i ++ ) {
2021-07-19 23:58:54 -07:00
try {
updateAllFields = this . getNodeParameter ( 'updateAllFields' , i ) as boolean ;
2022-12-02 12:54:28 -08:00
options = this . getNodeParameter ( 'options' , i , { } ) ;
2022-08-01 13:47:55 -07:00
bulkSize = ( options . bulkSize as number ) || bulkSize ;
2021-07-19 23:58:54 -07:00
const row : IDataObject = { } ;
row . fields = { } as IDataObject ;
2022-12-02 12:54:28 -08:00
if ( updateAllFields ) {
2021-07-19 23:58:54 -07:00
// Update all the fields the item has
row . fields = { . . . items [ i ] . json } ;
// remove id field
2023-01-27 02:01:31 -08:00
delete ( row . fields as any ) . id ;
2021-07-19 23:58:54 -07:00
if ( options . ignoreFields && options . ignoreFields !== '' ) {
2022-08-01 13:47:55 -07:00
const ignoreFields = ( options . ignoreFields as string )
. split ( ',' )
. map ( ( field ) = > field . trim ( ) )
. filter ( ( field ) = > ! ! field ) ;
2021-07-19 23:58:54 -07:00
if ( ignoreFields . length ) {
// From: https://stackoverflow.com/questions/17781472/how-to-get-a-subset-of-a-javascript-objects-properties
row . fields = Object . entries ( items [ i ] . json )
. filter ( ( [ key ] ) = > ! ignoreFields . includes ( key ) )
. reduce ( ( obj , [ key , val ] ) = > Object . assign ( obj , { [ key ] : val } ) , { } ) ;
}
2020-10-22 14:36:30 -07:00
}
2021-07-19 23:58:54 -07:00
} else {
fields = this . getNodeParameter ( 'fields' , i , [ ] ) as string [ ] ;
2019-07-07 10:00:05 -07:00
2023-05-04 03:17:22 -07:00
const rowFields : IDataObject = { } ;
2021-07-19 23:58:54 -07:00
for ( const fieldName of fields ) {
2023-05-04 03:17:22 -07:00
rowFields [ fieldName ] = items [ i ] . json [ fieldName ] ;
2021-07-19 23:58:54 -07:00
}
2023-05-04 03:17:22 -07:00
row . fields = rowFields ;
2019-07-07 10:00:05 -07:00
}
2021-07-19 23:58:54 -07:00
row . id = this . getNodeParameter ( 'id' , i ) as string ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
rows . push ( row ) ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
if ( rows . length === bulkSize || i === items . length - 1 ) {
endpoint = ` ${ application } / ${ table } ` ;
2019-07-07 10:00:05 -07:00
2021-07-19 23:58:54 -07:00
// Make one request after another. This is slower but makes
// sure that we do not run into the rate limit they have in
// place and so block for 30 seconds. Later some global
// functionality in core should make it easy to make requests
// according to specific rules like not more than 5 requests
// per seconds.
2019-07-07 10:00:05 -07:00
2022-08-01 13:47:55 -07:00
const data = { records : rows , typecast : options.typecast ? true : false } ;
2021-03-12 01:23:35 -08:00
2021-07-19 23:58:54 -07:00
responseData = await apiRequest . call ( this , requestMethod , endpoint , data , qs ) ;
2021-07-03 05:07:48 -07:00
2022-08-30 08:55:33 -07:00
const executionData = this . helpers . constructExecutionMetaData (
2023-02-27 19:39:43 -08:00
this . helpers . returnJsonArray ( responseData . records as IDataObject [ ] ) ,
2022-08-30 08:55:33 -07:00
{ itemData : { item : i } } ,
) ;
returnData . push ( . . . executionData ) ;
2021-07-03 05:07:48 -07:00
2021-07-19 23:58:54 -07:00
// empty rows
rows . length = 0 ;
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-09-21 06:44:45 -07:00
returnData . push ( { json : { error : error.message } } ) ;
2021-07-19 23:58:54 -07:00
continue ;
}
throw error ;
2021-07-03 05:07:48 -07:00
}
2019-07-07 10:00:05 -07:00
}
} else {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` The operation " ${ operation } " is not known! ` ) ;
2019-07-07 10:00:05 -07:00
}
2022-08-30 08:55:33 -07:00
return this . prepareOutputData ( returnData ) ;
2019-07-07 10:00:05 -07:00
}
2021-10-27 13:00:13 -07:00
}