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
Related
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!
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)
I have a loopback app with mongoDB as below:
when i login as Admin i cannot use post method on dishes. and i get authorization required error.
that becomes possible only when i change the dishes role to ALLOW everyone.
how can i acheive the wanted result with keeping everyone on DENY and only ALLOW certain users to certain operations?
thank you. here is my code..
app/server/model-config.json:
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../node_modules/loopback-ds-timestamp-mixin",
"../common/mixins",
"./mixins"
]
},
"User": {
"dataSource": "db"
},
"AccessToken": {
"dataSource": "db",
"public": false
},
"ACL": {
"dataSource": "MongoDB",
"public": false
},
"RoleMapping": {
"dataSource": "MongoDB",
"public": false
},
"Role": {
"dataSource": "MongoDB",
"public": false
},
"dishes": {
"dataSource": "MongoDB",
"public": true
},
"Customer": {
"dataSource": "MongoDB",
"public": true
},
"Comments": {
"dataSource": "MongoDB",
"public": true
}
}
app/common/modles/dishes.json:
{
"name": "dishes",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"description": {
"type": "string",
"required": true
},
"category": {
"type": "string",
"required": true
},
"image": {
"type": "string",
"required": true
},
"label": {
"type": "string",
"required": true,
"default": "''"
},
"price": {
"type": "string",
"required": true,
"default": "0"
}
},
"mixins": {
"TimeStamp": true
},
"validations": [],
"relations": {
"comments": {
"type": "hasMany",
"model": "Comments",
"foreignKey": ""
},
"customers": {
"type": "hasMany",
"model": "Customer",
"foreignKey": ""
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW",
"property": "create"
},
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW"
}
],
"methods": {}
}
app/common/modles/comments.json:
{
"name": "Comments",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"Rating": {
"type": "number",
"required": true,
"default": 5
},
"comment": {
"type": "string",
"required": true
}
},
"mixins": {
"TimeStamp": true
},
"validations": [],
"relations": {
"dishes": {
"type": "belongsTo",
"model": "dishes",
"foreignKey": ""
},
"customer": {
"type": "belongsTo",
"model": "Customer",
"foreignKey": "customerId"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW",
"property": "create"
},
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
],
"methods": {}
}
app/common/modles/customer.json:
{
"name": "Customer",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"comments": {
"type": "hasMany",
"model": "Comments",
"foreignKey": "customerId"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
}
],
"methods": {}
}
and app/server/boot/script.js:
module.exports = function(app) {
var MongoDB = app.dataSources.MongoDB;
MongoDB.automigrate('Customer', function(err) {
if (err) throw (err);
var Customer = app.models.Customer;
Customer.create([
{username: 'Admin', email: 'admin#admin.com', password: 'abcdef'},
{username: 'muppala', email: 'muppala#ust.hk', password: 'abcdef'}
], function(err, users) {
if (err) throw (err);
var Role = app.models.Role;
var RoleMapping = app.models.RoleMapping;
Role.find({ name: 'admin' }, function(err, results) {
if (err) { throw err; }
if (results.length < 1) {
// now we know the DB doesn't have it already, so do the Role creation...
//create the admin role
Role.create({
name: 'admin'
}, function(err, role) {
if (err) throw (err);
//make admin
role.principals.create({
principalType: RoleMapping.USER,
principalId: users[0].id
}, function(err, principal) {
if (err) throw (err);
});
});
}
});
});
});
};
Seeing your last question I imagine what happened.
Somehow the collection Role was created but not mapped to User.
I suggest you to change:
Role.find({ name: 'admin' }, function(err, results) {
if (err) { throw err; }
if (results.length < 1) {
// now we know the DB doesn't have it already, so do the Role creation...
//create the admin role
Role.create({
name: 'admin'
}, function(err, role) {
if (err) throw (err);
//make admin
role.principals.create({
principalType: RoleMapping.USER,
principalId: users[0].id
}, function(err, principal) {
if (err) throw (err);
});
});
}
});
By:
Role.create({
name: 'admin'
}, function(err, role) {
if (err) throw (err);
//make admin
role.principals.create({
principalType: RoleMapping.USER,
principalId: users[0].id
}, function(err, principal) {
if (err) throw (err);
});
});
Drop the Role collection:
db.Role.drop()
and execute Loopback again.
Note: I was doing the same assigment and worked for me.
I am also having the same trouble as I come through the same assignment. Mr Kike's answer works for mine.
First, from cmd, type
>mongo
>use conFusion (Note: conFusion is the name of database of this assignment)
>show collections (Note: to see all collections in database collections)
>db.Role.drop()
And then run the loopback with node . again
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 ?
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.