Sailjs-Waterline: Serialize, Parse and initialize an Model - json

Let's assume that I found user using next command:
User.findOne(id).exec(function(err, user){
redis.set(JSON.stringify(user), id);
})
After that I'm loading from redis my object
redis.get(id, function(err, reply){
if(!err && reply) {
var user = JSON.parse(reply);
// here methods like user.save() or any of defined manually by developer is unavailable
//
} else {
..
}
})
User model example:
module.exports = {
attributes : {
// Simple attribute:
// name: 'STRING',
// Or for more flexibility:
// phoneNumber: {
// type: 'STRING',
// defaultValue: '555-555-5555'
// }
email : {
type: 'string',
unique: true
},
// ...
verifyPass: function(pass, callback) {
var obj = this.toObject();
if (callback) {
return Password.compare(pass, obj.local.password, callback);
}
return Password.compareSync(pass, obj.local.password);
},
// retrieve profile for sending to front end
getProfile: function() {
var obj = this.toObject();
var profile = {};
profile.id = obj.id;
// ...
return profile;
},
I need all of that methods to be work whenever I parse waterline model from json. Is there a way to initialize it without triggering db at all. Also would be nice if I could to call user.save().

There's currently no documented public API for this unfortunately, but you can use,
var user = new PersonCollection._model(values, {showJoins: true});
See how that works for you!

Related

Angular 6 AWS Cognito How to Handle newPasswordRequired

I am completely at a loss here. I have been struggling with this for several hours now trying multiple different approaches and none are getting me anywhere. My problem is I cannot seem to figure out how it the new Password is meant to be retrieved from the user within the newPasswordRequired callback after an authentication request to Cognito. Here is my code in its current state. Please don't hesitate to tell me what I can do better, as I am fairly new to Angular and completely new to using Cognito authentication.
public login(email: string, password: string): Observable<UserModel> {
const cognitoUser = new CognitoUser(this.getUserData(email));
cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');
const authenticationDetails = new AuthenticationDetails(CognitoUtils.getAuthDetails(email, password));
const self = this;
return Observable.create((obs: Observer<UserModel>) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: result => {
this.session = result;
const token = result.getIdToken();
const accessToken = result.getAccessToken();
this.localStorage.setToken(token);
this.localStorage.setAccessToken(accessToken);
obs.complete();
},
onFailure: err => {
obs.error(err);
},
newPasswordRequired: (userAttributes, requiredAttributes) => {
let dialogRef: MatDialogRef<NewPasswordComponent>;
const config = new MatDialogConfig();;
config.role = 'dialog';
config.width = '40%';
config.data = { newPass: self.newPass };
dialogRef = self.dialog.open(NewPasswordComponent, config);
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, userAttributes, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
});
}
});
});
}
Based on what you have provided, it looks like the issue is when you respond with completeNewPasswordChallenge you're passing in userAttributes which is returned from the newPasswordRequired callback and won't work.
Instead, you need to see what attributes are required (i.e. requiredAttributes) and pass them in as an object. For example, if "name" is the required attribute, then pass in the following way:
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, {"name":"John Doe"}, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
Hope this helps!

Updating sub array in JSON with a REST API in Mean Stack

I'm developing a MEAN stack application and I'm hung up on how to actually update a document that has been saved into the MongoDB already. I've seen that I have to use patch instead of post in my REST API paths, but it's still a little clouded to me. I want to insert a new Package into the Package JSON Array in the User JSON.
Possible Duplicate, but he's overriding a value in the array and not adding a new object into it.
My JSON Schema:
//User schema
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
require: true
},
username:{
type:String,
required: true
},
password:{
type:String,
required: true
},
packages: [{
from: String,
to: String,
tracking: String
}]
});
My REST API Paths
//Update
router.patch('/update', (req, res) => {
const username = req.body.username;
const packages = req.body.packages;
User.getUserByUsername(username, (err, user) => {
if(!user){
return res.json({success: false, msg: 'User not found'});
} else {
User.addPackages(user, req.body.packages, (err, user) => {
if(err){
res.json({success: false, msg:'Failed to update packages'});
} else {
res.json({success: true, msg:'update packages'});
}
})
}
});
});
My Module's:
module.exports.addPackages = function(user, packages, callback){
User.findOneAndUpdate(
{username:user.username},
{$push: {"packages" : {
"to" : packages.to,
"from" : packages.from,
"tracking" : packages.tracking
}}},
{new:true},
function(err, newPackage){
if (err) throw err;
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback){
const query = {username: username}
User.findOne(query, callback);
}
They're updating into my MongoDB, but just the object ID and not the values...
db.your_collection.update({},
{$set : {"new_field":1}},
{upsert:false,
multi:true})

Sequelize findAll is not a function

I'm making a project with Sequelize and I'm stucked in this step. The problem is that when I try to log in and the passport-local code is executed, when it reaches the User.findAll(...) it throws that findAll is not a function.
If I make console.log(User) it shows [function].
My structure:
/config/config.js
/config/passport.js
/models/index.js
/models/nuke_users.js (generated by sequelize-auto)
/index.js
config.js:
//Setting up the config
var Sequelize = require('sequelize');
var sequelize = new Sequelize('rocarenav2', 'root', '123456', {
host: "localhost",
port: 3306,
dialect: 'mysql'
});
module.exports = sequelize;
passport.js:
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var User = require('../models/nuke_users');
var crypto = require('crypto');
function hashPasswordForNuke(password) {
return md5password = crypto.createHash('md5').update(password).digest('hex');
}
// expose this function to our app using module.exports
module.exports = function(passport) {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, {})
.then(function (user) {
done(err, user);
})
.catch(function (error){
done(error);
});
});
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
User.findAll({
where: {
'user_email': email
}
}).then(function (user) {
if(!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if ((user.user_password).localeCompare(hashPasswordForNuke(password)) === -1)
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
return done(null, user);
})
.catch(function (error){
done(error);
});
}));
};
models/index.js
'use strict';
var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var basename = path.basename(module.filename);
var config = require(__dirname + '/../config/config');
var db = {};
//Create a Sequelize connection to the database using the URL in config/config.js
var sequelize = config;
//Load all the models
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(function(file) {
var model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
//Export the db Object
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
/models/nuke_users.js
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('nuke_users', {
user_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "",
references: {
model: 'reps_table',
key: 'PostName'
}
},
user_email: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: ""
},
user_avatar: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: ""
},
user_password: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: ""
}
}, {
tableName: 'nuke_users'
});
};
/index.js
...
var models = require('./models/');
...
So, what am I doing wrong?
The nuke_users module is exporting a function that, when called, returns the Model. Because you aren't calling this function, it is not returning the Model, and thus the function you are looking for does not exist.
To call this exported function you would need to pass in the sequelize instance and DataTypes, as so:
var User = require('../models/nuke_users')(sequelize, DataTypes);
In your case you are using a loader in the index.js file, and it is exporting the db object which contains the models keyed by their name.
var models = require('../models'); // loads index.js
var User = models.nuke_user; // the model keyed by its name
User.findOne(...); // search the model
Instead of returning the model, export it from NukeUser.js:
const NukeUser = sequelize.define('nuke_users', {
// ...
});
module.exports = NukeUser;
Then in index.js:
const NukeUser = require('../models/NukeUser');
NukeUser.findAll() //.then() ...
You need to check the models route in the collections, for example, in my case, I had read() as a method in my collections like that:
async read(id) {
try {
if(id) {
return await this.model.findOne({where: {id: id}});
} else {
return await this.model.findAll();
}
} catch (e) {
console.error(`Error in reading data with the id: ${id}`);
}
}
So, findAll() wouldn't work. Instead, I used read()

F5 doesn't work RIGHT on my webapp

i have a strange problem:
we have to make a project in IT, therefore we use AngularJS.
The problem is, when I use F5 for refresh, the page loads, but it is not able to show the right content again. There's just background and my title, everything in my UI-VIEW isn't there.
We have Login-Page with redirecting:
app.run(function($rootScope, $location, sessionFactory) {
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
let loggedIn = sessionFactory.isAuthenticated();
if (toState.authenticate && !loggedIn) {
event.preventDefault();
$state.go('login');
}
});
});
without this redirecting block, F5 works.
Otherwise I have to go to Login-Page and login again to get it work again.
I'm sorry guys, I'm new at programming. So if there's any code you need, just let me know. Hope someone can help.
UPDATE: this is our authentification_service.js
a group member wrote it. Does it help?
var app = angular.module('TheApp');
/**
* This service manages sessions: Retrieving token from server and storing token data.
*/
app.factory('sessionFactory', ['$http', 'jwtDecoder', function($http, jwtDecoder) {
var sessionFactory = {
sessionData: null
};
sessionFactory.login = function(username, password) {
//todo: encrypt password....
var user = { username : username, password : password};
// returns a POST request, success or error of that request
// must be handled by the caller of the login function
return $http({
method: 'POST',
url: 'rest/auth/login',
data: user
});
};
sessionFactory.logout = function() {
return $http({
method: 'POST',
url: 'rest/auth/logout',
data: this.sessionData
});
}
sessionFactory.deleteSessionData = function() {
this.sessionData = null;
}
sessionFactory.setSessionData = function(sessionData) {
this.sessionData = {
token: sessionData.token,
userData: jwtDecoder.getUserData(sessionData.token) // holds user id, todo: permissions
};
console.log(this.sessionData.userData);
};
sessionFactory.getToken = function() {
return this.sessionData.token;
};
sessionFactory.getUserId = function() {
return this.sessionData.userData.userId;
}
sessionFactory.isAuthenticated = function() {
return this.sessionData != null;
};
return sessionFactory;
}]);
/**
* This service injects authentification token in HTTP requests.
*/
app.factory('tokenInjector', ['$injector', function($injector) {
var tokenInjector = {
request: function(config) {
// cannot use sessionFactory directly, because else Angular will spot a circular dependency -> error
var sessionFactory = $injector.get('sessionFactory');
// if the user is logged in, add an Authorization header (with token) to each http request
if(sessionFactory.isAuthenticated()) {
config.headers.Authorization = 'Bearer ' + sessionFactory.getToken();
//config.url = config.url + "?token=" + sessionFactory.getToken(); // add token to request url
}
return config;
}
};
return tokenInjector;
}]);
/**
* Decodes JWTs received from the server with a JWT deconding lib
* and returns a useable result.
*/
app.factory('jwtDecoder', function() {
var jwtDecoder = {};
// extracts all needed frontend user data from token
jwtDecoder.getUserData = function(token) {
var payload = jwt_decode(token);
var userData = {
userId: payload.userId //,
//groupId: ?,
//permissions: payload.permissions, // is array of perm
};
return userData;
}
return jwtDecoder;
})

How do you mock MySQL (with node-orm2) in Node.js/Express?

I am using node.js/express with https://github.com/dresende/node-orm2 to use my MySQL database.
I am new to the node.js world and I am quite stuck so far, I don't know how to unit test (not integration test) a simple function.
Here is my server.js, loading my user model (ORM)
var express = require('express'),
orm = require('orm'),
config = require('./config/config.js'),
auth = require('./services/authentication'),
helper = require('./middlewares/helper.js'),
friends = require('./modules/friends.js');
var app = express();
app.use(orm.express('mysql://' + config.mysql.username + ':' + config.mysql.pwd + '#' + config.mysql.host + ':' + config.mysql.port + '/' + config.mysql.db, {
define: function(db, models, next) {
db.load("./models/models.js", function(err) { // loaded!
models.user = db.models.user;
});
}
}));
var middlewares = [auth.authenticate, helper.retrieveUser];
app.get('/friends', middlewares, friends.findActiveFriends);
app.listen(3000);
console.log('Listening on port 3000...');
here is the user model :
module.exports = function (db, cb) {
var User = db.define('user', {
uid : { type: 'number', rational: false, unique: true, required: true },
first_name : { type: 'text', size: 100, required: true },
last_name : { type: 'text', size: 100, required: true },
picture : { type: 'text', size: 255, required: false },
email : { type: 'text', size: 255, required: true },
creation_date : { type: 'date', time: true },
modification_date : { type: 'date', time: true }
}, {
methods: {
fullName: function () {
return this.first_name + ' ' + this.last_name;
}
},
hooks: {
beforeCreate: function (next) {
if (this.creation_date == undefined) {
this.creation_date = new Date();
}
if (this.modification_date == undefined) {
this.modification_date = new Date();
}
return next();
}
}
});
// CUSTOM FUNCTIONS
User.getByUid = function(uid, callback) {
this.find({ uid: uid }, function(err, users) {
if(err) callback(err);
if (users.length == 1) {
callback(null, users[0]);
} else {
callback('No user found with uid=' + uid);
}
});
};
User.hasMany("friends", User, {
status: { type: 'enum', values: ['pending', 'refused', 'active'] }
}, {
reverse: 'friendsrev', mergeId: 'user_id', mergeAssocId: 'friend_id'
});
return cb();
};
and here is my methods to find active friends in friends.js:
var _findActiveFriends = function(req, res) {
req.currentUser.getFriends({
status: 'active'
}, function(err, friends) {
if (err) throw err;
res.send(JSON.stringify(friends));
});
};
I would like to know how can I write a simple test (with mocha and sinon.js ?) by mocking the database connection and the request also. I need to mock the value of req.currentUser which is a user returned by the ORM in a middleware.
I just want to run unit tests and do not use a real DB or make some HTTP calls.
thanks for your help.
If you want to mock the req using sinon.js, you can do something like the following.
var sinon = require('sinon');
var friend = require('./friend');
it('some test', function(done) {
var req = {
currentUser: {
// Add all the properties/functions that you are concerned with here.
// and you can/should wrap them around sinon.spy().
// If you are not concerned with that function (i.e. you are not using it)
// then you can simply use sinon.stub() to return a stub function.
}
};
var res = {
send: sinon.spy(function(obj) {
assert.ok(obj); // yes object exists
done(); // end of test
};
};
var next = function() {};
friend.findActiveFriend(req, res, next);
});
This way you shouldn't be connecting to the model, which tests friend.js only.
Also, since I just noticed you are using orm.express, you may also want to simply mock req.models with the stubbed function you desire as above.