Update a user record with a pointer using Parse Cloud Code - parse-platform

I have viewed all the articles on here, and haven't done any javascript coding in the past. Hoping someone can help me.
I have a class called rank and of course the parse _User class. I have added a pointer to the _User class to the rank class (the column name is called user_rank, which allows me to give a user a rank - seems simple enough.
What I am trying to achieve, is to use Cloud Code to change a user's rank as the administrator of the app (so it's something I do in my admin app, not the user does in their app).
This is what I have, but all I get is an error 101 'Object not found'. I have no doubt I am doing this all wrong, but I have tried to piece together responses from other posts with no success.
Any help is greatly appreciated.
Updated code with Davi's change below - now throwing error schema mismatch for _User.user_rank; expected Pointer but got String
Parse.Cloud.define("setUserRank", async (request, response) => {
let { userObjectId, rankObjectId } = request.params;
const userQuery = new Parse.Query(Parse.User);
const rankQuery = new Parse.Query('rank');
// Get the user object to change the rank of
try{
let user = await userQuery.get(userObjectId, { useMasterKey: true});
let rank = await rankQuery.get(rankObjectId, { useMasterKey: true});
console.log(user);
console.log("Running");
const rankRelation = user.relation('user_rank');
rankRelation.add(user_rank);
user.save(null, { useMasterKey: true});
return ("User Rank Changed"));
} catch (err) {
throw new Error(err.message)
}
});

I think the problem happens because of this line:
const rankQuery = new Parse.Query(Parse.rank);
In the case of you custom classes, you need to pass the class name as a string:
const rankQuery = new Parse.Query('rank');

Related

Mentioning a user in the System.History

I'm trying to add a new comment to a work item which mentions a user, but using the traditional "#adamh" as you would do on the website does not seem to work via the API.
The data updates fine, however the "#adamh" is just plain text, I need to be able to somehow chuck an identity into here. Can anyone point me in the right direction?
Thanks!
A snippet is here
const vsts = require('vso-node-api');
const item = require('vso-node-api/WorkItemTrackingApi')
const ti = require('vso-node-api/interfaces/WorkItemTrackingInterfaces');
// your collection url
const collectionUrl = "https://myArea.visualstudio.com/defaultcollection";
// ideally from config
const token = "helloWorld";
async function run() {
let authHandler = vsts.getPersonalAccessTokenHandler(token);
let connection = new vsts.WebApi(collectionUrl, authHandler);
let itemTracking = await connection.getWorkItemTrackingApi();
//Add all task data to new array
let taskData = await itemTracking.getWorkItems([15795,15796])
let newData = taskData[0]
let wijson = [
{
"op": "add",
"path": "/fields/System.History",
"value": "#adamh"
}
];
const updateItem = itemTracking.updateWorkItem(null, wijson, 15795).catch(err => {
console.log(err)
}).then(() => console.log("updated"))
return newData
}
const express = require('express')
const app = express()
app.get('/', async (req, res) => {
let data = await run()
res.send(data)
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
You can use the format shown here as part of the text value for your new comment:
...
This will create a mention link to that user. The link text can be the person's name or any other text you choose to put there. An email alert will be sent to the mentioned user if your system is configured to do so (same as in the UI).
To get your users' userid strings, you can follow the method shown here.
You can use the # to notify another team member about the discussion. Simply type # and their name.
It's using the #mention control , the person you #mention will receive an email alert with your comment and a link to the work item, commit, changeset, or shelveset.
There is not any public API shows how this work in VSTS, you could try to use F12 in google browser to track the process. Another workaround is directly using API to send a notification to the user you want to mention at.

Read both key values and files from multipart from data post request in ASP.NET WebAPI

I have an endpoint that needs to accept a file upload and also some other information from the client request. With the following code I can upload the file successfully but can't seem to figure out how to read the other info.
I make a test request from Postman with the following form data:
image -- myimage.jpg -- of type File
email -- a#b.com -- of type Text
The backend code looks like this:
[HttpPost]
public async Task<HttpResponseMessage> SharePhoto()
{
try
{
var provider = new MultipartMemoryStreamProvider();
var data = await Request.Content.ReadAsMultipartAsync(provider);
// this is how I get the image which I am succesfully passing to EmailService
var item = (StreamContent)provider.Contents[0];
using (var stream = new MemoryStream())
{
await item.CopyToAsync(stream);
String emailAddress;
EmailService.SendSharedPhoto(emailAddress, stream);
return Request.CreateResponse();
}
}
catch
{
// do stuff
}
}
In this example I am able to access provider.Contents[1] but can't seem to be able to get the value from it into emailAddress. I'm thinking it may be possible to use the same trick as the await item.CopyToASync(stream) from the image upload, but I'm hoping I can get a simpler solution to that. Any ideas?
I just barely answered a very similar question to this yesterday. See my answer here complete with sample controller code.
The method I ended up using is:
If the form elements are strings (and it worked for me since the mobiel frontend took responsability for input data) you can do this:
var streamContent = (StreamContent)provider.Contents[1];
var memStream = new MemoryStream();
await streamContent.CopyToAsync(memStream);
var actualString = Encoding.UTF8.GetString(x.ToArray());
If however the field needs to represent a collection of items, like for example the email list: ["a#b.com", "x#c.com"], etc a JavaScriptSerializer can be user, like so:
var streamContent = (StreamContent)provider.Contents[1];
var emailAddresses = await str.ReadAsStringAsync();
var jsSerializer = new JavaScriptSerializer();
var deserializedData = jsSerializer.Deserialize<string[]>(emailAddresses);
Note that this is nowhere near safe, though it is few lines of code and happens to work.

Cloud code causes "Tried to save an object with a pointer to a new, unsaved object" when saving pointers

In my app, I have users and posts, and I am trying to instantiate my Activity class which has pointers to user and post objects. Here is my code:
var Activity = Parse.Object.extend("Activity");
var User = Parse.Object.extend("_User");
var Post = Parse.Object.extend("Posts");
var activityHandler = function(request, response){
//toUser is the objectId of the User not the object itself.
var toUserId = request.toUser;
var fromUserId = request.fromUser;
var postId = request.post;
var arg = request.argument;
var toUser = new User();
toUser.set("objectId",toUserId);
var fromUser = new User();
fromUser.set("objectId",fromUserId);
var post = new Post();
post.set("objectId",postId);
var activity = new Activity();
activity.set("activityFrom", fromUser);
activity.set("activityTo", toUser);
activity.set("argument", arg);
activity.set("post", post);
activity.save(null, {
useMasterKey: true,
success: function(o){
console.log("activity logged successfully");
response.success();
},
error: function(err){
console.log(err);
},
}
);
};
Parse.Cloud.define("createActivity", activityHandler);
The code is self-explanatory, but just to summarize, the client sends the appropriate user and post IDs, and the cloud function should instantiate a new Activity object, with pointers to the users and post. However, when I try to save, I'm getting:
{"code":141,"error":"Uncaught Tried to save an object with a pointer to a new, unsaved object."}
The problem is that, the object IDs sent to the function are completely valid, already-existing objects in the database. ACL is also not an issue as I'm on the master key.
What am I doing wrong?
It was my bad.
There were multiple problems with my code.
I was using object.set("objectId", id); syntax, which actually should be object.id = id. I'm not sure if it is necessary, maybe it would work without that change too, but I've read everywhere that it's a bad practice.
I wasn't getting the request parameters correctly. I was using request.param1 instead of request.params.param1, and the parameter variable was undefined. I haven't checked if it was undefined or not for a long time, as the error message wasn't really helpful about parameter being undefined, from my perspective back then. (now it makes sense, no ID, and it automatically tries to create a new object)
I've corrected them and it does save correctly now.
This also happens when you forget to call the method:
response.success(object);

Filtering a loaded kml file in OpenLayers

I'm trying to create an interactive search engine (for finding event tickets) of which one of its features is a visual map that shows related venues using OpenLayers. I have a plethora of venues (3000+) in a kml file that I would like to selectively show a filtered subsection of. Below is the code I have but when I try to run it has a JavaScript error. Running firebug and chrome developer tools makes me think that it is not getting passed the parameters I give because it says that the variables are null. However, I cannot figure out why they are not getting passed. Any insight is greatly appreciated.
var map, drawControls, selectControl, selectedFeature, select;
$('#kml').load('venuesComplete.kml');
kml=$('#kml').html();
function showVenues(state, city, venue){
filterStrategy = new OpenLayers.Strategy.Filter({});
var kmllayer = new OpenLayers.Layer.Vector("KML", {
strategies: [filterStrategy,
new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "venuesComplete.kml",
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true
})
})
});
select = new OpenLayers.Control.SelectFeature(kmllayer);
kmllayer.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
map.addControl(select);
select.activate();
filter = new OpenLayers.Filter.Comparison({
type: OpenLayers.Filter.Comparison.LIKE,
property: "",
value: ""
});
function clearFilter(){
filterStrategy.setFilter(null);
}
function setFilter(property, value){
filter.value = value;
filter.property = property;
filterStrategy.setFilter(filter);
}
var vector_style = new OpenLayers.Style();
if(venue!=""){
setFilter('name', venue);
}else if(city!=""){
setFilter('description', city);
}else if(state!=""){
setFilter('description', state);
}
map.addLayer(kmllayer);
function onPopupClose(evt) {
select.unselectAll();
}
function onFeatureSelect(event) {
var feature = event.feature;
var selectedFeature = feature;
var popup = new OpenLayers.Popup.FramedCloud("chicken",
feature.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(100,100),
"<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description +'<br>'+feature.attributes,
null,
true,
onPopupClose
);
document.getElementById('venueName').value=feature.attributes.name;
document.getElementById("output").innerHTML=event.feature.id;
feature.popup = popup;
map.addPopup(popup);
}
function onFeatureUnselect(event) {
var feature = event.feature;
if(feature.popup) {
map.removePopup(feature.popup);
feature.popup.destroy();
delete feature.popup;
}
}
}
function init() {
map = new OpenLayers.Map('map');
var google_map_layer = new OpenLayers.Layer.Google(
'Google Map Layer',
{type: google.maps.MapTypeId.HYBRID}
);
map.addLayer(google_map_layer);
state="";
state+=document.getElementById('stateProvDesc').value;
city="";
city+=document.getElementById('cityZip').value;
venue="";
venue+=document.getElementById('venueName').value;
showVenues(state,city,'Michie Stadium');
map.addControl(new OpenLayers.Control.LayerSwitcher({}));
map.zoomToMaxExtent();
}
IF I UNDERSTAND CORRECTLY, your kml does not load properly. if this is not the case, please disconsider my answer.
it is very important to check if your kml layer was properly loaded. i have a map that loads multiple dynamic (from php) kml layers and it is not uncommon to have a large layer simply not load. when that happens, the operation is aborted, but, as far as openlayers is concerned, the layer was properly loaded.
so i do 2 things: i check if the amount of loaded data meets the expected number of features in my orginal php kml parser (i use a jquery or ajax call for that) and then, in case there is a discrepancy, i try reloading (since this is a loop, i limit it to 5 attempts, so as not to loop infinitely).
check out some of my code here

FindItems() and BindToItems() give inconsistent results for EmailMessage.Sender.Address

After quite a lot of debugging, I've refined a complicated Managed EWS problem down to the following two simple-ish test cases. The first one works, the second one fails:
var view = new ItemView(100) { PropertySet = new PropertySet { EmailMessageSchema.Id } };
var findResults = ews.FindItems(WellKnownFolderName.Inbox, view)
var bindResults = ews.BindToItems(findResults.Select(r => r.Id), new PropertySet { EmailMessageSchema.Sender });
// Sanity check
Assert.AreEqual(1, bindResults.Count());
// The results I care about
Assert.AreEqual("David Seiler", bindResults[0].Sender.Name);
Assert.AreEqual("david.seiler#yahoo.com", bindResults[0].Sender.Address);
One might try to cut out the BindToItems() call, and use FindItems() directly:
var view = new ItemView(100) { PropertySet = new PropertySet { EmailMessageSchema.Sender } };
var findResults = ews.FindItems(WellKnownFolderName.Inbox, view)
// This part still works fine
Assert.AreEqual(1, findResults.Count());
// So does this
Assert.AreEqual("David Seiler", findResults[0].Sender.Name);
// ...but this fails! Sender.Address is null
Assert.AreEqual("david.seiler#yahoo.com", findResults[0].Sender.Address);
Can anyone tell me where I've gone wrong? It really seems, from the documentation, as though this should work. Not all properties can be read through FindItems(), it's true, but those properties usually throw when I try to access them, and anyway there's a list of those properties on MSDN and Sender isn't on it. What's going on?
Actually I don't know why, but in the second option, it only load basic information of the sender like the name, but not the Address.
If you want to load all the sender properties but do not want to bind the full message you can add the following line before the first assert
service.LoadPropertiesForItems(findResults.Items, new PropertySet(EmailMessageSchema.Sender));

Resources