create list item in sharepoint using Graph API in SPFx - sharepoint

I am trying to create a new list item to an existing list in sharepoint using spfx react with Graph API.
const obj: string = JSON.stringify({
'LinkTitle': `Item ${new Date()}`,
'Contactlist': '555555555',
'CompanyName': 'dfvb',
'Country': 'asd'
});
this.props.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient): void => {
// get information about the current user from the Microsoft Graph
client
.api("sites('root')/lists('Contactlist')/items")
.post(obj).then(() => {
console.log('obj' + obj);
});
});
but it is posting a list item with no values.
sharepoint list item with no values
I want the obj to be posted in Contactlist list.

Sample test demo for your reference.
const obj: string = JSON.stringify(
{
"fields": {
'Title': 'Lin',
'Company': 'Microsoft'
}
}
);
this.props.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient): void => {
client
.api("/sites/siteid/lists/77d9ee4c-9142-40d1-8edb-9bdfd226be2a/items")
.header('Content-Type','application/json')
.version("v1.0")
.post(obj, (err, res, success) => {
if (err) {
console.log(err);
}
if (success)
{
console.log("success");
}
})
});

Related

Displaying request results from backend to html (MEAN)

Currently developing an angular app with a node.js + express backend. I have successfully requested the correct documents from mongoDB and am able to view these documents via terminal. My problem lies within getting that same data and displaying it to my html template. So far, this is what I have:
//api.js
router.get('/user/paPolicies/:id', (req, res) => {
const myquery = { _id: ObjectId(req.params.id) };
var data = req.body;
return db.collection('users').findOne(myquery, {paPolicies: 1})
.then(result => {
if(result) {
console.log(result.paPolicies)
} else {
console.log('No document matches the provided query.')
}
return result;
})
.catch(err => console.log(`Failed to find document: ${err}`))
})
//users.service.ts
getUserPaPolicies(_id: string, paPolicies: object) {
return this.http.get(`./api/user/paPolicies/${_id}`, paPolicies)
}
//user-view-portfolio.component.ts
export class UserViewPortfolioComponent implements OnInit {
id: any;
paPolicies: any = [];
constructor(private usersService: UsersService, private authService: AuthService) { }
ngOnInit(): void {
this.id = this.authService.getUserId();
console.log(this.id);
this.usersService.getUserPaPolicies(this.id, this.paPolicies).subscribe(result => {
this.paPolicies = result;
console.log(result)
});
}
}
The code I've written as seen above is able to GET the document I've requested for. However, this data can only be viewed within the terminal. An important thing to note is that paPolicies is an array and if need be, I can edit this post to add the output for console.log(result.paPolicies).
The problem(s):
The console.log within the component code does not even show any output when I view chrome dev tools console.
Apparently, if I remove this chunk of code from the component.ts:
this.usersService.getUserPaPolicies(this.id, this.paPolicies).subscribe(result => { this.paPolicies = result; console.log(result) });
the terminal would stop showing the results of my request also.
How would I modify the code to be able to display the data in my html template?
Apart from not knowing how your HTML template in the Angular frontend looks like shouldn't you send a response instead of returning the result in order to send the data to the frontend?
router.get('/user/paPolicies/:id', (req, res) => {
const myquery = { _id: ObjectId(req.params.id) };
var data = req.body;
return db.collection('users').findOne(myquery, {paPolicies: 1})
.then(result => {
if(result) {
console.log(result.paPolicies)
} else {
console.log('No document matches the provided query.')
}
// return result;
res.send(result);
})
.catch(err => console.log(`Failed to find document: ${err}`))
})

How to get access to entire subdocument(array of objects within array of objects) using moongoose?

my Schema :
var schoolSchema = new mongoose.Schema({
name: String,
classes:[
{
c_name:String,
c_strn:String,
students:[
{s_name:String,
s_roll:String,
s_address:String
}
]
}
],
});
var school = mongoose.model('school',schoolSchema);
Sample Doc :
var sainik = new school({name:'sain',
classes:
[
{
c_name:'1(one)',
c_strn:'100',
students:[
{
s_name:"Leo",
s_roll:"17",
s_address:"strt24",
},
{
s_name:"Kyle",
s_roll:"18",
s_address:"strt24",
}
]//array of students
}
]//array of classes
});
sainik.save().then(()=>console.log("Save it"));
Code :
app.get('/home/:id/:cid',function(req, res){
school.find().exec(function(err,data){
if (err){
console.log(err);
}
else{
console.log(data);
res.render("classDet",{data:data});
}
})
});
Here i need to know how to get access to the individual classes using the class id and to print the array of students as list in the "classDet"
Basically I am creating a school management system in which there will be many classes and inside that classes will be list of students.
I want to print all the students in each classes only when the parent class is access.
You can try either one of these :
app.get('/home/:id/:cid', function (req, res) {
school.find({ id: id, 'classes.c_id': cid }, { _id: 0, 'classes.c_id.$.students': 1 }).lean().exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
app.get('/home/:id/:cid', function (req, res) {
school.aggregate([{ $match: { id: id } }, { $unwind: '$classes' }, { $match: { 'classes.c_id': cid } }, { $project: { _id: 0, students: '$classes.students' } }]).exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
Note : There will be a slight difference in both results, you can test it & choose one out of those, Also keep in mind if in case id from request is being searched against _id in DB then you need to convert id string to ObjectId() before using it in queries. Also we're using .lean() in .find() to convert mongoose docs into Js objects in order to access keys in data while working on it in code.

Async REST PATCH best practice

I have a rest api that receives a patch request with the following possible parameters:
firstname
lastname
image
birthday
If I receive the image parameter I will need to upload the image to a cdn and after that to update and save the user profile.
If I don't receive the image parameter I will update and save the user profile.
What I had in mind was:
if photo then upload -> promise -> update fields -> save
else update fields -> save
My question: is there a best practice so I don't need to double a portion of code for each case?
app.patch('/users', authenticate, (req, res) => {
let body = _.pick(req.body, ['birthday', 'country', 'city', 'gender', 'firstname', 'lastname', 'photo']);
if ( body.photo ){
cloudinary.uploader.upload(body.photo,function(error, result) {
if( error ) {
res.status(400).send(error);
}
body.photo = result.url;
req.user = { ... req.user, ... body};
console.log ('update profile');
res.send(req.user);
});
}
req.user = { ... req.user, ... body};
console.log ('update profile');
res.send(req.user);
});
I have assumed that there is a createUser function that return a promise.
let userData = req.body;
userData.photo = undefined;
return Promise.resolve()
.then(function() {
if (req.body.photo) {
return promisifiedUpload(body.photo);
} else {
return undefined;
}
})
.then(function(result) {
if (result && result.url) {
userData.photo = result.url;
}
return createUser(userData);
})
.then(function(it) {
res.send(it);
})
.catch(function(err) {
res.status(400).send(err);
});
There are plenty of helper libraries that help you turn functions that work with node-style callbacks like function(err, resp){...} to promises, but for the sake of clarity this is how you could use one:
function promisifiedUpload(url) {
return new Promise(function(resolve, reject) {
cloudinary.uploader.uproad(url, function(err, resp) {
if (err) {
reject(err);
} else {
resolve(resp);
}
});
});
}
Some material that might come in handy:
ecma6 promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
npm util.promisify https://nodejs.org/api/util.html#util_util_promisify_original
How do I convert an existing callback API to promises?

How to handle errors without setting res headers twice in node.js

I am a little confused with the req, res parameters for node and the best way to handle these when I'm using asynchronous calls. A function I currently have is supposed to add an Item to my DB, update some DB models accordingly, and then send a response saying the updates were successful. However, the functions that this function asynchronously calls can send an erroneous response if an error happens. If this happens, I get the error Can't set headers after they are sent, since I am trying to call res.send twice. Would appreciate someone's help in figuring out a more optimal way to handle errors. Thanks!
the main function:
item.author = req.user._id;
item.description = req.query.description;
item.rating = req.query.rating;
item.save()
.then(result => {
// update user's items
UserController.updateItems(req.user._id, result._id);
ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
res.send(result);
})
.catch(error => {
res.send(error);
});
updateItemRating:
export const updateItemRating = (itemId, rating, res) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
return res.status(404).send('Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
res.status(404).send('Error updating item with new rating');
}
});
});
};
updateItems:
export const updateItems = (userId, itemId) => {
User.findOneAndUpdate(
{ _id: userId },
{ $push: { items: [itemId] } },
(err, user) => {
console.log('user' + user);
if (err) {
console.log('got an error in updateItems');
}
});
};
Function call to updateItems and updateItemRating both are asynchronous . Response send is getting called multiple time , and also not sure which method send is getting called first . To fix you problem I can suggest you to apply below technique :
Callback : You can pass callback as argument which will do res.send and same callback you can call on error or success condition .
UserController.updateItems(req.user._id, result._id,function(status,message){res.status(status).send(message);});
You can update item rating method like :
export const updateItemRating = (itemId, rating, callback) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
callback(404,'Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
callback(404,'Error updating item with new rating');
}else{
callback(200);
}
});
});
};
Async Module : You can synchronize you method call using this module.
Instead of having your update functions send the result, you should throw() an error, that way your outer function will catch the error and you can use that to return it.
Another way to think about this is that your outer function is handling the success case with a res.send(), so it should also be responsible for the res.send of the error case.
The less your database layer knows about the caller the easier it will be to reuse our code.
Create a custom error type to encapsulate the 404:
function NotFoundError(message) {
this.message = (message || "");
}
NotFoundError.prototype = new Error();
Then use that in your inner function:
export const updateItemRating = (itemId, rating, res) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
throw new NotFoundError('Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
throw new NotFoundError('Error updating item with new rating');
}
});
});
};
And your main becomes:
item.save()
.then(result => {
// update user's items
UserController.updateItems(req.user._id, result._id);
ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
res.send(result);
})
.catch(error => {
if (error instanceof NotFoundError) {
res.status(404).send(error.message);
}
else {
res.send(error);
}
});

Multiple mongoose call with async

i'm new with node and mongoose.
I do multiple queries to find my datas so I use async to populate an array and then send it back the the client.
I try to get all the stats from the games I found.
Here is my Schema : [Team] 1 ---> N [Game] 1 ---> N [Stat]
var json = []; // The variable I will send back
async.waterfall([
function( team, next ) { // Find all games from a team (with its id)
dbGame.find({ team_id: req.params._id }).exec( next );
},
function( games, next ) {
for (key in games) { // For all games, finds their stats
dbStat.find({ game_id: games[key]._id }).exec(function(err, stats) {
json.push({
_id : games[key]._id,
stats : stats
}); // The json I need to return
});
if ( Number(key) === Number(games.length -1) ) {
next(json);
}
}
}
], function () {
res.status(200);
res.json(json);
});
The variable json send is always empty because of the asynchronous and I don't know how to have the full one.
update #1
Ok it's cool it's working.
But just a trouble, all the stats are in all json in the object :
Juste a problem : The stats from all games are stored in all json.
[{
_id:"57cb15f73945ac9808fe5422",
stats:Array[13]
}, {
_id:"57ff76fd78ca91a17b25dd1b",
stats :Array[13]
}]
But I think i can sort then. Here's my code now :
async.waterfall([
function(next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
jsonResponse(res, 200, json);
});
Try below:
var json = []; // The variable I will send back
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function() {
res.status(200);
res.json(json);
});
Comment if it has any issue or I have missed something as I have not tested it.
You are not passing the results of the first find to the next step of waterfall, and when you call next(json); you are passing json as error, as the first argument is an error. Try:
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next(err, games));
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
res.status(200);
res.json(json);
});

Resources