Many-to-many relation in loopback with different relation property name - loopback

I've the following models:
Account
Page
AccountPage
the AccountPage is a "through" model, here are my configurations for those models
AccountPage.json
{
"name": "AccountPage",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"subscriptionDate": {
"type": "date"
}
},
"validations": [],
"relations": {
"subscriber": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "subscriberId"
},
"page": {
"type": "belongsTo",
"model": "Page",
"foreignKey": "pageId"
}
},
"acls": [],
"methods": {}
}
Page.json
{
"name": "Page",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string"
}
},
"validations": [],
"relations":
"subscribers": {
"type": "hasMany",
"model": "Account",
"foreignKey": "subscriberId",
"through": "AccountPage",
"keyThrough": "pageId"
}
},
"acls": [],
"methods": {}
}
Account.json
{
"name": "Account",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"pages": {
"type": "hasMany",
"model": "Page",
"foreignKey": "",
"through": "AccountPage",
"keyThrough": "subscriberId"
}
},
"acls": [],
"methods": {}
}
When I access GET /Pages/{id}/subscribers, I got
"Relation \"account\" is not defined for AccountPage model"
If I change "subscriber" relation name in AccountPage.json to "account" it works ok, is this a bug or am I do something wrong?

I figured it out, it was misconfiguring the foreignKey and keyThrough in Page, and Account models. The foreignKey of Account relation in the Page model should have foreigKey of pageId, as according to the documentation "Specify custom foreignKey (for this model) on the related model" so it should be pageId not subscriberId.
So Page model relation will be (Page.json):
"subscribers": {
"type": "hasMany",
"model": "Account",
"foreignKey": "pageId",
"through": "AccountPage",
"keyThrough": "subscriberId"
}
and Account model relation will be (Account.json):
"pages": {
"type": "hasMany",
"model": "Page",
"foreignKey": "subscriberId",
"through": "AccountPage",
"keyThrough": "pageId"
}
Now it is working fine

Related

Loopback: Relation Through - not working

So, I am stuck on an issue, that should be simple, and I am sure I am missing something obvious
I am following this documentation:
so I have 3 tables
client, team, client-team
client.json
{
"name": "client",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"teams": {
"type": "hasMany",
"model": "team",
"foreignKey": "teamId",
"through": "client-team"
}
},
"acls": [],
"methods": {}
}
team.json
{
"name": "team",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"type": {
"type": "string",
"required": true,
"default": "first-team"
},
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"clients": {
"type": "hasMany",
"model": "client",
"foreignKey": "clientId",
"through": "client-team"
}
},
"acls": [],
"methods": {}
}
client-team.json
{
"name": "client-team",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"clientId": {
"type": "string",
"required": true
},
"teamId": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientId"
},
"team": {
"type": "belongsTo",
"model": "Team",
"foreignKey": "teamId"
}
},
"acls": [],
"methods": {}
}
so all the relations set correctly (I think)...
then in my clients I do have 1 client
[
{
"name": "Client name",
"id": "59876185508eb519385779c6"
}
]
and in my teams I have many, but for sure this:
[
{
"type": "type",
"name": "Team name",
"id": "5ae8a37add2989a32d37f83d"
}
]
And then I go to my
localhost:3000/explorer
To POST a client-team
like this
{
"clientId": "59876185508eb519385779c6",
"teamId": "5ae8a37add2989a32d37f83d"
}
and I get the 200 response with:
{
"clientId": "59876185508eb519385779c6",
"teamId": "5ae8a37add2989a32d37f83d",
"id": "5ae961873a7e3b33f0579fc3"
}
so the connection is there....
But then, when I go to "GET client/id" and do
id: 59876185508eb519385779c6
filter: {"include":["teams"]}
this is the response
{
"name": "Chelsea FC",
"id": "59876185508eb519385779c6",
"teams": []
}
The same happens in the "GET teams/id" and I use
id: 5ae8a37add2989a32d37f83d
filter: {"include":["clients"]}
or if I go to "GET teams/{id}/clients"
and put
id: 5ae8a37add2989a32d37f83d
I get
[]
So what am I doing wrong? I am sure I am missing a stupid, obvious thing :/
using mongo if that makes any difference
There are three issues here:
You described mongodb identifiers as string, that's why you store strings in database instead of object ids. (it's not required as the datasource should understand the real type)
Your models start from lower case letter. The same should be in the relations also. (the first part of the problem, it's fixing issue with ids)
Incorrect relations for client and team models (the second part of the problem, it's fixing includes)
client-team.json
{
"name": "client-team",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"clientId": {
"type": "objectId", // !!! changed (not required)
"required": true
},
"teamId": {
"type": "objectId", // !!! changed (not required)
"required": true
}
},
"validations": [],
"relations": {
"client": {
"type": "belongsTo",
"model": "client", // !!! changed
"foreignKey": "clientId"
},
"team": {
"type": "belongsTo",
"model": "team", // !!! changed
"foreignKey": "teamId"
}
},
"acls": [],
"methods": {}
}
client.json
{
"name": "client",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"teams": {
"type": "hasMany",
"model": "team",
"foreignKey": "clientId", // !!! changed (we describing id of this model, not team)
"through": "client-team"
}
},
"acls": [],
"methods": {}
}
team.json
{
"name": "team",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"type": {
"type": "string",
"required": true,
"default": "first-team"
},
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"clients": {
"type": "hasMany",
"model": "client",
"foreignKey": "teamId", // !!! changed (the same as the previous)
"through": "client-team"
}
},
"acls": [],
"methods": {}
}

Loopback 'chat' application database relationships

I'm using Loopback 4.1.0 (generator-loopback#5.5.0 loopback-workspace#4.0.0).
I have an extended User model called 'Client', which holds all the information about the users.
I have a PersistedModel called 'Chat', which holds things like creation, last message.
I am trying to setup the relations between the two.
A Chat can have 2 or more members.
A Client can have none or many chats.
Ultimately, I need a way of finding out if a one-to-one chat exists between the logged in Client, and the Client being browsed.
I thought I'd cracked it, but it's just returning a blank object when I GET /Client/{ClientID}/chats
// client.json
...
"relations": {
"chats": {
"type": "hasMany",
"model": "Chat",
"foreignKey": "clientId",
"options": {
"validate": true,
"forceId": false
},
"through": "ChatMembership"
}
}....
// chat.json
...
"relations": {
"members": {
"type": "hasMany",
"model": "Client",
"foreignKey": "chatId",
"options": {
"validate": true,
"forceId": false
},
"through": "ChatMembership"
}
}....
// chat-membership.json
{
"name": "ChatMembership",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"joined": {
"type": "date",
"defaultFn": "now"
}
},
"validations": [],
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": ""
},
"chat": {
"type": "belongsTo",
"model": "Chat",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
I think you may need to define the foreign key in the chat-membership.json

Using custom Loopback user model with loopback-component-passport

I'm trying to use a extended loopback user model together with the loopback-component-passport for facebook login. The login itself is working but i can't get it to use my custom user model instead of the builtin "Users".
steps i took:
- create custom user model with slc loopback:model extending "User"
{
"name": "myuser",
"plural": "myusers",
"base": "User",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"mytestproperty": {
"type": "string",
"default": "myteststring"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
- Setup passport component with the new user model:
module.exports = function (app) {
var passportConfigurator = new PassportConfigurator(app);
passportConfigurator.init();
passportConfigurator.setupModels({
userModel: app.models.myuser,
userIdentityModel: app.models.UserIdentity,
userCredentialModel: app.models.UserCredential
});
passportConfigurator.configureProvider('facebook-login',
require('../../providers.json')['facebook-login']);
};
Problem:
When i log in with via facebook the passport component still uses the "Users" model as seen in my db.json storage:
{
"ids": {
"User": 2,
"UserCredential": 1,
"UserIdentity": 2,
"AccessToken": 2,
"ACL": 1,
"RoleMapping": 1,
"Role": 1,
"myuser": 1
},
"models": {
"User": {
"1": "{\"username\":\"facebook.13371337\",\"password\":\"blablabla\",\"email\":\"blablabla\",\"id\":1}"
},
"UserCredential": {},
"UserIdentity": {
"1": "{\"provider\":\"ALL MY IDENTITY INFO BLABLABLA}"
},
"AccessToken": {
"1337": "{\"id\":\"1337\",\"ttl\":1209600,\"created\":\"2017-03-01T09:34:51.965Z\",\"userId\":1}"
},
"ACL": {},
"RoleMapping": {},
"Role": {},
"myuser": {}
}
}
As you can see "Users" is populated with my newly created user and "myuser" is empty.
Am i mistaking something or what is the correct way to extend the loopback user together with passport?
Any tips or references to a example are greatly appreciated!
You will have to extend all passport related models, so you can have them linked to your custom user model.
user.json
{
"name": "user",
"plural": "users",
"base": "User",
"relations": {
"accessTokens": {
"type": "hasMany",
"model": "accessToken",
"foreignKey": "userId"
},
"identities": {
"type": "hasMany",
"model": "userIdentity",
"foreignKey": "userId"
},
"credentials": {
"type": "hasMany",
"model": "userCredential",
"foreignKey": "userId"
}
},
"validations": [],
"acls": [],
"methods": []
}
user-identity.json
{
"name": "userIdentity",
"plural": "userIdentities",
"base": "UserIdentity",
"properties": {},
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},
"acls": [],
"methods": []
}
user-credential.json
{
"name": "userCredential",
"plural": "userCredentials",
"base": "UserCredential",
"properties": {},
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},
"acls": [],
"methods": []
}
access-token.json
{
"name": "accessToken",
"plural": "accessTokens",
"base": "AccessToken",
"properties": {},
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},
"acls": [],
"methods": {}
}
server.js (relevant part)
const PassportConfigurator = require('loopback-component-passport').PassportConfigurator
const passportConfigurator = new PassportConfigurator(app)
let providersConfig = require('./providers.json')
passportConfigurator.init()
passportConfigurator.setupModels({
userModel: app.models.user,
userIdentityModel: app.models.userIdentity,
userCredentialModel: app.models.userCredential
})
for (let s in providersConfig) { // Configure providers based on providers.json config
let c = providersConfig[s]
c.session = c.session !== false
passportConfigurator.configureProvider(s, c)
}
There is also an example repository, which might be useful for you.

How do I change the schema of a postgres data source in strongloop?

I'm trying to tell strongloop that my gallery table has moved to the products schema. Adding it to the model definition in common/models/gallery.json seemingly has no effect. New to strongloop. What am I doing wrong?
My current schema. "schema": "products" is the only thing added.
{
"name": "gallery",
"plural": "galleries",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true,
"schema": "products"
},
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string",
"required": true
},
"description": {
"type": "string"
},
"uuid": {
"type": "uuid"
},
"test": {
"type": "string"
},
"order": {
"type": "number"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
uuid is a placeholder
Answer is here: https://docs.strongloop.com/display/public/LB/PostgreSQL+connector
Correct options value:
"options": {
"validateUpsert": true,
"postgresql": {
"schema": "products"
}
}

Loopback.io find with include

I'm using Loopback.io, with MongoDB. I can't figure how to use the "include" filter.
The goal is to have "theuser" be included fully in the "group" when done a find() or findOne(). All I get now is the id's in an array. Thanks
theuser.json:
{
"name": "theuser",
"plural": "theusers",
"base": "User",
"idInjection": true,
"properties": {
"peerId": {
"type": "string",
"required": false
},
"peerStatus": {
"type": "string",
"required": false
}
},
"validations": [],
"relations": {
"notes": {
"type": "hasMany",
"model": "note",
"foreignKey": "ownerId"
},
"groups": {
"type": "hasMany",
"model": "group",
"foreignKey": "groupId"
}
},
group.json:
{
"name": "group",
"plural": "groups",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"host": {
"type": "belongsTo",
"model": "theuser",
"foreignKey": "ownerId"
},
"clients": {
"type": "hasMany",
"model": "theuser",
"foreignKey": "clientId"
}
},
"acls": [],
"methods": []
}
I'm trying to find a group like so:
Group.findOne({
filter: {
where: {"ownerId": 1},
include: {relation: "clients"}
}
}
Group.findOne({
where: {
ownerId: 1
},
include: 'clients'
}, cb);