Sails.JS: AUTO INCREMENT Implementation Failed - mysql

I'm developing a Sails.JS app that performs CRUD operations with a MySQL database using waterline ORM. My models have an attribute id set to auto-increment with the beforeCreate function, yet every time I try to call the create function, this error occurs:
{
"error": {
"name": "UsageError",
"code": "E_INVALID_NEW_RECORD",
"details": "Missing value for required attribute id. Expected a number, but instead, got: undefined"
} }
Here's my model:
module.exports = {
primaryKey: 'id',
attributes: {
id: {
type: "number",
required: true,
unique: true,
autoIncrement: true
},
name: {
type: "string",
required: true
},
beforeCreate : function(item, next) {
Counter.native(function(err, collection) {
if (err) console.log(err);
collection.findAndModify(
{ _id: "id" },
null,
{ $inc: { seq: 1 } },
{ new:true, upsert:true },
function(err, object){
if (err) console.log(err);
item.id = object.seq;
next();
}
);
});
}
};
Here's my model controller:
module.exports = {
create :function(req,res,next) {
let params = {name:req.param('name') };
model.create(params , function(err , createdData) {
if(err){
return res.badRequest({
error: err
});
}
else
{
return res.json({ added});
}
});
},

Related

Sequelize, Mysql how to return deleted ids

I have a GraphQL mutation that deletes items. I'm trying to retrieve the deleted ids. However null ids are returned but items are deleted from the database.
Shema
type Mutation{
deletePropertyItems(id: [ID!]!): DeletePropertyItemsRes!
}
type DeletePropertyItemsRes {
success: Boolean!
message: String!
propertyItem:PropertyItem
}
type PropertyItem {
id: ID!
title: String!
description: String!
}
Mutation Resolver
async deletePropertyItems(_, { id }, { models }) {
try {
const deletedRows = await models.PropertyItem.destroy({
where: { id: id },
});
if (deletedRows) {
return {
success: true,
message: "Property Item(s) Deleted Successfully!",
propertyItem: deletedRows,
};
}
return {
success: false,
message: "Operation not Successful",
};
} catch (error) {
throw new Error(error.message);
}
},
Apollo Studio Error

How can I bulk update records using sequelize.js and ignore certain columns

How can I bulk update records using sequelize.js. All of the time, I will be having only 150 users to update. In this case how do I find unique records to udpate in MySql ? At the moment update of record is not happening
1.email field is unique
2.existing records if changed need to be updated
3.new records need to be inserted
server.js
const manageNomineesSchema = require('./server/models/managenominees');
const ManageNomineesModel = manageNomineesSchema(sequelize, DataTypes);
app.post('/service/managenominees', upload.single('file'), async (req, res, next) => {
try {
if(req.file){
let filePath = req.file.path;
fs.createReadStream(filePath)
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => {
console.log(results);
const allNominees = results.map(
nominees => {
return {
name: nominees.name,
email: nominees.email
}
});
const emailCount = await ManageNomineesModel.count({ col: 'email' });
if(emailCount == 0){
await ManageNomineesModel.bulkCreate(allNominees);
} else {
await ManageNomineesModel.bulkCreate(allNominees,
{ updateOnDuplicate: ["email"] },
{attributes: { exclude: ['createdAt'] }
})
}
res.status(200).json({ message: "Nominees inserted successfully !"});
});
}
} catch (e) {
res.status(500).json({ fail: e.message });
}
});
managenominees.js
module.exports = (sequelize, DataTypes) => {
const managenominees = sequelize.define('managenominees', {
id: {
type: DataTypes.INTEGER(10),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(200),
allowNull: false
},
email: {
type: DataTypes.STRING(100),
allowNull: false
},
access: {
type: DataTypes.STRING(10),
allowNull: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
}, {
timestamps: true,
tableName: 'managenominees'
});
return managenominees;
};
After adding a PK inside the where clause where: { id: ['id']}, it started updating the records against the column updateOnDuplicate: ["name"]
const emailCount = await ManageNomineesModel.count({ col: 'email' });
if(emailCount == 0){
await ManageNomineesModel.bulkCreate(allNominees);
res.status(200).json({ message: "Nominees inserted successfully !"});
} else {
await ManageNomineesModel.bulkCreate(allNominees,
{ updateOnDuplicate: ["name"],
where: { id: ['id']}
});
res.status(200).json({ message: "Nominee records updated successfully !"});
}

Express js - how to remove certain field from response

In my current login api, it returns
"user": {
"id": "3e85decc-2af4-436c-8b7f-e276771234f5",
"email": "cccc#cccc.com",
"password": "$xxxxxxxxxx",
"createdAt": "2021-05-14T08:48:31.000Z",
"updatedAt": "2021-05-14T08:48:31.000Z"
},
"token": "xxxxx"
}
I want to remove the password field in my response, so I use delete user.password in my code, but it's not working
/api/users.js
router.post('/login', (req, res, next) => {
passport.authenticate('local', {session: false}, (err, user, info) =>{
if (err || !user) {...}
req.login(user, {session: false}, (err) => {
if (err) {
res.send(err);
}
const token = jwt.sign(user, 'your_jwt_secret');
console.log(user) // show dataValues and _previousDataValues instead of normal JSON object
delete user.password // not working
return res.json({user, token});
});
})(req, res);
});
I tried to log user object for above file, it returns:
users {
dataValues: {
id: '3e85decc-2af4-436c-8b7f-e276771234f5',
email: 'cccc#cccc.com',
password: '$xxxxxxxxxx',
createdAt: 2021-05-14T08:48:31.000Z,
updatedAt: 2021-05-14T08:48:31.000Z
},
_previousDataValues: {
id: '3e85decc-2af4-436c-8b7f-e276771234f5',
email: 'cccc#cccc.com',
password: '$xxxxxxxxxx',
createdAt: 2021-05-14T08:48:31.000Z,
updatedAt: 2021-05-14T08:48:31.000Z
},
_changed: Set(0) {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [ 'id', 'email', 'password', 'createdAt', 'updatedAt' ]
},
isNewRecord: false
}
This is my user model. I am using Sequelize.
const Sequelize = require('sequelize');
const DataTypes = Sequelize.DataTypes;
const db = require('../sequelize')
let users = db.define('users', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
primaryKey: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: 'email'
},
password: {
type: DataTypes.STRING,
},
},
{
hooks: {
beforeCount(options) {
options.raw = false;
}
}
}
);
module.exports = users;
Finally solved it using user.get({plain: true})
let plainUser = user.get({plain: true})
delete plainUser['password']
return res.json({user, token});
It's probably not a good idea to delete properties from the Model directly. Try using ToJSON() to convert the Model to a plain Javascript object and delete the password from that.
plainUser = user.ToJSON();
delete plainUser.password
why dont you re-create your result response, something like this:
let response = {
"user": {
"id": user.dataValues.id,
"email": user.dataValues.email,
"createdAt": user.dataValues.createdAt,
"updatedAt": user.dataValues.updatedAt
},
"token": "xxxxx"
}
JSON.stringify(response)
Try below code in your model to override the sequelize toJSON function
const User = db.define('users', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
primaryKey: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: 'email'
},
password: {
type: DataTypes.STRING,
},
},
{
hooks: {
beforeCount(options) {
options.raw = false;
}
}
}
);
User.prototype.toJSON = function () {
const values = Object.assign({}, this.get());
delete values.password;
return values;
};
or using omit() withlodash for cleaner code
User.prototype.toJSON = function () {
const values = {
..._.omit(this.get(), ['password'])
};
return values;
};

GraphQL error update mutation "Resolve function for \"User.id\" returned undefined"

I am a newbie to GraphQL and trying to write an update mutation. However, I am receiving Resolve function for \"User.id\" returned undefined" error although the database is actually got updated.
What am I doing wrong?
userSchema.js:
import Sequelize from 'sequelize';
import SqlHelper from '../helpers/sqlhelper';
const config = require('../../config');
const sequelizer = new SqlHelper(config).Init();
const createUser = sequelizer.define(
'createUser',
{
...
}
);
const updateUser = sequelizer.define(
'updateUser',
{
id: {
type: Sequelize.UUID,
field: 'Id',
primaryKey: true,
defaultValue: Sequelize.UUIDV4,
},
username: {
type: Sequelize.STRING,
field: 'Username',
allowNull: true,
},
email: {
type: Sequelize.STRING,
field: 'Email',
allowNull: true,
},
firstname: {
type: Sequelize.STRING,
field: 'FirstName',
allowNull: true,
},
lastname: {
type: Sequelize.STRING,
field: 'LastName',
allowNull: true,
},
....
},
{
// define the table's name
tableName: 'Users',
},
);
module.exports = User;
UserResolver.js:
import User from '../dbschemas/user';
import Sequelize from 'sequelize';
const Op = Sequelize.Op;
export default {
Mutation: {
createUser: async (obj, args) =>
(await User.create(args)),
updateUser: async (obj, args) =>
(await User.update(args,
{
where: {
id: args.id,
},
returning: true
}))
}
};
Although calling updateUser from GraphiQL updates the records (in db), it results in a "Resolve function for \"User.id\" returned undefined" error:
mutation{
updateUser(id: "2ecd38ca-cf12-4e79-ac93-e922f24af9e3",
username: "newUserTesting",
email: "testemail#yahoo.com",
lastname: "TestUserLName",
firstname: "fname1") {
id
}
}
{
"data": null,
"errors": [
{
"message": "Resolve function for \"User.id\" returned undefined",
"locations": [
{
"line": 16,
"column": 4
}
],
"path": [
"updateUser",
"id"
]
}
]
}
The issue is clear, your resolver does not return an object containing id.
The docs say that Model.update returns an array in which the 2nd element is the affected row.
Hence, your resolver should look like:
async updateUser(obj, args) {
const resultArray = await User.update( ... )
return resultArray[1]
}
... To be replaced by whatever you need.
So apparently, update does NOT return affected rows for MSSQL, only the number of records affected.
This is true only for postgres when returning: true:
public static update(values: Object, options: Object): Promise<Array<affectedCount, affectedRows>>
Setting returning: true (for MSSQL) returns undefined (and order of params in the array is not even in the right order... i.e. first affectedRows -> undefined, then affectedCount ->num of affected rows.)
Tho get an object back you would need to do something like this:
Mutation: {
createUser: async (obj, args) =>
(await User.create(args.user)),
updateUser: async (obj, args, context, info) =>{
let user = args.user;
let response = await User.update(user,
{
where: {
[Op.or]: [{ email: user.email }, { id: user.id }, { username: user.username }, { lastname: user.lastname}]
},
//returning: true //not working... only for postgres db :(
}).then(ret => {
console.log('ret', ret);
return ret[0];
}).catch(error => {
console.log('error', error)
});
if (response > 0) return user; //return record
//return response > 0; //return true
}
}

Sails/Waterline: Return model relation

So, I'm creating a small application using SailsJS. My database is MySQL.
When I'm testing, first I create a "market" record, then a "stock" record linked to "market" and in another moment, I retrieve this stock record.
I have the following models:
Stock model:
module.exports = {
attributes: {
intern_id: {
type: 'string'
},
tick: {
type: 'string'
},
market: {
model: 'market'
}
}
};
Market model:
module.exports = {
attributes: {
tick: {
type: 'string'
},
name: {
type: 'string'
},
description: {
type: 'string'
},
stocks: {
collection: 'stock',
via: 'market'
},
}
}
Then, first I create a "market" and use the object returned to associate with my "stock" object:
Create and get my market record:
Market.create({tick: 'BVMF', name: 'Bovespa', description: 'Bolsa de Valores'}).exec(function(err, market) {
if(err) done(err);
});
var market = Market.findOne({tick: 'BVMF'}).then(function(results){return results;});
Create my stock record:
Stock.create({intern_id: '1234', tick: 'VALE5', description: 'Vale SA', market: market}).exec(function(err, stock) {
if(err) done(err);
});
And then, when I try to get back this stock, the market object is not retrieved, even if I call populate('market'):
Stock.findOne({tick: 'VALE5'}).populate('market').exec(function(err, record) {
console.log(record);
});
Not too much information, but I guess.
Instead:
Stock.findOne({tick: 'VALE5'}).populate('market').exec(function(err, record) {
console.log(marketObj);
});
Should be:
Stock.findOne({tick: 'VALE5'}).populate('market').exec(function(err, record) {
console.log(record);
});
Second answer:
You forgot about asynchronous. You should do this like that:
Market.create({tick: 'BVMF', name: 'Bovespa', description: 'Bolsa de Valores'}).exec(function(err, market) {
if(err){
done(err);
} else {
Stock.create({intern_id: '1234', tick: 'VALE5', description: 'Vale SA', market: market.id}).exec(function(err, stock) {
if(err){
done(err);
} else {
Stock.findOne({tick: stock.tick}).populate('market').exec(function(err, record) {
console.log(record); // and there is your "Stock" with populated "market"
});
}
});
}
});