Pushwoosh create message get 200, but say UnknownDevices - pushwoosh

My system running at least two years. Most push notification requests got 200. But, recently, I found I got 500 frequently.
Nealy 10% push notifications got 500.
Please HELP!!!
I have resend the problem messages, every thing is fine.
Example:
Request is:
{
"request": {
"application": "3DXXX-59XXX",
"username": "MyXXXXX",
"password": "********",
"notifications": [
{
"send_date": "now",
"content": {
"en": "Subscriber ID. 9000 Close User 01"
},
"link": "",
"data": {
"userID": "12345"
},
"wp_type": "",
"wp_background": "",
"wp_count": "",
"ios_badges": 51,
"ios_sound": "short-tone.caf",
"devices": [
"APA91bHZHEhIMjVYwxyMk-4-YObazHfcxlQq7CmYto930nuIqHlQGCdzUQsnDcnHTB78wUcTlm-qhV3ipMqe9HO3kTqD9j_zgzSUUAdoGK0fbeRRGMNn69Z63BlQ9RqIdioZ4J2NFA0DLOUkroImk-it8p_3Glr5bRlnrl1_wT3ycXfsgvQZq4g"
],
"page_id": "0",
"android_sound": "five_sectoneone"
}
]
}
}
Response is:
{"status_code":500,"status_message":"Invalid devices list"}
==============================================================
========================= Update =============================
After I upgrade Pushwoosh API from V1.2 to V1.3, the problem is gone.
However, new problem comes.
My sample is:
request :
{
"request": {
"application": "3DXXX-59XXX",
"auth": "*********WqLiS5ZM2****************************************9eib******",
"notifications": [
{
"send_date": "now",
"content": {
"en": "Jones Residence Tue,17Jul 12:12 Test from Robbie......."
},
"link": "",
"data": {
"userID": "12345"
},
"wp_type": "",
"wp_background": "",
"wp_count": "",
"ios_badges": 39,
"ios_sound": "short-tone.caf",
"devices": [
"298eeXXXXa26849cc77da16adXXXXc1c801df12e79bad1e724829aXXXXcbe07d" //I hashed real ID here
],
"page_id": "0",
"android_sound": "five_sectoneone"
}
]
}
}
Response is:
{
"status_code": 200,
"status_message": "OK",
"response": {
"Messages": [
"D954-3C45B1AA-AA6293E5"
],
"UnknownDevices": {
"D954-3C45B1AA-AA6293E5": [
"298eeXXXXa26849cc77da16adXXXXc1c801df12e79bad1e724829aXXXXcbe07d" //I hashed real ID here
]
}
}
}

I see you already wrote the question in Pushwoosh community. I'll post the answer here as well.
The "UnknownDevice" warning indicates that the push token that you have put in the "devices" section of the createMessage request isn't in our databases.
There are several reasons for that:
1) The application was deleted from the device.
2) The push token was renewed. APNs/GCM/etc. tends to change push tokens from time to time, and we remove outdated push tokens from our servers. (This happens VERY rarely)
3) There is a misprint in your request. (I'm sure this is not the case)
Please note that in order to keep your userbase up-to-date you can use getUnregisteredDevices method, which would return a last thousand of removed push tokens. You will need to call this on a regular basis.
P.S. https://community.pushwoosh.com/questions/998/pushwoosh-create-message-get-200-but-says-unknowndevices
P.P.S. Do not use 1.2 API, it is very old and will be deprecated soon.

I think, you use wrong device token. Don't use device UUID. You can send device token to your database after put it deviceArray in the Java code.
Edit PushwooshiOS.js file;
pushNotification.registerDevice(
function(status)
{
var deviceToken = status['deviceToken'];
console.warn('registerDevice: ' + deviceToken);
$.ajax({
url : "http://ip:port/deviceid/"+deviceToken,
dataType : "json",
success : function(a, b, c) {
console.log("send tokens to server after call t in your json array");
},
error : function(a, b, c) {
console.log("err a ", a);
console.log("err b ", b);
console.log("err c ", c);
console.log("err c ", c);
}
});
onPushwooshiOSInitialized(deviceToken);
},
function(status)
{
console.warn('failed to register : ' + JSON.stringify(status));
//alert(JSON.stringify(['failed to register ', status]));
}
);
Java code
String method = "createMessage";
URL url = new URL(PUSHWOOSH_SERVICE_BASE_URL + method);
JSONArray deviceArray = new JSONArray();
// put your device tokens
deviceArray.put(deviceToken);
JSONArray notificationsArray = new JSONArray()
.put(new JSONObject().put("send_date", "now")
.put("content", "A test push")
.put("devices",deviceArray));
JSONObject requestObject = new JSONObject()
.put("application", APPLICATION_CODE)
.put("auth", AUTH_TOKEN)
.put("notifications", notificationsArray);
JSONObject mainRequest = new JSONObject().put("request", requestObject);
JSONObject response = SendServerRequest.sendJSONRequest(url, mainRequest.toString());

Related

Why doesn't GraphQL.NET honour the errors.extensions schema?

I recently rewrote some GraphQL services from Java to .NET Core.
In Java, I was able to provide custom error messages to the clients using the errors.extensions in the response, ie:
{
"data": {
"someMutation": null
},
"errors": [{
"cause": null,
"message": "Unauthorized",
"httpStatusCode": 0,
"extensions": {
"uiMessage": "Oh no, your session expired. You'll need to login again to continue.",
"httpStatusDescription": "Unauthorized",
"httpStatusCode": 401
},
"errorType": "ValidationError",
"path": null,
"localizedMessage": "Unauthorized",
"suppressed": []
}
]
}
However, in .NET, I don't seem to be able to replicate this format.
ErrorInfo.Extensions is added to the root of the response, not to the the Errors object itself, eg:
{
"data": {
"someMutation": null
},
"errors": [{
"message": "Auth token not provided"
}
],
"extensions": {
"httpStatusCode": 401,
"httpStatusDescription": null,
"uiMessage": "Oh no, your session expired. You'll need to login again to continue.",
}
}
The GraphQL spec reads (ref https://spec.graphql.org/October2021/#sec-Errors, https://spec.graphql.org/October2021/#example-8b658):
GraphQL services may provide an additional entry to errors with key
extensions. This entry, if set, must have a map as its value. This
entry is reserved for implementors to add additional information to
errors however they see fit, and there are no additional restrictions
on its contents.
eg:
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"],
"extensions": {
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
}
]
}
I created a new test project (.NET Core 3.1) using the latest versions of the libraries (GraphQL 7.1.1 et al) but am still unable to add custom properties to errors.extensions.
This is the test mutation which intentionally throws an exception:
Field<StringGraphType>("greet")
.Argument<NonNullGraphType<StringGraphType>>("name")
.Resolve(context => {
try {
throw new Exception("Invalid input");
return "Hello " + context.GetArgument<String>("name");
} catch(Exception ex) {
// This doesn't seem to get returned anywhere in the response
Dictionary<String, object> extraData = new Dictionary<string, object>();
extraData.Add("error1", "message1");
// Add the error to the response using the overloaded constructor
context.Errors.Add(new ExecutionError("Oh dear, that went wrong", extraData));
// This gets added to the root of the response
context.OutputExtensions.Add("error2", "message2");
return null;
}
});
the mutation to invoke it:
mutation {greet(name:"Chewbacca")}
and the response (I don't know where errors.extensions.details comes from):
{
"errors": [
{
"message": "Oh dear, that went wrong",
"extensions": {
"details": "GraphQL.ExecutionError: Oh dear, that went wrong"
}
}
],
"data": {
"greet": null
},
"extensions": {
"error2": "message2"
}
}
I would imagine that the GraphQL.NET library would expose an Extensions dictionary on the ExecutionError object so one could add custom values in the usual manner, eg:
ExecutionError executionError = new ExecutionError("Oh dear, that went horribly wrong");
executionError.Extensions.Add("customError", "Your custom error here")
context.Errors.Add(executionError);
Which would result in a response similar to this:
{
"data": {
"someMutation": null
},
"errors": [{
"message": "Oh dear, that went horribly wrong",
"extensions": {
"customError": "Your custom error here"
}
}
]
}
I am hopeful that some bright individual in the community can (slap me upside the head and) point me in the right direction.

Fetch data in a POST Request in asp.net core

I am using an external web link to get data and fetch it to json The reason why I need to handle it by the controller is to filter the data of it. Sadly, an api link was programmatically incorrect because instead of requesting it as GET method, it was programmed as POST method. I had this code simple code below but the return was a header data not the actual data of the api.
[HttpPost, Route("get/subproject")]
public ActionResult subproject()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(#"https://thisisjustasample.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage hrm = client.PostAsync("/api/new/get/subproject/details/get_dto", null).Result;
return Ok(hrm);
}
}
The output of the code above is this.
{
"version": "1.1",
"content": {
"headers": [
{
"key": "Content-Length",
"value": [
"29942142"
]
},
{
"key": "Content-Type",
"value": [
"application/json; charset=utf-8"
]
},
{
"key": "Expires",
"value": [
"-1"
]
}
]
}
}
What I need is this data below.
{
"sub_project_id": 267892,
"engineeringMigrationId": 0,
"modality_id": 21,
"id": null,
"reportID": null,
"month": null,
"year": null,
"cycle_id": 204
}
Any help would be appreciated. Thanks in advance.
Don't return hrm directly, If you want to get the response data, you need return.
hrm.Content.ReadAsStringAsync().Result
Demo
1.return Ok(hrm);
2.return Ok(hrm.Content.ReadAsStringAsync().Result);

Agora cloud recording is not saving file in S3 when streaming from android

I have already viewed this question
I followed the procedure, first called acquire then start then stop
I followed the procedure , first called acquire then start then stop
Here is my acquire call
https://api.agora.io/v1/apps/{{APPID}}/cloud_recording/acquire
Request body
{
"cname": "{{AccessChannel}}",
"uid": "{{RecordingUID}}",
"clientRequest":{
}
}
The response
{
"resourceId": "nUwUbQf9Zg6tsgtLslGnDg0lk8RYaUE09pqOuSIgwfzZ8g-RNe4wP9vsFnBhU785sHbxN0_dI8MVBqLWrCy7TCUo4U3XXne-2hA7expaSg-NgpZ5PcnFK-IrNSvmFNJqL4PGS9hw2q7lDYEtJH_0VgBG4Qdsg2TofWuF2DW7IMAt8NYBp9Hh_06d1K3pIcMJFPUWvfV7fInPDrecYuDmZdiRS9AYydjQlHa6jb51S0sKXzTVYELCJZAXP-ALS_whAh_ojFeltKLKzOC9iYodyAAwq4QnclbL3LKvBeVtCEfS9WjyYzyHDhCngJ-JrIwW"
}
Then I call start
URL : https://api.agora.io/v1/apps/{{APPID}}/cloud_recording/resourceid/{{resourceId}}/mode/mix/start
Body:
{
"cname":"{{AccessChannel}}",
"uid":"{{RecordingUID}}",
"clientRequest":{
"recordingConfig":{
"maxIdleTime":300,
"streamTypes":2,
"channelType":1,
"videoStreamType":0,
"transcodingConfig":{
"height":640,
"width":360,
"bitrate":500,
"fps":15,
"mixedVideoLayout":1,
"backgroundColor":"#FF0000"
},
"subscribeVideoUids":[
"123",
"456"
],
"subscribeAudioUids":[
"123",
"456"
],
"subscribeUidGroup":0
},
"storageConfig":{
"vendor":1,
"region":0,
"bucket":"{{Bucket}}",
"accessKey":"{{AccessKey}}",
"secretKey":"{{SecretKey}}",
"fileNamePrefix":[
"directory1",
"directory2"
]
},
"recordingFileConfig":{
"avFileType":[
"hls"
]
}
}
}
Response
{
"resourceId": "nUwUbQf9Zg6tsgtLslGnDg0lk8RYaUE09pqOuSIgwfzZ8g-RNe4wP9vsFnBhU785sHbxN0_dI8MVBqLWrCy7TCUo4U3XXne-2hA7expaSg-NgpZ5PcnFK-IrNSvmFNJqL4PGS9hw2q7lDYEtJH_0VgBG4Qdsg2TofWuF2DW7IMAt8NYBp9Hh_06d1K3pIcMJFPUWvfV7fInPDrecYuDmZdiRS9AYydjQlHa6jb51S0sKXzTVYELCJZAXP-ALS_whAh_ojFeltKLKzOC9iYodyAAwq4QnclbL3LKvBeVtCEfS9WjyYzyHDhCngJ-JrIwW",
"sid": "6068aec4fd4fc47b623bceaf1f2c8f6b"
}
And finally I called stop
URL: https://api.agora.io/v1/apps/{{APPID}}/cloud_recording/resourceid/{{resourceId}}/sid/{{sid}}/mode/mix/stop
Body:
{
"cname": "{{AccessChannel}}",
"uid": "{{RecordingUID}}",
"clientRequest": {}
}
And the response is
{
"resourceId": "nUwUbQf9Zg6tsgtLslGnDg0lk8RYaUE09pqOuSIgwfzZ8g-RNe4wP9vsFnBhU785sHbxN0_dI8MVBqLWrCy7TCUo4U3XXne-2hA7expaSg-NgpZ5PcnFK-IrNSvmFNJqL4PGS9hw2q7lDYEtJH_0VgBG4Qdsg2TofWuF2DW7IMAt8NYBp9Hh_06d1K3pIcMJFPUWvfV7fInPDrecYuDmZdiRS9AYydjQlHa6jb51S0sKXzTVYELCJZAXP-ALS_whAh_ojFeltKLKzOC9iYodyAAwq4QnclbL3LKvBeVtCEfS9WjyYzyHDhCngJ-JrIwW",
"sid": "6068aec4fd4fc47b623bceaf1f2c8f6b",
"code": 435
}
I am not sure what did I do wrong ?
My streaming is running on a android device , only one broadcaster no audience
And I called the API from postman
I have double checked every parameter
Please make sure you are passing the authentication token in the request body of start API.
Here is sample
{
"cname":"{{AccessChannel}}",
"uid":"{{RecordingUID}}",
"clientRequest":{
"token": "<place token here>",
"recordingConfig":{
"maxIdleTime":300,
"streamTypes":2,
"channelType":1,
"videoStreamType":0,
"transcodingConfig":{
"height":640,
"width":360,
"bitrate":500,
"fps":15,
"mixedVideoLayout":1,
"backgroundColor":"#FF0000"
},
"subscribeVideoUids":[
"123",
"456"
],
"subscribeAudioUids":[
"123",
"456"
],
"subscribeUidGroup":0
},
"storageConfig":{
"vendor":1,
"region":0,
"bucket":"{{Bucket}}",
"accessKey":"{{AccessKey}}",
"secretKey":"{{SecretKey}}",
"fileNamePrefix":[
"directory1",
"directory2"
]
},
"recordingFileConfig":{
"avFileType":[
"hls"
]
}
}
}

How do you get all the email body parts? And how do you know how many parts exist?

I'm trying to read emails responded by the Gmail API.
I have trouble accessing all the "parts". And don't have great ways to traverse through the response. I'm also lost as to how many parts can exist so that I can make sure I read the different email responses properly. I've shortened the response below...
{ "payload": { "mimeType": "multipart/mixed", "filename": "",
], "body": { "size": 0 }, "parts": [ {
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=\"us-ascii\""
},
{
"name": "Content-Transfer-Encoding",
"value": "quoted-printable"
}
],
"body": {
"size": 2317,
"data": "RGVhciBNSVQgQ2x1YiBWb2x1bnRlZXJzIGluIEFzaWEsDQoNCkJ5IG5vdyBlYWNoIG9mIHlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBpbnZpdGF0aW9ucyB0byB0aGUgcmVjZXB0aW9ucyBpbiBib3RoIFNpbmdhcG9yZSBhbmQgSG9uZyBLb25nIHdpdGggUHJlc2lkZW50IFJlaWYgb24gTm92ZW1iZXIgNyBhbmQgTm92ZW1iZXIg"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=\"us-ascii\""
},
{
"name": "Content-Transfer-Encoding",
"value": "quoted-printable"
}
],
"body": {
"size": 9116,
"data": "PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwiIHhtbG5zOm89InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9mZmljZSIgeG1sbnM6dz0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTpvZmZpY2U6d29yZCIgeG1sbnM6bT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9vZmZpY2UvMjA"
}
}
] }, {
"partId": "1",
"mimeType": "text/plain",
"filename": "",
"body": {
"size": 411,
"data": "X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18NClRoYW5rIHlvdSBmb3IgYWxsb3dpbmcgdXMgdG8gcmVhY2ggeW91IGJ5IGVtYWlsLCB0aGUgbW9zdCBpbW1lZGlhdGUgbWVhbnMgZm9yIHNoYXJpbmcgaW5mb3JtYXRpb24gd2l0aCBNSVQgYWx1bW5pLiANCklmIHlvdSB3b3VsZCBsaWtlIHRvIHVuc3Vic2NyaWJlIGZyb20gdGhpcyBtYWlsaW5nIGxpc3Qgc2VuZCBhIGJsYW5rIGVtYWlsIHRvIGxpc3RfdW5zdWJzY3JpYmVAYWx1bS5taXQuZWR1IGFuZCBwdXQgdGhlIGxpc3QgbmFtZSBpbiB0aGUgc3ViamVjdCBsaW5lLg0KRm9yIGV4YW1wbGU6DQpUbzogbGlzdF91bnN1YnNjcmliZUBhbHVtLm1pdC5lZHUNCkNjOg0KU3ViamVjdDogYXNpYW9mZg0K"
} } ] } }
Is there something I'm missing?
A MIME message is not just an array it's a full blown tree structure. So you'll have to traverse it to correctly handle it. Luckily JSON parsers are plentiful and the problem can easily be handled with recursion. In many languages there exist very useful email parsing libraries that can make accessing traditional parts (e.g. the text/plain or text/html displayable part, or attachments) not too laborious.
You'll have to set up walker functions to traverse through the json and pick out the bits you are after. Here is a part of what I wrote. This may help you jumpstart your code. NOTE: this is used inside of wordpress...hence the special jQuery call. Not needed if you do not need to use jquery inside wordpress.
function makeApiCall() {
gapi.client.load('gmail', 'v1', function() {
//console.log('inside call: '+myquery);
var request = gapi.client.gmail.users.messages.list({
'userId': 'me',
'q': myquery
});
request.execute(function(resp) {
jQuery(document).ready(function($) {
//console.log(resp);
//$('.ASAP-emailhouse').height(300);
$.each(resp.messages, function(index, value){
messageId = value.id;
var messagerequest = gapi.client.gmail.users.messages.get({
'userId': 'me',
'id': messageId
});//end var message request
messagerequest.execute(function(messageresp) {
//console.log(messageresp);
$.each(messageresp, responsewalker);
function responsewalker(key, response){
messagedeets={};
$.each(messageresp.payload.headers, headerwalker);
function headerwalker(headerkey, header){
if(header.name =='Date'){
d = new Date(header.value);
var curr_date = d.getDate();
var curr_month = d.getMonth() + 1; //Months are zero based
var curr_year = d.getFullYear();
var formatteddate = curr_month+'/'+curr_date+'/'+curr_year;
messagedeets['date']=formatteddate;
//$('.ASAP-emailhouse').append('<p>'+header.value+'</p>');
}
if(header.name =='Subject'){
//console.log(header.value);
messagedeets.subject=header.value;
}
}
messagedeets.body = {};
$.each(messageresp.payload.parts, walker);
function walker(partskey, value) {
//console.log(value.body);
if (value.body.data !== "undefined") {
//console.log(value.body);
var messagebody = atob(value.body.data);
messagedeets.body.partskey = messagebody;
}
console.log(messagedeets);
$('.ASAP-emailhouse').append('<div class="messagedeets"><p class="message-date">'+messagedeets.date+': <span class="message-subject">'+messagedeets.subject+'</span></p><p>'+messagedeets.body.partskey+'</p></div>');
}//end responsewalker
//$('.ASAP-emailhouse').append('</li>');
}
//$('.ASAP-emailhouse').append('</ul>');
});//end message request
});//end each message id
});//end jquery wrapper for wordpress
});//end request execute list messages
});//end gapi client load gmail
}
The MIME parts you are looking for are in an array. JSON does not tell you up front how many items are in an array. Even MIME itself does not provide a way of knowing how many parts are present without looking at the entire message. You will just have to traverse the entire array to know how many parts are in it, and process each part as you encounter it.
To know how much parts exists, you can just use the Length property.
Example :
json.payload.parts.length
For your example, this property is 2 because there are 2 parts.

Facebook API (javascript) getting latest school education info

I'm very new to the facebook api for my website, and I am using the javascript sdk. I want to get the users latest school information, including school name, course and year of study. This is what I have so far but it breaks the login script and returns 'response.education.school is undefined'. I'm guessing I'll need some kind of for loop to go through the education array as most users have more than one school listed?
function login() {
FB.login(function(response) {
if(response.authResponse) {
// connected
FB.api('/me', function(response) {
fbLogin(response.id, response.name, response.firstname, response.email,
response.education.school.name, response.education.concentration.name, response.education.year.name);
});
} else {
// cancelled
}
}, {scope: 'email, user_education_history, user_hometown'});
}
response.education.school is undefined
This is because responce.education is an array of objects. This would be an example for me (actual information removed)
"education": [
{
"school": {
"id": "",
"name": ""
},
"year": {
"id": "",
"name": ""
},
"concentration": [
{
"id": "",
"name": ""
}
],
"type": ""
},
...
]
You need to iterate over it and process each educaional step e.g.
for(ed in response.education) {
var school = response.education[ed].school;
var schoolName = school.name;
...
}
And so on; you are currently passing an aobject structure to your fbLogIn that can't handle it. If you want the latest school education, you simply pick the one that has the most recent year.name value.