I have Inventory table in my Parse database with two relevant fields How can I do that with swift ? I create cloud code with Js below
Parse.Cloud.define("retrieveproducts", function(request, response) {
var productDictionary ={};
var query = new Parse.Query("Post");
query.each(
function(result){
var num = result.get("quantity");
if(result.get("productid") in productDictionary){
productDictionary[result.get("productid")] += num;
}
else{
productDictionary[result.get("productid")] = num;
}
}, {
success: function() {
response.success(productDictionary);
},
error: function(error) {
response.error("Query failed. Error = " + error.message);
}
});
});
I want to call this function : but i have some trouble calling it with this call
let params = ["productid": String(), "string": String()]
PFCloud.callFunctionInBackground("retrieveproducts", withParameters: params) {
(ratings, error) in
if (error == nil) {
print("\(params)")
}
}
Swift Code
let reportedId = "someUsersObjectId"
PFCloud.callFunctionInBackground("suspendUser", withParameters: ["userId": reportedId]) {
(result, error) in
if (error == nil) {
print(result)
}
}
Cloud Code:
Parse.Cloud.define("suspendUser", function(request, response) {
Parse.Cloud.useMasterKey()
//var User = Parse.Object.extend(Parse.User);
//var user = new User();
//user.id = request.params.userId;
user = new Parse.User({id:request.params.userId})
user.fetch().then(function() {
user.set("suspended", true)
user.save().then(function(newMostRecentAgain) {
response.success("Cloud code called! " + request.params.userId)
}, function(error) {
// The save failed. Error is an instance of Parse.Error.
})
});
});
So let me walk you through what's going on here, so you can better figure out how to do your code (since I can'e exactly know what you wanna do).
1: in my Swift code, withParameters is how you pass info to your cloud code, and it's in the style of a dictionary. Here I'm passing the user I wanna report's objectId.
PFCloud.callFunctionInBackground("suspendUser", withParameters: ["userId": reportedId]) {
2: In my Cloud code, I'm getting the Parse Object of the user I reported with the objectId I'm passing with the params. Since you can't pass an entire object to the cloud code, I'm rebuilding the object with the objectId and the user.Fetch() you see.
user = new Parse.User({id:request.params.userId})
3: in my Cloud code, after I successfully save my user after changing it's value for "suspended", I send a string back to the IOS user.
response.success("Cloud code called! " + request.params.userId)
4: Finally, in my Swift Code, I then print the returned string from success
print(result)
Does this make things a little bit more clear? Let me know if anything doesn't make sense.
Related
I'm trying to get user lang from User class in Parse Cloud. lang is one of the columns in User class. I wanna get lang of the user. My entire Cloud Code is as following (it didn't work):
Parse.Cloud.beforeSave('Order', function(request, response) {
var orderState = request.object.get('orderState');
var subtitleMessage = '';
var userLang = '';
var currentUser = request.object.get('user');
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('objectId', currentUser.id);
.find()
.then((result)=>{
userLang = result.get('lang');
})
if (orderState === undefined || ['nonApproved', 'approved', 'delivered', 'canceled'].indexOf(orderState) < 0) {
response.error("OrderState is null or not one the ['nonApproved', 'approved', 'delivered', 'canceled']!");
} else {
var query = new Parse.Query(Parse.Installation);
query.include('user');
query.equalTo('user', request.object.get('user'));
Parse.Push.send(
{
where: query,
data: {
title: "MyTitle",
alert: subtitleMessage
}
},
{
success: function() {
},
error: function(error) {
response.error(error)
},
useMasterKey: true
}
);
response.success();
}
});
The answer from Jake T. has some good points. Based on his answer and your comment on the question, you could try something like this:
Parse.Cloud.beforeSave('Order', function(request, response) {
var currentUser = request.object.get('user');
currentUser.fetch({useMasterKey: true}).then(function(user) {
var userLang = user.get('lang');
// send push notifications based on userLang here
response.success();
}).catch(function(error) {
// handle any errors here
console.error(error);
});
});
Verify you actually have a User object shell from request.object.get("user"); And, if you do, you can just call currentUser.fetch() instead of performing a query, unless there are other objects you may need to include.
Since you used a query, the result is an array, even if there is only a single object returned (or none, it would be simply []). So, you're doing Array.get("lang"), which shouldn't do anything. Try if( results && results.length > 0 ) user = results[0];, then you should be able to access user.get("lang");
You should have ACL / CLP's set up for your User class. Ideally, these should not be accessible by people who are not the user or master. So, if that is set up properly, your solution may be passing {useMasterKey:true} as an option to the query / fetch.
My cloud code returns this error when I run it ever since migrating to Azure. It worked fine before migrating. I do recall seeing a post about needing to change some parts of cloud code (e.g. Parse.Cloud.useMasterKey();) now that Parse has gone open source, but I cannot find it anywhere.
Here's the error:
2016-04-20 10:01:37.627 App Name [Error]: {
result = Fail;
} (Code: 141, Version: 1.12.0)
2016-04-20 10:01:37.628 App Name {
NSLocalizedDescription = {
result = Fail;
};
code = 141;
error = {
result = Fail;
};
temporary = 0;
}
And here's my cloud function:
Parse.Cloud.define("addFriendRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var currentUser=Parse.User.current();
var query = new Parse.Query(Parse.User);
var userId = request.params.userId;
query.get(userId,
{
success: function (object)
{
console.log(object.objectId);
var toUserFriend = object.relation("friends");
toUserFriend.add(currentUser);
object.save();
console.log("Success");
var jsonObject = {
"result" : "Success"
};
response.success(jsonObject);
},
error: function (object, error)
{
console.log("Fail");
var jsonObject={
"result" : "Fail"
};
response.error(jsonObject);
}
});
});
Thank you!
I believe the issue you are having originates from where you have
var currentUser = Parse.User.current();
This code is wrong.
You can however get the user that made the request using.
var user = request.user;
I wrote some backend code for a Parse.com mobile app a couple of years ago, and have just been asked to add a feature. However, I found that after a small tweak the code wouldn't succeed. So, I rolled back to the working copy, downloaded, then deployed that back and it wouldn't work either! I wonder if this is a change in the Parse software?
The code is failing at the save method as all the logs are fine until then. The log for the error case shows 'No message provided'. If I don't use the message attribute it just shows '{}', so I presume it's empty. I have put the promise resolution in the error case to stop the job timing out while I debug. One thing I have never understood is why I have to make two Seed objects and piggy-back off one to save correctly. If I did a.save(null,...) it wouldn't work.
Any help would be fantastic. Thanks!
PS: Apologies for the indenting below - it is correct in my file.
function flush() {
//Clear the previous records from the class.
var Seed = Parse.Object.extend("Seeds");
var _ = require("underscore");
var arr = [];
var query = new Parse.Query(Seed);
return query.find().then(function(oldSeeds) {
_.each(oldSeeds, function(oldSeed) {
arr.push(oldSeed.destroy());
});
return Parse.Promise.when(arr);
});
}
Parse.Cloud.job("fetchjson", function(request, status) {
var url = 'someurl';
flush().then(function() { Parse.Cloud.httpRequest({url: url}).then(function(httpResponse){
var Seed = Parse.Object.extend("Seeds");
var jsonobj = JSON.parse(httpResponse.text);
var _ = require("underscore");
var results = [];
// do NOT iterate arrays with `for... in loops`
_.each(jsonobj.seeds, function(s) {
var p = new Parse.Promise();
results.push(p); // Needs to be done here or when() will execute immediately with no promises.
var seed = new Seed();
var a = new Seed(s);
var image_url = a.get("image")
//Get the JSON.
Parse.Cloud.httpRequest({url: image_url}).then(function(response) {
console.log("Fetching image at URL: " + image_url);
//Create a new image object and save, passing ref through promise.
var file = new Parse.File('thumb.jpg', { base64: response.buffer.toString('base64', 0, response.buffer.length) });
return file.save();
}).then(function(thumb) {
console.log("Attaching thumb to object");
//Set image ref as object attribute.
a.set("imageFile", thumb);
console.log("Parsing views into viewsint");
//Save decimal string as int into another attribute.
a.set("viewsInt", parseInt(a.get("views")));
console.log("Parsing description into descriptionarray");
//Save string as array into another attribute.
var dar = new Array(1);
//dar[0] = a.get("description")
a.set("descriptionarray", [a.get("description")]);
}, function(error) {
console.log("Error occurred :(");
}).then(function(){
console.log("Saving object");
//Save the object and resolve the promise so we can stop.
seed.save(a,{
success: function(successData){
console.log(successData);
p.resolve(successData);
},
error: function(error){
console.log(error.message);
p.resolve(error);
}
});
});
});
// .when waits for all promises to be resolved. This is async baby!
Parse.Promise.when(results).then(function(data){
console.log("All objects saved");
status.success("Updated Succesfully");
});
}, function(error) {
//Oh noes :'(
console.error('Request failed with response code ' + httpResponse.status);
status.error("Update Failed");
});
});
});
I changed your code a bit and put some comments to explain:
// DEFINE THESE ON THE TOP. NO NEED TO REPEAT.
var _ = require("underscore");
var Seed = Parse.Object.extend("Seeds");
function flush() {
//Clear the previous records from the class.
var arr = [];
var query = new Parse.Query(Seed);
return query.find().then(function(oldSeeds) {
_.each(oldSeeds, function(oldSeed) {
arr.push(oldSeed.destroy());
});
return Parse.Promise.when(arr);
});
}
Parse.Cloud.job("fetchjson", function(request, status) {
var url = 'someurl';
flush().then(function() {
Parse.Cloud.httpRequest({url: url}).then(function(httpResponse){
var jsonobj = JSON.parse(httpResponse.text);
var results = [];
_.each(jsonobj.seeds, function(s) {
// ONE SEED OBJECT WITH INITIAL SET OF DATA FROM JSON
var seed = new Seed(s);
var image_url = seed.get("image")
// A SERIAL PROMISE FOR EACH SEED
var promise = Parse.Cloud.httpRequest({url: image_url}).then(function(response) {
console.log("Fetching image at URL: " + image_url);
//Create a new image object and save, passing ref through promise.
var file = new Parse.File('thumb.jpg', { base64: response.buffer.toString('base64', 0, response.buffer.length) });
return file.save();
}).then(function(thumb) {
// SETTING MORE PROPERTIES
//Set image ref as object attribute.
console.log("Attaching thumb to object");
seed.set("imageFile", thumb);
//Save decimal string as int into another attribute.
console.log("Parsing views into viewsint");
seed.set("viewsInt", parseInt(seed.get("views")));
//Save string as array into another attribute.
console.log("Parsing description into descriptionarray");
seed.set("descriptionarray", [seed.get("description")]);
// SAVING THE OBJECT
console.log("Saving object");
return seed.save();
});
// PUSH THIS PROMISE TO THE ARRAY TO PERFORM IN PARALLEL
results.push(promise);
});
Parse.Promise.when(results).then(function(data){
console.log("All objects saved");
status.success("Updated Succesfully");
});
}, function(error) {
console.error('Request failed with response code ' + httpResponse.status);
status.error("Update Failed");
});
});
});
Thanks knshn. I had refactored the code a lot since that version (including several of the changes you made), but I had posted the version that was identical to that which was working fine before. Your changes let me see the right error. For some reason doing the simple single object implementation didn't work for me originally, hence the nasty workaround. It works now though.
I have now found the culprit - the Seed class had an attribute called 'id'. With the old version this worked fine, but when I deployed that code now it gave an error 101: 'object not found for update'. This must be because the new Parse code is mixing that up with the internal objectId and getting confused that the id is different to what it expects. I wonder how that could still work with the rollback though. Perhaps the at version was tagged to use the older Parse code.
My fix was to use a different name for the id - 'seed_id'.
I'm trying to simply query my database filtering results with a parameter (named: "lesson") I've just obtained from another query.
The problem is that when I try to print with an "alert" command lesson's value it says: undefined.
It is wierd that when I put "lesson" inside a tuple's field it works.
In particular line
obj.set("student", lesson);
added just for debugging purpose, actually writes a consistent value into the table.
This is the whole code:
Parse.Cloud.job("checkingTwoTables", function(request, response) {
Parse.Cloud.useMasterKey();
var stud,lesson;
//select first student of the list and check for his enters
var wholeTable = new Parse.Query("enter");
wholeTable.find({
success: function(result) {
if(result.length != 0)
{
//pick student name and lesson we're interested in
stud = result[0].get("student");
lesson = result[0].get("lesson");
}
else {}
},
error: function(error) {
console.log("failed");
}
});
alert("lesson value:" + lesson);
var selectionQuery = new Parse.Query("enter");
selectionQuery.equalTo("lesson", "cns");
selectionQuery.find({
success: function(results) {
for (var i = 0; i < results.length; i++)
{
var obj = results[i];
obj.set("raggiunto", 77);
obj.set("student", lesson); // <<-------HERE IS THE ISSUE
obj.save(null,{
success: function (object) {
response.success(object);
},
error: function (object, error) {
response.error(error);
}
});
}
},
error: function(error) {
console.log("failed");
}
}); //code continues...
You are having this exact issue.
.find() is making a request with the server. You cannot guarantee when it would finish thus you cannot guarantee that your second query will run after the first one completes. Therefore, when your second query runs, the value of lesson may still have not been retrieved yet by the first query, hence the "undefined".
The solution would be to move the second query inside the success block of the first query.
I'm using Parse to represent the state of a beer keg (among other things). I'd like to check the user's notifications, stored in a "Notifications" table, to see if they'd like to receive a notification when the keg is filled.
I have all of the logic for setting the user's notification settings as well as sending notifications in cloud/notifications.js. All of the logic for updating the keg is in cloud/beer.js. I created an exported function called "sendKegRefillNotification" which performs a query.find() on the Notifications table and gets called from beer.js.
The problem is that it doesn't seem to be executing query.find() when I call the function from beer.js, however when I call the same function from a job within notifications.js, it works just fine.
main.js:
require("cloud/beer.js");
require("cloud/notifications.js");
beer.js:
var notify = require("cloud/notifications.js");
var Keg = Parse.Object.extend("Keg");
var fillKeg = function(beerName) {
var promise = new Parse.Promise();
var keg = new Keg();
keg.set("beerName", beerName)
keg.set("kickedReports", []);
keg.save(null, { useMasterKey: true }).then(function(keg) {
console.log("Keg updated to " + beerName + ".");
promise.resolve(keg);
notify.sendKegRefillNotification(keg);
},
function(keg, error) {
promise.reject(error);
});
return promise;
}
Parse.Cloud.define("beerFillKeg", function(request, response) {
var beerName = request.params.name;
if (!beerName) {
response.error("No beer was specified.");
return;
}
if (!util.isUserAdmin(request.user)) {
response.error("User does not have permission to update the keg.");
return;
}
fillKeg(beerName).then(function(keg) {
kegResponse(keg).then(function(result) {
response.success(result);
});
},
function(error) {
response.error(error);
});
});
function kegResponse(keg) {
var promise = new Parse.Promise();
var result = {
id: keg.id,
beer: {
name: keg.get("beerName")
},
filled: keg.createdAt,
kickedReports: []
};
var kickedReports = keg.get("kickedReports");
if (!kickedReports || kickedReports.length == 0) {
promise.resolve(result);
} else {
util.findUsers(kickedReports).then(function(users) {
result.kickedReports = util.infoForUsers(users);
promise.resolve(result);
}, function(users, error) {
console.log(error);
promise.resolve(result);
});
}
return promise;
}
notifications.js:
var Keg = Parse.Object.extend("Keg");
var Notification = Parse.Object.extend("Notifications");
exports.sendKegRefillNotification = function(keg) {
var beerName = keg.get("beerName");
console.log("Sending notifications that keg is refilled to '" + beerName + "'.");
var promise = new Parse.Promise();
var query = new Parse.Query(Notification);
query.include("user");
query.equalTo("keg_filled", true);
query.find({ useMasterKey: true }).then(function(notifications) {
console.log("Found notifications!");
promise.resolve("Found notifications!");
},
function(notifications, error) {
console.error("No notifications");
console.error(error);
promise.reject(error);
});
return promise;
}
Parse.Cloud.job("beerSendRefillNotification", function(request, status) {
var query = new Parse.Query(Keg);
query.descending("createdAt");
query.first().then(function(keg) {
if (!keg) {
status.error("No keg");
return;
}
exports.sendKegRefillNotification(keg);
},
function(keg, error) {
response.error(error);
});
});
When I run the job "beerSendRefillNotification" from the Parse dashboard, I can tell that query.find() is getting called because it prints "Found notifications!":
E2015-02-23T06:59:49.006Z]v1564 Ran job beerSendRefillNotification with:
Input: {}
Result: success/error was not called
I2015-02-23T06:59:49.055Z]false
I2015-02-23T06:59:49.190Z]Sending notifications that keg is refilled to 'test'.
I2015-02-23T06:59:49.243Z]Found notifications!
However, when I call the cloud function "beerFillKeg", it isn't because it's not printing "Found notifications!" or "No notifications":
I2015-02-23T07:00:17.414Z]v1564 Ran cloud function beerFillKeg for user HKePOEWZvC with:
Input: {"name":"Duff"}
Result: {"beer":{"name":"Duff"},"filled":{"__type":"Date","iso":"2015-02-23T07:00:17.485Z"},"id":"olLXh0F54E","kickedReports":[]}
I2015-02-23T07:00:17.438Z]false
I2015-02-23T07:00:17.523Z]Keg updated to Duff.
I2015-02-23T07:00:17.525Z]Sending notifications that keg is refilled to 'Duff'.
I finally understand it. In sendKegRefillNotification, you're calling query.find({...}), then returning an object. That find is asynchronous, and you're doing nothing to wait for the result. I think you need to return the find function call, rather than an object you set within that method.
In other words, you're running along, leaving some async running code behind you.
Edit: I understand what you tried to do. It sort of makes sense. You defined a promise, and thought the caller would wait for the promise. The problem is, the promise is defined in an asynchronous block. It doesn't yet have any meaning at the moment the caller gets it.
It looks like Parse doesn't allow you to run a query from inside a callback from save(). When I moved "notify.sendKegRefillNotification(keg);" to outside of the callback, it worked.
var fillKeg = function(beerName) {
var promise = new Parse.Promise();
var keg = new Keg();
keg.set("beerName", beerName)
keg.set("kickedReports", []);
keg.save(null, { useMasterKey: true }).then(function(keg) {
console.log("Keg updated to " + beerName + ".");
console.log("Send notifications.");
promise.resolve(keg);
},
function(keg, error) {
promise.reject(error);
});
notify.sendKegRefillNotification(keg); // Now this works
return promise;
}
Can anyone shed some more light on why this worked?