2023-10-06 06:31:18 -07:00
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
import type {
IExecuteFunctions ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
IPairedItemData ,
} from 'n8n-workflow' ;
2024-08-29 06:55:53 -07:00
import { NodeConnectionType , deepCopy } from 'n8n-workflow' ;
2023-10-06 06:31:18 -07:00
export class SplitInBatchesV3 implements INodeType {
description : INodeTypeDescription = {
displayName : 'Loop Over Items (Split in Batches)' ,
name : 'splitInBatches' ,
icon : 'fa:sync' ,
2024-06-06 04:34:30 -07:00
iconColor : 'dark-green' ,
2023-10-06 06:31:18 -07:00
group : [ 'organization' ] ,
version : 3 ,
description : 'Split data into batches and iterate over each batch' ,
defaults : {
name : 'Loop Over Items' ,
color : '#007755' ,
} ,
2024-08-29 06:55:53 -07:00
inputs : [ NodeConnectionType . Main ] ,
2023-10-06 06:31:18 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
2024-08-29 06:55:53 -07:00
outputs : [ NodeConnectionType . Main , NodeConnectionType . Main ] ,
2023-10-06 06:31:18 -07:00
outputNames : [ 'done' , 'loop' ] ,
properties : [
{
displayName :
'You may not need this node — n8n nodes automatically run once for each input item. <a href="https://docs.n8n.io/getting-started/key-concepts/looping.html#using-loops-in-n8n" target="_blank">More info</a>' ,
name : 'splitInBatchesNotice' ,
type : 'notice' ,
default : '' ,
} ,
{
displayName : 'Batch Size' ,
name : 'batchSize' ,
type : 'number' ,
typeOptions : {
minValue : 1 ,
} ,
default : 1 ,
description : 'The number of items to return with each call' ,
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
2024-07-29 05:27:23 -07:00
placeholder : 'Add option' ,
2023-10-06 06:31:18 -07:00
default : { } ,
options : [
{
displayName : 'Reset' ,
name : 'reset' ,
type : 'boolean' ,
default : false ,
description :
'Whether the node will be reset and so with the current input-data newly initialized' ,
} ,
] ,
} ,
] ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] | null > {
// Get the input data and create a new array so that we can remove
// items without a problem
const items = this . getInputData ( ) . slice ( ) ;
const nodeContext = this . getContext ( 'node' ) ;
const batchSize = this . getNodeParameter ( 'batchSize' , 0 ) as number ;
const returnItems : INodeExecutionData [ ] = [ ] ;
const options = this . getNodeParameter ( 'options' , 0 , { } ) ;
if ( nodeContext . items === undefined || options . reset === true ) {
// Is the first time the node runs
const sourceData = this . getInputSourceData ( ) ;
nodeContext . currentRunIndex = 0 ;
nodeContext . maxRunIndex = Math . ceil ( items . length / batchSize ) ;
nodeContext . sourceData = deepCopy ( sourceData ) ;
// Get the items which should be returned
returnItems . push . apply ( returnItems , items . splice ( 0 , batchSize ) ) ;
// Save the incoming items to be able to return them for later runs
nodeContext . items = [ . . . items ] ;
// Reset processedItems as they get only added starting from the first iteration
nodeContext . processedItems = [ ] ;
} else {
// The node has been called before. So return the next batch of items.
nodeContext . currentRunIndex += 1 ;
returnItems . push . apply (
returnItems ,
( nodeContext . items as INodeExecutionData [ ] ) . splice ( 0 , batchSize ) ,
) ;
const addSourceOverwrite = ( pairedItem : IPairedItemData | number ) : IPairedItemData = > {
if ( typeof pairedItem === 'number' ) {
return {
item : pairedItem ,
sourceOverwrite : nodeContext.sourceData ,
} ;
}
return {
. . . pairedItem ,
sourceOverwrite : nodeContext.sourceData ,
} ;
} ;
function getPairedItemInformation (
item : INodeExecutionData ,
) : IPairedItemData | IPairedItemData [ ] {
if ( item . pairedItem === undefined ) {
return {
item : 0 ,
sourceOverwrite : nodeContext.sourceData ,
} ;
}
if ( Array . isArray ( item . pairedItem ) ) {
return item . pairedItem . map ( addSourceOverwrite ) ;
}
return addSourceOverwrite ( item . pairedItem ) ;
}
const sourceOverwrite = this . getInputSourceData ( ) ;
const newItems = items . map ( ( item , index ) = > {
return {
. . . item ,
pairedItem : {
sourceOverwrite ,
item : index ,
} ,
} ;
} ) ;
nodeContext . processedItems = [ . . . nodeContext . processedItems , . . . newItems ] ;
returnItems . map ( ( item ) = > {
item . pairedItem = getPairedItemInformation ( item ) ;
} ) ;
}
nodeContext . noItemsLeft = nodeContext . items . length === 0 ;
if ( returnItems . length === 0 ) {
nodeContext . done = true ;
return [ nodeContext . processedItems , [ ] ] ;
}
nodeContext . done = false ;
return [ [ ] , returnItems ] ;
}
}