With the Javascript code below, do I need to define the name of my Parse database (_User) I want it to access? At the moment I'm getting 'error code: 141', so I thought maybe it doesn't know what database I want it to query, since I have more than one.
Parse.Cloud.define("test", function(request, response) {
Parse.Cloud.useMasterKey();
var friendRequestId = request.params.friendRequest;
var query = new Parse.Query("FriendRequest");
query.get(friendRequestId, {
success: function() {
response.success("Success!");
},
error: function(error) {
response.error(error);
}
});
});
For those who want to see where I call the PFCloud
[PFCloud callFunctionInBackground:#"test" withParameters:#{#"friendRequest" : friendRequest.objectId} block:^(id object, NSError *error) {
if (!error) {
NSLog(#"Success");
}
else {
NSLog(#"Error");
}
}];
Thanks!
What do you mean, which database you want it to access? You define the class you'd like to query new Parse.Query("class"); So you're querying through the FriendRequest class. Perhaps your class name isn't actually FriendRequest? It's case sensitive.
I recommend that you use Parse Promises rather than callbacks. Also, an alternative to querying for an object based on id is fetching the object based on id. Here's my suggestion for what your cloud code should look like:
**Note, OP requested some additional help in the comments, so this answer is being updated to reflect his other questions.
Parse.Cloud.define("test", function( request, response )
{
Parse.Cloud.useMasterKey() //You only need this if you're, say, editing a user that didn't make this request or accessing an object that users don't have access to
var FriendRequest = Parse.Object.extend("FriendRequeset");
var friendRequestId = request.params.friendRequest;
var friendRequest = new FriendRequest();
var user1;
var user2;
friendRequest.id = friendRequestId;
friendRequest.fetch().then
(
function( friendRequest )
{
user1 = friendRequest.get("user1");
user2 = friendRequest.get("user2");
return Parse.Object.fetchAll([user1, user2]);
},
function( error )
{
response.error("Error fetching FriendRequest: " + error.message); //This will give you a bit more of a description of your errors
}
).then //fetchAll
function( objects )
{
//set up your relations. I've never used relations, but I'm sure you can find some examples
return Parse.Object.saveAll([user1, user2]);
},
function( error )
{
response.error("Error fetching users: " + error.message);
}
).then //saveAll
(
function( objects )
{
response.success("Success!");
},
function( error )
{
response.error("Error saving the users: " + error.message);
}
);
});
Notice I added some output to your error call. That'll help you a lot while debugging down the road. Get used to doing that.
And finally, check your logs from your Parse dashboard. It usually tells you what line you hit an error on. Look for the "on main.js: xxxx:yy" the xxxx is your line number where the error occurred.
edit - I saw the NSLogs from your obj-c code and mistook those for your response.success/error calls from cloud code. If you saw this before I edited it, I totally lied about that probably being your error :p
Related
I've been trying to modify the sample dashboard widget at this location
https://learn.microsoft.com/en-us/vsts/extend/develop/add-dashboard-widget?view=vsts#part-2-hello-world-with-vsts-rest-api
However, reluctantly have to admit I simply can't understand the structure required to extend it
Near the end, it uses "load: function" and returns the outputs of a REST API call, which I can consume however I want
However, I need to make more than one different REST call, and I simply cannot figure out how to get that info usable in my function
I modified the code so it starts like this:
VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/Work/RestClient","VSS/Service", "TFS/WorkItemTracking/RestClient" ],
I then created a handle for the other call I want to make like this:
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var queryResults = queryClient.getQuery(projectId, "Shared Queries/My Bugs");
However, I cannot consume the contents of queryResults - I know it's working up to a point as if I put in an invalid URL it will error as it knows it can't access anything there. If the URL is correct, no matter what I've tried - even stringify just to see what comes back - I get 'undefined' or something similar (it's definitely a valid JavaScript object)
The key seems to be right at the end when you have "load: function" except that only allows one thing to be returned? The reason I know this is if I change the function that it returns to be the one I've written rather than the one from the sample, it works fine - but the problem remains the same in that I can only process the results of one API call.
You can call more than one APIs, the code in that article is just the simple sample.
For Widget extension, you just need to return the status (e.g. Success()) in load function, so you can return status at the end of the function. For example:
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
var getAnOhterQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Bug")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
var r1= getQueryInfo(widgetSettings);
var r2=getAnOhterQueryInfo(widgetSettings);
if(r1==true && r2==true){
return WidgetHelpers.WidgetStatusHelper.Success();
}else{
return WidgetHelpers.WidgetStatusHelper.Failure("failed, check error in console");
}
}
I'd like to write a feature like this:
Scenario: new Singleton create
When a new, unmatchable identity is received
Then a new tin record should be created
And a new bronze record should be created
And a new gold record should be created
which would tie to steps like this:
defineSupportCode(function ({ Before, Given, Then, When }) {
var expect = require('chai').expect;
var chanceGenerator = require('./helpers/chanceGenerator')
var request = require('./helpers/requestGenerator')
let identMap;
// reset identMap before each scenario
Before(function () {
identMap = [];
});
// should generate a valid identity
// persist it in a local variable so it can be tested in later steps
// and persist to the db via public endpoint
When('a new, unmatchable identity is received', function (callback) {
identMap.push(chanceGenerator.identity());
request.pubPostIdentity(identMap[identMap.length-1], callback);
});
// use the local variable to retrieve Tin that was persisted
// validate the tin persisted all the props that it should have
Then('a new tin record should be created', function (callback) {
request.pubGetIdentity(identMap[identMap.length-1], callback);
// var self = this;
// request.pubGetIdentity(identMap[identMap.length-1], callback, () => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
// request.pubGetIdentity(identMap[identMap.length-1], (callback) => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
});
The issue that I'm having is that I can't do anything in the Then callback. That is where I'd like to be able to verify the response has the right data.
Here are relevant excerpts from the helper files:
var pubPostIdentity = function (ident, callback) {
console.log('pubIdentity');
var options = {
method: 'POST',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
},
body: JSON.stringify(ident)
};
console.log('ident: ', ident);
request(options, (err, response, body) => {
if (err) {
console.log('pubPostIdentity: ', err);
callback(err);
}
console.log('pubPostIdentity: ', response.statusCode);
callback();
});
}
// accept an identity and retrieve from staging via identity public endpoint
var pubGetIdentity = function (ident, callback) {
console.log('pubGetIdentity');
var options = {
method: 'GET',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
}
};
request(options, (err, response) => {
if (err) {
console.log('pubGetIdentity: ', err);
callback(err);
}
console.log('pubGetIdentity: ', response.body);
callback();
});
}
Something that we are considering as an option is to re-write the feature to fit a different step definition structure. If we re-wrote the feature like this:
Scenario: new Singleton create
When a new, unmatchable 'TIN_RECORD' is received
Then the Identity Record should be created successfully
When the Identity Record is retreived for 'tin'
Then a new 'tin' should be created
When the Identity Record is retreived for 'bronze'
Then a new 'bronze' should be created
When the Identity Record is retreived for 'gold'
Then a new 'gold' should be created
I believe it bypasses the instep callback issue we are wrestling with, but I really hate the breakdown of the feature. It makes the feature less readable and comprehensible to the business.
So... my question, the summary feature presented first, is it written wrong? Am I trying to get step definitions to do something that they shouldn't? Or is my lack of Js skills shining bright, and this should be very doable, I'm just screwing up the callbacks?
Firstly, I'd say your rewritten feature is wrong. You should never go back in the progression Given, When, Then. You are going back from the Then to the When, which is wrong.
Given is used for setting up preconditions. When is used for the actual test. Then is used for the assertions. Each scenario should be a single test, so should have very few When clauses. If you want, you can use Scenario Outlines to mix several very similar tests together.
In this case, is recommend to take it back to first principles and see if that works. Then build up slowly to get out working.
I suspect in this case that the problem is in some exception being thrown that isn't handled. You could try rewriting it to use promises instead, which will then be rejected on error. That gives better error reporting.
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) {
I want to update a field within the User class without being logged in as a user. From reading online and other responses people say I should use the 'masterkey' to do so. Here is my cloud code where I have added in the master key. The code is executed but when I go to my data browser the totalScore and predictions values are still the same and not updated to the new values.
Parse.initialize("key", "key");
Parse.Cloud.define("userUpdate", function(request, response) {
Parse.Cloud.useMasterKey();
var publicReadACL = new Parse.ACL();
publicReadACL.setPublicWriteAccess(true);
request.object.setACL(publicReadACL);
var User = Parse.Object.extend("User");
var query = new Parse.Query(User);
query.equalTo("username", request.params.username);
query.find({
success: function(user) {
user.set("totalScore", request.params.totalS);
user.set("totalPredictions", request.params.totalG);
user.save()
},
error: function() {
response.error("f");
}
});
});
Any help would be massively appreciated.
Hopefully you've figured this out by now, but if you haven't...I would first say check to make sure that you're passing the "totalScore" and "totalPredictions" as numbers. If you pass them as strings and Parse is expecting a Number, it won't update. And generally, I believe it's best practice to query the user class as follows:
var query = new Parse.Query(Parse.User);
query.get(user.objectId, {
success: function(userAgain) {
userAgain.set("totalScore", totalScore);
userAgain.save(null, {
error: function(userAgain, error) {
// This will error, since the Parse.User is not authenticated
}
});
}
});
Then of course you'd still need to include the master key stuff etc...
When running the following from the UserController on Google Chrome, with ember-couchdb-kit-0.9, Ember Data v1.0.0-beta.3-56-g8367aa5, Ember v1.0.0, and this couchdb adapter:
customerSignUp: function () {
var model = this.get('model');
var customer = this.get('store').createRecord('customer', {
description: 'Why hello sir',
user: model
});
customer.save().then(function() {
model.set('customer', customer);
model.save();
});
}
with these models:
App.User = App.Person.extend({
name: DS.attr('string'),
customer: DS.belongsTo('customer', {async: true })
App.Customer = DS.Model.extend({
user: DS.belongsTo('user', {async: true}),
description: DS.attr('string')
});
neither the user nor the customer has their relationship set properly (in the Ember Debugger the user has null and the customer has <computed>, rather than some sort of <EmberPromiseObject> which is what they have when it works).
This only happens when the object in question is persisted. If the save() calls are omitted, both have correctly set relationships, but of course the database hasn't been updated with this information. Whenever the saves happen, the relationships are overwritten with empty entries.
I found that the problem was in the adapter's serializeBelongsTo function, which I've now changed my copy to the following:
serializeBelongsTo: function(record, json, relationship) {
console.log("serializeBelongsTo");
console.log(record.get('user'));
console.log(json);
console.log(relationship);
var attribute, belongsTo, key;
attribute = relationship.options.attribute || "id";
console.log(attribute);
key = relationship.key;
console.log(key);
belongsTo = Ember.get(record, key);
console.log(belongsTo);
if (Ember.isNone(belongsTo)) {
return;
}
json[key] = Ember.get(belongsTo, attribute);
console.log(Ember.get(belongsTo, attribute));
console.log(json);
if (relationship.options.polymorphic) {
return json[key + "_type"] = belongsTo.constructor.typeKey;
}
else {
return json;
}
}
attribute, belongsTo, and key all log as correct, but
console.log(Ember.get(belongsTo, attribute)); returns undefined,
which I've tried to change to
console.log(Ember.get(Ember.get(belongsTo, 'content'), attribute));
since console.log(belongsTo); told me the id attribute was hidden inside a content object. Attached is a screenshot showing what I mean.
The change doesn't fix the problem though, and I keep getting undefined. No matter what method I use to try to get the id out of the belongsTo object, I always get either null or undefined. Here are some examples of things I've tried to get content out of the object:
var content = belongsTo.content;
var content = Ember.get(belongsTo, 'content');
var content = belongsTo.get('content');
console.log(json); returns Object {description: "Why hello sir", user: undefined}
Here's a pastebin showing relevant output: http://pastebin.com/v4mb3PJ2
Update
A very confusing update!
When I save the model from a different function:
saveModel: function() {
this.get('model').save().then(
function( data, textStatus, jqXHR ) {
console.log('Saved successfully.');
},
function( jqXHR, textStatus, errorThrown ) {
console.log(jqXHR);
console.log(errorThrown);
console.log(textStatus);
}
);
}
The model is correctly saved. Everything in serializeBelongsto works exactly as expected.
Here's a different pastebin showing output for this case: http://pastebin.com/Vawur8Q0
I figured out the problem. Basically the belongsTo object in serializeBelongsTo wasn't really resolved by the time it was being referenced, which I found out by querying isFulfilled. So I implemented by saving side this way:
function saveOn (target, attribute) {
target.addObserver(attribute, function () {
if (target.get(attribute)) {
console.log("Inside with %#".fmt(attribute));
target.removeObserver(attribute);
Ember.run.once(target, function() {
target.save();
});
}
});
};
customerSignUp: function () {
var model = this.get('model');
var customer = this.get('store').createRecord('customer', {
description: 'Why hello sir'
});
customer.save().then(function () {
model.set('customer', customer);
customer.set('user', model);
saveOn(customer, 'user.isFulfilled');
saveOn(model, 'customer.isFulfilled');
});
}
Now everything works like a charm. It might be a good idea for serializeBelongsTo to take this into account though. This line: console.log(Ember.get(belongsTo, 'isFulfilled')); was coming up false in my case. There was just a race condition of some sort between the creation of the record and it's serialization!
I'd like to make my saveOn function return a promise though, which I could then use to chain multiple saveOns together. That way I wouldn't have to do a customer.save() to make sure the id's were populated.