sails js many to many record insertion error - node.js

I have two models: Users and InstalledApps. Models are like this:
//Users Model
attributes:{
name: 'string',
age: 'string',
installedApps:{
collection: 'installedApps',
via: 'users'
}
}
My InstalledApps model is like this:
attributes:{
deviceID : 'string',
users:{
collection:'users',
via:'installedApps'
}
}
Now I have already created the user with id 1 and 2.
But when I insert data in InstalledApps via postman like this:
{
"users": [1,2],
"deviceID": "123456",
}
It pops an error: Unknown rule: default. I don't know where I am wrong?

Your models are fine. Have you made sure you're posting the data as JSON? In the Body tab in Postman, select raw and then change from Text to JSON (application/json). Remove the surplus comma after "123456" and your request should go through without problems. Also, make sure you're doing a POST request to the /installedApps route, e.g. http://localhost:1337/installedApps.
I tested this with the model definitions you posted in a fresh Sails.js app, getting installedApps entries inserted correctly with associations to users entries.

I think you made your models wrong.
You should use http://sailsjs.com/documentation/concepts/models-and-orm/associations/through-associations .
Then you should have 3 models. USER, APP, INSTALLED_APPS.
Your user model will get:
//Users Model
attributes:{
name: 'string',
age: 'string',
installedApps:{
collection: 'APP',
via: 'user',
through: 'installedApps'
}
}
and your APP model:
//APP Model
attributes:{
device: 'string',
installedApps:{
collection: 'APP',
via: 'app',
through: 'installedApps'
}
}
and finally your INSTALLED_APPS:
//INSTALLED_APPS Model
attributes:{
user: {
model: 'user'
},
app: {
model: 'app'
}
}
Then you will just use simple create of INSTALLED APP for every user/app or blueprint http://sailsjs.com/documentation/reference/blueprint-api/add-to
Hope it helps.

Related

Updating aliases with sequalize db

In my data model, I have an instance: db.items , which has relation "hasMany" with db.images, db.services. So one item can have many images and many services.
I would like to update my instance calling the following:
let res = await db.items
.upsert(
{
id: req.body.id,
type: req.body.type,
name: req.body.name,
images: req.body.images,
services: req.body.services
},
{
include: [
{
model: db.images,
as: "images"
},
{
model: db.services,
as: "services"
}
]
}
);
The problem is that all the aliases are not updated, but the name and type of the item updated. What could be the problem? Include perfectly works with db.create() call, but with upsert - maybe there is a different way of doing things?

When sails default model attributes get their values?

When sails fill default global attributes which we added on config/models.js ,
default settings looks like :
attributes: {
id: { type: 'number', autoIncrement: true },
createdAt: { type: 'number', autoCreatedAt: true },
updatedAt: { type: 'number', autoUpdatedAt: true },
}
Now if we add sth like creatorId to this default attributes , how we should fill it once for all our models ?
attributes: {
id: { type: 'number', autoIncrement: true },
createdAt: { type: 'number', autoCreatedAt: true },
updatedAt: { type: 'number', autoUpdatedAt: true },
creatorId: { type: 'number'}
}
After this change , all models have creatorId with 0 value , how I can set userId to all of my models creatorId before save without repeating my self?
In the controller you are creating the entry in the database this should be quite straight forward. Let's assume that you have two models, User, which comes with Sails built-in authentication, and a Thing, something that someone can own.
In the Thing model, I'd change the ownerId to be owner and associate it with the User model like so:
attributes: {
id: { ... },
createdAt: { ... },
updatedAt: { ... },
owner: {
model: 'User',
required: yes // Enable this when all the stuff in the db has this set
},
}
This creates an association or one-to-many relationship if you know database terminology.
Now in the controller where you create your object to be inserted:
Thing.create({
someAttribute: inputs.someValue,
someOtherAttribute: inputs.someOtherValue,
owner: this.req.me.id
});
If you want to use the created object right away, append .fetch() to the chain after .create({...}) like so:
var thing = await Thing.create({ ... }).fetch();
Let me know if something is unclear.
I'd actually recommend you invest the $9 in buying the SailsJS course. It's an official course, taught by the creator of SailsJS, Mike McNeil. It takes you from npm i sails -g to pushing to production on the Heroku cloud platform. It teaches basic Vue (parasails flavour), using MailGun, Stripe payments, and more. They link to the course on the site here
Update
Did some further digging, and was inspired by a couple of similar cases.
What you can do is expand your model with a custom method that wraps the .create() method. This method can receive the request object from your controllers, but doing this, rather than the previous suggestion, will probably be more work than just adding ownerId: this.req.me.id, to existing calls. I1ll demonstrate anyway.
// Your model
module.exports = {
attributes: { ... },
proxyCreate(req, callback) {
if(!req.body.ownerId){
req.body.ownerId = req.me.id // or req.user.id, cant remember
// which works here
}
Thing.create(request.body, callback);
}
}
And in your controller:
...
// Change from:
Thing.create(req.body);
// To:
Thing.proxyCreate(req);
...
Update #2
Another idea I had was adding the middleware on a per-route basis. I don't know the complexity of your routes, but you can create a custom middleware for only those routes.
In router.js you edit your routes (I'll show one for brevity):
....
'POST /api/v1/things/upload-thing': [
{ action: 'helpers/add-userid-to-ownerid' },
{ action: 'new-thing' }
],
....
In helpers/add-userid-to-ownerid:
module.exports: {
fn: function(req, res) {
if(!req.body.ownerId){
req.body.ownerId = req.me.id;
}
}
}

Build query to get chat conversation messages in sails js

Lets say i have the following model schema described as below to add some chat functionality between 2 users in my sails app. For instance if i have user A sending a message to user B, then user B will reply back to user A which will always create 2 conversations between both users. So my question is how can i query both conversations to get messages from A and B. I tried something like this but maybe theres a simple logic.
// User Model
attributes: {
username: {
type: 'string',
required: true,
unique: true,
},
conversations_sender: {
collection: conversation,
via: 'sender'
},
conversations_recipient: {
collection: conversation,
via: 'recipient'
}
}
// Conversation model
attributes: {
sender: {
model: user
},
recipient: {
model: user
},
messages: {
collection: 'message',
via: 'conversation'
}
}
// Message model
attributes: {
text: {
type: 'string'
},
conversation: {
model: 'conversation'
},
}
// Conversation Controller
get: function(req, res) {
var params = {
or : [
{
sender: req.param('sender'),
recipient: req.param('recipient')
},
{
sender: req.param('recipient'),
recipient: req.param('sender')
}
]
}
Conversation.find(params) ...
}
You should rethink your schema a bit, see this link that has a good database design for your needs:
http://www.9lessons.info/2013/05/message-conversation-database-design.html
You should be able then to fetch all the messages with the 2 user ids like this:
Conversation.findAll({sender: ..., receiver: ...})
Also you will need a timestamp for the messages, in the future you'll want to sort them somehow and also make the nice 'Read yesterday' feature

Sails.js Model: create 2 association to self failed

I'm pretty new on Nodejs and sails.
I'm implementing a server which is similiar to Twitter. In user model, there should be 2 fields: follower and following, and the 2 fields are association of the model 'user' itself.
My question is when the model have only 1 association, either follower or following, it works.
However, when both follower and following included, there would be en error.
The code is something like this:
module.exports = {
attributes: {
alias: {
type:'string',
required: true,
primaryKey: true
},
pwd: {
type: 'string',
required: true
},
follower: {
collection: 'user',
via: 'alias'
},
following:{
collection: 'user',
via: 'alias'
}
}
The code will cause such error:
usr/local/lib/node_modules/sails/node_modules/waterline/node_modules/waterline-schema/lib/waterline-schema/references.js:115
throw new Error('Trying to associate a collection attribute to a model tha
^
Error: Trying to associate a collection attribute to a model that doesn't have a Foreign Key. user is trying to reference a foreign key in user
at References.findReference (/usr/local/lib/node_modules/sails/node_modules/waterline/node_modules/waterline-schema/lib/waterline-schema/references.js:115:11)
at References.addKeys (/usr/local/lib/node_modules/sails/node_modules/waterline/node_modules/waterline-schema/lib/waterline-schema/references.js:72:22)
For such usage your model definition is incorrect, namely the via keywords. As per the waterline associations docs the via keyword references the other side of the association. So, for a follower the other side is following and vice-versa. In other words:
follower: {
collection: 'user',
via: 'following'
},
following:{
collection: 'user',
via: 'follower'
}
You can check a full working example at: https://github.com/appscot/sails-orientdb/blob/master/test/integration-orientdb/tests/associations/manyToMany.selfReferencing.js

Unique property fails in Sails.js

The following code represents an Account Model in Sails.js v0.9.4 .
module.exports = {
attributes: {
email: {
type: 'email',
unique: true,
required: true
},
password:{
type: 'string',
minLength: 6,
maxLength: 15,
required:true
}
}
};
When I send two POSTS and a PUT request via Postman to localhost:8080/account, the unique property of the email fails.
Specifically, I send the following HTTP requests from Postman:
POST http://localhost:8080/account?email=foo#gmail.com&password=123456
POST http://localhost:8080/account?email=bar#gmail.com&password=123456
PUT http://localhost:8080/account?id=1&email=bar#gmail.com
GET http://localhost:8080/account
The last GET request shows me:
[
{
"email": "bar#gmail.com",
"password": "123456",
"createdAt": "2013-09-30T18:33:00.415Z",
"updatedAt": "2013-09-30T18:34:35.349Z",
"id": 1
},
{
"email": "bar#gmail.com",
"password": "123456",
"createdAt": "2013-09-30T18:33:44.402Z",
"updatedAt": "2013-09-30T18:33:44.402Z",
"id": 2
}
]
Should this happen?
*For those who don't know, Waterline generates by default an id which automatically increments in every insertion.
This is because your schema is not updated in your disk database (".tmp/disk.db").
You need to shutdown sails, drop your DB and restart sails.
The DB will be reconstruct with your good schema.
Attention : the data will be drop too !
If you want keep your data, you can just update the schema part of ".tmp/disk.db".
What I have doing to keep data and rebuild schema by sails.js :
copy ".tmp/disk.db"
clean ".tmp/disk.db"
shutdown sails.js
start sails.js
-> the database is empty and the schema is updated
copy old "counters" part
copy old "data" part
You must have this in your schema (file ".tmp/disk.db" -> "schema" part) for the unique field :
"xxx": {
"type": "string",
"unique": true
},
I hope this help you.
I ran into this same issue. To solve it, you have to avoid using the 'disk' ORM adapter. For some reason it appears that it doesn't support uniqueness checks.
Other adapters such as mongo and mysql should support uniqueness checks, so this shouldn't be an issue outside of development.
For the course of development, change the default adapter in config/adapters.js from 'disk' to 'memory'. Should look like this:
module.exports.adapters = {
// If you leave the adapter config unspecified
// in a model definition, 'default' will be used.
'default': 'memory',
// In-memory adapter for DEVELOPMENT ONLY
memory: {
module: 'sails-memory'
},
...
};
I'm not certain this is the issue, but have you added schema:true to your models and adapters?
My mongo adapter config looks like this:
module.exports.adapters = {
'default': 'mongo',
mongo: {
module: 'sails-mongo',
url: process.env.DB_URL,
schema: true
}
};
And my User model looks like this (trimmed a bit):
module.exports = {
schema: true,
attributes: {
username: {
type: 'string',
required: true,
unique: true
}
//...
}
};
There is no need to delete current database to solve this, in stead change the waterline migrate option from safe to alter. This way the underlying database will adapt this setting.
I wouldn't recommend migrate: alter in a production environment, though. ;)
Here is my /config/local.js:
module.exports = {
...
models: {
migrate: 'alter'
},
}
According to the official documentation of sails
You should configure the option "migrate" in "alter" to create the schemas with their indexes
There's nothing wrong with adding or removing validations from your
models as your app evolves. But once you go to production, there is
one very important exception: unique. During development, when your
app is configured to use migrate: 'alter', you can add or remove
unique validations at will. However, if you are using migrate: safe
(e.g. with your production database), you will want to update
constraints/indices in your database, as well as migrate your data by
hand.
http://sailsjs.com/documentation/concepts/models-and-orm/validations
var InvoiceSchema = new Schema({
email: {type: 'email', required: true}
name : {type: String}
});
InvoiceScheme({email: 1}, {unique: true});
Set Uniquee In Nodejs

Resources