2020-04-29 18:57:36 -07:00
import {
BINARY_ENCODING ,
IExecuteFunctions ,
} from 'n8n-core' ;
import {
IBinaryData ,
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
2021-04-16 09:33:36 -07:00
NodeApiError ,
NodeOperationError ,
2020-04-29 18:57:36 -07:00
} from 'n8n-workflow' ;
2020-11-04 03:25:18 -08:00
import {
OptionsWithUri ,
} from 'request' ;
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' ,
2020-04-29 18:57:36 -07:00
icon : 'file:facebook.png' ,
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-11-04 03:25:18 -08:00
color : '#3B5998' ,
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' ,
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.' ,
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' ,
description : 'The HTTP Method to be used for the request.' ,
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 : '' ,
} ,
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 : '' ,
description : 'The version of the Graph API to be used in the request.' ,
required : true ,
} ,
{
displayName : 'Node' ,
name : 'node' ,
type : 'string' ,
default : '' ,
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.' ,
placeholder : 'me' ,
required : true ,
} ,
{
displayName : 'Edge' ,
name : 'edge' ,
type : 'string' ,
default : '' ,
2020-08-04 02:35:28 -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' ,
required : false ,
} ,
2020-06-28 08:30:26 -07:00
{
displayName : 'Ignore SSL Issues' ,
name : 'allowUnauthorizedCerts' ,
type : 'boolean' ,
default : false ,
description : 'Still download the response even if SSL certificate validation is not possible. (Not recommended)' ,
} ,
2020-04-29 18:57:36 -07:00
{
displayName : 'Send Binary Data' ,
name : 'sendBinaryData' ,
type : 'boolean' ,
displayOptions : {
show : {
httpRequestMethod : [
'POST' ,
'PUT' ,
] ,
} ,
} ,
default : false ,
required : true ,
description : 'If binary data should be send as body.' ,
} ,
{
displayName : 'Binary Property' ,
name : 'binaryPropertyName' ,
type : 'string' ,
required : false ,
default : '' ,
placeholder : 'file:data' ,
displayOptions : {
hide : {
sendBinaryData : [
false ,
] ,
} ,
show : {
httpRequestMethod : [
'POST' ,
'PUT' ,
] ,
} ,
} ,
description : ` Name of the binary property which contains the data for the file to be uploaded.<br />
For Form - Data Multipart , multiple can be provided in the format : < br / >
" sendKey1 :binaryProperty1 , sendKey2 :binaryProperty2 ` ,
} ,
{
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 : {
'/httpRequestMethod' : [
'GET' ,
] ,
} ,
} ,
description : 'The list of fields to request in the GET request.' ,
default : { } ,
options : [
{
name : 'field' ,
displayName : 'Field' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
default : '' ,
description : 'Name of the field.' ,
} ,
] ,
} ,
] ,
} ,
{
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 : '' ,
description : 'Name of the parameter.' ,
} ,
{
displayName : 'Value' ,
name : 'value' ,
type : 'string' ,
default : '' ,
description : 'Value of the parameter.' ,
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Query Parameters JSON' ,
name : 'queryParametersJson' ,
type : 'json' ,
default : '{}' ,
placeholder : '{\"field_name\": \"field_value\"}' ,
description : 'The query parameters to send, defined as a JSON object' ,
required : false ,
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 ( ) ;
let response : any ; // tslint:disable-line:no-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 ++ ) {
2020-05-02 07:54:21 -07:00
const graphApiCredentials = this . getCredentials ( 'facebookGraphApi' ) ;
2020-04-29 18:57:36 -07:00
const hostUrl = this . getNodeParameter ( 'hostUrl' , itemIndex ) as string ;
const httpRequestMethod = this . getNodeParameter ( 'httpRequestMethod' , itemIndex ) as string ;
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 ;
const options = this . getNodeParameter ( 'options' , itemIndex , { } ) as IDataObject ;
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 } ` ;
}
const requestOptions : OptionsWithUri = {
headers : {
accept : 'application/json,text/*;q=0.99' ,
} ,
method : httpRequestMethod ,
uri ,
json : true ,
gzip : true ,
qs : {
access_token : graphApiCredentials ! . accessToken ,
} ,
2020-06-28 08:30:26 -07:00
rejectUnauthorized : ! this . getNodeParameter ( 'allowUnauthorizedCerts' , itemIndex , false ) as boolean ,
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 ) {
const fieldsCsv = ( fields . field as IDataObject [ ] ) . map ( field = > field . name ) . join ( ',' ) ;
requestOptions . qs . fields = fieldsCsv ;
}
}
// 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 [ ] ) {
requestOptions . qs [ queryParameter . name as string ] = queryParameter . value ;
}
}
}
// Add the query parameters defined as a JSON object
if ( options . queryParametersJson ) {
let queryParametersJsonObj = { } ;
try
{
queryParametersJsonObj = JSON . parse ( options . queryParametersJson as string ) ;
} catch { /* Do nothing, at least for now */ }
const qs = requestOptions . qs ;
requestOptions . qs = {
. . . qs ,
. . . queryParametersJsonObj ,
} ;
}
}
const sendBinaryData = this . getNodeParameter ( 'sendBinaryData' , itemIndex , false ) as boolean ;
if ( sendBinaryData ) {
const item = items [ itemIndex ] ;
if ( item . binary === undefined ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'No binary data exists on item!' ) ;
2020-04-29 18:57:36 -07:00
}
const binaryPropertyNameFull = this . getNodeParameter ( 'binaryPropertyName' , itemIndex ) as string ;
let propertyName = 'file' ;
let binaryPropertyName = binaryPropertyNameFull ;
if ( binaryPropertyNameFull . includes ( ':' ) ) {
const binaryPropertyNameParts = binaryPropertyNameFull . split ( ':' ) ;
propertyName = binaryPropertyNameParts [ 0 ] ;
binaryPropertyName = binaryPropertyNameParts [ 1 ] ;
}
if ( item . binary [ binaryPropertyName ] === undefined ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` No binary data property " ${ binaryPropertyName } " does not exists on item! ` ) ;
2020-04-29 18:57:36 -07:00
}
const binaryProperty = item . binary [ binaryPropertyName ] as IBinaryData ;
requestOptions . formData = {
[ propertyName ] : {
value : Buffer.from ( binaryProperty . data , BINARY_ENCODING ) ,
options : {
filename : binaryProperty.fileName ,
contentType : binaryProperty.mimeType ,
} ,
} ,
} ;
}
try {
// Now that the options are all set make the actual http request
response = await this . helpers . request ( requestOptions ) ;
} catch ( error ) {
if ( this . continueOnFail ( ) === false ) {
2021-04-16 09:33:36 -07:00
throw new NodeApiError ( this . getNode ( ) , error ) ;
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' ) {
if ( this . continueOnFail ( ) === false ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'Response body is not valid JSON.' ) ;
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 ;
}
2020-05-02 07:54:21 -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
}
}