2023-01-27 03:22:44 -08:00
import type {
2023-03-09 09:13:15 -08:00
IHookFunctions ,
IWebhookFunctions ,
2020-01-20 13:52:34 -08:00
IDataObject ,
ILoadOptionsFunctions ,
INodePropertyOptions ,
INodeType ,
2020-10-01 05:01:39 -07:00
INodeTypeDescription ,
2020-01-20 13:52:34 -08:00
IWebhookResponseData ,
2023-08-01 08:32:30 -07:00
MultiPartFormData ,
2020-01-20 13:52:34 -08:00
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { jsonParse } from 'n8n-workflow' ;
2020-01-20 13:52:34 -08:00
2022-08-17 08:50:24 -07:00
import { jotformApiRequest } from './GenericFunctions' ;
2020-01-27 01:32:27 -08:00
interface IQuestionData {
name : string ;
text : string ;
}
2020-01-20 13:52:34 -08:00
export class JotFormTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'JotForm Trigger' ,
name : 'jotFormTrigger' ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
2020-01-20 13:52:34 -08:00
icon : 'file:jotform.png' ,
group : [ 'trigger' ] ,
version : 1 ,
description : 'Handle JotForm events via webhooks' ,
defaults : {
name : 'JotForm Trigger' ,
} ,
inputs : [ ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'jotFormApi' ,
required : true ,
2020-10-22 06:46:03 -07:00
} ,
2020-01-20 13:52:34 -08:00
] ,
webhooks : [
{
name : 'default' ,
httpMethod : 'POST' ,
responseMode : 'onReceived' ,
path : 'webhook' ,
} ,
] ,
properties : [
{
2022-06-03 10:23:49 -07:00
displayName : 'Form Name or ID' ,
2020-01-20 13:52:34 -08:00
name : 'form' ,
type : 'options' ,
required : true ,
typeOptions : {
2020-10-22 06:46:03 -07:00
loadOptionsMethod : 'getForms' ,
2020-01-20 13:52:34 -08:00
} ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>' ,
2020-01-20 13:52:34 -08:00
} ,
2020-01-27 01:32:27 -08:00
{
displayName : 'Resolve Data' ,
name : 'resolveData' ,
type : 'boolean' ,
default : true ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
2022-08-17 08:50:24 -07:00
description :
'By default does the webhook-data use internal keys instead of the names. If this option gets activated, it will resolve the keys automatically to the actual names.' ,
2020-01-27 01:32:27 -08:00
} ,
{
displayName : 'Only Answers' ,
name : 'onlyAnswers' ,
type : 'boolean' ,
default : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether to return only the answers of the form and not any of the other data' ,
2020-01-27 01:32:27 -08:00
} ,
2020-01-20 13:52:34 -08:00
] ,
} ;
methods = {
loadOptions : {
2023-04-19 07:00:49 -07:00
// Get all the available forms to display them to user so that they can
2020-01-20 13:52:34 -08:00
// select them easily
async getForms ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = [ ] ;
const qs : IDataObject = {
limit : 1000 ,
2022-08-17 08:50:24 -07:00
} ;
2020-01-20 13:52:34 -08:00
const forms = await jotformApiRequest . call ( this , 'GET' , '/user/forms' , { } , qs ) ;
2023-11-17 07:42:30 -08:00
if ( ! Array . isArray ( forms ? . content ) ) return [ ] ;
2020-01-20 13:52:34 -08:00
for ( const form of forms . content ) {
const formName = form . title ;
const formId = form . id ;
returnData . push ( {
name : formName ,
value : formId ,
} ) ;
}
return returnData ;
} ,
} ,
} ;
2022-12-02 12:54:28 -08:00
2020-01-20 13:52:34 -08:00
webhookMethods = {
default : {
async checkExists ( this : IHookFunctions ) : Promise < boolean > {
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
const formId = this . getNodeParameter ( 'form' ) as string ;
const endpoint = ` /form/ ${ formId } /webhooks ` ;
2020-01-26 23:22:10 -08:00
2020-01-20 13:52:34 -08:00
try {
2020-01-26 23:22:10 -08:00
const responseData = await jotformApiRequest . call ( this , 'GET' , endpoint ) ;
2023-02-27 19:39:43 -08:00
const webhookUrls = Object . values ( responseData . content as IDataObject ) ;
2020-01-26 23:22:10 -08:00
const webhookUrl = this . getNodeWebhookUrl ( 'default' ) ;
if ( ! webhookUrls . includes ( webhookUrl ) ) {
return false ;
}
2023-02-27 19:39:43 -08:00
const webhookIds = Object . keys ( responseData . content as IDataObject ) ;
2020-01-26 23:22:10 -08:00
webhookData . webhookId = webhookIds [ webhookUrls . indexOf ( webhookUrl ) ] ;
2021-04-16 09:33:36 -07:00
} catch ( error ) {
2020-01-20 13:52:34 -08:00
return false ;
}
return true ;
} ,
async create ( this : IHookFunctions ) : Promise < boolean > {
const webhookUrl = this . getNodeWebhookUrl ( 'default' ) ;
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
const formId = this . getNodeParameter ( 'form' ) as string ;
const endpoint = ` /form/ ${ formId } /webhooks ` ;
const body : IDataObject = {
webhookURL : webhookUrl ,
} ;
const { content } = await jotformApiRequest . call ( this , 'POST' , endpoint , body ) ;
2023-02-27 19:39:43 -08:00
webhookData . webhookId = Object . keys ( content as IDataObject ) [ 0 ] ;
2020-01-20 13:52:34 -08:00
return true ;
} ,
async delete ( this : IHookFunctions ) : Promise < boolean > {
let responseData ;
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
const formId = this . getNodeParameter ( 'form' ) as string ;
const endpoint = ` /form/ ${ formId } /webhooks/ ${ webhookData . webhookId } ` ;
try {
responseData = await jotformApiRequest . call ( this , 'DELETE' , endpoint ) ;
2022-08-17 08:50:24 -07:00
} catch ( error ) {
2020-01-20 13:52:34 -08:00
return false ;
}
if ( responseData . message !== 'success' ) {
return false ;
}
delete webhookData . webhookId ;
return true ;
} ,
} ,
} ;
async webhook ( this : IWebhookFunctions ) : Promise < IWebhookResponseData > {
2023-08-01 08:32:30 -07:00
const req = this . getRequestObject ( ) as MultiPartFormData . Request ;
2020-01-27 01:32:27 -08:00
const formId = this . getNodeParameter ( 'form' ) as string ;
const resolveData = this . getNodeParameter ( 'resolveData' , false ) as boolean ;
const onlyAnswers = this . getNodeParameter ( 'onlyAnswers' , false ) as boolean ;
2023-08-01 08:32:30 -07:00
const { data } = req . body ;
const rawRequest = jsonParse < any > ( data . rawRequest as string ) ;
data . rawRequest = rawRequest ;
let returnData : IDataObject ;
if ( ! resolveData ) {
if ( onlyAnswers ) {
returnData = data . rawRequest as unknown as IDataObject ;
} else {
returnData = data ;
}
return {
workflowData : [ this . helpers . returnJsonArray ( returnData ) ] ,
} ;
}
// Resolve the data by requesting the information via API
const endpoint = ` /form/ ${ formId } /questions ` ;
const responseData = await jotformApiRequest . call ( this , 'GET' , endpoint , { } ) ;
// Create a dictionary to resolve the keys
const questionNames : IDataObject = { } ;
for ( const question of Object . values < IQuestionData > ( responseData . content as IQuestionData [ ] ) ) {
questionNames [ question . name ] = question . text ;
}
// Resolve the keys
let questionKey : string ;
const questionsData : IDataObject = { } ;
for ( const key of Object . keys ( rawRequest as IDataObject ) ) {
if ( ! key . includes ( '_' ) ) {
continue ;
}
questionKey = key . split ( '_' ) . slice ( 1 ) . join ( '_' ) ;
if ( questionNames [ questionKey ] === undefined ) {
continue ;
}
questionsData [ questionNames [ questionKey ] as string ] = rawRequest [ key ] ;
}
if ( onlyAnswers ) {
returnData = questionsData as unknown as IDataObject ;
} else {
// @ts-ignore
data . rawRequest = questionsData ;
returnData = data ;
}
return {
workflowData : [ this . helpers . returnJsonArray ( returnData ) ] ,
} ;
2020-01-20 13:52:34 -08:00
}
}