Create temp sails model - sails.js

For unit test I need to create temp model object without saving it in DB, just to have some instance method.
Example of model structure
//User.js - my model
module.exports = {
attributes: {
firstName : { type: 'string' },
lastName : { type: 'string' },
getName: function () {
return this.firstName + ':' + this.lastName;
}
}
};
I have next object
const testData = {
firstName: 'Foo',
lastName: 'Bar'
};
This line creates new record in db
User.create(testData);
Is it possible to avoid record creation? How can I do it?

To answer your initial question, functionality to switch off or on whether Sails actually creates a record in your database when you call Model.create() to the best of my knowledge does not exist, without a workaround as you have or I have described in the comments.
With that said, Sails does offer a simple solution for testing and development purposes.
As a convenience during development, Sails provides a built-in database adapter called sails-disk. This adapter simulates a real database by reading and writing database records to a JSON file on your computer's hard drive.
If you are using a Sails version of < v1 see documentation:
Add a connection to the database in config/connection.js
localDiskDb: {
adapter: 'sails-disk'
},
In the model you are testing, point the model towards the sails-disk connection.
...
module.exports = {
connection: "localDiskDb",
attributes: {
...
If you are using Sails version >= v1 see documentation:
Instead, add sails-disk as a datastore in config/datastores.js
localDiskDb: {
adapter: 'sails-disk'
},
And in the model you are testing, as above, point the model towards the sails-disk datastore.
...
datastore: 'localDiskDb',
...

Related

Sailsjs. Best way to create (and manage) indexes on sails-mongo (mongodb)

I was using sailsjs 0.12. It supported index attributes on models, also
i was using npm package Sails-hooks-mongoat to create inverse indexes and so.
It wasn't ideal, but it worked. Right now they dropped the index attribute and mongoat is currently unsafe and pending updates to work on Sails.js 1.0.
I would like to know the best approach to:
Create Indexes on new deployments.
Migrate (ensure?) indexes on deployment updates.
Since you are not allowed to run 'migrate: alter' in production (even if you try), one option is to create those index in bootstrap file ('config/bootstrap.js').
Imagine you have an User model like this:
var User = {
attributes: {
email : { type: 'string', unique: true, required: true },
username : { type: 'string', unique: true, required: true },
pin: { type: 'string'}
}
};
module.exports = User;
Then you can manually create the missing indexes like this in bootstrap file:
module.exports.bootstrap = async function(done) {
console.log("Loading bootstrap...")
if (process.env.NODE_ENV === 'test') {
}
if (process.env.NODE_ENV === 'production') {
console.log("CREATING DATABASE INDEX BY HAND")
// USER MODEL
var db = User.getDatastore().manager;
await db.collection(User.tableName).createIndex( { email: 1 }, {unique: true} );
await db.collection(User.tableName).createIndex( { username: 1 }, {unique: true} );
// PANDA MODEL
// db = Panda.getDatastore().manager;
// await db.collection(Panda.tableName).createIndex( { name: 1 }, {unique: true} );
}
// await initializeDatabase() // custom DB initialization...
return done();
};
The index are created only once, subsequent runs will not recreate those indexes.
The ensureIndex was an alias to createIndex function and it has been deprecated.
References:
Waterline manager reference
MongoDB create index reference
In development mode you can specify custom indexes in the model within Sails, or it will remove them when lifting the server and performing migration.
My (preferred) alternative approach is to manage all indexes on my own within the DB. In order to do so, you have to change the "migrate" attribute from "alter" to "safe" in models.js config file.
Note that in production mode "migrate" attribute is always set to "safe".

GraphQL check for unique record on mutation

On a GraphQL mutation, how can I check in the resolve function if the record I´m trying to create is unique ?
Consider the code:
const createUser = {
type: UserType,
description: 'Creates an user',
args: {
data: {
name: 'user',
type: new GraphQLNonNull(UserInputType)
}
},
resolve(root, args) {
let data = args.data;
UserModel.findByName(data.name);
??? How can I return an error here if there is a user with
??? the given name already stored in database ?
const model = new UserModel(data);
model.save();
}
}
Should I keep that logic on the GraphQL Server or should I leave it to the database layer. If I leave it to the database layer, how can I return the database error to my client, so that he knows he´s trying to create an invalid user ?
I´m the following stack: MongoDB, express, Mongoose, graphql-js on server, ReactJS and Relay on client.

Sailsjs - Prevent non-model fileds to be saved in mongo document

I recently started working with Sails and mongo.
I use Sails blueprints to generate part of my api.
The problem is, that the request body I send is being saved to the mongo collection, regardless of the fields defined in the model.
So for example, let's say I have the following Event model:
module.exports = {
attributes: {
title: {
type: 'string',
required: true
},
}
}
When I Send a POST request to the /event/ endpoint with the following params:
{"title":"Some Event", "random":"string"}
The saved mongo document contains also the "random":"string" value, even though it's not part of the model.
I've tried to come up with some common method to remove non-model attributes before creation for all models, but the possible solutions seemed not right and dirty.
Am I missing something?
Any help would be appreciated!
You can use schema option in your model. Just add it to model declaration and that's it.
// api/models/Model.js
module.exports = {
schema: true,
attributes: {
title: {
type: 'string',
required: true
}
}
};

How do I specify a field that can be validated but not stored in Waterline?

I'm looking at the example in the Waterline docs here.
var User = Waterline.Collection.extend({
types: {
// snip
password: function(password) {
return password === this.passwordConfirmation;
});
},
attributes: {
// snip
password: {
type: 'string',
password: true
},
passwordConfirmation: {
type: 'string'
}
}
});
Is there a way to tell Waterline that passwordConfirmation is not part of the schema so that it is not created if migrate is set to alter or drop, or if using a schemaless DB engine?
The actual use case I want is for a clear text password field to validated from the request, but use beforeCreate to populate a hash field that is actually stored (but not allowing the password property to be stored in the process).
Thanks.
Waterline doesn't support transient fields that are validated but not persisted. You can add the schema: true property to your model which will have it filter out any attributes that aren't explicitly declared, but still make them available in lifecycle callbacks. You'd have to do the validation for those attributes manually (in beforeCreate() or beforeValidate() for example), and you'd lose the ability to add arbitrary fields to schemaless dbs, but it's not necessarily a bad solution.
For your case though, I don't see why it's exactly necessary. Why not just hash the password in beforeCreate and save it back to password?
beforeCreate: function (values, cb) {
values.password = hash(values.password);
return cb();
}

Cannot use Collection as Model PhalconPHP

I'm trying to setup MongoDB collection for my PhalconPHP application.
I have the mongo and collectionManager set up.
$di->set('collectionManager', function(){
return new Phalcon\Mvc\Collection\Manager();
}, true);
//MongoDB Database
$di->set('mongo', function() {
$mongo = new MongoClient("localhost:27017");
return $mongo->selectDb("test");
}, true);
I have a model
class User extends \Phalcon\Mvc\Collection {
....
}
Then I got this error
Unexpected value type: expected object implementing Phalcon\Mvc\ModelInterface, object of type MyApp\Models\User given
Disclaimer: This was extracted from the question.
I was using Model validation instead Validation in the model body.