2023-01-27 03:22:44 -08:00
import type {
2020-04-29 18:57:36 -07:00
IDataObject ,
2023-03-06 08:33:32 -08:00
IExecuteFunctions ,
2024-02-14 07:29:09 -08:00
IHttpRequestMethods ,
2020-04-29 18:57:36 -07:00
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
2024-02-14 07:29:09 -08:00
IRequestOptions ,
2023-02-27 19:39:43 -08:00
JsonObject ,
2020-04-29 18:57:36 -07:00
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeApiError , NodeOperationError } from 'n8n-workflow' ;
2020-04-29 18:57:36 -07:00
2020-05-02 07:54:21 -07:00
export class FacebookGraphApi implements INodeType {
2020-04-29 18:57:36 -07:00
description : INodeTypeDescription = {
2020-05-02 07:54:21 -07:00
displayName : 'Facebook Graph API' ,
name : 'facebookGraphApi' ,
2021-04-30 12:22:46 -07:00
icon : 'file:facebook.svg' ,
2020-04-29 18:57:36 -07:00
group : [ 'transform' ] ,
version : 1 ,
description : 'Interacts with Facebook using the Graph API' ,
defaults : {
2020-05-02 07:54:21 -07:00
name : 'Facebook Graph API' ,
2020-04-29 18:57:36 -07:00
} ,
inputs : [ 'main' ] ,
2020-05-02 07:54:21 -07:00
outputs : [ 'main' ] ,
2020-04-29 18:57:36 -07:00
credentials : [
{
2020-05-02 07:54:21 -07:00
name : 'facebookGraphApi' ,
2020-04-29 18:57:36 -07:00
required : true ,
} ,
] ,
properties : [
{
displayName : 'Host URL' ,
name : 'hostUrl' ,
type : 'options' ,
options : [
{
name : 'Default' ,
value : 'graph.facebook.com' ,
} ,
{
name : 'Video Uploads' ,
value : 'graph-video.facebook.com' ,
2020-10-22 06:46:03 -07:00
} ,
2020-04-29 18:57:36 -07:00
] ,
default : 'graph.facebook.com' ,
2022-08-01 13:47:55 -07:00
description :
'The Host URL of the request. Almost all requests are passed to the graph.facebook.com host URL. The single exception is video uploads, which use graph-video.facebook.com.' ,
2020-04-29 18:57:36 -07:00
required : true ,
} ,
{
displayName : 'HTTP Request Method' ,
name : 'httpRequestMethod' ,
type : 'options' ,
options : [
{
name : 'GET' ,
value : 'GET' ,
} ,
{
name : 'POST' ,
value : 'POST' ,
} ,
{
name : 'DELETE' ,
value : 'DELETE' ,
} ,
] ,
default : 'GET' ,
2022-05-06 14:01:25 -07:00
description : 'The HTTP Method to be used for the request' ,
2020-04-29 18:57:36 -07:00
required : true ,
} ,
{
displayName : 'Graph API Version' ,
name : 'graphApiVersion' ,
type : 'options' ,
options : [
{
2020-05-05 11:48:57 -07:00
name : 'Default' ,
2020-04-29 18:57:36 -07:00
value : '' ,
} ,
2023-08-01 00:43:07 -07:00
{
name : 'v17.0' ,
value : 'v17.0' ,
} ,
{
name : 'v16.0' ,
value : 'v16.0' ,
} ,
2022-12-01 09:07:54 -08:00
{
name : 'v15.0' ,
value : 'v15.0' ,
} ,
2022-07-05 00:04:53 -07:00
{
name : 'v14.0' ,
value : 'v14.0' ,
} ,
2022-03-13 04:12:57 -07:00
{
name : 'v13.0' ,
value : 'v13.0' ,
} ,
2021-10-09 09:27:58 -07:00
{
name : 'v12.0' ,
value : 'v12.0' ,
} ,
{
name : 'v11.0' ,
value : 'v11.0' ,
} ,
2021-03-08 08:36:17 -08:00
{
name : 'v10.0' ,
value : 'v10.0' ,
} ,
{
name : 'v9.0' ,
value : 'v9.0' ,
} ,
{
name : 'v8.0' ,
value : 'v8.0' ,
} ,
2020-05-05 11:48:57 -07:00
{
name : 'v7.0' ,
value : 'v7.0' ,
} ,
2020-04-29 18:57:36 -07:00
{
name : 'v6.0' ,
2020-05-02 07:54:21 -07:00
value : 'v6.0' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v5.0' ,
2020-05-02 07:54:21 -07:00
value : 'v5.0' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v4.0' ,
2020-05-02 07:54:21 -07:00
value : 'v4.0' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v3.3' ,
2020-05-02 07:54:21 -07:00
value : 'v3.3' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v3.2' ,
2020-05-02 07:54:21 -07:00
value : 'v3.2' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v3.1' ,
2020-05-02 07:54:21 -07:00
value : 'v3.1' ,
2020-04-29 18:57:36 -07:00
} ,
{
name : 'v3.0' ,
2020-05-02 07:54:21 -07:00
value : 'v3.0' ,
2020-04-29 18:57:36 -07:00
} ,
] ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'The version of the Graph API to be used in the request' ,
2020-04-29 18:57:36 -07:00
required : true ,
} ,
{
displayName : 'Node' ,
name : 'node' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
'The node on which to operate. A node is an individual object with a unique ID. For example, there are many User node objects, each with a unique ID representing a person on Facebook.' ,
2020-04-29 18:57:36 -07:00
placeholder : 'me' ,
required : true ,
} ,
{
displayName : 'Edge' ,
name : 'edge' ,
type : 'string' ,
default : '' ,
2022-08-01 13:47:55 -07:00
description :
'Edge of the node on which to operate. Edges represent collections of objects which are attached to the node.' ,
2020-04-29 18:57:36 -07:00
placeholder : 'videos' ,
} ,
2020-06-28 08:30:26 -07:00
{
displayName : 'Ignore SSL Issues' ,
name : 'allowUnauthorizedCerts' ,
type : 'boolean' ,
default : false ,
2022-05-06 14:01:25 -07:00
description : 'Whether to connect even if SSL certificate validation is not possible' ,
2020-06-28 08:30:26 -07:00
} ,
2020-04-29 18:57:36 -07:00
{
2024-01-03 03:08:16 -08:00
displayName : 'Send Binary File' ,
2020-04-29 18:57:36 -07:00
name : 'sendBinaryData' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
httpRequestMethod : [ 'POST' , 'PUT' ] ,
2020-04-29 18:57:36 -07:00
} ,
} ,
default : false ,
required : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether binary data should be sent as body' ,
2020-04-29 18:57:36 -07:00
} ,
{
2024-01-03 03:08:16 -08:00
displayName : 'Input Binary Field' ,
2020-04-29 18:57:36 -07:00
name : 'binaryPropertyName' ,
type : 'string' ,
default : '' ,
placeholder : 'file:data' ,
displayOptions : {
hide : {
2022-08-01 13:47:55 -07:00
sendBinaryData : [ false ] ,
2020-04-29 18:57:36 -07:00
} ,
show : {
2022-08-01 13:47:55 -07:00
httpRequestMethod : [ 'POST' , 'PUT' ] ,
2020-04-29 18:57:36 -07:00
} ,
} ,
2024-01-03 03:08:16 -08:00
hint : 'The name of the input binary field containing the file to be uploaded' ,
2022-08-01 13:47:55 -07:00
description :
2024-01-03 03:08:16 -08:00
'For Form-Data Multipart, they can be provided in the format: <code>"sendKey1:binaryProperty1,sendKey2:binaryProperty2</code>' ,
2020-04-29 18:57:36 -07:00
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
default : { } ,
options : [
{
displayName : 'Fields' ,
name : 'fields' ,
placeholder : 'Add Field' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
'/httpRequestMethod' : [ 'GET' ] ,
2020-04-29 18:57:36 -07:00
} ,
} ,
2022-05-06 14:01:25 -07:00
description : 'The list of fields to request in the GET request' ,
2020-04-29 18:57:36 -07:00
default : { } ,
options : [
{
name : 'field' ,
displayName : 'Field' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Name of the field' ,
2020-04-29 18:57:36 -07:00
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Query Parameters' ,
name : 'queryParameters' ,
placeholder : 'Add Parameter' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
description : 'The query parameters to send' ,
default : { } ,
options : [
{
name : 'parameter' ,
displayName : 'Parameter' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Name of the parameter' ,
2020-04-29 18:57:36 -07:00
} ,
{
displayName : 'Value' ,
name : 'value' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Value of the parameter' ,
2020-04-29 18:57:36 -07:00
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Query Parameters JSON' ,
name : 'queryParametersJson' ,
type : 'json' ,
default : '{}' ,
2022-08-01 13:47:55 -07:00
placeholder : '{"field_name": "field_value"}' ,
2020-04-29 18:57:36 -07:00
description : 'The query parameters to send, defined as a JSON object' ,
2020-10-22 06:46:03 -07:00
} ,
2020-04-29 18:57:36 -07:00
] ,
} ,
] ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
2022-12-02 06:25:21 -08:00
let response : any ;
2020-05-02 07:54:21 -07:00
const returnItems : INodeExecutionData [ ] = [ ] ;
2020-04-29 18:57:36 -07:00
for ( let itemIndex = 0 ; itemIndex < items . length ; itemIndex ++ ) {
2021-08-20 09:57:30 -07:00
const graphApiCredentials = await this . getCredentials ( 'facebookGraphApi' ) ;
2020-04-29 18:57:36 -07:00
const hostUrl = this . getNodeParameter ( 'hostUrl' , itemIndex ) as string ;
2024-02-14 07:29:09 -08:00
const httpRequestMethod = this . getNodeParameter (
'httpRequestMethod' ,
itemIndex ,
) as IHttpRequestMethods ;
2020-05-02 07:54:21 -07:00
let graphApiVersion = this . getNodeParameter ( 'graphApiVersion' , itemIndex ) as string ;
2020-04-29 18:57:36 -07:00
const node = this . getNodeParameter ( 'node' , itemIndex ) as string ;
const edge = this . getNodeParameter ( 'edge' , itemIndex ) as string ;
2022-12-02 12:54:28 -08:00
const options = this . getNodeParameter ( 'options' , itemIndex , { } ) ;
2020-04-29 18:57:36 -07:00
2020-05-02 07:54:21 -07:00
if ( graphApiVersion !== '' ) {
graphApiVersion += '/' ;
}
2020-04-29 18:57:36 -07:00
let uri = ` https:// ${ hostUrl } / ${ graphApiVersion } ${ node } ` ;
if ( edge ) {
uri = ` ${ uri } / ${ edge } ` ;
}
2024-02-14 07:29:09 -08:00
const qs : IDataObject = {
access_token : graphApiCredentials.accessToken ,
} ;
const requestOptions : IRequestOptions = {
2020-04-29 18:57:36 -07:00
headers : {
accept : 'application/json,text/*;q=0.99' ,
} ,
method : httpRequestMethod ,
uri ,
json : true ,
gzip : true ,
2022-12-02 12:54:28 -08:00
rejectUnauthorized : ! this . getNodeParameter ( 'allowUnauthorizedCerts' , itemIndex , false ) ,
2020-04-29 18:57:36 -07:00
} ;
if ( options !== undefined ) {
// Build fields query parameter as a comma separated list
if ( options . fields !== undefined ) {
const fields = options . fields as IDataObject ;
if ( fields . field !== undefined ) {
2022-08-01 13:47:55 -07:00
const fieldsCsv = ( fields . field as IDataObject [ ] ) . map ( ( field ) = > field . name ) . join ( ',' ) ;
2024-02-14 07:29:09 -08:00
qs . fields = fieldsCsv ;
2020-04-29 18:57:36 -07:00
}
}
// Add the query parameters defined in the UI
if ( options . queryParameters !== undefined ) {
const queryParameters = options . queryParameters as IDataObject ;
2020-05-02 07:54:21 -07:00
if ( queryParameters . parameter !== undefined ) {
2020-04-29 18:57:36 -07:00
for ( const queryParameter of queryParameters . parameter as IDataObject [ ] ) {
2024-02-14 07:29:09 -08:00
qs [ queryParameter . name as string ] = queryParameter . value ;
2020-04-29 18:57:36 -07:00
}
}
}
2024-02-14 07:29:09 -08:00
requestOptions . qs = qs ;
2020-04-29 18:57:36 -07:00
// Add the query parameters defined as a JSON object
if ( options . queryParametersJson ) {
let queryParametersJsonObj = { } ;
2022-08-01 13:47:55 -07:00
try {
2020-04-29 18:57:36 -07:00
queryParametersJsonObj = JSON . parse ( options . queryParametersJson as string ) ;
2022-08-01 13:47:55 -07:00
} catch {
/* Do nothing, at least for now */
}
2020-04-29 18:57:36 -07:00
requestOptions . qs = {
. . . qs ,
. . . queryParametersJsonObj ,
} ;
}
}
const sendBinaryData = this . getNodeParameter ( 'sendBinaryData' , itemIndex , false ) as boolean ;
if ( sendBinaryData ) {
2023-01-06 06:09:32 -08:00
const binaryPropertyNameFull = this . getNodeParameter ( 'binaryPropertyName' , itemIndex ) ;
2020-04-29 18:57:36 -07:00
let propertyName = 'file' ;
let binaryPropertyName = binaryPropertyNameFull ;
if ( binaryPropertyNameFull . includes ( ':' ) ) {
const binaryPropertyNameParts = binaryPropertyNameFull . split ( ':' ) ;
propertyName = binaryPropertyNameParts [ 0 ] ;
binaryPropertyName = binaryPropertyNameParts [ 1 ] ;
}
2023-03-06 08:33:32 -08:00
const binaryData = this . helpers . assertBinaryData ( itemIndex , binaryPropertyName ) ;
2022-08-01 13:47:55 -07:00
const binaryDataBuffer = await this . helpers . getBinaryDataBuffer (
itemIndex ,
binaryPropertyName ,
) ;
2020-04-29 18:57:36 -07:00
requestOptions . formData = {
[ propertyName ] : {
2022-01-03 13:42:42 -08:00
value : binaryDataBuffer ,
2020-04-29 18:57:36 -07:00
options : {
2023-03-06 08:33:32 -08:00
filename : binaryData.fileName ,
contentType : binaryData.mimeType ,
2020-04-29 18:57:36 -07:00
} ,
} ,
} ;
}
try {
// Now that the options are all set make the actual http request
response = await this . helpers . request ( requestOptions ) ;
} catch ( error ) {
2022-12-02 12:54:28 -08:00
if ( ! this . continueOnFail ( ) ) {
2023-02-27 19:39:43 -08:00
throw new NodeApiError ( this . getNode ( ) , error as JsonObject ) ;
2020-04-29 18:57:36 -07:00
}
2020-05-02 07:55:03 -07:00
let errorItem ;
if ( error . response !== undefined ) {
// Since this is a Graph API node and we already know the request was
// not successful, we'll go straight to the error details.
const graphApiErrors = error . response . body ? . error ? ? { } ;
errorItem = {
statusCode : error.statusCode ,
. . . graphApiErrors ,
headers : error.response.headers ,
} ;
} else {
// Unknown Graph API response, we'll dump everything in the response item
errorItem = error ;
}
returnItems . push ( { json : { . . . errorItem } } ) ;
2020-04-29 18:57:36 -07:00
continue ;
}
if ( typeof response === 'string' ) {
2022-12-02 12:54:28 -08:00
if ( ! this . continueOnFail ( ) ) {
2022-08-01 13:47:55 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'Response body is not valid JSON.' , {
itemIndex ,
} ) ;
2020-04-29 18:57:36 -07:00
}
2020-05-02 07:55:03 -07:00
returnItems . push ( { json : { message : response } } ) ;
2020-04-29 18:57:36 -07:00
continue ;
}
2022-08-01 13:47:55 -07:00
returnItems . push ( { json : response } ) ;
2020-04-29 18:57:36 -07:00
}
2020-05-02 07:54:21 -07:00
return [ returnItems ] ;
2020-04-29 18:57:36 -07:00
}
}