I have a custom User, Role and RoleMapping, named AdminUser, AdminRole and AdminRoleMapping. I have populated the db with data, and I am using Mysql and Loopback 3.19. My model configuration is:
{
"name": "AdminRole",
"plural": "adminRoles",
...
"base": "Role",
"relations": {
"users": {
"type": "hasMany",
"model": "AdminUser",
"foreignKey": "principalId",
"through": "AdminRoleMapping"
}
},
...
}
{
"name": "AdminUser",
"plural": "adminUsers",
"base": "User",
...
"roles": {
"type": "hasMany",
"model": "AdminRole",
"foreignKey": "principalId",
"through": "AdminRoleMapping"
}
},
...
}
{
"name": "AdminRoleMapping",
"description": "Map principals to roles",
"plural": "adminRoleMappings",
"base": "RoleMapping",
....
"relations": {
"principal": {
"type": "belongsTo",
"model": "AdminUser",
"foreignKey": "principalId"
},
"roles": {
"type": "belongsTo",
"model": "AdminRole",
"foreignKey": "roleId"
}
},
....
}
When I try to access GET /adminUsers/{id}/roles then I get status error 500
"message": "Relation \"adminRole\" is not defined for AdminRoleMapping model"
What is wrong with my setup? I have followed the docs.
After a bit of trial and error, renaming the relation names in the intermediate table, fixed the issue:
{
"name": "AdminRoleMapping",
"description": "Map principals to roles",
"plural": "adminRoleMappings",
"base": "RoleMapping",
...
"relations": {
"adminUser": {
"type": "belongsTo",
"model": "AdminUser",
"foreignKey": "principalId"
},
"adminRole": {
"type": "belongsTo",
"model": "AdminRole",
"foreignKey": "roleId"
}
},
...
}
Related
I'll try to provide as much information as I think might be relevant. I'm using NodeJS, MongoDB, .rest, and Render, though in this case I'm just trying to get things working locally first. Here are some of the files.
//swagger.js
const swaggerAutogen = require('swagger-autogen')();
const doc = {
info: {
title: 'My API',
description: 'Welcome to my fumbling mess',
},
host: 'localhost:3000',
schemes: ['http'],
};
const outputFile = './swagger-output.json';
const endpointsFiles = ['routes/index.js'];
swaggerAutogen(outputFile, endpointsFiles, doc);
//routes/index.js
const routes = require('express').Router();
routes.use('/characters', require('./characters'));
routes.use('/', require('./swagger-route'));
module.exports = routes;
//routes/swagger-route.js
const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('../swagger-output.json');
router.use('/api-docs', swaggerUi.serve);
router.use('/api-docs', swaggerUi.setup(swaggerDocument));
module.exports = router;
//swagger.output.json
{
"swagger": "2.0",
"info": {
"title": "My API",
"description": "Welcome to my fumbling mess",
"version": "1.0.0"
},
"host": "localhost:3000",
"basePath": "/",
"schemes": [
"http"
],
"paths": {
"/characters/": {
"get": {
"description": "",
"produces": [
"application/json"
],
"parameters": [],
"responses": {
"200": {
"description": "OK"
},
"500": {
"description": "Internal Server Error"
}
}
},
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"characterName": {
"example": "any"
},
"playerName": {
"example": "any"
},
"race": {
"example": "any"
},
"class": {
"example": "any"
},
"level": {
"example": "any"
},
"alignment": {
"example": "any"
},
"stats": {
"example": "any"
}
}
}
}
],
"responses": {
"201": {
"description": "Created"
},
"400": {
"description": "Bad Request"
}
}
}
},
"/characters/{id}": {
"get": {
"description": "",
"produces": [
"application/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "OK"
},
"500": {
"description": "Internal Server Error"
}
}
},
"put": {
"description": "",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"characterName": {
"example": "any"
},
"playerName": {
"example": "any"
},
"race": {
"example": "any"
},
"class": {
"example": "any"
},
"level": {
"example": "any"
},
"alignment": {
"example": "any"
},
"stats": {
"example": "any"
}
}
}
}
],
"responses": {
"204": {
"description": "No Content"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"delete": {
"description": "",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
}
}
}
}
And finally, running http://localhost:3000/api-docs/ doesn't result in the actual frontend of the Swagger UI like it should. Instead I get this is raw html in the attached image.
Any ideas of what I could be doing wrong?
I tried in swagger-route.js to change the 5th line from router.get() to router.use()
I tried forgetting about localhost and changing the host paths to my Render web service but that just caused the same thing to happen on Render.
I have a collection called dish-category which contains many dishes, and a dish collection that contains many dish-options (another collection).
the list of dishes from each dish-category is available in the API but the nested collection of dish options from each dish is not available on strapi.
the following are the settings for my models:
dish-category.settings.json:
{
"kind": "collectionType",
"collectionName": "dish_categories",
"info": {
"name": "DishCategory",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Description": {
"type": "text"
},
"dishes": {
"collection": "dish",
"via": "dish_category"
}
}
}
dish.settings.json:
{
"kind": "collectionType",
"collectionName": "dishes",
"info": {
"name": "Dish",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Description": {
"type": "text"
},
"Total": {
"type": "decimal",
"min": 0,
"required": false,
"default": 0
},
"dish_categories": {
"collection": "dish-category"
},
"dish_options": {
"collection": "dish-option",
"via": "dish"
},
"dish_category": {
"via": "dishes",
"model": "dish-category"
}
}
}
dish-option.settings.json:
{
"kind": "collectionType",
"collectionName": "dish_options",
"info": {
"name": "DishOption",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Price": {
"type": "decimal",
"min": 0,
"default": 0
},
"dish": {
"via": "dish_options",
"model": "dish"
}
}
}
on the dish-category/controllers/dish-category.js file I tried populating the attribute:
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
* to customize this controller
*/
module.exports = {
async find(params, populate){
return strapi.query('dish-category').find(params, ["dishes","dish.dish_options"]);
}
};
I am having trouble displaying nested relations with multiple values, I have tried looking up solutions online, I came across this thread https://forum.strapi.io/t/simple-nested-array-issue/1045/4 but the solution doesn't work for me and it seems like the link to the example is no longer available.
I've been trying to create a collection that contains an array of objects in loopback.
I want a store data formatted like this:
{
id: "16356135616aaasad", //autogenerated by mongo
"name" : "a name",
"valores": [
{"valor": 567, "fecha": "2016-08-18T00:00:00.000Z"},
{"valor": 569, "fecha": "2016-08-19T00:00:00.000Z"},
...
]
}
I have the following configuration in loopback:
indicador.json
{
"name": "Indicador",
"plural": "indicadores",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true,
"autoId": true
},
"properties": {
"nombre": {
"type": "string",
"required": true
}
},
"relations": {
"historico": {
"type": "embedsMany",
"model": "Valor"
}
}
}
this is a base collection associated to another model (not persistent)
valor.json
{
"name": "Valor",
"plural": "valores",
"base": "Model",
"properties": {
"valor": {
"type": "number",
"required": true
},
"fecha": {
"type": "date",
"required": true
}
}
}
The problem it's when i try to send a post to the endpoint. If i send this data
{
"nombre": "UF",
"valores": [
{
"valor": 0,
"fecha": "2016-08-18"
}
]
}
The API responses this:
{
"error": {
"name": "ValidationError",
"status": 422,
"message": "The `Indicador` instance is not valid. Details: `valores` contains invalid item at index `0`: `id` is blank (value: [ { valor: 0, fecha: 2016...} ]).",
"statusCode": 422,
"details": {
"context": "Indicador",
"codes": {
"valores": [
"invalid"
]
},
"messages": {
"valores": [
"contains invalid item at index `0`: `id` is blank"
]
}
},
"stack": "ValidationError: The `Indicador` instance is not valid. Details: `valores` contains invalid item at index `0`: `id` is blank (value: [ { valor: 0, fecha: 2016...} ]).\n at /home/dev/app/node_modules/loopback-datasource-juggler/lib/dao.js:322:12\n at ModelConstructor.<anonymous> (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:492:11)\n at ModelConstructor.next (/home/dev/app/node_modules/loopback-datasource-juggler/lib/hooks.js:81:12)\n at ModelConstructor.<anonymous> (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:489:23)\n at ModelConstructor.trigger (/home/dev/app/node_modules/loopback-datasource-juggler/lib/hooks.js:71:12)\n at ModelConstructor.Validatable.isValid (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:455:8)\n at /home/dev/app/node_modules/loopback-datasource-juggler/lib/dao.js:318:9\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)"
}
}
¿Why i get this error?
The id inside the objects of the array that I need isn't necessary for me. I don't understand why happen this.
You need to specify that embedded model doe not need id
"relations": {
"historico": {
"type": "embedsMany",
"model": "Valor",
"options": {
"forceId": false,
"validate": true,
"persistent": false
}
}
}
You need to add "defaultFn":"uuid" to the id property in the json of the model that is going to be embeded in order to have the id generated. In your case it will look like the following:
{
"name": "Valor",
"plural": "valores",
"base": "Model",
"properties": {
"id":{
"type": "string",
"defaultFn":"uuid",
"id":true
},
"valor": {
"type": "number",
"required": true
},
"fecha": {
"type": "date",
"required": true
}
}
}
Hi I'm trying this example
https://docs.strongloop.com/display/public/LB/HasAndBelongsToMany+relations
I have these two models :
Part
{
"name": "Part",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"partNumber": {
"type": "number"
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "Assembly",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
Assembly
{
"name": "Assembly",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "Part",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
When I try to add a element in assembly I have a undefined element.
I'm using
assembly.js
Assembly.parts.add(part, function(err) {
...
});
but assembly.parts is undefined.
What am I doing wrong?
Thanks
You used a static method on Assembly that is wrong.
Relation methods should used as prototype methods. That make sense.
So you can call like this :
assembly_instance.parts.add(...
I don't know if I'm just blind or something but how can I do the following:
I have a User model with a hasOne relation to a UserData model. I only want one property of UserData directly in the results of User.
The relation in User looks like this:
"relations": {
"userData": {
"type": "hasOne",
"model": "UserData"
}
}
And the default scope in User:
"scope": {
"include": "userData"
}
So the result for one User is:
[
{
"id": 5,
"email": "example#example.com",
"name": "Example",
"userData": {
"id": 5,
"birthdate": "1971-09-06T00:00:00.000Z"
}
}
]
But what I want is this:
[
{
"id": 5,
"email": "example#example.com",
"name": "Example",
"birthdate": "1971-09-06T00:00:00.000Z"
}
]
How can I achive this?
Edit:
The two model definitions:
ChiliUser:
{
"name": "ChiliUser",
"base": "ChiliUserData",
"idInjection": true,
"options": {
"validateUpsert": true,
"mysql": {
"table": "person"
}
},
"properties": {
"id": {
"type": "number"
},
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"vorname": {
"type": "string"
},
"name": {
"type": "string"
},
"spitzname": {
"type": "string"
},
"strasse": {
"type": "string"
},
"plz": {
"type": "number"
},
"ort": {
"type": "string"
},
"geolat": {
"type": "string"
},
"geolng": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
ChiliUserData:
{
"name": "ChiliUserData",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true,
"mysql": {
"table": "person_data"
}
},
"properties": {
"id": {
"type": "number"
},
"person_id": {
"type": "number"
},
"birthdate": {
"type": "date"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
Are you using the built-in User model? If you are, you can simply extend it with your UserData model using base: "User", and the underlying schema will be as you desire:
{
"name": "UserData",
"base": "User",
"properties": {}
...
}
But this setup would mean you'd use UserData over User in your code. Since the built-in User model gets in the way, I'd recommend a different word like Person or Customer, in which case it would look like:
{
"name": "Person",
"base": "User",
"properties": {
...extra user data properties only...
}
...
}
This way, only Person will have all the extra "user data" fields on it, but will also include all the fields/methods defined for User inside node_modules/loopback/common/models/User.json. If you're using MySQL, the Person table will be created with the combination of fields from both User and Person.