Can't figure out Parse Config with Cloud Code - javascript

Been searching for solutions for hours and getting close to no luck. I just don't see much documentation on the matter of Parse Config. At least with solutions because I tried everything I could find.
So basically I'm trying to set a default picture when someone saves an object with a status as "denied."
It started with me looking at this: Set default profile picture for parse signup
And here's what I got.
//Accepts/denies picture request
Parse.Cloud.afterSave("Requests", function(request) {
var toUserId = request.object.get("to").id;
var fromUserId = request.object.get("from").id;
var toUser = null;
var fromUser = null;
var status = request.object.get("status");
if (status === "accepted") {
.....
} else if (status === "denied") {
Parse.Config.get().then(function(config) {
request.object.set('photo', config.get("denied"));
}).then(function() {
console.log('Success: Denied photo set.');
}, function(error) {
console.log('error: denied photo not set');
});
} else if (status === "waiting") {
....
}
});
I get a success everytime, but I get nothing as the photo file. I'm stuck and not sure what else to do here. The status changes to denied correctly, but I don't get anything to show up as a file in the photo spot, stays as undefined..
I2015-08-24T01:54:09.837Z]v46 after_save triggered for Requests for user oE3FhNfyWW:
Input: {"object":{"createdAt":"2015-08-24T01:54:03.398Z","from":{"__type":"Pointer","className":"_User","objectId":"odv4R9OWso"},"objectId":"InB8Iods8U","status":"denied","to":{"__type":"Pointer","className":"_User","objectId":"oE3FhNfyWW"},"updatedAt":"2015-08-24T01:54:09.834Z"}}
Result: Success
I2015-08-24T01:54:09.973Z]Success: Denied photo set.

I notice the code doesn't say request.object.save(), which might explain why the object isn't changed when you check later on.
But saving seems strange, since this function runs after saving. That's either wasteful or infinitely-loopy. Since the goal is to modify request.object (the object just saved), then do this on beforeSave().
Remember to call response.success() or .error() at the end of beforeSave().

Related

Access control with Angular & UI-Router. Call Stack Size errors & not reaching Express auth route

I'm trying to add access control to my Angular app and running into some puzzling troubles...
On the front-end, the authentication function is being called repeatedly until it is stopped for being too large of a call stack. Each time, the $http request within the function is triggering the errorCallback function. The expected behavior is for the auth function to fire once every time ui-router's state changes, changing $rootScope values that indicate the browser's level of authentication.
First, the factory responsible for making the GET request:
.factory ('authRequest', function ($http) {
return {
authStatus : function() {
$http.get('/auth', {'withCredentials' : true}).then(function successCallback(response) {
console.log("Successful authorization check.");
return response.status;
}, function errorCallback(response) {
if (response.status) {
console.log("Failed to authenticate.");
return response.status;
}
console.log("Failed to receive a response.");
return 'errNoResponse';
});
}
}
})
Then, the ng-controller for processing the factory's response:
//Navbar controller, set to fire upon each state change and verify authorization.
.controller('navCtrl', function ($rootScope, $state, authRequest) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log('authRequest value: ' + [authRequest]);
if (authRequest.authStatus === 202) {
console.log('Auth check succeeded, assigned user privileges.');
$rootScope.loggedIn = true;
$rootScope.loggedInAdmin = false;
if (toState = 'users' || 'login') {
event.preventDefault();
}
} else if (authRequest.authStatus === 222) {
console.log('Auth check succeeded, assigned admin privileges.');
$rootScope.loggedIn = true;
$rootScope.loggedInAdmin = true;
if (toState = 'login') {
event.preventDefault();
}
} else {
console.log('Auth check failed.');
$rootScope.loggedIn = false;
$rootScope.loggedInAdmin = false;
event.preventDefault();
$state.go('login');
}
});
})
Meanwhile, on the back-end, I'm not seeing evidence of the /auth Express route being reached with any of the requests. I have a console log set to go off when /auth receives a GET request, but I'm not seeing any activity in the console. Every other Express route is being accessed without issue. The expected behavior is to receive the request, decode the request's JWT cookie header, then send a response code back according to what sort of user privileges are listed. Here's the Express route for /auth:
// GET /auth. Fired every time the app state changes. Verifies JWT authenticity and sends a response based on the user's privileges. 202 is an auth'd user, 222 is an auth'd admin. 401 is no token or error.
app.get('/auth', function (req, res, next) {
console.log('Authorization request received.')
var decoded = jwt.verify(req.cookie, [JWTAuthSecret]);
if (decoded) {
if (decoded.admin === true) {
return res.status(222).send(res.status);
console.log(decoded.sub + ' is an admin, responded with code 222');
} else {
return res.status(202).send(res.status);
console.log(decoded.sub + ' is not an admin, responded with code 202');
}
} else {
return res.status(401).send(res.status);
console.log('Decode failed, responded with code 401');
};
});
With the current setup, the app is hanging indefinitely. As mentioned earlier, a ton of auth requests are being produced upon each state change. Each one logs an "authRequest value: [object Object]" then "Auth check failed." Eventually I get the following error:
angular.js:13550RangeError: Maximum call stack size exceeded
at angular.js:10225
at n.$broadcast (angular.js:17554)
at Object.transitionTo (angular-ui-router.js:3273)
at Object.go (angular-ui-router.js:3108)
at app.js:275
at n.$broadcast (angular.js:17552)
at Object.transitionTo (angular-ui-router.js:3273)
at Object.go (angular-ui-router.js:3108)
at app.js:275
at n.$broadcast (angular.js:17552)
So there seems to be a problem with the frequency of the calls on the front end, as well as a problem with actually getting the data sent to the /auth route.
This is my first time working with Angular factories, so my instinct is to assume my factory implementation is wonky... I haven't figured out how it might be fixed on my own, though.
Thanks for reading, hope I can get some advice on what to change to make this work.
I see a couple issues. This might not solve everything, but will hopefully help narrow things down.
One is that authRequest.authStatus is a function but you're never calling it. In your controller, you need to call the function. That's part of the reason nothing's pinging the backend.
authRequest.authStatus().then(function(status) {
if (status === 202) {
//do stuff
} else if (status === 222) {
//do other stuff
}
});
Now, in your factory, you're not returning anything to the function, so make sure you do that.
.factory ('authRequest', function ($http) {
return {
authStatus : function() {
return $http.get('url').then(callbacks);
}
}
})
The accepted answer provided me with solutions to the front-end troubles I was facing. The GET request to /auth was successfully being sent to Express as intended, but was still responding with an error code despite a valid authentication cookie.
Checking the backend logs, I was receiving an error saying JsonWebTokenError: jwt must be provided when trying to decode the JWT. Turns out req.cookie isn't the correct syntax to check the request's cookie - req.cookies is.
After changing that, my cookie output went from being undefined to [object Object]. The error I was getting changed as well to TypeError: jwtString.split is not a function.
After double checking the way cookies are referenced in the Express docs, I realized I wasn't calling the JWT cookie by name. The updated code is:
app.get('/auth', function (req, res, next) {
console.log('Authorization request received.')
console.log ('Cookie Data: ' + req.cookies.CookieName);
var decoded = jwt.verify(req.cookies.CookieName, [JWTAuthSecret]);
if (decoded) {
if (decoded.admin === true) {
return res.status(222).send(res.status);
console.log(decoded.sub + ' is an admin, responded with code 222');
} else {
return res.status(202).send(res.status);
console.log(decoded.sub + ' is not an admin, responded with code 202');
}
} else {
return res.status(401).send(res.status);
console.log('Decode failed, responded with code 401');
};
});
With this change and the front-end changes, the app's authentication and authorization are working as intended.

Disable Display of Generic Browser Push Notification

I have implemented the browser push notification functionality and its working fine. I used this guide as the reference https://developers.google.com/web/fundamentals/getting-started/push-notifications/step-01?hl=en
However as payload is still not supported, I decided to query my server to get the notification data for each user which is also working fine.
There is one issue though. For some cases, after getting data from the server, I want to control whether to show the notification or not. I am not able to figure out how to do this. I tried returning false, throwing errors etc. But is always shows the default notification even if I don't call showNotification method. Let me know how to solve this. Following is the relevant code
self.addEventListener('push', function(event) {
event.waitUntil(
fetch('/getPushNotificationData/').then(function(response){
if (response.status !== 200) {
// I don't want to show any notification in this case
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
return response.json().then(function(data){
var shouldDisplay = data.shouldDisplay;
if (shouldDisplay=='1'){
var title = data.title;
var message = data.message;
var url = data.url;
return self.registration.showNotification(title, {
body: message,
data: url
});
}
else{
// I don't want to show any notification in this case also
return true;
}
});
})
);
});

chrome.hid.send fails on second use

Something about my use of chrome.hid.send seems to be leaving the bus in a bad state. I consistently can NOT get my second usage of the API call to work. Sometimes, it will also fail on the first usage. WITH THE EXACT SAME CODE, I can come back and try a while later (maybe 10min) and the first send will work.
The device I'm working with does not return a response to all messages sent to it. The test message for example, is just a dummy message that is ignored by the device. I've tested this both on a mac and a PC. My call stack depth is 2 at this point in my application (literally first one is kicked off by a button click and then a setTimeout calls the same method 5s later).
I've testing sending buffers of length 64Bytes as well as 58Bytes. The properties from the HidDeviceInfo object read "maxInputReportSize":64,"maxOutputReportSize":64
Params on first usage:
Params on second usage:
I really can't identify how I'm using the API incorrectly. When messages do succeed, I can see them on the device side.
// Transmits the given data
//
// #param[in] outData, The data to send as an ArrayBuffer
// #param[in] onTxCompleted, The method called on completion of the outgoing transfer. The return
// code is passed as a string.
// #param[in] onRxCompleted, The method called on completion of the incoming transfer. The return
// code is passed as a string along with the response as an ArrayBuffer.
send: function(outData, onTxCompleted, onRxCompleted) {
if (-1 === connection_) {
console.log("Attempted to send data with no device connected.");
return;
}
if (0 == outData.byteLength) {
console.log("Attempted to send nothing.");
return;
}
if (COMMS.receiving) {
console.log("Waiting for a response to a previous message. Aborting.");
return;
}
if (COMMS.transmitting) {
console.log("Waiting for a previous message to finish sending. Aborting.");
return;
}
COMMS.transmitting = true;
var dummyUint8Array = new Uint8Array(outData);
chrome.hid.send(connection_, REPORT_ID, outData, function() {
COMMS.transmitting = false;
if (onTxCompleted) {
onTxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '');
}
if (chrome.runtime.lastError) {
console.log('Error in COMMS.send: ' + chrome.runtime.lastError.message);
return;
}
// Register a response handler if one is expected
if (onRxCompleted) {
COMMS.receiving = true;
chrome.hid.receive(connection_, function(reportId, inData) {
COMMS.receiving = false;
onRxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '', inData);
});
}
});
}
// Example usage
var testMessage = new Uint8Array(58);
var testTransmission = function() {
message[0] = 123;
COMMS.send(message.buffer, null, null);
setTimeout(testTransmission, 5000);
};
testTranmission();
The issue is that Windows requires buffers to be the full report size expected by the device. I have filed a bug against Chromium to track adding a workaround or at least a better error message to pinpoint the problem.
In general you can get more detailed error messages from the chrome.hid API by enabling verbose logging with the --enable-logging --v=1 command line options. Full documentation of Chrome logging is here.

Parse CloudCode order of execution

Im trying to send a push message to everyone with read access every time a new note is saved.
In pseudocode it should get the ACL. Evaluate each member in the ACL and return an array of all users with read access. Then send a push notification to each member.
I've tried running separate task one by one and it works properly. However when I put everything together in the following code I get strange results. Looking at the log I can see it not executing in order as I expect. I first though the getACL call was an asynchronous call so I tried to implement promises with no luck. Then after help from stackoverflow I find out that getACL is not asynchronous therefore the code should just work, right?
This is what I'm trying:
Parse.Cloud.afterSave("Notes", function(request) {
var idsToSend = [];
var i = 0;
console.log("1 start");
var objACL = request.object.getACL();
var ACLinJSON = objACL.toJSON();
console.log("2 ACL = " + ACLinJSON);
for (var key in ACLinJSON) {
if (ACLinJSON[key].read == "true") {
idsToSend[i] = key.id;
console.log("3 i = " + i + " = " + idsToSend[i]);
i++;
}
}
console.log("4 idsToSend = " + idsToSend);
//lookup installations
var query = new Parse.Query(Parse.Installation);
query.containedIn('user', idsToSend);
Parse.Push.send({
where: query,
data: {
alert: "note updated"
}
}, {
success: function() {
console.log("Success sent push");
},
error: function(error) {
console.error("can’t find user"); //error
}
});
});
And this is the response I see from parse log
I2014-08-04T08:08:06.708Z]4 idsToSend =
I2014-08-04T08:08:06.712Z]2 ACL = [object Object]
I2014-08-04T08:08:06.714Z]1 start
I2014-08-04T08:08:06.764Z]Success sent push
Everything is completely out of order??
How can I execute the above function in the way it's written?
I've found the logs are not in order when I run things too, could be a timing issue or something, ignore the order when they're in the same second, I have done other tests to confirm things really do run in order on my own Cloud Code... had me completely confused for a while there.
The issue you're having is that log #3 is never being hit... try tracing ACLinJSON on it's own to see the actual structure. When you append it to a string it outputs [object Object] as you have seen, so do console.log(ACLinJSON); instead.
Here's the structure I've seen:
{
"*":{"read":true},
"Administrator":{"write":true}
}
Based on that I would expect your loop to work, but it may have a different level of wrapping.
UPDATE:
Turns out the issue was looking for the string "true" instead of a boolean true, thus the fix is to replace the following line:
// replace this: if (ACLinJSON[key].read == "true") {
if (ACLinJSON[key].read == true) {

How to handle SoundCloud's JavaScript API 404 error while streaming a track

I read about this SoundCloud's API bug that returns a 404 error while trying to stream a track even if the "streamable" property was set to true.
I found some other questions about the topic (see "Tracks for “The Hives” are not streaming via the api" or "Soundcloud API SC.stream (track not loading)"), but what I would like to know is how to detect the error, and how to workaround it. I tried with some try {} catch() {} but it seems that I can't detect the problem.
Here is some background information:
I have a php returning a JSON array with a list of tracks to play. My script reads the array one-by-one, and then tries to play the current one. Everything works fine, but when a 404 error is returned the script ends, without jumping to the next track. It seems that I am unable to detect that kind of problem.
Here is the JavaScript method that manages the stream:
playTrack = function() {
// console.log('playTrack');
SC.get(
"/tracks/" + playlist[ now_playing ].sndc_id,
function(track, error) {
if (error) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
} else {
try {
SC.stream(
"/tracks/" + playlist[ now_playing ].sndc_id,
function(sound, error) {
sound_object = sound;
if (error || !sound_object) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
} else {
sound_object.play(
{
'from': parseInt(current_position),
'onfinish': function() {
current_position = 0;
updateCounter();
$('#radio-waveform-position').css( { 'opacity': '0', 'width' : '0%' } );
loadNext();
},
'onplay': function() {
$('#radio-like').removeClass('liked');
playing = true;
updateInfo();
},
'whileplaying': function() {
current_position = this.position;
$('#radio-waveform-position').css( { 'opacity': '1', 'width' : (100 * current_position / this.duration).toFixed(3) + '%' } );
},
'ondataerror': function() {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
}
}
);
if ($('html').hasClass('touch') || !autoplay) $('#radio-play-pause').click();
}
}
);
} catch (err) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
}
}
}
);
}
It seems that both the SC.get and SC.stream "error" return parameters are always empty for this kind of "faulty" tracks. As you can see, I tried to wrap everything in a try() {} catch() {} but without success. Also, the sound_object.play() ondataerror method is completely ignored.
The script can be seen in action here: http://foggetabout.it/
I read that someone found a workaround for that, but there was no explanation. Has anyone any idea on how to solve it?
Run into something similar. Not sure if it is connected but it appears some tracks are only streamable with flash so the GET method states they’re streamable but the stream can fail if flash is not available.
One way to get around it is to check when SoundManager has loaded the track using the onload & readyState, which (from docs: http://www.schillmania.com/projects/soundmanager2/doc/) has 4 states:
0 = uninitialised
1 = loading
2 = failed/error
3 = loaded/success
So:
sound_object.play({
// Exisiting…
onload: function() {
if (this.readyState == 2) {
// Handle error
}
});
There are some caveats on when the state changes so if this becomes a problem you could also try using durationEstimate as it returns null for the failed tracks I’m coming across.
For a lot of songs streamable is set to true and its stream_url is defined, but the resource itself is not there.
Using curl, hitting the i1 endpoint with your desired track_id and a registered API_KEY is the first step to checking for the actual resources on a SoundCloud hosted track. (Warning: the i1 endpoint is not officially documented in SoundCloud's HTTP API Reference)
curl https://api.soundcloud.com/i1/tracks/{{track_id}}/streams?client_id={{API_KEY}}
This will return a JSON object that looks something like:
{
"http_mp3_128_url": "{some_url}",
"preview_mp3_128_url": "{some_url}"
}
If the http_mp3_128_url key-value pair exists, then that track has an accessible HTTP resource and would be streamable.
More info here - https://github.com/francismakes/i1-soundcloud
Building off of roj's answer, I used a bit of code like this to first get into handling 404s on the SC track streaming:
sound.play({
onload: function () {
if (this.readyState === 2){
alert('error');
}
},

Categories