Facebook api giving 400 error with FacebookRequestError msg - node.js

const bizSdk = require('facebook-nodejs-business-sdk');
const AdAccount = bizSdk.AdAccount;
const AdsInsights = bizSdk.AdsInsights;
let access_token = '';
let ad_account_id = '';
let app_secret = '';
let app_id = '';
const api = bizSdk.FacebookAdsApi.init(access_token);
const account = new AdAccount(ad_account_id);
const showDebugingInfo = true; // Setting this to true shows more debugging info.
if (showDebugingInfo) {
api.setDebug(true);
}
let ads_insights;
let ads_insights_id;
const logApiCallResult = (apiCallName, data) => {
console.log(apiCallName);
if (showDebugingInfo) {
console.log('Data:' + JSON.stringify(data));
}
};
const fields = [
'website_ctr:link_click',
'purchase_roas:omni_purchase',
'website_purchase_roas:offsite_conversion.fb_pixel_purchase',
'mobile_app_purchase_roas:app_custom_event.fb_mobile_purchase',
];
const params = {
'level' : 'adset',
'filtering' : [{'field':'delivery_info','operator':'IN','value':['active']}],
'breakdowns' : ['days_1'],
'time_range' : {'since':'2020-03-01','until':'2020-03-31'},
};
(new AdAccount(ad_account_id)).getInsights(
fields,
params
)
.then((result) => {
logApiCallResult('ads_insights api call complete.', result);
ads_insights_id = result[0].id;
})
.catch((error) => {
console.log(error);
});
Hi, Here is my node.js code which i copied from facebook.
But, when i am running the code i am getting below error.
status: 400,
response: {
error: {
message: 'Syntax error "Expected "(" instead of ","." at character 109: website_ctr:link_click,purchase_roas:omni_purchase,website_purchase_roas:offsite_conversion.fb_pixel_purchase,mobile_app_purchase_roas:app_custom_event.fb_mobile_purchase',
type: 'OAuthException',
code: 2500,
fbtrace_id: 'ANEo2UqslTEnJqp5yeVRVEW'
}
}
I never used this before so it is confusing for me.
I there any issue with the fields i am sending.
Please have a look

Just solved it myself. The error Syntax error "Expected "(" instead of ","." at character XX comes from the parameter - website_purchase_roas:offsite_conversion.fb_pixel_purchase.
It looks like Node doesn't know how to handle a property:value pair in which the value has a . . Also the Facebook API seems to be expecting enumerated Field types, so it would be interesteing to see what it resolves too.
The line which caused this in my own was this - actions:onsite_conversion.post_save, and pushed the same error.
Full Error - 'Syntax error "Expected "(" instead of ","." at character 324:.

Related

Getting Error [Cannot read properties of undefined (reading 'generatetypeinfo')] in Node JS API post method

I am new to Restful API development using NodeJS and SQL Server. I am trying to do a simple [post] operation where I am passing an array of objects to the API endpoint and then calling a SQL Server procedure with a table valued parameter. I am getting the below error
Cannot read properties of undefined (reading 'generateTypeInfo')
I was really shocked to see that there is not a single help topic found over Google regarding this error. I do not want to learn ASP.NET Core for this because JavaScript has an easy learning curve. Am I doing a mistake by developing a Rest API by using the combination of NodeJS and SQL Server? Below is my Related .JS file called in Post endpoint
const sql = require("mssql/msnodesqlv8");
const dataAccess = require("../DataAccess");
const fn_CreateProd = async function (product) {
let errmsg = "";
let connPool = null;
await sql
.connect(global.config)
.then((pool) => {
global.connPool = pool;
result = pool.request().query("select * from products where 1=2");
return result;
})
.then((retResult) => {
const srcTable = retResult.recordset.toTable("tvp_products");
let newsrcTable = Array.from(srcTable.columns);
console.log('Source table b4 mapping',srcTable)
newsrcTable = newsrcTable.map((i) => {
i.name = i.name.toUpperCase();
return i;
});
console.log('Source table after convert array with mapping',newsrcTable)
const prdTable = dataAccess.generateTable(
newsrcTable,
product,
"tvp_products"
);
console.log("Prepared TVp data", prdTable);
const newResult = dataAccess.execute(`sp3s_ins_products_tvp`, [
{ name: "tblprods", value: prdTable },
]);
console.log("Result of Execute Final procedure", newResult);
return newResult;
})
.then(result => {
console.log("Result of proc", result);
if (!result.errmsg) errmsg = "Products Inserted successfully";
else errmsg = result.errmsg;
})
.catch((err) => {
console.log("Enter catch of Posting prod", err.message);
errmsg = err.message;
})
.finally((resp) => {
sql.close();
});
return { retStatus: errmsg };
};
module.exports = fn_CreateProd;
and Content of Generatetable function are as below :
const generateTable = (columns, entities,tvpName) => {
const table = new mssql.Table(tvpName);
// const testobj = {type : [sql.numeric],name : 'Sanjay'}
// console.log('Columns testobj',testobj.type)
columns.forEach(column => {
// console.log('COlumn data for COlumn :',column)
if (column && typeof column === 'object' && column.name && column.type) {
let colOptions = {}
if (column.type==mssql.Numeric)
{
colOptions.scale=column.scale
colOptions.precision=column.precision
}
else
if (column.type==mssql.VarChar || column.type==mssql.Char )
{
colOptions.length = column.length
}
// console.log (`Column name type for column :${column.name} -${colType}-Actual :${column['type']}`)
if (column.hasOwnProperty('options')) {
table.columns.add(column.name.toUpperCase(), colType,column.options);
} else {
table.columns.add(column.name.toUpperCase(),colOptions)
}
}
});
console.log('Generated table',table)
const newEntities = entities.map(obj=>keystoUppercase(obj))
// console.log('New entities after uppercase',newEntities)
newEntities.forEach(entity => {
table.rows.add(...columns.map(i =>
entity[i.name]));
});
return table;
};
I have found the solution now. Actually, if you can see the code of generateTable function, I was adding the columns into the table but not mentioning the data type of the columns due to which this error was coming. I have added one more property [type] in the [colOptions] object being passed to columns.add command in the function [Generatetable]. Thanks a lot anyway to you for quick replies by Dale. K.

NodeJS/Express MongoDB toArray() doesn't work

I am trying to get all documents into array so i can use array.forEach to do something with each document. I'm not into using stream.
according to mongodb official documentation, find() should return a cursor:
Consider the following example that applies toArray() to the cursor returned from the find() method:
var allProductsArray = db.products.find().toArray();
if (allProductsArray.length > 0) { printjson (allProductsArray[0]); }
https://www.mongodb.com/docs/manual/reference/method/cursor.toArray/
My current codebase:
//success, get and list apps in json
var userId = new ObjectId(resultUser._id);
var appRes = capidb.collection("api_applications").find({application_owner: userId}).toArray()
if (appRes) {
console.log("UserID: " + userId + " | : "+appRes.length)
res.send("ae")
//appRes.forEach(function(appRes) {
//console.log(appRes.application_name)
//})
} else {
res.status(204)
.json({success: false, code: "NoContent", message: "There wasn't any applications to list."})
}
doesn't work as properly. The console.log("UserID: " + userId + " | : "+appRes.length)
outputs: UserID: <the userId> | : undefined
<the userId> works properly, just masked out the actuall userId of mine.
So the .length returns "undefined", and why is that?
UPDATE:
I also tried this documentation: https://mongodb.github.io/node-mongodb-native/4.8/#find-all-documents
making an async function:
const findApplications = async function(userid) {
const appRes = await capidb.collection("api_applications").find({application_owner: userid}).toArray();
return appRes;
}
and initiating it:
//success, get and list apps in json
var userId = new ObjectId(resultUser._id);
const appRes = findApplications(userId);
if (appRes) {
console.log("UserID: " + userId + " | : "+appRes.length)
res.send("ae")
//appRes.forEach(function(appRes) {
//console.log(appRes.application_name)
//})
} else {
res.status(204)
.json({success: false, code: "NoContent", message: "There wasn't any applications to list."})
}
and that also returns undefined.
ASYNC UPDATE:
I tried to make the router function async, to access the findApplications() function:
router.get("/:tat", async function(req, res) {
...
}
and i also made the function request await:
const appRes = await findApplications(userId);
even tho now i get the error:
const appRes = await findApplications(userId);
^^^^^
SyntaxError: await is only valid in async function

Error calling a TypeScript function in firebase cloud function

I have some issue writing typescripe code for a firebase cloud function.
I assume it is mainly a syntax problem.
The code is below, but the relevant part is the what concerns the call of the myLocalFunc function. The rest is only here to provide some context. The part reading:
response:Response<any>
in:
const myLocalFunc = (mail:string, flag:boolean, response:Response<any>) => {...}
is wrong. Because I get this error message:
error TS2315: Type 'Response' is not generic.
What is the proper syntax?
const myLocalFunc = (mail:string, flag:boolean, response:Response<any>) => {
admin.auth().getUserByEmail(mail)
.then(function(userRecord) {
// Do some useful work.
const data = {
boolField: flag,
};
const refStr = "/Users/"+userRecord.uid;
admin.database().ref(refStr).set(data);
})
.catch(function(error) {
console.log("Error fetching user data:", error);
let rspMsg = "This user (";
rspMsg += mail;
rspMsg += ") does not exists.";
response.send(rspMsg);
});
};
exports.myFunc = functions.https.onRequest(function(req, resp) {
resp.set("Access-Control-Allow-Origin", "*");
resp.set("Access-Control-Allow-Methods", "GET, POST");
corsHandler(req, resp, async () => {
const from = String(req.body.from);
const idToken = String(req.body.token);
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
const uid = decodedToken.uid;
const refStr = "/Users/"+uid;
const ref = admin.database().ref(refStr);
ref.on("value", function(snapshot) {
console.log("snapshot.val():", snapshot.val());
if (snapshot.val() !== undefined) {
const snv = snapshot.val();
if (snv.adminRights !== undefined) {
if (snv.adminRights) {
// Only if we reach this point,
// can we perform the operation next line.
myLocalFunc(from, true, resp);
}
}
}
}, function(errorObject) {
console.log("The read failed: " + errorObject);
});
}).catch(function(error) {
// Handle error
functions.logger.log("(FL)error:", error);
console.log("(CL)error:", error);
});
}); // End corsHandler.
});
Note:
I got the idea of trying Response<any> for the type (without much conviction) after reading some some documentation for functions.https.onRequest.
If I change the code to:
const myLocalFunc = (mail:string, flag:boolean, response) => {...}
which is in fact what I started with.
I get this error:
error TS7006: Parameter 'response' implicitly has an 'any' type.
If I try to change the code to this:
const myLocalFunc = (mail:string, flag:boolean, response:Response) => {...}
I get these two errors:
37:18 - error TS2339: Property 'send' does not exist on type 'Response'.
37 response.send(rspMsg); // This works.
~~~~
79:46 - error TS2345: Argument of type 'Response<any>' is not assignable to parameter of type 'Response'.
Type 'Response<any>' is missing the following properties from type 'Response': headers, ok, redirected, statusText, and 9 more.
79 myLocalFunc(from, true, resp);
The req and res parameters in onRequest() are Request and Response objects from Express. When not importing Response from Express, it's another interface which also is not generic.
import {Response} from "express";
const myLocalFunc = (mail: string, flag: boolean, response: Response) => {...}

nodeJS: how to call an async function within a loop in another async function call

I am trying to call one async function from inside a loop run by another async function. These functions call APIs and I am using request-promise using nodeJS.
functions.js file
const rp = require("request-promise");
// function (1)
async email_views: emailId => {
let data = {};
await rp({
url: 'myapiurl',
qs: { accessToken: 'xyz', emailID: emailId },
method: 'GET'
})
.then( body => { data = JSON.parse(body) })
.catch( error => { console.log(error} );
return data;
};
The above JSON looks like this:
...
data:{
records: [
{
...
contactID: 123456,
...
},
{
...
contactID: 456789,
...
}
]
}
...
I am running a loop to get individual record, where I am getting a contactID associated with each of them.
// function#2 (also in functions.js file)
async contact_detail: contactId => {
let data = {};
await rp({
url: 'myapiurl2',
qs: { accessToken: 'xyz', contactID: contactId },
method: 'GET'
})
.then( body => { data = JSON.parse(body) })
.catch( error => { console.log(error} );
return data;
};
The above function takes one contactId as parameter and gets that contact's detail calling another API endpoint.
Both functions work fine when they are called separately. But I am trying to do it inside a loop like this:
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
for( let i=0; i<records.length; i++) {
...
const cId = records[i].contactID;
const contact = await contact_detail(cId); // function#2
names += contact.data.firstName + " " + contact.data.lastName + " ";
...
}
console.log(names);
...
The problem is I am only getting the first contact back from the above code block, i.e. even I have 20 records from function#1, in the loop when I am calling contact_detail (function#2) for each contactID (cId), I get contact detail once, i.e. for the first cId only. For rest I get nothing!
What is the correct way to achieve this using nodeJs?
UPDATE:
const { App } = require("jovo-framework");
const { Alexa } = require("jovo-platform-alexa");
const { GoogleAssistant } = require("jovo-platform-googleassistant");
const { JovoDebugger } = require("jovo-plugin-debugger");
const { FileDb } = require("jovo-db-filedb");
const custom = require("./functions");
const menuop = require("./menu");
const stateus = require("./stateus");
const alexaSpeeches = require("./default_speech");
const app = new App();
app.use(new Alexa(), new GoogleAssistant(), new JovoDebugger(), new FileDb());
let sp = "";
async EmailViewsByContactIntent() {
try {
const viewEmailId =
this.$session.$data.viewEmailIdSessionKey != null
? this.$session.$data.viewEmailIdSessionKey
: this.$inputs.view_email_Id_Number.value;
let pageIndex =
this.$session.$data.viewEmailPageIndex != null
? this.$session.$data.viewEmailPageIndex
: 1;
const result = await custom.email_views_by_emailId(
viewEmailId,
pageIndex
);
const records = result.data.records;
if (records.length > 0) {
const totalRecords = result.data.paging.totalRecords;
this.$session.$data.viewEmailTotalPages = totalRecords;
sp = `i have found a total of ${totalRecords} following view records. `;
if (totalRecords > 5) {
sp += `i will tell you 5 records at a time. for next 5 records, please say, next. `;
this.$session.$data.viewEmailIdSessionKey = this.$inputs.view_email_Id_Number.value;
this.$session.$data.viewEmailPageIndex++;
}
for (let i = 0; i < records.length; i++) {
const r = records[i];
/* Here I want to pass r.contactID as contactId in the function contact_detail like this: */
const contact = await custom.contact_detail(r.contactID);
const contact_name = contact.data.firstName + " " + contact.data.lastName;
/* The above two lines of code fetch contact_name for the first r.contactID and for the rest I get an empty string only. */
const formatted_date = r.date.split(" ")[0];
sp += `contact ID ${spellOut_speech_builder(
r.contactID
)} had viewed on ${formatted_date} from IP address ${
r.ipAddress
}. name of contact is, ${contact_name}. `;
}
if (totalRecords > 5) {
sp += ` please say, next, for next 5 records. `;
}
} else {
sp = ``;
}
this.ask(sp);
} catch (e) {
this.tell(e);
}
}
I am building an alexa skill using JOVO framework and nodeJS.
UPDATE #2
As a test, I only returned the contactId which I am passing to the contact_detail function and I am getting the correct value back to the above code under my first UPDATE.
async contact_detail: contactId => {
return contactId;
}
It seems even after getting the value right, the function is somehow failing to execute. However, the same contact_detail function works perfectly OK, when I am calling it from another place. Only doesn't not work inside a loop.
What could be the reason?
I must be missing something but don't know what!
You are mixing async await and promises together which is causing you confusion. You typically would use one of the other(as async await effectivly provides syntax sugar so you can avoid dealing with the verbose promise code) in a given location.
Because you mixed the two you are in a weird area where the behavior is harder to nail down.
If you want to use async await your functions should look like
async contact_detail: contactId => {
try {
const body = await rp({
url: 'myapiurl2',
qs: { ... }
});
return JSON.parse(body);
} catch(e) {
console.log(e);
//This will return undefined in exception cases. You may want to catch at a higher level.
}
};
or with promises
async contact_detail: contactId => {
return rp({
url: 'myapiurl2',
qs: { ... }
})
.then( body => JSON.parse(body))
.catch( error => {
console.log(error);
//This will return undefined in exception cases. You probably dont want to catch here.
});
};
Keep in mind your current code executing the function will do each call in series. If you want to do them in parallel you will need to call the function a bit differently and use something like Promise.all to resolve the result.
Here you go:
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
await Promise.all(records.map(async record => {
let cId = record.contactID;
let contact = await contact_detail(cId);
names += contact.data.firstName + " " + contact.data.lastName + " ";
});
console.log(names);
...
I'm posting this as an answer only because I need to show you some multi-line code as part of throubleshooting this. Not sure this solves your issue yet, but it is a problem.
Your contact_detail() function is not properly returning errors. Instead, it eats the error and resolves with an empty object. That could be what is causing your blank names. It should just return the promise directly and if you want to log the error, then it needs to rethrow. Also, there's no reason for it to be declared async or to use await. You can just return the promise directly. You can also let request-promise parts the JSON response for you too.
Also, I notice, there appears to be a syntax error in your .catch() which could also be part of the problem.
contact_detail: contactId => {
return rp({
url: 'myapiurl2',
qs: { accessToken: 'xyz', contactID: contactId },
json: true,
method: 'GET'
}).catch( error => {
// log error and rethrow so any error propagates
console.log(error);
throw error;
});
};
Then, you would call this like you originally were (note you still use await when calling it because it returns a promise):
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
for( let i=0; i<records.length; i++) {
...
const cId = records[i].contactID;
const contact = await contact_detail(cId);
names += contact.data.firstName + " " + contact.data.lastName + " ";
...
}
console.log(names);
...

Response undefined - aws-api-gateway-client

I have created an AWS API Gateway to invoke a Lambda function to generate random numbers:
Lambda Function :
exports.handler = (event, context, callback) => {
let min = parseInt(event.min);
let max = parseInt(event.max);
let generatedNumber = Math.floor(Math.random() * max) + min;
context.done(null, {generatedNumber: generatedNumber});
};
Body mapping Template in API gateway for get method:
{
"min" : $input.params('min'),
"max" : $input.params('max')
}
When I access API endpoint like below:
https://abcdefgh.execute-api.ap-south-1.amazonaws.com/DEV/number?min=10&max=20
I get the proper response :
{"generatedNumber":28}
But when I try to access the API in node.js using aws-api-gateway-client I am receiving the below response :
_currentUrl: 'https://abcdefgh.execute-api.ap-south-1.amazonaws.com/DEV/number' },
response: undefined
The current url should be set to 'https://abcdefgh.execute-api.ap-south-1.amazonaws.com/DEV/number?min=20&max=40' but it is set to 'https://abcdefgh.execute-api.ap-south-1.amazonaws.com/DEV/number'.
Here is my node.js code to access this api:
let AWS = require('aws-sdk');
AWS.config.loadFromPath('./config.json');
//AWS.config.region = 'ap-south-1';
let lambda = new AWS.Lambda();
let apigClientFactory = require('aws-api-gateway-client').default;
let config = {
invokeUrl: 'https://abcdefgh.execute-api.ap-south-1.amazonaws.com/DEV',
accessKey: '<access-key>',
secretKey: '<secret-key>',
region: 'ap-south-1'
};
let apigClient = apigClientFactory.newClient(config);
let apiParams = '{"min": 20,"max": 40}';
let body = {
}
let additionalParams = {
}
apigClient.invokeApi(apiParams, '/number', 'GET', additionalParams, body)
.then(function (result) {
console.log(result);
})
.catch(function (error) {
console.log(error);
});
I tried changing apiParams to :
let apiParams = {"min": 20,"max": 40};
The I receive the below error:
'{"message": "Could not parse request body into json: Unexpected character (\\\',\\\' (code 44)): expected a value\\n at [Source: [B#42feb146; line: 2, column: 14]"}' } }
What is wrong in my code?
Thanks in advance
Try modifying the mapping template:
{
"min" : "$input.params('min')",
"max" : "$input.params('max')"
}
Source: input-variable-reference
I found the problem. I need to pass parameters in additionalParmaeters object like :
let additionalParams = {
queryParams: {
min: 20, max: 40
}
}
But the text
var params = {
//This is where any header, path, or querystring request params go. The key is the parameter named as defined in the API
userId: '1234',
};
is misleading because query parameters were not passed when parameters were added to params object ( maybe it was for me ), but were only passed when passed inside additionalPrams.
Hope it helps.

Resources