Keystonejs get insterted id value - node.js

I am using updateItem method to save new record.
Following is the code example :
var vv = new vvmodel.model();
vvmodel.updateItem(vv, obj, function (error) {
if(error)
console.log('error :: ', error);
});
This is not sending any object from which we can have inserted id record. How can we get the inserted record id ?
Thanks

you should use update handler in keystoneJs
once updateHandler.process runs without error, you can access .id field on vv
var vv = new vvmodel.model({
// .... initial fields
}),
var updater = vv.getUpdateHandler(req, res, {
errorMessage: 'There was an error creating your new model:'
});
updater.process(req.body, {
flashErrors: true,
logErrors: true,
fields: 'field1, field2, field3'
}, function(err) {
if (err) {
locals.validationErrors = err.errors;
} else {
req.flash('success', 'Your model has been added');
return res.redirect('/vv/detail/' + vv.id); // here you can access the id if request was successful.
}
next();
});
see more detailed example use with sydjs site source

Related

Sequelize not retrieving all data after insert

I have noticed that my backend is not retrieving the expected data after an insert.
In my React application, I have one function which inserts data into the database and after getting a response, a new request is sent to update the current component state with the newly fetched data.
All my functions are using await/async and in the backend, all transactions are correctly used and committed in order.
My client is calling the following endpoints:
-POST: api/ticket ( INSERT AN ITEM)
-GET: api/ticket (GET ALL ITEMS)
Here is what the backend is showing which looks correct to me, the problem is that in the 'SELECT' statement, the inserted item is not retrieved.
The transactions are started from two different routes but I don't see why it should be an issue.
In addition, I tried to change the AddItem function to output the same findAll statement which is called when using the GET method and the data returned are correct.
So why if I separate these two flows I do not get all the items? I always need to refresh the page to get the added item.
START TRANSACTION;
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): INSERT INTO `messages` (`id`,`message`,`createdAt`,`updatedAt`,`ticketId`,`userId`) VALUES (DEFAULT,?,?,?,?,?);
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): COMMIT;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): START TRANSACTION;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): SELECT `ticket`.`id`, `ticket`.`subject`, `ticket`.`status`, `ticket`.`createdAt`, `ticket`.`updatedAt`, `ticket`.`deletedAt`, `ticket`.`userId`, `messages`.`id` AS `messages.id`, `messages`.`message` AS `messages.message`, `messages`.`sender` AS `messages.sender`, `messages`.`createdAt` AS `messages.createdAt`, `messages`.`updatedAt` AS `messages.updatedAt`, `messages`.`deletedAt` AS `messages.deletedAt`, `messages`.`ticketId` AS `messages.ticketId`, `messages`.`userId` AS `messages.userId`, `messages->user`.`id` AS `messages.user.id`, `messages->user`.`firstname` AS `messages.user.firstname`, `messages->user`.`surname` AS `messages.user.surname`, `messages->user`.`email` AS `messages.user.email`, `messages->user`.`password` AS `messages.user.password`, `messages->user`.`stripeId` AS `messages.user.stripeId`, `messages->user`.`token` AS `messages.user.token`, `messages->user`.`birthDate` AS `messages.user.birthDate`, `messages->user`.`status` AS `messages.user.status`, `messages->user`.`confirmationCode` AS `messages.user.confirmationCode`, `messages->user`.`createdAt` AS `messages.user.createdAt`, `messages->user`.`updatedAt` AS `messages.user.updatedAt`, `messages->user`.`deletedAt` AS `messages.user.deletedAt` FROM `tickets` AS `ticket` LEFT OUTER JOIN `messages` AS `messages` ON `ticket`.`id` = `messages`.`ticketId` AND (`messages`.`deletedAt` IS NULL) LEFT OUTER JOIN `users` AS `messages->user` ON `messages`.`userId` = `messages->user`.`id` AND (`messages->user`.`deletedAt` IS NULL) WHERE (`ticket`.`deletedAt` IS NULL);
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): COMMIT;
-- POST '/api/ticket
exports.addMessage = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
var ticketId = req.body.ticketId;
const userId = req.body.userId;
const message = req.body.message;
const subject = req.body.subject;
// Validate input - If new ticket, a subject must be provided
if (!ticketId && !subject) {
return res
.status(400)
.send({ message: "New ticket must have a subject" });
}
// Validate input - If ticket exists, userId and message must be provided
if (!userId && !message && ticketId) {
return res
.status(400)
.send({ message: "UserID and message are required" });
}
// Create ticket is no ticketID was provided
if (!ticketId) {
const [ticket, created] = await Ticket.findOrCreate({
where: {
subject: subject,
userId: userId,
},
transaction: t,
});
ticketId = ticket.id;
}
// Create a new message object
const messageObject = await db.message.create(
{
message: message,
userId: userId,
ticketId: ticketId,
},
{ transaction: t }
);
// Output message object
return res.send(messageObject);
});
} catch (err) {
console.log(err);
return res.status(500).send({
message:
err.message || "Some error occurred while creating the ticket message.",
});
}
};
-- GET: api/ticket
exports.findAll = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
const tickets = await db.ticket.findAll(
{
include: [{ model: db.message, include: [db.user] }],
},
{ transaction: t }
);
tickets.forEach((ticket) => {
console.log(JSON.stringify(ticket.messages.length));
});
return res.send(tickets);
});
} catch (err) {
console.log(err);
res.status(500).send({
message: err.message || "Some error occurred while retrieving Tickets.",
});
}
};
You sent a response to a client before the transaction actually was committed. You just need to move res.send(messageObject); outside the transaction call.
You can try to look what's going on in the current version of your code if you add several console.log with messages to see what the actual order of actions is (I mean a couple of messages in POST (the last statement inside transaction and after transaction before res.send) and at least one at the beginning of GET).
Actually if the transaction was rolled back you'd send an uncommited and already removed object/record that I suppose is not your goal.

Mongoose using $push on an array field overwrites last entry

I am trying to create an eventlog type of field on mongodb records where I can store a list of activity. The first time I run the function, it appends to the array correctly but subsequent calls overwrite the last entry instead of appending. If I restart the server or refresh the page in the browser, it will append once again then repeat the same behavior.
I'm learning node and javascript so I'm sure it's some mistake I've made but I don't seem able to figure it out.
Javascript on the client is a tabulator event.
cellEdited:function(cell){
//cell - cell component
const oldValue = cell.cell.oldValue;
const newValue = cell.cell.value;
const title = cell.cell.column.definition.title;
var report = cell.cell.row.data;
report.event = `Updated ${title} from ${oldValue} to ${newValue}`;
$.ajax({
type: 'POST',
url: '/api/update',
data: report,
dataType: 'json'
});
}
The route that its calling on the server:
app.post('/api/update', isAuthenticated, function(req, res) {
var report = req.body;
var reason = '';
if (typeof report.event !== 'undefined') {
reason = report.event;
delete report.event;
} else {
reason = 'Report updated';
}
db.DamageReport.findOneAndUpdate({ _id: report._id}, report, function (err, doc) {
if (err) {
console.log('Err updating report ', err);
return res.send(500, { error: err});
}
/*
* Write eventlog
*/
var event = {"date": new Date(), "user": req.user.email, "event": reason };
appendLog(doc._id, event);
return res.json(doc);
});
});
The appendLog function:
function appendLog(id, entry) {
/*
* entry format:
* date: Date
* user: String
* event: String
*/
if (typeof(entry.date) !== 'object') {
entry.date = new Date();
}
db.DamageReport.findByIdAndUpdate(id, {$push: {eventLog: entry}}, function(err, result) {
if (err) {
return console.log('Error writing eventLog: ', err);
}
return(result);
});
}
It wouldn't append more than one because the previous save contained the Eventlog array in it's original form so every time it saved, it set it back to the original array and then appended the last update.

NodeJS API GET by Id. How to detect item not found?

I'm developing a GET endpoint to fetch elements from a database(dynamoDB). I'm using Swagger to define the data model in my api. This is the operationId method in my controller:
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
res.status(200).send(config);
});
}
I would like to send a 404 message if the id was not found.
I've noticed in swagger-ui that the body of the response comes empty
Response Body
[]
when the id is not found in the db.
How can I detect in my code when id was not found? I've tried to check if the body of the response is empty:
if(!(config.body))
but this doesn't work because the body is not null
You can check the count of config at server side, if config count is 0 the send desire response code 404 else return 200 response code with data as shown below
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
if (config.length == 0){
res.status(404).end();
} else {
res.status(200).send(config);
}
});
}
Try adding a length check in your callback, like so:
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
if(typeof config === 'array' && 0 < config.length){
res.status(200).send(config);
} else {
res.status(404).send();
}
});
}
I would also suggest that you should simply use the getItem query instead of scan:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#getItem-property
Since the config object keys value is one when result is not found so what you can do is check the length of keys of that object like this :
if ( Object.keys(config).length == 1 )return res.status(400).send("Error 404");

Parse Server set User ACL and assign Role

I am converting my code from parse.com to parse-server with little success. The idea of the afterSave function is to assign user only ACL and assign the user to the 'user' role.
The ACL part works fine, however I am unable to assign the user to the role.
Parse.Cloud.afterSave(Parse.User, function(req, res) {
if (!req.object.existed()) {
var user = req.object;
var acl = new Parse.ACL(user);
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
user.setACL(acl);
return user.save({}, { useMasterKey: true }).then(function() {
var query = new Parse.Query(Parse.Role);
query.equalTo("name", 'user');
return query.first({useMasterKey: true});
}).then(function(roles) {
if (roles.length < 1) return Parse.Promise.error("no such role");
roles[0].getUsers({useMasterKey: true}).add(user);
return roles[0].save({}, { useMasterKey: true });
}).then(function() {
return user;
});
}
});
"however I am unable to assign the user to the role". add function(err) to see which step failed, and see error message.
The code you shows is incorrect.
try this one
return user.save({}, { useMasterKey: true }).then(function() {
var query = new Parse.Query(Parse.Role);
query.equalTo("name", 'user');
return query.first({useMasterKey: true});
//first will return one object or null
}).then(function(role) {
//.getUsers() is equal .relation('users')
if(role){
role.getUsers().add(user);
return role.save(null, { useMasterKey: true });
}else{
return Parse.Promise.error("no such role");
}
}, console.error).then(function() {
return user;
}, console.error);
query.first() return Object not Array, so you should query, the returned result, without accessing array index. Second last .then block code should be
if (!roles) return Parse.Promise.error("no such role");
roles.getUsers({useMasterKey: true}).add(user);
return roles.save({}, { useMasterKey: true });

unable to add property to the json object

I am trying to add status to a response on successful update but I am not able to add the status property to json object of form. Here is my code
apiRouter.post('/forms/update', function(req, res){
if(req.body.id !== 'undefined' && req.body.id){
var condition = {'_id':req.body.id};
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form;
objForm.status = "saved successfully";
return res.send(objForm);
});
}else{
res.send("Requires form id");
}
});
and here is the response that I get, notice status is missing
{
"_id": "5580ab2045d6866f0e95da5f",
"test": "myname",
"data": "{\"name\":3321112,\"sdfsd\"344}",
"__v": 0,
"id": "5580ab2045d6866f0e95da5f"
}
I am not sure what I am missing.
Try to .toObject() the form:
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form.toObject();
objForm.status = "saved successfully";
return res.send(objForm);
});
Mongoose query result are not extensible (object are frozen or sealed), so you can't add more properties. To avoid that, you need to create a copy of the object and manipulate it:
var objectForm = Object.create(form);
objectForm.status = 'ok';
Update: My answer is old and worked fine, but i will put the same using ES6 syntax
const objectForm = Object.create({}, form, { status: 'ok' });
Another way using spread operator:
const objectForm = { ...form, status: 'ok' }
Try changing res.send(objForm) to res.send(JSON.stringify(objForm)). My suspicion is that the the Mongoose model has a custom toJson function so that when you are returning it, it is transforming the response in some way.
Hopefully the above helps.
Create empty object and add all properties to it:
const data = {};
data._id = yourObject._id; // etc
data.status = "whatever";
return res.send(data);
Just create a container.
array = {};
Model.findOneAndUpdate(condition, function(err, docs){
array = docs;
array[0].someField ="Other";
});

Resources