2023-01-27 03:22:44 -08:00
import type { IHookFunctions , IWebhookFunctions } from 'n8n-core' ;
2020-01-05 10:34:09 -08:00
2023-01-27 03:22:44 -08:00
import type {
2020-01-05 18:32:22 -08:00
IDataObject ,
2020-01-06 16:30:40 -08:00
ILoadOptionsFunctions ,
2020-10-01 05:01:39 -07:00
INodePropertyOptions ,
INodeType ,
INodeTypeDescription ,
IWebhookResponseData ,
2020-01-05 10:34:09 -08:00
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeOperationError } from 'n8n-workflow' ;
2020-01-05 10:34:09 -08:00
2022-08-17 08:50:24 -07:00
import { zendeskApiRequest , zendeskApiRequestAllItems } from './GenericFunctions' ;
import { conditionFields } from './ConditionDescription' ;
2020-01-05 10:34:09 -08:00
2022-08-17 08:50:24 -07:00
import { triggerPlaceholders } from './TriggerPlaceholders' ;
2021-04-24 10:57:49 -07:00
2020-01-05 10:34:09 -08:00
export class ZendeskTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'Zendesk Trigger' ,
2020-01-07 19:15:37 -08:00
name : 'zendeskTrigger' ,
2021-09-05 06:13:25 -07:00
icon : 'file:zendesk.svg' ,
2020-01-05 10:34:09 -08:00
group : [ 'trigger' ] ,
version : 1 ,
description : 'Handle Zendesk events via webhooks' ,
defaults : {
name : 'Zendesk Trigger' ,
} ,
inputs : [ ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'zendeskApi' ,
required : true ,
2020-06-09 06:53:17 -07:00
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
authentication : [ 'apiToken' ] ,
2020-06-09 06:53:17 -07:00
} ,
} ,
} ,
{
name : 'zendeskOAuth2Api' ,
required : true ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
authentication : [ 'oAuth2' ] ,
2020-06-09 06:53:17 -07:00
} ,
} ,
} ,
2020-01-05 10:34:09 -08:00
] ,
webhooks : [
{
name : 'default' ,
httpMethod : 'POST' ,
responseMode : 'onReceived' ,
path : 'webhook' ,
} ,
] ,
properties : [
2020-06-09 06:53:17 -07:00
{
displayName : 'Authentication' ,
name : 'authentication' ,
type : 'options' ,
options : [
{
2020-06-14 15:19:35 -07:00
name : 'API Token' ,
value : 'apiToken' ,
2020-06-09 06:53:17 -07:00
} ,
{
name : 'OAuth2' ,
value : 'oAuth2' ,
} ,
] ,
2020-06-14 15:19:35 -07:00
default : 'apiToken' ,
2020-06-09 06:53:17 -07:00
} ,
2020-01-05 10:34:09 -08:00
{
displayName : 'Service' ,
name : 'service' ,
type : 'options' ,
required : true ,
options : [
{
name : 'Support' ,
value : 'support' ,
2020-10-22 06:46:03 -07:00
} ,
2020-01-05 10:34:09 -08:00
] ,
default : 'support' ,
} ,
2020-01-06 16:30:40 -08:00
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
service : [ 'support' ] ,
2020-01-06 16:30:40 -08:00
} ,
} ,
default : { } ,
2020-01-05 10:34:09 -08:00
options : [
{
2022-06-20 07:54:01 -07:00
displayName : 'Field Names or IDs' ,
2020-01-06 16:30:40 -08:00
name : 'fields' ,
2022-08-17 08:50:24 -07:00
description :
'The fields to return the values of. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
2020-01-06 16:30:40 -08:00
type : 'multiOptions' ,
default : [ ] ,
2021-04-24 10:57:49 -07:00
typeOptions : {
loadOptionsMethod : 'getFields' ,
} ,
2020-01-05 10:34:09 -08:00
} ,
] ,
2020-01-06 16:30:40 -08:00
placeholder : 'Add Option' ,
} ,
{
displayName : 'Conditions' ,
name : 'conditions' ,
placeholder : 'Add Condition' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
service : [ 'support' ] ,
2020-10-22 06:46:03 -07:00
} ,
2020-01-06 16:30:40 -08:00
} ,
2022-05-06 14:01:25 -07:00
description : 'The condition to set' ,
2020-01-06 16:30:40 -08:00
default : { } ,
options : [
{
name : 'all' ,
displayName : 'All' ,
2022-08-17 08:50:24 -07:00
values : [ . . . conditionFields ] ,
2020-01-06 16:52:37 -08:00
} ,
{
name : 'any' ,
displayName : 'Any' ,
2022-08-17 08:50:24 -07:00
values : [ . . . conditionFields ] ,
2020-01-06 16:52:37 -08:00
} ,
2020-01-06 16:30:40 -08:00
] ,
2020-01-05 10:34:09 -08:00
} ,
] ,
} ;
2022-12-02 12:54:28 -08:00
2020-01-06 16:30:40 -08:00
methods = {
loadOptions : {
2021-04-24 10:57:49 -07:00
// Get all the fields to display them to user so that he can
// select them easily
async getFields ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = triggerPlaceholders ;
const customFields = [
'text' ,
'textarea' ,
'date' ,
'integer' ,
'decimal' ,
'regexp' ,
'multiselect' ,
'tagger' ,
] ;
2022-08-17 08:50:24 -07:00
const fields = await zendeskApiRequestAllItems . call (
this ,
'ticket_fields' ,
'GET' ,
'/ticket_fields' ,
) ;
2021-04-24 10:57:49 -07:00
for ( const field of fields ) {
2023-02-27 19:39:43 -08:00
if ( customFields . includes ( field . type as string ) && field . removable && field . active ) {
2021-04-24 10:57:49 -07:00
const fieldName = field . title ;
const fieldId = field . id ;
returnData . push ( {
name : fieldName ,
value : ` ticket.ticket_field_ ${ fieldId } ` ,
description : ` Custom field ${ fieldName } ` ,
} ) ;
}
}
return returnData ;
} ,
2020-01-06 16:30:40 -08:00
// Get all the groups to display them to user so that he can
// select them easily
async getGroups ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = [ ] ;
const groups = await zendeskApiRequestAllItems . call ( this , 'groups' , 'GET' , '/groups' ) ;
for ( const group of groups ) {
const groupName = group . name ;
const groupId = group . id ;
returnData . push ( {
name : groupName ,
value : groupId ,
} ) ;
}
return returnData ;
} ,
// Get all the users to display them to user so that he can
// select them easily
async getUsers ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = [ ] ;
const users = await zendeskApiRequestAllItems . call ( this , 'users' , 'GET' , '/users' ) ;
for ( const user of users ) {
const userName = user . name ;
const userId = user . id ;
returnData . push ( {
name : userName ,
value : userId ,
} ) ;
}
returnData . push ( {
name : 'Current User' ,
value : 'current_user' ,
2020-01-07 19:15:37 -08:00
} ) ;
2020-01-06 16:30:40 -08:00
returnData . push ( {
name : 'Requester' ,
value : 'requester_id' ,
2020-01-07 19:15:37 -08:00
} ) ;
2020-01-06 16:30:40 -08:00
return returnData ;
} ,
2020-10-22 06:46:03 -07:00
} ,
2020-01-06 16:30:40 -08:00
} ;
2022-12-02 12:54:28 -08:00
2020-01-05 10:34:09 -08:00
webhookMethods = {
default : {
async checkExists ( this : IHookFunctions ) : Promise < boolean > {
2020-08-25 12:50:13 -07:00
const webhookUrl = this . getNodeWebhookUrl ( 'default' ) as string ;
2020-01-05 10:34:09 -08:00
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
2020-08-25 12:50:13 -07:00
const conditions = this . getNodeParameter ( 'conditions' ) as IDataObject ;
let endpoint = '' ;
2022-08-17 08:50:24 -07:00
const resultAll = [ ] ,
resultAny = [ ] ;
2020-08-25 12:50:13 -07:00
2022-04-18 09:43:09 -07:00
const conditionsAll = conditions . all as [ IDataObject ] ;
2020-08-25 12:50:13 -07:00
if ( conditionsAll ) {
for ( const conditionAll of conditionsAll ) {
2021-04-17 08:08:07 -07:00
const aux : IDataObject = { } ;
2020-08-25 12:50:13 -07:00
aux . field = conditionAll . field ;
aux . operator = conditionAll . operation ;
2022-08-17 08:50:24 -07:00
if ( conditionAll . operation !== 'changed' && conditionAll . operation !== 'not_changed' ) {
2020-08-25 12:50:13 -07:00
aux . value = conditionAll . value ;
} else {
aux . value = null ;
}
resultAll . push ( aux ) ;
}
2020-01-05 10:34:09 -08:00
}
2020-08-25 12:50:13 -07:00
const conditionsAny = conditions . any as [ IDataObject ] ;
if ( conditionsAny ) {
for ( const conditionAny of conditionsAny ) {
2021-04-17 08:08:07 -07:00
const aux : IDataObject = { } ;
2020-08-25 12:50:13 -07:00
aux . field = conditionAny . field ;
aux . operator = conditionAny . operation ;
2022-08-17 08:50:24 -07:00
if ( conditionAny . operation !== 'changed' && conditionAny . operation !== 'not_changed' ) {
2020-08-25 12:50:13 -07:00
aux . value = conditionAny . value ;
} else {
aux . value = null ;
}
resultAny . push ( aux ) ;
}
}
2022-04-18 09:43:09 -07:00
// get all webhooks
// https://developer.zendesk.com/api-reference/event-connectors/webhooks/webhooks/#list-webhooks
const { webhooks } = await zendeskApiRequest . call ( this , 'GET' , '/webhooks' ) ;
for ( const webhook of webhooks ) {
if ( webhook . endpoint === webhookUrl ) {
webhookData . targetId = webhook . id ;
2020-08-25 12:50:13 -07:00
break ;
}
}
// no target was found
if ( webhookData . targetId === undefined ) {
2020-01-05 10:34:09 -08:00
return false ;
}
2020-08-25 12:50:13 -07:00
2022-12-29 03:20:43 -08:00
endpoint = '/triggers/active' ;
2022-08-17 08:50:24 -07:00
const triggers = await zendeskApiRequestAllItems . call ( this , 'triggers' , 'GET' , endpoint ) ;
2022-04-18 09:43:09 -07:00
2020-08-25 12:50:13 -07:00
for ( const trigger of triggers ) {
2022-08-17 08:50:24 -07:00
const toDeleteTriggers = [ ] ;
// this trigger belong to the current target
if ( trigger . actions [ 0 ] . value [ 0 ] . toString ( ) === webhookData . targetId ? . toString ( ) ) {
toDeleteTriggers . push ( trigger . id ) ;
}
// delete all trigger attach to this target;
if ( toDeleteTriggers . length !== 0 ) {
await zendeskApiRequest . call (
this ,
'DELETE' ,
'/triggers/destroy_many' ,
{ } ,
{ ids : toDeleteTriggers.join ( ',' ) } ,
) ;
}
2020-08-25 12:50:13 -07:00
}
return false ;
2020-01-05 10:34:09 -08:00
} ,
async create ( this : IHookFunctions ) : Promise < boolean > {
2020-01-07 19:15:37 -08:00
const webhookUrl = this . getNodeWebhookUrl ( 'default' ) as string ;
2020-01-05 10:34:09 -08:00
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
2020-01-06 16:30:40 -08:00
const service = this . getNodeParameter ( 'service' ) as string ;
2022-04-18 09:43:09 -07:00
2020-01-06 16:30:40 -08:00
if ( service === 'support' ) {
const message : IDataObject = { } ;
2022-08-17 08:50:24 -07:00
const resultAll = [ ] ,
resultAny = [ ] ;
2020-01-06 16:30:40 -08:00
const conditions = this . getNodeParameter ( 'conditions' ) as IDataObject ;
const options = this . getNodeParameter ( 'options' ) as IDataObject ;
2022-04-18 09:43:09 -07:00
2020-01-06 16:30:40 -08:00
if ( Object . keys ( conditions ) . length === 0 ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'You must have at least one condition' ) ;
2020-01-05 18:32:22 -08:00
}
2022-04-18 09:43:09 -07:00
2020-01-06 16:30:40 -08:00
if ( options . fields ) {
2022-04-18 09:43:09 -07:00
for ( const field of options . fields as string [ ] ) {
2020-01-06 16:30:40 -08:00
message [ field ] = ` {{ ${ field } }} ` ;
}
} else {
2020-01-07 19:15:37 -08:00
message [ 'ticket.id' ] = '{{ticket.id}}' ;
2020-01-06 16:30:40 -08:00
}
2022-04-18 09:43:09 -07:00
2020-01-06 16:30:40 -08:00
const conditionsAll = conditions . all as [ IDataObject ] ;
2020-01-06 16:52:37 -08:00
if ( conditionsAll ) {
2020-01-07 19:15:37 -08:00
for ( const conditionAll of conditionsAll ) {
2021-04-17 08:08:07 -07:00
const aux : IDataObject = { } ;
2020-01-06 16:52:37 -08:00
aux . field = conditionAll . field ;
aux . operator = conditionAll . operation ;
2022-08-17 08:50:24 -07:00
if (
conditionAll . operation !== 'changed' &&
conditionAll . operation !== 'not_changed'
) {
2020-01-06 16:52:37 -08:00
aux . value = conditionAll . value ;
} else {
aux . value = null ;
}
2020-01-07 19:15:37 -08:00
resultAll . push ( aux ) ;
2020-01-06 16:52:37 -08:00
}
}
2022-04-18 09:43:09 -07:00
2020-01-06 16:52:37 -08:00
const conditionsAny = conditions . any as [ IDataObject ] ;
if ( conditionsAny ) {
2020-01-07 19:15:37 -08:00
for ( const conditionAny of conditionsAny ) {
2021-04-17 08:08:07 -07:00
const aux : IDataObject = { } ;
2020-01-06 16:52:37 -08:00
aux . field = conditionAny . field ;
aux . operator = conditionAny . operation ;
2022-08-17 08:50:24 -07:00
if (
conditionAny . operation !== 'changed' &&
conditionAny . operation !== 'not_changed'
) {
2020-01-06 16:52:37 -08:00
aux . value = conditionAny . value ;
} else {
aux . value = null ;
}
2020-01-07 19:15:37 -08:00
resultAny . push ( aux ) ;
2020-01-06 16:30:40 -08:00
}
}
2022-04-18 09:43:09 -07:00
const urlParts = new URL ( webhookUrl ) ;
2020-01-06 16:30:40 -08:00
const bodyTrigger : IDataObject = {
trigger : {
2022-04-18 09:43:09 -07:00
title : ` n8n-webhook: ${ urlParts . pathname } ` ,
2020-01-06 16:30:40 -08:00
conditions : {
all : resultAll ,
2020-01-06 16:52:37 -08:00
any : resultAny ,
2022-08-17 08:50:24 -07:00
} ,
2020-01-06 16:30:40 -08:00
actions : [
{
2022-04-18 09:43:09 -07:00
field : 'notification_webhook' ,
2020-01-06 16:30:40 -08:00
value : [ ] ,
2020-08-25 12:50:13 -07:00
} ,
] ,
2020-01-06 16:30:40 -08:00
} ,
2020-01-07 19:15:37 -08:00
} ;
2022-04-18 09:43:09 -07:00
2020-01-06 16:30:40 -08:00
const bodyTarget : IDataObject = {
2022-04-18 09:43:09 -07:00
webhook : {
2022-08-17 08:50:24 -07:00
name : 'n8n webhook' ,
2022-04-18 09:43:09 -07:00
endpoint : webhookUrl ,
2022-08-17 08:50:24 -07:00
http_method : 'POST' ,
status : 'active' ,
request_format : 'json' ,
subscriptions : [ 'conditional_ticket_events' ] ,
2020-01-06 16:30:40 -08:00
} ,
} ;
2020-08-25 12:50:13 -07:00
let target : IDataObject = { } ;
// if target id exists but trigger does not then reuse the target
// and create the trigger else create both
if ( webhookData . targetId !== undefined ) {
target . id = webhookData . targetId ;
} else {
2022-04-18 09:43:09 -07:00
// create a webhook
// https://developer.zendesk.com/api-reference/event-connectors/webhooks/webhooks/#create-or-clone-webhook
2022-08-17 08:50:24 -07:00
target = ( await zendeskApiRequest . call ( this , 'POST' , '/webhooks' , bodyTarget ) )
. webhook as IDataObject ;
2020-08-25 12:50:13 -07:00
}
2023-02-27 19:39:43 -08:00
( ( bodyTrigger . trigger as IDataObject ) . actions as IDataObject [ ] ) [ 0 ] . value = [
target . id ,
JSON . stringify ( message ) ,
] ;
2020-08-25 12:50:13 -07:00
2020-01-06 16:30:40 -08:00
const { trigger } = await zendeskApiRequest . call ( this , 'POST' , '/triggers' , bodyTrigger ) ;
webhookData . webhookId = trigger . id ;
webhookData . targetId = target . id ;
2020-01-05 10:34:09 -08:00
}
return true ;
} ,
async delete ( this : IHookFunctions ) : Promise < boolean > {
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
try {
2020-01-05 18:32:22 -08:00
await zendeskApiRequest . call ( this , 'DELETE' , ` /triggers/ ${ webhookData . webhookId } ` ) ;
2022-04-18 09:43:09 -07:00
await zendeskApiRequest . call ( this , 'DELETE' , ` /webhooks/ ${ webhookData . targetId } ` ) ;
2022-08-17 08:50:24 -07:00
} catch ( error ) {
2020-01-05 10:34:09 -08:00
return false ;
}
2022-04-18 09:43:09 -07:00
delete webhookData . triggerId ;
2020-01-07 19:15:37 -08:00
delete webhookData . targetId ;
2020-01-05 10:34:09 -08:00
return true ;
} ,
} ,
} ;
async webhook ( this : IWebhookFunctions ) : Promise < IWebhookResponseData > {
const req = this . getRequestObject ( ) ;
return {
2023-02-27 19:39:43 -08:00
workflowData : [ this . helpers . returnJsonArray ( req . body as IDataObject ) ] ,
2020-01-05 10:34:09 -08:00
} ;
}
}