Sails 1.0 cannot access autogenerated associated entity routes - sails.js

I have an association between Client and Budget as follows:
//Client.js
module.exports = {
primaryKey: 'id',
attributes: {
id: {
type: 'number',
unique: true,
autoIncrement: true
},
name: {
type: 'string'
},
phone: {
type: 'string',
unique: true
},
email: {
type: 'string',
unique: true
},
budgets: {
collection: 'budget',
via: 'client'
},
}
};
// Budget.js
module.exports = {
primaryKey: 'id',
attributes: {
id: {
type: 'number',
unique: true,
autoIncrement: true
},
client: {
model: 'client'
},
budgetItems: {
type: 'json'
}
}
};
So, POST is working for both entities so I can create both of them, but:
GET /budget/1
returns the budget and the id of the associated client.
GET /budget/1/client
returns the client id without populating(as i've seen in documentation it should be populated).
GET /client/1
returns the client attributes and there is not a field related with budgets.
GET /client/1/budgets
returns 404 NOT FOUND
I'm following this and this official documentation
So what I could be missing?
Is just generating one direction associations, and I've compared with official documentation and third party examples and my code looks fine.
Thanks in advance!
Update:
I still looking for troubles and if I run sails with --silly option it shows there is the following route available:
Binding route :: get /client/:parentid/budgets POLICY: localize
Binding route :: get /client/:parentid/budgets POLICY: isauth
Binding route :: get /client/:parentid/budgets BLUEPRINT: populate
but if I try to access returns a 404 Not Found and console shows the following error, thrown by populate.js from Sails core code:
verbo: In populate blueprint action: Specified parent record (1)
does not have a budgets.
Update2:
Debugging with sails console I've seen the association is generated properly. Given Client.findOne({id: 1}).populate('budgets').then((client)=>{console.log(client)}) print the client attributes and the associated Budgets but still return 404 Not Found when: GET /client/1/budgets

I have created quick demo and it seems working fine for me.
For demo I have used sails version 1.2.2 and sails-disk as database and there is some minor difference in Model attributes as below
Client.js
module.exports = {
attributes: {
name: {
type: 'string'
},
phone: {
type: 'string',
unique: true,
required: true
},
email: {
type: 'string',
unique: true,
required: true
},
budgets: {
collection: 'Budget', // <<== B is capital here
via: 'client'
},
},
};
Budget.js
module.exports = {
attributes: {
client: {
model: 'Client' // <<== C is capital here
},
budgetItems: {
type: 'json'
}
},
};
Let me know if this is helpful

thanks to SailsJS team we've found the problem and it was related with a third party package and just had to remove it from my project.
It was sails-hook-deep-orm who's owner has been warned. I hope someone with the same issue will reach this post.
Thanks u all anyway!!
The issue is available there

Related

Mongoose trying to populate using the pre find hook does not work when I have nested schemas

Hello I have the following schema definition in in a Node + Express app,
changeDataSchema is nested inside the roleSchema
There are some commong options that the changeDataSchema and the roleSchema both should have and these options are declared in a javascript object called commonFields and the spread operator is used to spread these options to both the schemas.
// Following are the common options for both the schemas
const commonFields = {
roleName: {
type: String,
trim: true,
validate: { validator: requiredValidator, msg: "Role Name is required" },
},
permissions: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Permission",
},
],
required: [true, "Permissions are required"],
},
};
// CHANGE DATA SCHEMA
const changeDataSchema = new Schema({
...commonFields, // <----- Spread operator is used to spread the common options
action: {
type: String,
trim: true,
required: [true, "Action is required"],
enum: {
values: [CREATE, EDIT, DELETE],
message: "Action should either be Create, Edit or Delete",
},
},
});
// ROLE SCHEMA
const roleSchema = new Schema(
{
...commonFields, // <----- Spread operator is used to spread the common options
changeData: changeDataSchema,
lockedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
default: null,
},
},
{ timestamps: true }
);
As you can see both the schemas do a child referencing to the Permission documents. (Declared in the common fields)
So I want both the schemas to be populated with permissions when doing a pre find.
To achieve this I have written the following code,
// Following is to populate the roles with permissions
roleSchema.pre(/^find/, function (next) {
this.populate({
path: "permissions", <--------- This works
select: "-__v",
});
next();
});
// Following is to populate the changeData documents with permissions
changeDataSchema.pre(/^find/, function (next) {
this.populate({
path: "permissions", <-------- This does NOT work
select: "-__v",
});
next();
});
From the above two pre hooks, only the one I have written for the roleSchema works when querying a role. Even though I have written a pre hook for populating the changeData it does not work when finding a role.
How can I get the changeData also populated with permissions when finding a role?
Thank you.

Populating multiple fields with a pre "find" middleware

I am trying to specify the fields that should always be populated for the user document in a pre "find" middleware, like this:
userSchema.pre(/^find/, function (next) {
this.populate("followers following"); next();
});
Here is the user schema:
const userSchema = new mongoose.Schema<IUser>(
{
firstName: {
type: String,
required: [true, "You must provide your first name."],
},
lastName: {
type: String,
required: [true, "You must provide your last name."],
},
profilePic: {
type: String,
},
email: {
type: String,
required: [true, "You must provide an email."],
unique: true,
},
password: {
type: String,
required: [true, "You must provide a password."],
},
isVerified: {
type: Boolean,
default: false,
},
verificationToken: {
type: String,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
followers: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
following: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
},
{
timestamps: true,
}
);
But when I send the request it's just stuck not sending any response.
It works just fine when I only populate one field, either 'followers' or 'following', but together it won't work.
I tried a bunch of different ways, but nothing seems to work.
If anyone can help I would be very thankful!
#1 Have you tried to seperate them ?
userSchema.pre('find', function (next) {
this.populate("followers").populate("following");
next();
});
#2 Or with an array of paths
userSchema.pre('find', function (next) {
this.populate(['followers', 'following']);
next();
});
As documentation states here and here:
The Document#populate() method does not support chaining. You need to
call populate() multiple times, or with an array of paths, to populate
multiple paths.
#3 Or to use deep-populate if you're populating across multiple levels as documented here:
I've got the working solution.
The issue is that if the populate() method in the middleware has more than one field, it calls the middleware for each field and it triggers infinite recursion, which makes the request hanging.
The workaround is a bit weird, but pretty straight forward. We have to add the _recursed option so the middleware knows to avoid populating recursively.
userSchema.pre(/^find/, function (next) {
if (this.options._recursed) {
return next();
}
this.populate({ path: "followers following", options: { _recursed: true } });
next();
});

How to give iDs to dynamic fields in React-Redux?

I created a simple dynamic fields in React-Redux with a plus button to add as many field as I want (hobbies) of an already existing form. I'm using mongodb as a database and so I have this error that tells me that my fields/data don't have iDs.
so how can I generate iDs for my data?
this below is my model with featherJs. as you can see this is how I added my hobbies array in the existing model called myService. I can see that my hobbies are created in mongo (using Robo 3T) which is great but i'm having difficulty reusing them (hobbies) in an other component in Redux. I'm not sure if I should give IDs to this fields or create a new service just for them. I never coded something in backend so I'm confused. what's the rule for this kind of situations.
Any other suggestions would be helpful.
warning in Redux: Each child in a list should have a unique "key" prop.
error in api : Cast to ObjectId failed for value at path "_id" for model "
const { Schema } = mongooseClient;
const myService = new Schema({
type: { type: String, enum: VALID_TYPES, required: true },
user: {
type: mongooseClient.Schema.Types.ObjectId,
ref: 'user',
required: true
},
comment: String,
hobbies: [{
type: mongooseClient.Schema.Types.ObjectId,
ref: 'hobbies',
default: [],
required: false }],
date: {
begin: { type: Date, default: Date.now },
current: { type: Date, default: Date.now },
end: { type: Date, required: true },
},
}, {
timestamps: true
});
return mongooseClient.model('myService', myService);
};

Sails v1.0: error while using custom primary key with mongo

I'm giving a try to the beta version of SailsJS (v1.0.0-32) and I'm having some issues while configuring a custom id. Bellow you'll find my current configuration:
The modelExample.js
module.exports = {
attributes: {
id:{
type: 'string',
columnName: '_id'
},
attr: {
type: 'number'
}
}
}
The model config config/models.js
attributes: {
createdAt: { type: 'number', autoCreatedAt: true, },
updatedAt: { type: 'number', autoUpdatedAt: true, },
id: { type: 'string', columnName: '_id' },
}
The element trying to be inserted:
{id:"600000", attr:40}
The error I get when trying to create a record with an attribute "id" included in the element trying to be created:
AdapterError: Unexpected error from database adapter: Invalid primary key value provided for `id`. Cannot interpret `600000` as a Mongo id.
(Usually, this is the result of a bug in application logic.)
Seems that mongo does not like the string 600000 as an id, but I'm not sure if maybe I'm misunderstanding something related to ids in mongo. In the old version of sails, I never had this issue since the id override was straightforward.
For more information, the sails-mongo adapter version is: "sails-mongo": "^1.0.0-5"
In order to use non-ObjectID primary keys with sails-mongo in Sails 1.0, you have to set dontUseObjectIds: true in your model, for example:
// api/models/User.js
module.exports = {
dontUseObjectIds: true,
attributes: {
id: { type: 'number', columnName: '_id' }, // <-- still need to set `columnName`!
name: { type: 'string' },
...etc...
}
}
This is implemented as of sails-mongo v1.0.0-7.

Sails Create multiple instance of a module in Sails

I'm trying to create 2 user in parallel and wait for it to be created after do something else. I was trying to use:
Promise.all([User.create(usr1), User.create(usr2), User.create(usr3)]).then(function(){
console.log("\o/");
}
without luck, because User.create() do not return a Promise.
I also tried:
User.create([usr1, usr2]).then(function(){
console.log('x')
})
Error that I get:
[Error (E_VALIDATION) 1 attribute is invalid] Invalid attributes sent to undefined:
• user
• A record with that user already exists (null).
Ugly code that is working:
User.create([usr1]).then(function(){
User.create[usr2].then(function(){ console.log('x') });
});
Also work with .exec()
User.create([usr1, usr2]).exec(function(){
console.log('x')
})
How can I use Promise or just waterline to create two user?
EDIT:
1) Calling it in mocha test
Code here
attributes: {
name: {
type: 'string',
required: true
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
minLength: 6,
},
//Association One-to-One, but using 'collection' to mantain sync updating
employer: {
collection: 'employer',
via: 'user'
},
employee: {
collection: 'employee',
via: 'user'
},
//Google Signin ID
googleId: 'string',
//Access token from the Google Authorization Server
googleAccessToken: 'string',
resetPasswordToken: String,
resetPasswordExpires: Date,
toJSON: function() {
var obj = this.toObject();
delete obj.password;
delete obj.resetPasswordToken;
delete obj.resetPasswordExpires;
return obj;
}
},
EDIT2:
This error only happens when I use sails-disk, in sails-mongo work