Why does not it work a join in loopback? - node.js

I inherited model
{
"name": "user",
"plural": "users",
"base": "User",
"relations": {
"roles": {
"type": "hasMany",
"model": "Role",
"foreignKey": "principalId",
"through": "RoleMapping"
}
},
And create hook, for saving role with user
UserModel.afterRemote('create', (context, user, next) => {
let body = context.req.body;
if (!body.hasOwnProperty('roleId')) {
next();
}
Role.findById(body.roleId)
.then(role => {
if (!role){
next();
}
return user.roles.add(role);
})
.then(roleMapping => {
next();
});
});
And record successfuly added to db(mongo), but while request GET
/api/users?access_token={}[include]=roles
record not joined, why?

your querystring is wrong
the include filter should be
/api/users?access_token={}&filter[include]=roles
(include filter)

Related

Loopback - how to get id of user and insert into related model? (hasMany)

What I am trying to figure out is how to get the id of the current authenticated user and use that when creating records in the DB as a foreign key of a different model?
To be more specific I need to get the id of the current authenticated user (model: CommonUser) and use that id as a FK when creating a new Event.
The relationships:
I have created a Model based on the User model called CommonUser. Common user has many Events. Event belongs to Common User.
So Event has a foreignKey called commonUserId.
How do I get the id of the user and use that when doing the insert?
I would have thought this would be automatic as part of the process as far as setting up relationships is concerned? Is that incorrect?
Also to complicate matters I have an Event Look-Up table (i will worry about this next so don't feel obligated to dive to deep) because Event also hasAndBelongsToMany through Event Lookup.
User
{
"name": "CommonUser",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"events": {
"type": "hasMany",
"model": "Event",
"foreignKey": "eventId",
"through": "EventLookUp"
},
"friends": {
"type": "hasMany",
"model": "CommonUser",
"through": "Friend",
"foreignKey": "friendId"
}
},
"acls": [],
"methods": {}
}
Event
{
"name": "Event",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
},
"radius": {
"type": "number",
"default": 50
},
"status": {
"type": "number"
},
"location": {
"type": "geopoint",
"required": true
}
},
"validations": [],
"relations": {
"owner": {
"type": "belongsTo",
"model": "CommonUser",
"foreignKey": "commonUserId"
},
"commonUsers": {
"type": "hasAndBelongsToMany",
"model": "CommonUser",
"foreignKey": "ownerId",
"through": "EventLookUp"
},
"galleries": {
"type": "hasOne",
"model": "Gallery",
"foreignKey": ""
},
"photos": {
"type": "hasMany",
"model": "Photo",
"foreignKey": "",
"through": "Gallery"
}
},
"acls": [],
"methods": {}
}
Event Lookup
{
"name": "EventLookUp",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
If I can be pointed in the right direction that would be fantastic. It's hard to find an answer reading through the documentation. I think I need to use an operation hook before insert and set the Event Models properties? What is loopback's best practice as far as this goes?
In loopback swagger when you login as a user using loopback's default users/login api , you get access token object as response.You can copy the id of access token and paste into the box in top right corner in swagger and set the access token.Thus internally your accesstoken is set in loopback and for your every request from swagger, loopback append the access token along with the request.In this way you can get the access token from ctx(context) in remote methods.
For create, findOrCreate, save an event obj:
Event.observe('before save', function updateUserId(ctx, next) {
let userId = ctx.options.accessToken.userId;`
if (ctx.instance) {
ctx.instance.commonUserId = userId;
}
next();
});
For updateAttributes for event obj:
Event.observe('before save', function updateUserId(ctx, next) {
let userId = ctx.options.accessToken.userId;
if (ctx.currentInstance) {
ctx.currentInstance.commonUserId = userId;
}
next();
});
I found a solution but I am unsure if it is best practice or if there is a better way to handle this.
Anyways I created an Operation Hook inside "/common/models/event.js" and appearently you can get access to the context within this hook.
If there is a better way to do this I would like to know.
'use strict';
module.exports = function (Event) {
Event.observe('before save', function updateForeignKeyCommonUserId(ctx, next) {
// console.log(ctx.options);
/* WHAT Options Looks Like */
// { accessToken:
// { id: 'Z20R1RsnumWEdDzR3TyCCbmZ0DTp4tOh2cviU6JGsrlNIYCs3KchQ7mdAnhTc1VQ',
// ttl: 1209600,
// created: 2018-06-26T17:41:28.298Z,
// userId: 3 },
// authorizedRoles: {} }
// console.log("THE USER ID IS: ");
// console.log(ctx.options.accessToken.userId); // Current User Id
var userId = ctx.options.accessToken.userId;
// So appearently "the context provides either an instance property or a pair of data and where properties"
// #todo: find out why there can be a 'instance' or 'data'.
if (ctx.instance) {
ctx.instance.commonUserId = userId; // instance
} else {
ctx.data.commonUserId = userId; // data
}
next();
});
};
If someone wouldn't mind explaining where context comes from and how it gets populated that would be awesome!

loopback hasandbelongstomany relation table doesn't exist

I have defined two models:
a 'part' model:
{
"name": "part",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"assemblies": {
"type": "hasAndBelongsToMany",
"model": "assembly",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
and an 'assembly' models:
{
"name": "assembly",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "part"
}
},
"acls": [],
"methods": {}
}
both models have an hasAndBelongToMany relation.
In a /server/boot/sample-model.js i have created some instance of this both models:
module.exports = function(app){
var Dev = app.dataSources.dev;
var Customer = app.models.customer;
var Order = app.models.order;
var Part = app.models.part;
var Assembly = app.models.assembly;
Dev.automigrate(['customer', 'order', 'assembly', 'part'], function(err) {
Customer.create([
{name: 'nicolas'},
{name: 'marie'},
{name: 'cyril'}
], function(err, customers){
Part.create([
{name: 'boulon'},
{name: 'ecrou'},
{name: 'cheville'},
], function(err, part){
//console.log(part[0])
Assembly.create([
{title: 'piece1'},
{title: 'piece2'},
{title: 'piece3'},
], function(err, assemblies){
//console.log(assemblies[0])
assemblies[0].parts.add(part[0], function(err){
if(err){
console.log(err)
}
})
})
})
});
});
}
but
assemblies[0].parts.add(part[0], function(err){
if(err){
console.log(err)
}
})
end with error:
{ [Error: ER_NO_SUCH_TABLE: Table 'database_development.assemblypart' doesn't exist]
code: 'ER_NO_SUCH_TABLE',
errno: 1146,
sqlState: '42S02',
index: 0 }
why loopback doesn't create the assemblypart table in my database ?
I ran into this same issue once and after many hours of struggling (using postgres connector), I've found several solutions.
Here's the shortest :
Instead of :
Dev.automigrate(['customer', 'order', 'assembly', 'part'], function(err) {
// Your code here
});
Try using this :
Dev.automigrate()
.then(function(err) {
// Your code here
});
I don't know exactly why, but in the second case the junction table is created.
Can you try this and let me know if it works for you ? If it's not can you provide some place where I can inspect and try running your code ?

Loopback user model instance has no instance methods

I'm fetching a user from a mongo database, but this user that I get, doesn't have any methods, neither all the properties I was expecting. Why would this happen?
This is the code:
app.models.MyUser.findOrCreate({where: {email: req.user.email}}, {
email: req.user.email,
password: sha1sum(JSON.stringify(req.user)),
firstName: req.user.displayName
}, function (err, user) {
if (err) throw err;
console.log(user.login); //undefined
res.json(user);
});
This is the code of my model:
{
"name": "MyUser",
"plural": "myusers",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"firstName": {
"type": "string"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": []
}
User.login is a static method, not a prototype method. See https://github.com/strongloop/loopback/blob/master/common/models/user.js#L164. You should be able to use user.constructor.login.

strongloop creating related model object in afterRemote method

say i have a game scenario.
a game belongs to a user.
game.json:
{
"name": "game",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"beer_points_required": {
"type": "number",
"required": true
},
"total_points": {
"type": "number",
"required": true
}
},
"validations": [],
"relations": {
"game_blngs_to_user": {
"type": "belongsTo",
"model": "user",
"foreignKey": ""
}
},
"acls": [],
"methods": []
}
user.json:
{
"name": "user",
"base": "User",
"idInjection": true,
"properties": {
"last_game": {
"type": "date",
"required": false
},
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
],
"methods": []
}
I'm attempting to create a game object for a user programmatically after the user has been created through CRUD, so inside the user.js i have:
var config = require('../../server/config.json');
var path = require('path');
var app = require('../app');
module.exports = function(user) {
user.afterRemote('create', function(context, user) {
console.log('> user.afterRemote triggered');
//create a game for each user thats created
var Game = app.models.game;
game.create({game_blngs_to_userId: user.id, beer_points_required: 0, total_points: 0},function(err, res){
if(err){
console.log('\n\n>>err');
console.log(err);
next(err);
return;
}
console.log(res);
});
});
However, this obviously didn't work lol so I'm wondering how to actually accomplish my goal. I've been staring at strong loops docs for a long time and it seems like actual usage of their api is not that well documented...well at least in my eyes. could anyone please shed some light on this for me?
Perhaps, you're missing 3rd parameter - next function in afterRemote callback.
user.afterRemote('create', function(context, user, next) {
...
var Game = app.models.game;
game.create({game_blngs_to_userId: user.id, beer_points_required: 0, total_points: 0},function(err, res){
if(err){
console.log(err);
next(err);
return;
}
next() // countinue execution
});
});
i think your user reference is undefined...try:
app.models.user.afterRemote

How to use Loopback ACL modify permissions on the user role

I am trying to understand the loopback acl but failed, if I can use loopback acl control role authorization, what should I do?
When I get request
GET http://localhost:1337/api/Employees 401 (Unauthorized)
{
"error": {
"name": "Error",
"status": 401,
"message": "Authorization Required",
"statusCode": 401,
"stack": "Error: Authorization Required
}
}
Here is an employee. The JSON configuration
{
"name": "Employee",
"base": "User",
"properties": {
"nickname": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [
{
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW",
"accessType": "READ"
}
],
"methods": []
}
The following code is to add an employee
{
"nickname": "",
"realm": "",
"username": "",
"credentials": "object",
"challenges": "object",
"email": "",
"emailVerified": false,
"verificationToken": "",
"status": "",
"created": "",
"lastUpdated": "",
"id": 0
}
I don't know the inside of the loopback acls. How do I go to change To achieve access control effect?
To support a custom role like admin, you need to define the role and add your users to the role. Here is some code I use for an internal project:
// Admin users
var adminUsers = require('../config/users.json').admins;
app.models.role.findOrCreate({where: {name: 'admin'}}, {name: 'admin'},
function (err, role) {
if (err) {
return console.error(err);
}
// role.principals() doesn't work here as the role.id might have a different
// type than roleMapping.roleId
app.models.roleMapping.find({where: {roleId: role.id.toString()}},
function (err, principals) {
if (err) {
return console.error(err);
}
var mapping = {};
principals.forEach(function (p) {
if (p.principalType === 'USER') {
mapping[p.principalId] = p;
}
});
app.models.user.find({where: {email: {inq: adminUsers}}},
function (err, users) {
if (err) {
return console.error(err);
}
if (users && users.length) {
users.forEach(function (user) {
if (mapping[user.id.toString()]) {
console.log('User %s (%s) found in role %s', user.username,
user.email, role.name);
return;
}
role.principals.create({principalType: 'USER', principalId: user.id},
function (err, mapping) {
if (err) {
return console.error(err);
}
console.log('User %s (%s) added to role %s', user.username,
user.email, role.name);
});
});
}
});
};
});

Resources