Facebook Conversion API showing Server event as Browser - facebook-graph-api

Been getting weird results from my FB CAPI in Facebook's event test tool.
Is it Facebook's bug or something is wrong with my payload?
Here's what I'm doing and I've been able to replicate this on different machines on different IPs.
Here's how I can replicate the problem a lot of the times:
I open the Event test tool for my pixel in Business
Manager. I open Graph API Explorer to send test
events to the above mentioned Event Test tool.
In Graph API Explorer I enter my Access Token. I use
the following JSON code to send a test payload to Event Test tool:
{
"data": [
{
"event_name": "ViewContent",
"event_time": 1661938013,
"event_id": "1661886269650_16619383723281",
"event_source_url": "https://example.com/?gtm_debug=1661936451103",
"action_source": "website",
"user_data": {
"client_ip_address": "111.111.111.111",
"client_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
"em": null,
"ph": null
},
"custom_data": {
"contents": null
}
}
],
"partner_agent": "gtmss-1.0.0-0.0.5",
"test_event_code": "TEST83629"
}
I then check Event Test tool and see the following message received:
Like you can see from the above screenshot, the event name is CUSTOM EVENT (blank), event though it was sent as a standard ViewContent. Also, the source is marked as WEBSITE, when obviously it was sent through Graph API and should be marked as SERVER.
I then go back to Graph API Explorer and change ONE NUMBER of client_ip_address to something like "112.111.111.111" and send the same payload again.
I check Event Test tool and this time I see the following message received:
WHY the same payload reacts so differently and is even marked as received from a WEBSITE, even though was sent through SERVER? And why does fiddling with IP sometimes fix the problem?
I've been able to replicate this issue with already three different users. Three different Business accounts and Three different pixels. What am I doing wrong?

I had encountered same issue. Also when using the same server-side GTM template: Conversions API Tag
The issue is that in your payload you are sending "em" and "ph" parameters under "user_data" as null. This somehow confuses the API. These values must either be a hashed string or not be defined at all.
See - https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters/
Edit the Conversions API Tag template code.
Find these lines:
// Commmon Event Schema Parameters
event.user_data.em = eventModel['x-fb-ud-em'] ||
(eventModel.user_data != null ? hashFunction(eventModel.user_data.email_address) : null);
event.user_data.ph = eventModel['x-fb-ud-ph'] ||
(eventModel.user_data != null ? hashFunction(eventModel.user_data.phone_number) : null);
and replace them with:
let emData = eventModel['x-fb-ud-em'] || (eventModel.user_data != null ? hashFunction(eventModel.user_data.email_address) : null);
if(emData != null) {
event.user_data.em = emData;
}
let phData = eventModel['x-fb-ud-ph'] || (eventModel.user_data != null ? hashFunction(eventModel.user_data.phone_number) : null);
if(phData != null) {
event.user_data.ph = phData;
}
This will make it so the data is not added at all if its not defined, instead of adding a null.

Related

Upload REST API shows 400 bad request

I have an ESB webservice client which upload document to Alfresco using following RestAPI .
http://:8080/alfresco/service/api/upload
I understand , API to be called as multipart / form data , with file and mandatory fields . When i executed , i got the following error ersponse back .
{
"status": {
"code": 400,
"name": "Bad Request",
"description": "Request sent by the client was syntactically incorrect."
},
"message": "Required parameters are missing",
"exception": "",
"callstack": [],
"server": "Enterprise v5.2.3 (r852994b1-b12) schema 10,065",
"time": "10-Jul-2020 12:26:47"
}
I use WSO2 ESB client , ESB code looks all good . The only place where i can debug is through wirelog .
wirelog shows alll good( please scee screen shot below) . Error message has confilicting information like
"description": "Request sent by the client was syntactically incorrect."
"message": "Required parameters are missing",
Is there a log i can get from Alfresco server side to identify what actually is the issue , which syntax is incorrect ?
what parameter is missing ?
Screen shot for wire log as below
please click this link to see image of wiretap
You can use api-explorer rest api to upload document.
https://api-explorer.alfresco.com/api-explorer/#!/nodes/createNode
Which documentation are you following? By having a quick look here, I'd say you're not sending everything you should be:
https://docs.alfresco.com/5.0/references/RESTful-UploadUploadPost.html
This is the condition you're seeing getting triggered, so I'd look more closely in what you're actually sending. Maybe use Fiddler or some similar software to record your request and inspect it.
// Ensure mandatory file attributes have been located. Need either destination, or site + container or updateNodeRef
if ((filename === null || content === null) || (destination === null && (siteId === null || containerId === null) && updateNodeRef === null))
{
exitUpload(400, "Required parameters are missing");
return;
}
Also, the web script in question can be found here, it's implemented in JS so it would be relatively easy to add additional (temporary) logging.
...alfresco\tomcat\webapps\alfresco\WEB-INF\lib\alfresco-remote-api-5.0.d.jar\alfresco\templates\webscripts\org\alfresco\repository\upload\

GMB MyBusiness API - How to set up real-time PubSub notifications?

I am working on integrating GMB into some of our internal apps, and would like to set up to receive real-time notifications for reviews and questions.
I have created a topic, and a subscription with a valid URL.
The next step is to tell GMB to send the notifications to the topic, and I believe the endpoint is the one below. However, it is very vague about the parameters it wants.
This is the documentation
https://developers.google.com/my-business/reference/rest/v4/accounts/updateNotifications
It wants a "Notification Settings Resource Name" in the URL, but it's not explained anywhere what that actually is. I have tried every possible value, but always get a 404 error response with the message "Requested entity was not found."
Has anyone successfully set this up? What values does the "getNotifications" endpoint want, and where in the various dashboards can this be found or created?
Any help is much appreciated!
As mentioned in the comments, you need to send the accountId as part of the URL.
To find this, you will first need to send a GET request to
https://mybusiness.googleapis.com/v4/accounts
This will return something along the following lines:
{
"accounts": [
{
"name": "accounts/102647145453118950380",
"accountName": "Tom Spencer",
"type": "PERSONAL",
"state": {
"status": "UNVERIFIED",
"vettedStatus": "NOT_VETTED"
},
"profilePhotoUrl": "//lh3.googleusercontent.com/a-/AOh14GgPkuJj03DeCa1isBAJALY4eOl09WGYVFrM4mG5=s132"
},
]
}
You can see here that accounts/102647145453118950380 is returned in the name field. Take this field, and construct the following URL:
https://mybusiness.googleapis.com/v4/accounts/102647145453118950380/notifications
Send a PUT request to this URL, with a request body resembling the following:
{
"topicName": "projects/{projectId}/topics/{topicId}",
"notificationTypes": [
"NEW_REVIEW",
"UPDATED_REVIEW"
]
}
Assuming you have pub/sub setup as per the documentation, this should send a message to your topic/subscribers whenever a new review is created or a review is updated.

Google IOT per device heartbeat alert using Stackdriver

I'd like to alert on the lack of a heartbeat (or 0 bytes received) from any one of large number of Google IOT core devices. I can't seem to do this in Stackdriver. It instead appears to let me alert on the entire device registry which does not give me what I'm looking for (How would I know that a particular device is disconnected?)
So how does one go about doing this?
I have no idea why this question was downvoted as 'too broad'.
The truth is Google IOT doesn't have per device alerting, but instead offers only alerting on an entire device registry. If this is not true, please reply to this post. The page that clearly states this is here:
Cloud IoT Core exports usage metrics that can be monitored
programmatically or accessed via Stackdriver Monitoring. These metrics
are aggregated at the device registry level. You can use Stackdriver
to create dashboards or set up alerts.
The importance of having per device alerting is built into the promise assumed in this statement:
Operational information about the health and functioning of devices is
important to ensure that your data-gathering fabric is healthy and
performing well. Devices might be located in harsh environments or in
hard-to-access locations. Monitoring operational intelligence for your
IoT devices is key to preserving the business-relevant data stream.
So its not easy today to get an alert if one among many, globally dispersed devices, loses connectivity. One needs to build that, and depending on what one is trying to do, it would entail different solutions.
In my case I wanted to alert if the last heartbeat time or last event state publish was older than 5 minutes. For this I need to run a looping function that scans the device registry and performs this operation regularly. The usage of this API is outlined in this other SO post: Google iot core connection status
For reference, here's a Firebase function I just wrote to check a device's online status, probably needs some tweaks and further testing, but to help anybody else with something to start with:
// Example code to call this function
// const checkDeviceOnline = functions.httpsCallable('checkDeviceOnline');
// Include 'current' key for 'current' online status to force update on db with delta
// const isOnline = await checkDeviceOnline({ deviceID: 'XXXX', current: true })
export const checkDeviceOnline = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'You must be logged in to call this function!');
}
// deviceID is passed in deviceID object key
const deviceID = data.deviceID
const dbUpdate = (isOnline) => {
if (('wasOnline' in data) && data.wasOnline !== isOnline) {
db.collection("devices").doc(deviceID).update({ online: isOnline })
}
return isOnline
}
const deviceLastSeen = () => {
// We only want to use these to determine "latest seen timestamp"
const stamps = ["lastHeartbeatTime", "lastEventTime", "lastStateTime", "lastConfigAckTime", "deviceAckTime"]
return stamps.map(key => moment(data[key], "YYYY-MM-DDTHH:mm:ssZ").unix()).filter(epoch => !isNaN(epoch) && epoch > 0).sort().reverse().shift()
}
await dm.setAuth()
const iotDevice: any = await dm.getDevice(deviceID)
if (!iotDevice) {
throw new functions.https.HttpsError('failed-get-device', 'Failed to get device!');
}
console.log('iotDevice', iotDevice)
// If there is no error status and there is last heartbeat time, assume device is online
if (!iotDevice.lastErrorStatus && iotDevice.lastHeartbeatTime) {
return dbUpdate(true)
}
// Add iotDevice.config.deviceAckTime to root of object
// For some reason in all my tests, I NEVER receive anything on lastConfigAckTime, so this is my workaround
if (iotDevice.config && iotDevice.config.deviceAckTime) iotDevice.deviceAckTime = iotDevice.config.deviceAckTime
// If there is a last error status, let's make sure it's not a stale (old) one
const lastSeenEpoch = deviceLastSeen()
const errorEpoch = iotDevice.lastErrorTime ? moment(iotDevice.lastErrorTime, "YYYY-MM-DDTHH:mm:ssZ").unix() : false
console.log('lastSeen:', lastSeenEpoch, 'errorEpoch:', errorEpoch)
// Device should be online, the error timestamp is older than latest timestamp for heartbeat, state, etc
if (lastSeenEpoch && errorEpoch && (lastSeenEpoch > errorEpoch)) {
return dbUpdate(true)
}
// error status code 4 matches
// lastErrorStatus.code = 4
// lastErrorStatus.message = mqtt: SERVER: The connection was closed because MQTT keep-alive check failed.
// will also be 4 for other mqtt errors like command not sent (qos 1 not acknowledged, etc)
if (iotDevice.lastErrorStatus && iotDevice.lastErrorStatus.code && iotDevice.lastErrorStatus.code === 4) {
return dbUpdate(false)
}
return dbUpdate(false)
})
I also created a function to use with commands, to send a command to the device to check if it's online:
export const isDeviceOnline = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'You must be logged in to call this function!');
}
// deviceID is passed in deviceID object key
const deviceID = data.deviceID
await dm.setAuth()
const dbUpdate = (isOnline) => {
if (('wasOnline' in data) && data.wasOnline !== isOnline) {
console.log( 'updating db', deviceID, isOnline )
db.collection("devices").doc(deviceID).update({ online: isOnline })
} else {
console.log('NOT updating db', deviceID, isOnline)
}
return isOnline
}
try {
await dm.sendCommand(deviceID, 'alive?', 'alive')
console.log('Assuming device is online after succesful alive? command')
return dbUpdate(true)
} catch (error) {
console.log("Unable to send alive? command", error)
return dbUpdate(false)
}
})
This also uses my version of a modified DeviceManager, you can find all the example code on this gist (to make sure using latest update, and keep post on here small):
https://gist.github.com/tripflex/3eff9c425f8b0c037c40f5744e46c319
All of this code, just to check if a device is online or not ... which could be easily handled by Google emitting some kind of event or adding an easy way to handle this. COME ON GOOGLE GET IT TOGETHER!

React Native Facebook API batch requests

I'm struggling with the batch request to the Facebook API in React Native. For a single request this tutorial works fine : https://github.com/facebook/react-native-fbsdk.
But can I create a batch request. Using GraphRequestBatch does not seem to work. Adding up request with addRequest() neither (such as suggested in https://github.com/facebook/react-native-fbsdk/issues/185).
Please help! I would like to send a batch of request with the same node and same edges, excepts only the time span changes.
Each request will look like :
const request = new GraphRequest('me/',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'posts.since(t1).until(t2).limit(n){likes.summary(true)}'
}
}
},
responseInfoCallback);
I tried creating several requests, each with a different t1 and t2, then add them up like this :
const graphmanager = new GraphRequestManager().addRequest(requesta);
graphmanager.addRequest(requestb);
...
graphmanager.start();
But only the first request gets executed.
Thanks for the help!

eXosip: login always fails on first try

I am developing a SIP-Application using eXosip. When I try to login to the server the first attempt always fails.
eXosip_lock(ctx);
eXosip_add_authentication_info(ctx, username, login, passwd, NULL, domain);
osip_message_t *reg;
int rid = eXosip_register_build_initial_register(ctx, account, server, NULL, 3600, &reg);
// Not sure what they do, but they seem to be necessary
osip_message_set_supported (reg, "100rel");
osip_message_set_supported (reg, "path");
eXosip_register_send_register(ctx, rid, reg);
eXosip_unlock(ctx);
(I've removed error checking to make it more readable.) The above code results in an EXOSIP_REGISTER_FAILED event. Reacting to this event by executing the following code results in a successful registration.
eXosip_lock(ctx);
osip_message_t *reg;
eXosip_register_build_register(ctx, rid, 3600, &reg);
eXosip_register_send_register(ctx, rid, reg);
eXosip_unlock(ctx);
I tried different accounts at different providers and it's always the same.
Granted, it works, but it makes the program flow confusing and error handling harder (e.g. for erroneous login credentials), which I'd like to avoid if possible.
Now I don't know if this behavior is to be expected (I couldn't find anything about it), but... can anybody help? Thanks in advance!
Ok, this was a bit stupid. For anyone who wonders or encounters the same, most servers will reply to the initial register with a 401 (unauthorized) or 407 (proxy authentication required). Knowing this I was able to successfully register using the following code:
eXosip_automatic_action(ctx);
if (evt->type == EXOSIP_REGISTER_FAILED)
{
if (evt->response != NULL &&
(evt->response->status_code == 401 || evt->response->status_code == 407))
{
eXosip_default_action(ctx, evt);
}
else
// login really failed
}
eXosip_automatic_action will handle 401, 407, 422, 3xx and reregister before the registration expires. eXosip_default_action will handle some more things regarding 401/407. Quite handy!
It's pretty old question, but I would like to offer a precise answer:
About the question itself:
eXosip_automatic_action(ctx); will handle every automatic actions but there are only 2 use-cases that cannot be handled with it and EXOSIP_REGISTRATION_FAILURE is not part of them.
Here is the correct way to activate all automatic actions required by an eXosip based application:
int status_code = 0;
if (evt->response != NULL)
status_code = evt->response->status_code;
eXosip_automatic_action(ctx);
if (evt->type == EXOSIP_CALL_MESSAGE_REQUESTFAILURE && evt->did < 0) {
/* evt->did<0 means dialog is over, but authentication is required (BYE was rejected) */
if (status_code == 407 || status_code == 401)
eXosip_default_action (evt);
}
if (evt->type == EXOSIP_MESSAGE_REQUESTFAILURE && evt->request != NULL) {
/* authentication is required for a message without context in exosip (MESSAGE, OPTIONS...) */
if (!MSG_IS_PUBLISH (evt->request) && (status_code == 407 || status_code == 401))
eXosip_default_action (evt);
}
As indicated in the code comments, the above code will also authenticate sip messages outside dialog (MESSAGE, OPTIONS, etc...) and sip messages within a terminated dialog (mostly BYE)