2023-01-27 03:22:44 -08:00
import type { IExecuteFunctions } from 'n8n-core' ;
2020-12-02 23:05:54 -08:00
2023-01-27 03:22:44 -08:00
import type {
2020-12-02 23:05:54 -08:00
IDataObject ,
2020-12-22 23:19:10 -08:00
ILoadOptionsFunctions ,
2020-12-02 23:05:54 -08:00
INodeExecutionData ,
2020-12-22 23:19:10 -08:00
INodePropertyOptions ,
2020-12-02 23:05:54 -08:00
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeApiError , NodeOperationError } from 'n8n-workflow' ;
2020-12-02 23:05:54 -08:00
2022-08-17 08:50:24 -07:00
import { iterableApiRequest } from './GenericFunctions' ;
2020-12-02 23:05:54 -08:00
2022-08-17 08:50:24 -07:00
import { eventFields , eventOperations } from './EventDescription' ;
2020-12-02 23:05:54 -08:00
2022-08-17 08:50:24 -07:00
import { userFields , userOperations } from './UserDescription' ;
2020-12-02 23:05:54 -08:00
2022-08-17 08:50:24 -07:00
import { userListFields , userListOperations } from './UserListDescription' ;
2020-12-22 23:19:10 -08:00
2022-04-08 14:32:08 -07:00
import moment from 'moment-timezone' ;
2020-12-02 23:05:54 -08:00
export class Iterable implements INodeType {
description : INodeTypeDescription = {
displayName : 'Iterable' ,
name : 'iterable' ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
2020-12-02 23:05:54 -08:00
icon : 'file:iterable.png' ,
group : [ 'input' ] ,
version : 1 ,
subtitle : '={{$parameter["operation"] + ": " + $parameter["resource"]}}' ,
2021-07-03 05:40:16 -07:00
description : 'Consume Iterable API' ,
2020-12-02 23:05:54 -08:00
defaults : {
name : 'Iterable' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'iterableApi' ,
required : true ,
} ,
] ,
properties : [
{
displayName : 'Resource' ,
name : 'resource' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2020-12-02 23:05:54 -08:00
options : [
{
name : 'Event' ,
value : 'event' ,
} ,
{
name : 'User' ,
value : 'user' ,
} ,
2020-12-22 23:19:10 -08:00
{
name : 'User List' ,
value : 'userList' ,
} ,
2020-12-02 23:05:54 -08:00
] ,
default : 'user' ,
} ,
. . . eventOperations ,
. . . eventFields ,
. . . userOperations ,
. . . userFields ,
2020-12-22 23:19:10 -08:00
. . . userListOperations ,
. . . userListFields ,
2020-12-02 23:05:54 -08:00
] ,
} ;
2020-12-22 23:19:10 -08:00
methods = {
loadOptions : {
// Get all the lists available channels
async getLists ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const { lists } = await iterableApiRequest . call ( this , 'GET' , '/lists' ) ;
const returnData : INodePropertyOptions [ ] = [ ] ;
for ( const list of lists ) {
returnData . push ( {
name : list.name ,
value : list.id ,
} ) ;
}
return returnData ;
} ,
} ,
} ;
2020-12-02 23:05:54 -08:00
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
const returnData : IDataObject [ ] = [ ] ;
2022-04-22 09:29:51 -07:00
const length = items . length ;
2020-12-02 23:05:54 -08:00
const timezone = this . getTimezone ( ) ;
const qs : IDataObject = { } ;
let responseData ;
2022-12-02 03:53:59 -08:00
const resource = this . getNodeParameter ( 'resource' , 0 ) ;
const operation = this . getNodeParameter ( 'operation' , 0 ) ;
2020-12-02 23:05:54 -08:00
if ( resource === 'event' ) {
if ( operation === 'track' ) {
// https://api.iterable.com/api/docs#events_trackBulk
const events = [ ] ;
for ( let i = 0 ; i < length ; i ++ ) {
const name = this . getNodeParameter ( 'name' , i ) as string ;
2022-11-18 07:29:44 -08:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) ;
2020-12-02 23:05:54 -08:00
if ( ! additionalFields . email && ! additionalFields . id ) {
2022-08-17 08:50:24 -07:00
throw new NodeOperationError (
this . getNode ( ) ,
'Either email or userId must be passed in to identify the user. Please add one of both via "Additional Fields". If both are passed in, email takes precedence.' ,
{ itemIndex : i } ,
) ;
2020-12-02 23:05:54 -08:00
}
const body : IDataObject = {
eventName : name ,
} ;
Object . assign ( body , additionalFields ) ;
if ( body . dataFieldsUi ) {
const dataFields = ( body . dataFieldsUi as IDataObject ) . dataFieldValues as IDataObject [ ] ;
const data : IDataObject = { } ;
for ( const dataField of dataFields ) {
data [ dataField . key as string ] = dataField . value ;
}
body . dataFields = data ;
delete body . dataFieldsUi ;
}
if ( body . createdAt ) {
body . createdAt = moment . tz ( body . createdAt , timezone ) . unix ( ) ;
}
events . push ( body ) ;
}
responseData = await iterableApiRequest . call ( this , 'POST' , '/events/trackBulk' , { events } ) ;
returnData . push ( responseData ) ;
}
}
if ( resource === 'user' ) {
if ( operation === 'upsert' ) {
// https://api.iterable.com/api/docs#users_updateUser
for ( let i = 0 ; i < length ; i ++ ) {
const identifier = this . getNodeParameter ( 'identifier' , i ) as string ;
const value = this . getNodeParameter ( 'value' , i ) as string ;
2022-11-18 07:29:44 -08:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) ;
2020-12-02 23:05:54 -08:00
const body : IDataObject = { } ;
if ( identifier === 'email' ) {
body . email = value ;
} else {
body . preferUserId = this . getNodeParameter ( 'preferUserId' , i ) as boolean ;
body . userId = value ;
}
Object . assign ( body , additionalFields ) ;
if ( body . dataFieldsUi ) {
const dataFields = ( body . dataFieldsUi as IDataObject ) . dataFieldValues as IDataObject [ ] ;
const data : IDataObject = { } ;
for ( const dataField of dataFields ) {
data [ dataField . key as string ] = dataField . value ;
}
body . dataFields = data ;
delete body . dataFieldsUi ;
}
responseData = await iterableApiRequest . call ( this , 'POST' , '/users/update' , body ) ;
2022-12-02 12:54:28 -08:00
if ( ! this . continueOnFail ( ) ) {
2020-12-02 23:05:54 -08:00
if ( responseData . code !== 'Success' ) {
2022-08-17 08:50:24 -07:00
throw new NodeOperationError (
this . getNode ( ) ,
` Iterable error response [400]: ${ responseData . msg } ` ,
{ itemIndex : i } ,
2020-12-02 23:05:54 -08:00
) ;
}
}
returnData . push ( responseData ) ;
}
}
if ( operation === 'delete' ) {
// https://api.iterable.com/api/docs#users_delete
// https://api.iterable.com/api/docs#users_delete_0
for ( let i = 0 ; i < length ; i ++ ) {
const by = this . getNodeParameter ( 'by' , i ) as string ;
let endpoint ;
if ( by === 'email' ) {
const email = this . getNodeParameter ( 'email' , i ) as string ;
2020-12-22 23:19:10 -08:00
endpoint = ` /users/ ${ email } ` ;
2020-12-02 23:05:54 -08:00
} else {
const userId = this . getNodeParameter ( 'userId' , i ) as string ;
endpoint = ` /users/byUserId/ ${ userId } ` ;
}
responseData = await iterableApiRequest . call ( this , 'DELETE' , endpoint ) ;
2022-12-02 12:54:28 -08:00
if ( ! this . continueOnFail ( ) ) {
2020-12-02 23:05:54 -08:00
if ( responseData . code !== 'Success' ) {
2021-04-16 09:33:36 -07:00
throw new NodeApiError ( this . getNode ( ) , responseData ) ;
2020-12-02 23:05:54 -08:00
}
}
returnData . push ( responseData ) ;
}
}
if ( operation === 'get' ) {
// https://api.iterable.com/api/docs#users_getUser
// https://api.iterable.com/api/docs#users_getUserById
for ( let i = 0 ; i < length ; i ++ ) {
const by = this . getNodeParameter ( 'by' , i ) as string ;
let endpoint ;
if ( by === 'email' ) {
const email = this . getNodeParameter ( 'email' , i ) as string ;
2022-12-29 03:20:43 -08:00
endpoint = '/users/getByEmail' ;
2020-12-02 23:05:54 -08:00
qs . email = email ;
} else {
const userId = this . getNodeParameter ( 'userId' , i ) as string ;
endpoint = ` /users/byUserId/ ${ userId } ` ;
}
responseData = await iterableApiRequest . call ( this , 'GET' , endpoint , { } , qs ) ;
2022-12-02 12:54:28 -08:00
if ( ! this . continueOnFail ( ) ) {
2020-12-02 23:05:54 -08:00
if ( Object . keys ( responseData ) . length === 0 ) {
2022-08-17 08:50:24 -07:00
throw new NodeApiError ( this . getNode ( ) , responseData , {
2022-12-29 03:20:43 -08:00
message : 'User not found' ,
2022-08-17 08:50:24 -07:00
httpCode : '404' ,
} ) ;
2020-12-02 23:05:54 -08:00
}
}
responseData = responseData . user || { } ;
returnData . push ( responseData ) ;
}
}
}
2020-12-22 23:19:10 -08:00
if ( resource === 'userList' ) {
if ( operation === 'add' ) {
//https://api.iterable.com/api/docs#lists_subscribe
const listId = this . getNodeParameter ( 'listId' , 0 ) as string ;
const identifier = this . getNodeParameter ( 'identifier' , 0 ) as string ;
const body : IDataObject = {
listId : parseInt ( listId , 10 ) ,
subscribers : [ ] ,
} ;
const subscribers : IDataObject [ ] = [ ] ;
for ( let i = 0 ; i < length ; i ++ ) {
const value = this . getNodeParameter ( 'value' , i ) as string ;
if ( identifier === 'email' ) {
subscribers . push ( { email : value } ) ;
} else {
subscribers . push ( { userId : value } ) ;
}
}
body . subscribers = subscribers ;
responseData = await iterableApiRequest . call ( this , 'POST' , '/lists/subscribe' , body ) ;
returnData . push ( responseData ) ;
}
if ( operation === 'remove' ) {
//https://api.iterable.com/api/docs#lists_unsubscribe
const listId = this . getNodeParameter ( 'listId' , 0 ) as string ;
const identifier = this . getNodeParameter ( 'identifier' , 0 ) as string ;
2022-11-18 07:29:44 -08:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , 0 ) ;
2020-12-22 23:19:10 -08:00
const body : IDataObject = {
listId : parseInt ( listId , 10 ) ,
subscribers : [ ] ,
campaignId : additionalFields.campaignId as number ,
channelUnsubscribe : additionalFields.channelUnsubscribe as boolean ,
} ;
const subscribers : IDataObject [ ] = [ ] ;
for ( let i = 0 ; i < length ; i ++ ) {
const value = this . getNodeParameter ( 'value' , i ) as string ;
if ( identifier === 'email' ) {
subscribers . push ( { email : value } ) ;
} else {
subscribers . push ( { userId : value } ) ;
}
}
body . subscribers = subscribers ;
responseData = await iterableApiRequest . call ( this , 'POST' , '/lists/unsubscribe' , body ) ;
returnData . push ( responseData ) ;
}
}
2020-12-02 23:05:54 -08:00
return [ this . helpers . returnJsonArray ( returnData ) ] ;
}
}