UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

272 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeformTrigger = void 0; const crypto_1 = require("crypto"); const n8n_workflow_1 = require("n8n-workflow"); const GenericFunctions_1 = require("./GenericFunctions"); const TypeformTriggerHelpers_1 = require("./TypeformTriggerHelpers"); class TypeformTrigger { description = { displayName: 'Typeform Trigger', name: 'typeformTrigger', icon: { light: 'file:typeform.svg', dark: 'file:typeform.dark.svg' }, group: ['trigger'], version: [1, 1.1], subtitle: '=Form ID: {{$parameter["formId"]}}', description: 'Starts the workflow on a Typeform form submission', defaults: { name: 'Typeform Trigger', }, inputs: [], outputs: [n8n_workflow_1.NodeConnectionTypes.Main], credentials: [ { name: 'typeformApi', required: true, displayOptions: { show: { authentication: ['accessToken'], }, }, testedBy: 'testTypeformTokenAuth', }, { name: 'typeformOAuth2Api', required: true, displayOptions: { show: { authentication: ['oAuth2'], }, }, }, ], webhooks: [ { name: 'default', httpMethod: 'POST', responseMode: 'onReceived', path: 'webhook', }, ], properties: [ { displayName: 'Authentication', name: 'authentication', type: 'options', options: [ { name: 'Access Token', value: 'accessToken', }, { name: 'OAuth2', value: 'oAuth2', }, ], default: 'accessToken', }, { displayName: 'Form Name or ID', name: 'formId', type: 'options', typeOptions: { loadOptionsMethod: 'getForms', }, options: [], default: '', required: true, description: 'Form which should trigger workflow on submission. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.', }, { displayName: 'Simplify Answers', name: 'simplifyAnswers', type: 'boolean', default: true, description: 'Whether to convert the answers to a key:value pair ("FIELD_TITLE":"USER_ANSER") to be easily processable', }, { displayName: 'Only Answers', name: 'onlyAnswers', type: 'boolean', default: true, description: 'Whether to return only the answers of the form and not any of the other data', }, ], }; methods = { loadOptions: { getForms: GenericFunctions_1.getForms, }, credentialTest: { async testTypeformTokenAuth(credential) { const credentials = credential.data; const options = { headers: { authorization: `bearer ${credentials.accessToken}`, }, uri: 'https://api.typeform.com/workspaces', json: true, }; try { const response = await this.helpers.request(options); if (!response.items) { return { status: 'Error', message: 'Token is not valid.', }; } } catch (err) { return { status: 'Error', message: `Token is not valid; ${err.message}`, }; } return { status: 'OK', message: 'Authentication successful!', }; }, }, }; webhookMethods = { default: { async checkExists() { const webhookData = this.getWorkflowStaticData('node'); const webhookUrl = this.getNodeWebhookUrl('default'); const formId = this.getNodeParameter('formId'); const endpoint = `forms/${formId}/webhooks`; const { items } = await GenericFunctions_1.apiRequest.call(this, 'GET', endpoint, {}); for (const item of items) { if (item.form_id === formId && item.url === webhookUrl) { webhookData.webhookId = item.tag; return true; } } return false; }, async create() { const webhookUrl = this.getNodeWebhookUrl('default'); const formId = this.getNodeParameter('formId'); const webhookId = 'n8n-' + (0, n8n_workflow_1.randomString)(10).toLowerCase(); const endpoint = `forms/${formId}/webhooks/${webhookId}`; // Generate a secret for webhook signature verification const webhookSecret = (0, crypto_1.randomBytes)(32).toString('hex'); const body = { url: webhookUrl, enabled: true, verify_ssl: true, secret: webhookSecret, }; await GenericFunctions_1.apiRequest.call(this, 'PUT', endpoint, body); const webhookData = this.getWorkflowStaticData('node'); webhookData.webhookId = webhookId; webhookData.webhookSecret = webhookSecret; return true; }, async delete() { const formId = this.getNodeParameter('formId'); const webhookData = this.getWorkflowStaticData('node'); if (webhookData.webhookId !== undefined) { const endpoint = `forms/${formId}/webhooks/${webhookData.webhookId}`; try { const body = {}; await GenericFunctions_1.apiRequest.call(this, 'DELETE', endpoint, body); } catch (error) { return false; } // Remove from the static workflow data so that it is clear // that no webhooks are registered anymore delete webhookData.webhookId; delete webhookData.webhookSecret; } return true; }, }, }; async webhook() { // Verify webhook signature if secret is configured if (!TypeformTriggerHelpers_1.verifySignature.call(this)) { const res = this.getResponseObject(); res.status(401).send('Unauthorized').end(); return { noWebhookResponse: true, }; } const version = this.getNode().typeVersion; const bodyData = this.getBodyData(); const simplifyAnswers = this.getNodeParameter('simplifyAnswers'); const onlyAnswers = this.getNodeParameter('onlyAnswers'); if (bodyData.form_response === undefined || bodyData.form_response.definition === undefined || bodyData.form_response.answers === undefined) { throw new n8n_workflow_1.NodeApiError(this.getNode(), bodyData, { message: 'Expected definition/answers data is missing!', }); } const answers = bodyData.form_response.answers; // Some fields contain lower level fields of which we are only interested of the values const subValueKeys = ['label', 'labels']; if (simplifyAnswers) { // Convert the answers to simple key -> value pairs const definition = bodyData.form_response.definition; // Create a dictionary to get the field title by its ID const definitionsById = {}; for (const field of definition.fields) { definitionsById[field.id] = field.title.replace(/\{\{/g, '[').replace(/\}\}/g, ']'); } // Convert the answers to key -> value pair const convertedAnswers = {}; for (const answer of answers) { let value = answer[answer.type]; if (typeof value === 'object') { for (const key of subValueKeys) { if (value[key] !== undefined) { value = value[key]; break; } } } convertedAnswers[definitionsById[answer.field.id]] = value; } if (onlyAnswers) { // Only the answers should be returned so do it directly return { workflowData: [this.helpers.returnJsonArray([convertedAnswers])], }; } else { // All data should be returned but the answers should still be // converted to key -> value pair so overwrite the answers. bodyData.form_response.answers = convertedAnswers; } } if (onlyAnswers) { // Return only the answers if (version >= 1.1) { return { workflowData: [ this.helpers.returnJsonArray([ answers.reduce((acc, answer) => { acc[answer.field.id] = answer; return acc; }, {}), ]), ], }; } return { workflowData: [this.helpers.returnJsonArray([answers])], }; } else { // Return all the data that got received return { workflowData: [this.helpers.returnJsonArray([bodyData])], }; } } } exports.TypeformTrigger = TypeformTrigger; //# sourceMappingURL=TypeformTrigger.node.js.map