feat(Facebook Lead Ads Trigger Node): Add Facebook Lead Ads Trigger Node (#7113)

Github issue / Community forum post (link here to close automatically):
https://community.n8n.io/t/facebook-lead-ads-integration/4590/19

---------

Co-authored-by: Marcus <marcus@n8n.io>
This commit is contained in:
Elias Meire 2023-10-20 13:43:55 +02:00 committed by GitHub
parent 647372be27
commit ac814a9c61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 835 additions and 25 deletions

View file

@ -29,6 +29,21 @@ function findReferencedMethods(obj, refs = {}, latestName = '') {
return refs; return refs;
} }
function addWebhookLifecycle(nodeType) {
if (nodeType.description.webhooks) {
nodeType.description.webhooks = nodeType.description.webhooks.map((webhook) => {
const webhookMethods =
nodeType?.webhookMethods?.[webhook.name] ?? nodeType?.webhookMethods?.default;
webhook.hasLifecycleMethods = Boolean(
webhookMethods?.checkExists && webhookMethods?.create && webhookMethods?.delete,
);
return webhook;
});
}
return nodeType;
}
(async () => { (async () => {
const loader = new PackageDirectoryLoader(packageDir); const loader = new PackageDirectoryLoader(packageDir);
await loader.loadAll(); await loader.loadAll();
@ -60,6 +75,7 @@ function findReferencedMethods(obj, refs = {}, latestName = '') {
.map((data) => { .map((data) => {
const nodeType = NodeHelpers.getVersionedNodeType(data.type); const nodeType = NodeHelpers.getVersionedNodeType(data.type);
NodeHelpers.applySpecialNodeParameters(nodeType); NodeHelpers.applySpecialNodeParameters(nodeType);
addWebhookLifecycle(nodeType);
return data.type; return data.type;
}) })
.flatMap((nodeData) => { .flatMap((nodeData) => {

View file

@ -61,12 +61,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue';
import type { INodeTypeDescription, IWebhookDescription } from 'n8n-workflow'; import type { INodeTypeDescription, IWebhookDescription } from 'n8n-workflow';
import { defineComponent } from 'vue';
import { useToast } from '@/composables';
import { FORM_TRIGGER_NODE_TYPE, OPEN_URL_PANEL_TRIGGER_NODE_TYPES } from '@/constants'; import { FORM_TRIGGER_NODE_TYPE, OPEN_URL_PANEL_TRIGGER_NODE_TYPES } from '@/constants';
import { copyPaste } from '@/mixins/copyPaste'; import { copyPaste } from '@/mixins/copyPaste';
import { useToast } from '@/composables';
import { workflowHelpers } from '@/mixins/workflowHelpers'; import { workflowHelpers } from '@/mixins/workflowHelpers';
export default defineComponent({ export default defineComponent({
@ -94,7 +94,7 @@ export default defineComponent({
} }
return (this.nodeType as INodeTypeDescription).webhooks!.filter( return (this.nodeType as INodeTypeDescription).webhooks!.filter(
(webhookData) => webhookData.restartWebhook !== true, (webhookData) => webhookData.restartWebhook !== true && !webhookData.hasLifecycleMethods,
); );
}, },
baseText() { baseText() {

View file

@ -0,0 +1,52 @@
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class FacebookLeadAdsOAuth2Api implements ICredentialType {
name = 'facebookLeadAdsOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'Facebook Lead Ads OAuth2 API';
documentationUrl = 'facebookleadads';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://www.facebook.com/v17.0/dialog/oauth',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://graph.facebook.com/v17.0/oauth/access_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'leads_retrieval pages_show_list pages_manage_metadata pages_manage_ads',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
];
}

View file

@ -1,15 +1,16 @@
import { createHmac } from 'crypto';
import type { import type {
IHookFunctions,
IWebhookFunctions,
IDataObject, IDataObject,
IHookFunctions,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodePropertyOptions, INodePropertyOptions,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
IWebhookFunctions,
IWebhookResponseData, IWebhookResponseData,
JsonObject, JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow'; import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
@ -17,7 +18,7 @@ import { snakeCase } from 'change-case';
import { facebookApiRequest, getAllFields, getFields } from './GenericFunctions'; import { facebookApiRequest, getAllFields, getFields } from './GenericFunctions';
import { createHmac } from 'crypto'; import type { FacebookWebhookSubscription } from './types';
export class FacebookTrigger implements INodeType { export class FacebookTrigger implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
@ -177,18 +178,27 @@ export class FacebookTrigger implements INodeType {
const object = this.getNodeParameter('object') as string; const object = this.getNodeParameter('object') as string;
const appId = this.getNodeParameter('appId') as string; const appId = this.getNodeParameter('appId') as string;
const { data } = await facebookApiRequest.call(this, 'GET', `/${appId}/subscriptions`, {}); const { data } = (await facebookApiRequest.call(
this,
'GET',
`/${appId}/subscriptions`,
{},
)) as { data: FacebookWebhookSubscription[] };
for (const webhook of data) { const subscription = data.find((webhook) => webhook.object === object && webhook.status);
if (
webhook.target === webhookUrl && if (!subscription) {
webhook.object === object &&
webhook.status === true
) {
return true;
}
}
return false; return false;
}
if (subscription.callback_url !== webhookUrl) {
throw new NodeOperationError(
this.getNode(),
`The Facebook App ID ${appId} already has a webhook subscription. Delete it or use another App before executing the trigger. Due to Facebook API limitations, you can have just one trigger per App.`,
);
}
return true;
}, },
async create(this: IHookFunctions): Promise<boolean> { async create(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node'); const webhookData = this.getWorkflowStaticData('node');

View file

@ -0,0 +1,29 @@
export interface FacebookEvent {
object: string;
entry: FacebookPageEventEntry[];
}
export interface FacebookPageEventEntry {
id: string;
time: number;
changes: [
{
field: 'leadgen';
value: {
ad_id: string;
form_id: string;
leadgen_id: string;
created_time: number;
page_id: string;
adgroup_id: string;
};
},
];
}
export interface FacebookWebhookSubscription {
object: string;
callback_url: string;
fields: string[];
status: boolean;
}

View file

@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.facebookLeadAdsTrigger",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Marketing & Content"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/credentials/facebookleadads/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.facebookleadadstrigger/"
}
]
}
}

View file

@ -0,0 +1,311 @@
import { createHmac } from 'crypto';
import {
NodeOperationError,
type IDataObject,
type IHookFunctions,
type INodeType,
type INodeTypeDescription,
type IWebhookFunctions,
type IWebhookResponseData,
} from 'n8n-workflow';
import {
appWebhookSubscriptionCreate,
appWebhookSubscriptionDelete,
appWebhookSubscriptionList,
facebookEntityDetail,
installAppOnPage,
} from './GenericFunctions';
import { listSearch } from './methods';
import type { FacebookForm, FacebookFormLeadData, FacebookPageEvent } from './types';
export class FacebookLeadAdsTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Facebook Lead Ads Trigger',
name: 'facebookLeadAdsTrigger',
icon: 'file:facebook.svg',
group: ['trigger'],
version: 1,
subtitle: '={{$parameter["event"]}}',
description: 'Handle Facebook Lead Ads events via webhooks',
defaults: {
name: 'Facebook Lead Ads Trigger',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'facebookLeadAdsOAuth2Api',
required: true,
},
],
webhooks: [
{
name: 'setup',
httpMethod: 'GET',
responseMode: 'onReceived',
path: 'webhook',
},
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName:
'Due to Facebook API limitations, you can use just one Facebook Lead Ads trigger for each Facebook App',
name: 'facebookLeadAdsNotice',
type: 'notice',
default: '',
},
{
displayName: 'Event',
name: 'event',
type: 'options',
required: true,
default: 'newLead',
options: [
{
name: 'New Lead',
value: 'newLead',
},
],
},
{
displayName: 'Page',
name: 'page',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
description: 'The page linked to the form for retrieving new leads',
modes: [
{
displayName: 'From List',
name: 'list',
type: 'list',
typeOptions: {
searchListMethod: 'pageList',
},
},
{
displayName: 'By ID',
name: 'id',
type: 'string',
placeholder: '121637951029080',
},
],
},
{
displayName: 'Form',
name: 'form',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
description: 'The form to monitor for fetching lead details upon submission',
modes: [
{
displayName: 'From List',
name: 'list',
type: 'list',
typeOptions: {
searchListMethod: 'formList',
},
},
{
displayName: 'By ID',
name: 'id',
type: 'string',
placeholder: '121637951029080',
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Simplify Output',
name: 'simplifyOutput',
type: 'boolean',
default: true,
description:
'Whether to return a simplified version of the webhook event instead of all fields',
},
],
},
],
};
methods = {
listSearch,
};
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default') as string;
const credentials = await this.getCredentials('facebookLeadAdsOAuth2Api');
const appId = credentials.clientId as string;
const webhooks = await appWebhookSubscriptionList.call(this, appId);
const subscription = webhooks.find(
(webhook) =>
webhook.object === 'page' &&
webhook.fields.find((field) => field.name === 'leadgen') &&
webhook.active,
);
if (!subscription) {
return false;
}
if (subscription.callback_url !== webhookUrl) {
throw new NodeOperationError(
this.getNode(),
`The Facebook App ID ${appId} already has a webhook subscription. Delete it or use another App before executing the trigger. Due to Facebook API limitations, you can have just one trigger per App.`,
);
}
return true;
},
async create(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default') as string;
const credentials = await this.getCredentials('facebookLeadAdsOAuth2Api');
const appId = credentials.clientId as string;
const pageId = this.getNodeParameter('page', '', { extractValue: true }) as string;
const verifyToken = this.getNode().id;
await appWebhookSubscriptionCreate.call(this, appId, {
object: 'page',
callback_url: webhookUrl,
verify_token: verifyToken,
fields: ['leadgen'],
include_values: true,
});
await installAppOnPage.call(this, pageId, 'leadgen');
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const credentials = await this.getCredentials('facebookLeadAdsOAuth2Api');
const appId = credentials.clientId as string;
await appWebhookSubscriptionDelete.call(this, appId, 'page');
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const bodyData = this.getBodyData() as unknown as FacebookPageEvent;
const query = this.getQueryData() as IDataObject;
const res = this.getResponseObject();
const req = this.getRequestObject();
const headerData = this.getHeaderData() as IDataObject;
const credentials = await this.getCredentials('facebookLeadAdsOAuth2Api');
const pageId = this.getNodeParameter('page', '', { extractValue: true }) as string;
const formId = this.getNodeParameter('form', '', { extractValue: true }) as string;
// Check if we're getting facebook's challenge request (https://developers.facebook.com/docs/graph-api/webhooks/getting-started)
if (this.getWebhookName() === 'setup') {
if (query['hub.challenge']) {
if (this.getNode().id !== query['hub.verify_token']) {
return {};
}
res.status(200).send(query['hub.challenge']).end();
return { noWebhookResponse: true };
}
}
const computedSignature = createHmac('sha256', credentials.clientSecret as string)
.update(req.rawBody)
.digest('hex');
if (headerData['x-hub-signature-256'] !== `sha256=${computedSignature}`) {
return {};
}
if (bodyData.object !== 'page') {
return {};
}
const events = await Promise.all(
bodyData.entry
.map((entry) =>
entry.changes
.filter(
(change) =>
change.field === 'leadgen' &&
change.value.page_id === pageId &&
change.value.form_id === formId,
)
.map((change) => change.value),
)
.flat()
.map(async (event) => {
const [lead, form] = await Promise.all([
facebookEntityDetail.call(
this,
event.leadgen_id,
'field_data,created_time,ad_id,ad_name,adset_id,adset_name,form_id',
) as Promise<FacebookFormLeadData>,
facebookEntityDetail.call(
this,
event.form_id,
'id,name,locale,status,page,questions',
) as Promise<FacebookForm>,
]);
const simplifyOutput = this.getNodeParameter('options.simplifyOutput', true) as boolean;
if (simplifyOutput) {
return {
id: lead.id,
data: lead.field_data.reduce(
(acc, field) => ({ ...acc, [field.name]: field.values[0] }),
{},
),
form: {
id: form.id,
name: form.name,
locale: form.locale,
status: form.status,
},
ad: { id: lead.ad_id, name: lead.ad_name },
adset: { id: lead.adset_id, name: lead.adset_name },
page: form.page,
created_time: lead.created_time,
};
}
return {
id: lead.id,
field_data: lead.field_data,
form,
ad: { id: lead.ad_id, name: lead.ad_name },
adset: { id: lead.adset_id, name: lead.adset_name },
page: form.page,
created_time: lead.created_time,
event,
};
}),
);
if (events.length === 0) {
return {};
}
return {
workflowData: [this.helpers.returnJsonArray(events)],
};
}
}

View file

@ -0,0 +1,222 @@
import type { OptionsWithUri } from 'request';
import type {
IDataObject,
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IWebhookFunctions,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
import type {
CreateFacebookAppWebhookSubscription,
FacebookAppWebhookSubscription,
FacebookAppWebhookSubscriptionsResponse,
FacebookFormListResponse,
FacebookPage,
FacebookPageListResponse,
} from './types';
export async function facebookApiRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
method: string,
resource: string,
body = {},
qs: IDataObject = {},
): Promise<any> {
const options: OptionsWithUri = {
headers: {
accept: 'application/json',
},
method,
qs,
body,
gzip: true,
uri: `https://graph.facebook.com/v17.0${resource}`,
json: true,
};
try {
return await this.helpers.requestOAuth2.call(this, 'facebookLeadAdsOAuth2Api', options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject, {
message: error?.error?.error?.message,
});
}
}
export async function appAccessTokenRead(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
): Promise<{ access_token: string }> {
const credentials = await this.getCredentials('facebookLeadAdsOAuth2Api');
const options: OptionsWithUri = {
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
method: 'POST',
form: {
client_id: credentials.clientId,
client_secret: credentials.clientSecret,
grant_type: 'client_credentials',
},
uri: credentials.accessTokenUrl as string,
json: true,
};
try {
return await this.helpers.request(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function facebookAppApiRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
method: string,
resource: string,
body?: { type: 'json'; payload: IDataObject } | { type: 'form'; payload: IDataObject },
qs: IDataObject = {},
): Promise<any> {
const tokenResponse = await appAccessTokenRead.call(this);
const appAccessToken = tokenResponse.access_token;
const options: OptionsWithUri = {
headers: {
accept: 'application/json',
authorization: `Bearer ${appAccessToken}`,
},
method,
qs,
gzip: true,
uri: `https://graph.facebook.com/v17.0${resource}`,
json: true,
};
if (body?.type === 'json') {
options.body = body.payload;
} else if (body?.type === 'form') {
options.form = body.payload;
}
try {
return await this.helpers.request(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function appWebhookSubscriptionList(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
appId: string,
): Promise<FacebookAppWebhookSubscription[]> {
const response = (await facebookAppApiRequest.call(
this,
'GET',
`/${appId}/subscriptions`,
)) as FacebookAppWebhookSubscriptionsResponse;
return response.data;
}
export async function appWebhookSubscriptionCreate(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
appId: string,
subscription: CreateFacebookAppWebhookSubscription,
) {
return facebookAppApiRequest.call(this, 'POST', `/${appId}/subscriptions`, {
type: 'form',
payload: { ...subscription },
});
}
export async function appWebhookSubscriptionDelete(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
appId: string,
object: string,
) {
return facebookAppApiRequest.call(this, 'DELETE', `/${appId}/subscriptions`, {
type: 'form',
payload: { object },
});
}
export async function facebookPageList(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
cursor?: string,
): Promise<FacebookPageListResponse> {
const response = (await facebookApiRequest.call(
this,
'GET',
'/me/accounts',
{},
{ cursor, fields: 'id,name' },
)) as FacebookPageListResponse;
return response;
}
export async function facebookEntityDetail(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
entityId: string,
fields = 'id,name,access_token',
): Promise<any> {
return facebookApiRequest.call(this, 'GET', `/${entityId}`, {}, { fields });
}
export async function facebookPageApiRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
method: string,
resource: string,
body = {},
qs: IDataObject = {},
): Promise<any> {
const pageId = this.getNodeParameter('page', '', { extractValue: true }) as string;
const page = (await facebookEntityDetail.call(this, pageId)) as FacebookPage;
const pageAccessToken = page.access_token;
const options: OptionsWithUri = {
headers: {
accept: 'application/json',
authorization: `Bearer ${pageAccessToken}`,
},
method,
qs,
body,
gzip: true,
uri: `https://graph.facebook.com/v17.0${resource}`,
json: true,
};
try {
return await this.helpers.request.call(this, options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function installAppOnPage(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
pageId: string,
fields: string,
) {
return facebookPageApiRequest.call(
this,
'POST',
`/${pageId}/subscribed_apps`,
{},
{ subscribed_fields: fields },
);
}
export async function facebookFormList(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
pageId: string,
cursor?: string,
): Promise<FacebookFormListResponse> {
const response = (await facebookPageApiRequest.call(
this,
'GET',
`/${pageId}/leadgen_forms`,
{},
{ cursor, fields: 'id,name' },
)) as FacebookFormListResponse;
return response;
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60"><path d="M59.5 30C59.5 13.71 46.29.5 30 .5S.5 13.71.5 30c0 14.72 10.79 26.93 24.89 29.14V38.53H17.9V30h7.49v-6.5c0-7.39 4.4-11.48 11.14-11.48 3.23 0 6.6.58 6.6.58v7.26h-3.72c-3.66 0-4.81 2.27-4.81 4.61V30h8.18l-1.31 8.53H34.6v20.61C48.71 56.93 59.5 44.72 59.5 30z" fill="#1877f2"/><path d="M41.48 38.53L42.79 30h-8.18v-5.53c0-2.33 1.14-4.61 4.81-4.61h3.72V12.6s-3.38-.58-6.6-.58c-6.74 0-11.14 4.08-11.14 11.48V30h-7.5v8.53h7.49v20.61c1.5.24 3.04.36 4.61.36s3.11-.12 4.61-.36V38.53h6.87z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 567 B

View file

@ -0,0 +1 @@
export * as listSearch from './listSearch';

View file

@ -0,0 +1,42 @@
import type { ILoadOptionsFunctions, INodeListSearchResult } from 'n8n-workflow';
import { facebookFormList, facebookPageList } from '../GenericFunctions';
const filterMatches = (name: string, filter?: string): boolean =>
!filter || name?.toLowerCase().includes(filter.toLowerCase());
export async function pageList(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const { data: pages, paging } = await facebookPageList.call(this, paginationToken);
return {
results: pages
.filter((page) => filterMatches(page.name, filter))
.map((page) => ({
name: page.name,
value: page.id,
url: `https://facebook.com/${page.id}`,
})),
paginationToken: paging?.cursors?.after,
};
}
export async function formList(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const pageId = this.getNodeParameter('page', '', { extractValue: true }) as string;
const { data: forms, paging } = await facebookFormList.call(this, pageId, paginationToken);
return {
results: forms
.filter((form) => filterMatches(form.name, filter))
.map((form) => ({
name: form.name,
value: form.id,
})),
paginationToken: paging?.cursors?.after,
};
}

View file

@ -0,0 +1,105 @@
import type { GenericValue } from 'n8n-workflow';
export type BaseFacebookResponse<TData> = { data: TData };
export type BasePaginatedFacebookResponse<TData> = BaseFacebookResponse<TData> & {
paging: { cursors: { before?: string; after?: string } };
};
export type FacebookAppWebhookSubscriptionsResponse = BaseFacebookResponse<
FacebookAppWebhookSubscription[]
>;
export interface FacebookAppWebhookSubscription {
object: string;
callback_url: string;
active: boolean;
fields: FacebookAppWebhookSubscriptionField[];
}
export interface FacebookAppWebhookSubscriptionField {
name: string;
version: string;
}
export interface CreateFacebookAppWebhookSubscription {
object: string;
callback_url: string;
fields: string[];
include_values: boolean;
verify_token: string;
}
export type FacebookPageListResponse = BasePaginatedFacebookResponse<FacebookPage[]>;
export type FacebookFormListResponse = BasePaginatedFacebookResponse<FacebookForm[]>;
export interface FacebookPage {
id: string;
name: string;
access_token: string;
category: string;
category_list: FacebookPageCategory[];
tasks: string[];
}
export interface FacebookPageCategory {
id: string;
name: string;
}
export interface FacebookFormQuestion {
id: string;
key: string;
label: string;
type: string;
}
export interface FacebookForm {
id: string;
name: string;
locale: string;
status: string;
page: {
id: string;
name: string;
};
questions: FacebookFormQuestion[];
}
export interface FacebookPageEvent {
object: 'page';
entry: FacebookPageEventEntry[];
}
export interface FacebookPageEventEntry {
id: string;
time: number;
changes: [
{
field: 'leadgen';
value: {
ad_id: string;
form_id: string;
leadgen_id: string;
created_time: number;
page_id: string;
adgroup_id: string;
};
},
];
}
export interface FacebookFormLeadData {
id: string;
created_time: string;
ad_id: string;
ad_name: string;
adset_id: string;
adset_name: string;
form_id: string;
field_data: [
{
name: string;
values: GenericValue[];
},
];
}

View file

@ -106,6 +106,7 @@
"dist/credentials/F5BigIpApi.credentials.js", "dist/credentials/F5BigIpApi.credentials.js",
"dist/credentials/FacebookGraphApi.credentials.js", "dist/credentials/FacebookGraphApi.credentials.js",
"dist/credentials/FacebookGraphAppApi.credentials.js", "dist/credentials/FacebookGraphAppApi.credentials.js",
"dist/credentials/FacebookLeadAdsOAuth2Api.credentials.js",
"dist/credentials/FigmaApi.credentials.js", "dist/credentials/FigmaApi.credentials.js",
"dist/credentials/FileMaker.credentials.js", "dist/credentials/FileMaker.credentials.js",
"dist/credentials/FlowApi.credentials.js", "dist/credentials/FlowApi.credentials.js",
@ -485,6 +486,7 @@
"dist/nodes/ExecutionData/ExecutionData.node.js", "dist/nodes/ExecutionData/ExecutionData.node.js",
"dist/nodes/Facebook/FacebookGraphApi.node.js", "dist/nodes/Facebook/FacebookGraphApi.node.js",
"dist/nodes/Facebook/FacebookTrigger.node.js", "dist/nodes/Facebook/FacebookTrigger.node.js",
"dist/nodes/FacebookLeadAds/FacebookLeadAdsTrigger.node.js",
"dist/nodes/Figma/FigmaTrigger.node.js", "dist/nodes/Figma/FigmaTrigger.node.js",
"dist/nodes/FileMaker/FileMaker.node.js", "dist/nodes/FileMaker/FileMaker.node.js",
"dist/nodes/Filter/Filter.node.js", "dist/nodes/Filter/Filter.node.js",

View file

@ -2,23 +2,23 @@
import type * as express from 'express'; import type * as express from 'express';
import type FormData from 'form-data'; import type FormData from 'form-data';
import type { PathLike } from 'fs';
import type { IncomingHttpHeaders } from 'http'; import type { IncomingHttpHeaders } from 'http';
import type { Readable } from 'stream';
import type { URLSearchParams } from 'url';
import type { OptionsWithUri, OptionsWithUrl } from 'request'; import type { OptionsWithUri, OptionsWithUrl } from 'request';
import type { RequestPromiseOptions } from 'request-promise-native'; import type { RequestPromiseOptions } from 'request-promise-native';
import type { PathLike } from 'fs'; import type { Readable } from 'stream';
import type { URLSearchParams } from 'url';
import type { AuthenticationMethod } from './Authentication';
import type { CODE_EXECUTION_MODES, CODE_LANGUAGES } from './Constants'; import type { CODE_EXECUTION_MODES, CODE_LANGUAGES } from './Constants';
import type { IDeferredPromise } from './DeferredPromise'; import type { IDeferredPromise } from './DeferredPromise';
import type { ExecutionStatus } from './ExecutionStatus';
import type { ExpressionError } from './ExpressionError';
import type { NodeApiError, NodeOperationError } from './NodeErrors';
import type { Workflow } from './Workflow'; import type { Workflow } from './Workflow';
import type { WorkflowHooks } from './WorkflowHooks';
import type { WorkflowActivationError } from './WorkflowActivationError'; import type { WorkflowActivationError } from './WorkflowActivationError';
import type { WorkflowOperationError } from './WorkflowErrors'; import type { WorkflowOperationError } from './WorkflowErrors';
import type { NodeApiError, NodeOperationError } from './NodeErrors'; import type { WorkflowHooks } from './WorkflowHooks';
import type { ExpressionError } from './ExpressionError';
import type { ExecutionStatus } from './ExecutionStatus';
import type { AuthenticationMethod } from './Authentication';
export interface IAdditionalCredentialOptions { export interface IAdditionalCredentialOptions {
oauth2?: IOAuth2Options; oauth2?: IOAuth2Options;
@ -1627,6 +1627,7 @@ export interface IWebhookDescription {
responseMode?: WebhookResponseMode | string; responseMode?: WebhookResponseMode | string;
responseData?: WebhookResponseData | string; responseData?: WebhookResponseData | string;
restartWebhook?: boolean; restartWebhook?: boolean;
hasLifecycleMethods?: boolean; // set automatically by generate-ui-types
ndvHideUrl?: boolean; // If true the webhook will not be displayed in the editor ndvHideUrl?: boolean; // If true the webhook will not be displayed in the editor
ndvHideMethod?: boolean; // If true the method will not be displayed in the editor ndvHideMethod?: boolean; // If true the method will not be displayed in the editor
} }