How do I use findOrCreateEach in waterline/sailsjs? - sails.js

I been looking around on the sails site and was lead to the waterline page. I am curious to how I can use the findOrCreateEach method. Specifically, number of arguments, what it will return, and how it will benefit me using it? I been searching, around and going to have to dive into the source code. I figure I ask here while I look.
Method without bluebird promises
Model.findOrCreateEach(/* What Goes Here */).exec(/* What Returns Here */);
With bluebird promises
Model.findOrCreateEach(/* What Goes Here */).then(/* What Returns Here */);

findOrCreateEach is deprecated; that's why it's not in the documentation. The best way to replicate the functionality is by using .findOrCreate() in an asynchronous loop, for example with async.map:
// Example: find or create users with certain names
var names = ["scott", "mike", "cody"];
async.map(names, function(name, cb) {
// If there is a user with the specified name, return it,
// otherwise create one
User.findOrCreate({name: name}, {name: name}).exec(cb);
},
function done(err, users) {
if (err) { <handle error and return> }
<users now contains User instances with the specified names>
});

Related

Handling Mongoose `populate()` Functionality in Wrapper Function

In our Node/MongoDB project we have a wrapper function we use for all findOne() operations in order to handle aspects like permissions centrally. The problem I'm running into involves Mongoose populate() functionality.
Because we're using a wrapper function on all our findOne()'s, I need to define the populate() logic at the wrapper function level. But because the number of populate()s I'll need varies from function to function, I have to end up writing conditional statements in our wrapper function, like this:
if (mongooseModelObject.modelName === "Staff" && this.parameters.populateArray) {
return await mongooseModelObject
.findOne(searchObject, options)
.setOptions({ authLevel: this.permissionString, permissions: true })
.populate(this.parameters.populateArray[0].targetId, this.parameters.populateArray[0].limit)
.populate(this.parameters.populateArray[1].targetId, this.parameters.populateArray[1].limit)
.populate(this.parameters.populateArray[2].targetId, this.parameters.populateArray[2].limit)
.populate(this.parameters.populateArray[3].targetId, this.parameters.populateArray[3].limit)
.populate(this.parameters.populateArray[4].targetId, this.parameters.populateArray[4].limit)
.populate(this.parameters.populateArray[5].targetId, this.parameters.populateArray[5].limit);
} else {
return await mongooseModelObject.findOne(searchObject, options).setOptions({ authLevel: this.permissionString, permissions: true });
}
What would make this much simpler is if I could pass an array to populate(). That way, whether I need to use populate() on one property or five, it will still work.
Is this something Mongoose allows for? Or do I have to chain populate() one by one like in my included code? if not, I'm open to other suggestions as well.
You could loop on the populateArray
await this.parameters.populateArray.reduce((tmp, {
targetId,
limit,
}) => tmp.populate(targetId, limit), mongooseModelObject
.findOne(searchObject, options)
.setOptions({
authLevel: this.permissionString,
permissions: true,
}));
According to populate documentation, you can't pass an array to it.

Writing nested path parameters in FeathersJS

How to generate multi level path parameters in feathers js like below :
api.com/category/catergoryId/subCatergory/subCatergoryId
The following is taken from this Feathers FAQ entry:
Normally we find that they actually aren't needed and that it is much better to keep your routes as flat as possible. For example something like users/:userId/posts is - although nice to read for humans - actually not as easy to parse and process as the equivalent /posts?userId=<userid> that is already supported by Feathers out of the box. Additionaly, this will also work much better when using Feathers through websocket connections which do not have a concept of routes at all.
However, nested routes for services can still be created by registering an existing service on the nested route and mapping the route parameter to a query parameter like this:
app.use('/posts', postService);
app.use('/users', userService);
// re-export the posts service on the /users/:userId/posts route
app.use('/users/:userId/posts', app.service('posts'));
// A hook that updates `data` with the route parameter
function mapUserIdToData(hook) {
if(hook.data && hook.params.userId) {
hook.data.userId = hook.params.userId;
}
}
// For the new route, map the `:userId` route parameter to the query in a hook
app.service('users/:userId/posts').hooks({
before: {
find(hook) {
hook.params.query.userId = hook.params.userId;
},
create: mapUserIdToData,
update: mapUserIdToData,
patch: mapUserIdToData
}
})
Now going to /users/123/posts will call postService.find({ query: { userId: 123 } }) and return all posts for that user.

Unable to reset model instance attribute in sailsjs

Before we start: I have made a very small git repo with seed data and two REST endpoints to test this issue here: https://github.com/juanpasolano/sails-nested-test
So I have 3 models: Appointment which has many procedure which has one to one procedureItem.
Since there is not nested population in sails I am getting procedures with procedureItem by hand using something like:
Appointment.find(1).exec(function(err, appointments){
if(err) return res.negotiate(err);
async.eachSeries(appointments, function(appointment, cb){
Procedure.find({appointment: appointment.id}).populate('procedureItem').exec(function(errP, procedures){
if(errP) return cb(errP);
appointment.procedures = procedures;
appointment.proceduress = procedures;
cb()
})
}, function(errE){
if(errE) return cb(errE);
res.ok(appointments)
So the issue is when I want to replace the proceduresattribute with the new array procedures (which has the nested depth i need) it just doesn't get set, if you hit the endpoints of the demo, there is no procedures attribute available.
As a test I have attached the same new procedures array to proceduress (double s) attribute and this one gets properly set.
I have tried using the .toObject() method with no luck.
Have tried sails 0.10.5 and 0.11.0 with no luck.
Yes, there is no procedures attribute, as Appointment has MANY Procedure, so there is no field procedures in DB in Appointment table. So if you do not populate like:
Appointment.find(1).populate('procedures').exec(function(err, appointments){
.....
}
There will not be attribute procedures in appointment Object
I have the exactly same problem a while ago, I believe it is because the "procedure" attribute is in use by the model and not be able to set. I end up cloning each row and then the value was able to be set.
To change your code:
if(err) return res.negotiate(err);
var toreturn=_.map(appointments, function(a){
var b=_.clone(a); // so you clone it to a new obj, then you can set the procedure...
b.procedures = 'aaaa'
return b;
})
//res.json(toreturn); // Here you will get appointments with procedures='aaa'
async.eachSeries(toreturn, function(appointment, cb){
Procedure.find({appointment: appointment.id}).populate('procedureItem').exec(function(errP, procedures){
if(errP) return cb(errP);
appointment.proceduress = procedures;
appointment.procedures = procedures;
cb()
})
}, function(errE){
if(errE) return cb(errE);
res.ok(toreturn)
})
I do not know why this happened exactly, but this is just one solution.
On a side note, may I suggest you instead of doing async for each appoint, will takes a query per appoint, do two big find, and then loop to merge them?
Something like this:
var globalApps=[];
Appointment.find().then(function(apps){
globalApps=apps;
var appIDs=apps.map(function(a){return a.id}); //Get all appointment IDs
return Procedure.find({appointment:appIDs}).populate('procedureItem');
}).then(function(procs){ //This procs is all procedures match the ids
var toReturn =_.map(globalApps,function(a){
var b=_.clone(a);
b.procedure=_.find(procs,{appointment:b.id}); // This is O(n^2) complexity matching, but you can do something smart.
return b;
});
// Now you have toReturn as the appointment array.
return res.json(toReturn);
})
I have spent a lot of time on this problem before, glad someone had the same problem
#sgress454 from Balderdash here. The issue you're encountering is that the procedures attribute isn't a plain Javascript variable; it has a "getter" and a "setter", which are necessary in order to allow these to work:
appointment.procedures.add(<some Procedure obj or id>);
appointment.procedures.remove(<some Procedure id>);
So attempting to set the procedures attribute to a new value won't work, because of the setter. Cloning the appointment is an acceptable workaround (you can just do appointment.toObject()) as it transforms those special attributes into plain old Javascript variables. But in your case, it seems like you don't actually need to replace the procedures array--you just want each procedure's procedureItem to be filled out. And since only attributes representing many-to-many associations have getters and setters, you can accomplish this without any cloning at all:
Appointment
// Find the appointments
.find({...some criteria...})
// Populate each appointment's procedure
.populate('procedure')
.exec(function(err, appointments) {
if (err) {return res.negotiate(err);}
// Loop through the returned appointments
async.each(appointments, function(appointment, appointmentCb) {
// For each one, loop through its procedures
async.each(appointment.procedures, function(procedure, procedureCb) {
// Look up the related procedureItem
ProcedureItem.findOne(procedure.procedureItem).exec(function(err, procedureItem) {
if (err) {return procedureCb(err);}
// Attach the procedureItem object to the procedure
procedure.procedureItem = procedureItem;
return procedureCb();
});
}, appointmentCb);
}, function done(err) {
if (err) {return res.negotiate(err);}
res.ok(appointments);
});
});
Note that there's a lot of optimization that could be done here--you could pluck out all of the ProcedureItem IDs after retrieving appointments, look up all the ProcedureItem instances at once, and then map them onto the procedures afterwards, saving a ton of queries.

Can I divide my tests into separate specs and then call them from another or is it better to use helper functions?

Just got started with Protractor for E2E testing and I am having a bit of trouble with the test case structure.
Not sure if can I divide my tests into separate specs and then call them from another or how can I make nice helper functions to handle this.
I am finding elements by a repeater and then I would like to make tests for each of the operation for each of the element in the repeater. Sort of like this:
describe('tasty', function () {
'use strict';
var ptor;
beforeEach(function () {
ptor = protractor.getInstance();
ptor.get('http://localhost:8000/');
});
it('Should sample three tasty fruits of every kind on my shopping list.', function () {
ptor.findElement(protractor.By.className('fruitstore')).click();
var fruitshelves = ptor.findElements(protractor.By.repeater('fruit in fruits').column('header'));
fruitshelves.then(function(arr) {
for (var i=0;i<arr.length; i++) {
// Pick up three fruits of this kind from the shelf and put in shopping cart
// Should be listed on my shopping list
// Open the wallet
// Should have money
// Pay for the fruits and put it in your shopping bag
// Should be able to complete the transaction
// For each one of the fruits in your shopping bag
// Take a bite
// Should be tasty
}
});
});
});
Based on the #langliman answer, I've managed to achieve the desired behaviour.
Note login.spec.js and Login.page.js should be located in the same folder.
Login.page.js file:
var LoginPage = function (ptor) {
//following PageObject pattern define the functions here.
}
module.exports.getLoginPage = function (ptor) {
return new LoginPage(ptor);
};
login.spec.js file:
(function () {
'use strict';
describe('login page', function () {
var ptor = protractor.getInstance();
var loginPageBuilder = require('./Login.page.js');
var loginPage = loginPageBuilder.getLoginPage(ptor);
it('should login as admin', function () {
loginPage.visit();
loginPage.enterUsername('user');
loginPage.enterPassword('password');
loginPage.login();
});
});
}());
I came to this question looking for a way to have helper functions shared between spec files in Protractor. In case others are looking for the same, turns out since Protractor is just running in Node, all you need to do is var helpers = require('./your-helper-file').
In case you want shared setup and before/after functions as well as helper methods, one solution is to require the tests from your spec helper instead of requiring your spec helper from the tests.
conf.js
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['e2e/spec.js']
}
e2e/spec.js
var chai = require('chai'),
homepage = require('./homepage.js'),
signin = require('./signin.js');
chai.should()
browser.baseUrl = 'http://localhost:3000'
homepage.test()
signin.test()
e2e/homepage.js
exports.test = function() {
describe('homepage', function() {
it('should have the right title', function() {
browser.get('/')
browser.getTitle().then(function(title){
title.should.eq('Home')
})
});
});
}
e2e/signin.js
exports.test = function() {
describe('signin', function() {
it('should have the right title', function() {
browser.get('/signin')
browser.getTitle().then(function(title){
title.should.eq('Sign in')
})
});
});
}
I'm looking at the same thing myself, and to some extent I had hoped that you would have an answer for me on this question. :-)
Having said that, it appears that protractor is new enough that nobody really knows the answer, and I guess that makes my answer as good as the next persons.
Firstly, I'm using the page object notation that is described on the protractor getting started page, towards the bottom: https://github.com/angular/protractor/blob/master/docs/getting-started.md
This gives an element of modularity, my view here is that I end up with a set of classes, one per page, that abstract away some of the detail. So, for example, I might have a "foo" class, which includes in it abstractions like "foo.get" and "foo.validate(id, name, otherData)". This would be a way to pull out repeated code.
The bit that I haven't worked out is how to create a library of modules and then assemble those into a single set of scenarios. I have a few thoughts though:
The underlying problem is the ability to include javascript files in each other - which really doesn't exist as a capability. There are third party libraries, which I'd prefer not to use, and I haven't seen a way to use Angular's module capability to do this.
End 2 end testing can be very dependent on the order of the tests. So one test may create data, another test may then use that data. As an example, if you want a test that logs people on, you may need a test that registers people first. You probably don't want to put registration on the front of every test that you run. As such, you probably need a lot of control over the order of your test scenarios anyway
As such, one option is to just put everything in one really big file. Which goes against everything we all learned in school, but I haven't really come up with a reason that wouldn't work. Then you can write functions and abstractions to your hearts content.
If you follow that to the next stage, another option is to write a series of javascript files with strict naming conventions, then use grunt to concatenate them for you before executing them. So, for example:
A set of files named xxxx.page.scenario.js, which contain the "page object" definitions - basically helper methods for each page
A set of files named xxxx.functions.scenario.js, which contain common components of your scenarios - so maybe you have a register and logon set of actions, and you make that into a library function
A set of files named nnxx.scenarios.scenario.js, which contain the actual scripts themselves. These are numbered at the start (the nn), so we can concatenate them in a reliable sequence and thereby control which order our scripts run
I'm not yet saying this is a good idea, just that it at least superficially looks like it could work, and would give the desired result. My main concern is that it feels fragile - so as the test suite grows in size it would perhaps become very difficult to maintain. Perhaps another way to do this would be, instead of numbering the scenarios, to instead define them as dependencies, and have something that makes sure that any given script runs after any script it declares itself to be dependent on. That would maybe allow for subsetting of the scripts as well - so you could say "run the bar script" and the framework would know that the bar script needs the foo script run first, and maybe the login script. But it's OK to leave all the other scripts out.
EDIT: I see astrolabe as potentially a good answer here, it looks like it explicitly allows you to modularise your tests. https://github.com/stuplum/astrolabe. I've just completed a proof of concept with it, and it seems to do everything I might hope. The code for it ends up something like:
clubs.part.scenario.js:
/**
* Partial for the page objects associated with clubs
*/
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'UI/index.html#clubs' },
title: { get: function() { return this.findElement(this.by.id('title')); } },
description: { get: function() { return this.findElement(this.by.id('description')); } },
clubTableElement: { value: function(rowNum, columnBinding) {
return this.findElement(this.by.repeater('club in clubs').row(rowNum).column(columnBinding)); } }
}
);
clubs.scenario.js:
/**
* End to end tests for the club functionality
*/
var homePage = require('../home/home.part.scenario.js');
var clubsPage = require('./clubs.part.scenario.js');
describe( 'Navigate to club list page', function() {
it ( 'should allow navigation to the club list page', function() {
homePage.go();
expect(homePage.clubsLink.getText()).toEqual('Clubs');
homePage.clubsLink.click();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
it ( 'should allow us to go directly to the club list page', function() {
clubsPage.go();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
});
I'm pretty happy with this structure, it doesn't do everything but it does most things. The sample code I've provided is from the tutorial that I've been working on for a while with angularjs, which I'm updating for e2e testing and Rails 4 at the moment, if you want the context that goes with that: http://technpol.wordpress.com/2013/11/16/5-end-to-end-testing/

How to use variables in MongoDB Map-reduce map function

Given a document
{_id:110000, groupings:{A:'AV',B:'BV',C:'CV',D:'DV'},coin:{old:10,new:12}}
My specs call for the specification of attributes for mapping and aggregation at run time, as the groupings the user is interested in are not known up front, but specified by the user at runtime.
For example, one user would specify [A,B] which will cause mapping emissions of
emit( {A:this.groupings.A,B:this.groupings.B},this.coin )
while another would want to specify [A,C] which will cause mapping emissions of
emit( {A:this.groupings.A,C:this.groupings.C},this.coin )
B/c the mapper and reducer functions execute server side, and don't have access to client variables, I haven't been able to come up with a way to use a variable map key in the mapper function.
If I could reference a list of things to group by from the scope of the execution of the map function, this is all very straightforward. However, b/c the mapping function ends up getting these from a different scope, I don't know how to do this, or if it's even possible.
Before I start trying to dynamically build java script to execute through the driver, does anyone have a better suggestion? Maybe a 'group' function will handle this scenario better?
As pointed out by #Dave Griffith, you can use the scope parameter of the mapReduce function.
I struggled a bit to figure out how to properly pass it to the function because, as pointed out by others, the documentation is not very detailed. Finally, I realised that mapReduce is expecting 3 params:
map function
reduce function
object with one or more of the params defined in the doc
Eventually, I arrived at the following code in Javascript:
// I define a variable external to my map and to my reduce functions
var KEYS = {STATS: "stats"};
function m() {
// I use my global variable inside the map function
emit(KEYS.STATS, 1);
}
function r(key, values) {
// I use a helper function
return sumValues(values);
}
// Helper function in the global scope
function sumValues(values) {
var result = 0;
values.forEach(function(value) {
result += value;
});
return result;
}
db.something.mapReduce(
m,
r,
{
out: {inline: 1},
// I use the scope param to pass in my variables and functions
scope: {
KEYS: KEYS,
sumValues: sumValues // of course, you can pass function objects too
}
}
);
You can pass global, read-only data into map-reduce functions using the "scope" parameter on the map-reduce command. It's not very well documented, I'm afraid.