-
Notifications
You must be signed in to change notification settings - Fork 8.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add WhatsApp Business Trigger Node #8840
Changes from 8 commits
9c3818a
d5ff5d5
64d7f02
6e7a5b2
12d36af
55760f4
80d8750
9f01040
1ac94cc
fcdb3d9
9b103c9
ac3720e
238cbaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import type { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow'; | ||
|
||
export class WhatsAppOAuth2Api implements ICredentialType { | ||
name = 'whatsAppOAuth2Api'; | ||
|
||
extends = ['oAuth2Api']; | ||
|
||
displayName = 'WhatsApp OAuth2 API'; | ||
|
||
documentationUrl = 'whatsApp'; | ||
|
||
properties: INodeProperties[] = [ | ||
{ | ||
displayName: 'Grant Type', | ||
name: 'grantType', | ||
type: 'hidden', | ||
default: 'clientCredentials', | ||
}, | ||
{ | ||
displayName: 'Authorization URL', | ||
name: 'authUrl', | ||
type: 'hidden', | ||
default: 'https://www.facebook.com/v19.0/dialog/oauth', | ||
required: true, | ||
}, | ||
{ | ||
displayName: 'Access Token URL', | ||
name: 'accessTokenUrl', | ||
type: 'hidden', | ||
default: 'https://graph.facebook.com/v19.0/oauth/access_token', | ||
required: true, | ||
}, | ||
{ | ||
displayName: 'Scope', | ||
name: 'scope', | ||
type: 'hidden', | ||
default: 'whatsapp_business_management whatsapp_business_messaging', | ||
}, | ||
{ | ||
displayName: 'Auth URI Query Parameters', | ||
name: 'authQueryParameters', | ||
type: 'hidden', | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Authentication', | ||
name: 'authentication', | ||
type: 'hidden', | ||
default: 'header', | ||
}, | ||
]; | ||
|
||
test: ICredentialTestRequest = { | ||
request: { | ||
baseURL: 'https://graph.facebook.com/v19.0', | ||
url: '/', | ||
ignoreHttpStatusErrors: true, | ||
}, | ||
rules: [ | ||
{ | ||
type: 'responseSuccessBody', | ||
properties: { | ||
key: 'error.type', | ||
value: 'OAuthException', | ||
message: 'Invalid access token', | ||
}, | ||
}, | ||
], | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why http error is concealed here? reason we have this test to validate user credentials information There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a copy of one of the other credentials, didn't change it. So I do not know. I do not know what this is doing. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import type { | ||
IDataObject, | ||
IExecuteFunctions, | ||
IHookFunctions, | ||
IHttpRequestMethods, | ||
ILoadOptionsFunctions, | ||
IRequestOptions, | ||
IWebhookFunctions, | ||
JsonObject, | ||
} from 'n8n-workflow'; | ||
import { NodeApiError } from 'n8n-workflow'; | ||
import type { | ||
WhatsAppAppWebhookSubscriptionsResponse, | ||
WhatsAppAppWebhookSubscription, | ||
} from './types'; | ||
|
||
export async function whatsAppApiRequest( | ||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, | ||
method: IHttpRequestMethods, | ||
resource: string, | ||
body = {}, | ||
qs: IDataObject = {}, | ||
): Promise<any> { | ||
const options: IRequestOptions = { | ||
headers: { | ||
accept: 'application/json', | ||
}, | ||
method, | ||
qs, | ||
body, | ||
gzip: true, | ||
uri: `https://graph.facebook.com/v19.0${resource}`, | ||
json: true, | ||
}; | ||
|
||
try { | ||
return await this.helpers.requestOAuth2.call(this, 'whatsAppOAuth2Api', options); | ||
} catch (error) { | ||
throw new NodeApiError(this.getNode(), error as JsonObject, { | ||
message: error?.error?.error?.message, | ||
}); | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this function is not used anywhere |
||
export async function appAccessTokenRead( | ||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, | ||
): Promise<{ access_token: string }> { | ||
const credentials = await this.getCredentials('whatsAppOAuth2Api'); | ||
|
||
const options: IRequestOptions = { | ||
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 whatsAppAppApiRequest( | ||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, | ||
method: IHttpRequestMethods, | ||
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: IRequestOptions = { | ||
headers: { | ||
accept: 'application/json', | ||
authorization: `Bearer ${appAccessToken}`, | ||
}, | ||
method, | ||
qs, | ||
gzip: true, | ||
uri: `https://graph.facebook.com/v19.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<WhatsAppAppWebhookSubscription[]> { | ||
const response = (await whatsAppAppApiRequest.call( | ||
this, | ||
'GET', | ||
`/${appId}/subscriptions`, | ||
)) as WhatsAppAppWebhookSubscriptionsResponse; | ||
return response.data; | ||
} | ||
|
||
export async function appWebhookSubscriptionCreate( | ||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, | ||
appId: string, | ||
subscription: IDataObject, | ||
) { | ||
return await whatsAppAppApiRequest.call(this, 'POST', `/${appId}/subscriptions`, { | ||
type: 'form', | ||
payload: { ...subscription }, | ||
}); | ||
} | ||
|
||
export async function appWebhookSubscriptionDelete( | ||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, | ||
appId: string, | ||
object: string, | ||
) { | ||
return await whatsAppAppApiRequest.call(this, 'DELETE', `/${appId}/subscriptions`, { | ||
type: 'form', | ||
payload: { object }, | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"node": "n8n-nodes-base.whatsAppTrigger", | ||
"nodeVersion": "1.0", | ||
"codexVersion": "1.0", | ||
"categories": ["Communication"], | ||
"resources": { | ||
"credentialDocumentation": [ | ||
{ | ||
"url": "https://docs.n8n.io/integrations/credentials/whatsapp/" | ||
} | ||
], | ||
"primaryDocumentation": [ | ||
{ | ||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.whatsapptrigger/" | ||
} | ||
] | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct me if I wrong, but oauth2 mechanism is not used, we are using client/app id and secret and manually requesting access token by
appAccessTokenRead
, this should not extend oAuth2Api and just have 2 parameters App ID and App SecretThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct, I tried getting OAuth2 to work, but wasn't able to.
So we should be able to simplify it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I changed it to a simplified cred version it stopped working so discarded the changes. Will keep it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add more details about issue?