Loopback and MongoDB - javascript

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
}
}
}

Related

Strapi: trouble populating multi-valued attribute for nested relation

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.

Dynamic values not setting in nested array

I am trying to change the values of my nested object by using the data from another object but for some reason it is never setting the value. If i put static text in there it works but just doesn't work if the data from my other object
const projectFormTypes = [
{
"type": "Theatrical",
"sections" : {
"project": {
"fields": [
{
"label": "Project Title",
"name": "title",
"required": true,
"type": "textbox",
"visible": true,
'value': ''
},
]
},
"participants": {
"fields": [
{
"label": "First Name",
"name": "firstName",
"required": false,
"type": "textbox",
"visible": true,
"value": "first name 1"
},
]
},
"earnings": {
"fields": [
{
"label": "Compensation Amount",
"name": "wages",
"prefix": "$",
"required": false,
"type": "textbox",
"visible": true,
"value": 100000
},
]
}
}
},
]
const projectData = [{"fileDetailRecordId":3,"detailRecordId":"341697P3","signatoryName":"comp 2","signatoryId":"comp sag","contract":"Theatrical","sagId":"aftra 2","title":"Project 2","principalPhotoDate":"2019-12-13","madeFor":"Interactive Media","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"In-Flight","commercialTitle":"Project 2","sessionDate":"2019-12-13","useType":"Clip Use","ssn":"987654","firstName":"test","middleName":"test","lastName":"test","loanOutNumber":"45687","loanOutName":"54854","performerType":"Background","performerCategory":"Dancer","paymentType":"Payroll","wages":"852963","subjectToPh":"852963","phContrib":"8529363","contribPercent":"10.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"},{"fileDetailRecordId":1,"detailRecordId":"341697P1","signatoryName":"comp name","signatoryId":"comp aftra","contract":"Theatrical","sagId":"aftra id","title":"Project 1","principalPhotoDate":"2019-12-13","madeFor":"Foreign TV","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"Network","commercialTitle":"Project 1","sessionDate":"2019-12-13","useType":"Phono Conversation","ssn":"456","firstName":"first name 1","middleName":"middle name 1","lastName":"last name 1","loanOutNumber":"456","loanOutName":"456","performerType":"AFTRA Staff","performerCategory":"Actor","paymentType":"Deferred","wages":"500","subjectToPh":"500","phContrib":"500","contribPercent":"1000.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"},{"fileDetailRecordId":2,"detailRecordId":"341697P2","signatoryName":"comp name","signatoryId":"comp aftra","contract":"Theatrical","sagId":"aftra id","title":"Project 1","principalPhotoDate":"2019-12-13","madeFor":"Foreign TV","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"Home Video","commercialTitle":"Project 1","sessionDate":"2019-12-13","useType":"Clip Use","ssn":"123","firstName":"last name 2","middleName":"last name 2","lastName":"last name 2","loanOutNumber":"456","loanOutName":"456","performerType":"AFTRA Staff","performerCategory":"Dance Coreographer","paymentType":"Deferred","wages":"800","subjectToPh":"800","phContrib":"800","contribPercent":"50.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"}]
const pp = Object.keys(projectFormTypes).forEach(function(r) {
for(let key in projectFormTypes[r].sections){
for(let o in projectFormTypes[r].sections[key].fields){
projectFormTypes[r].sections[key].fields[o].value = projectData[0][o.name];
}
}
});
console.log('result', projectFormTypes);
projectData[0][o.name]
should be
projectData[0][projectFormTypes[r].sections[key].fields[o].name]
o is the index in the fields array, and numbers don't have a name property. You want the name of the current element of the for loop.
But the code is simplified and less error prone if you use forEach() for all the nested loops.
const projectFormTypes = [{
"type": "Theatrical",
"sections": {
"project": {
"fields": [{
"label": "Project Title",
"name": "title",
"required": true,
"type": "textbox",
"visible": true,
'value': ''
}, ]
},
"participants": {
"fields": [{
"label": "First Name",
"name": "firstName",
"required": false,
"type": "textbox",
"visible": true,
"value": "first name 1"
}, ]
},
"earnings": {
"fields": [{
"label": "Compensation Amount",
"name": "wages",
"prefix": "$",
"required": false,
"type": "textbox",
"visible": true,
"value": 100000
}, ]
}
}
}, ]
const projectData = [{"fileDetailRecordId":3,"detailRecordId":"341697P3","signatoryName":"comp 2","signatoryId":"comp sag","contract":"Theatrical","sagId":"aftra 2","title":"Project 2","principalPhotoDate":"2019-12-13","madeFor":"Interactive Media","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"In-Flight","commercialTitle":"Project 2","sessionDate":"2019-12-13","useType":"Clip Use","ssn":"987654","firstName":"test","middleName":"test","lastName":"test","loanOutNumber":"45687","loanOutName":"54854","performerType":"Background","performerCategory":"Dancer","paymentType":"Payroll","wages":"852963","subjectToPh":"852963","phContrib":"8529363","contribPercent":"10.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"},{"fileDetailRecordId":1,"detailRecordId":"341697P1","signatoryName":"comp name","signatoryId":"comp aftra","contract":"Theatrical","sagId":"aftra id","title":"Project 1","principalPhotoDate":"2019-12-13","madeFor":"Foreign TV","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"Network","commercialTitle":"Project 1","sessionDate":"2019-12-13","useType":"Phono Conversation","ssn":"456","firstName":"first name 1","middleName":"middle name 1","lastName":"last name 1","loanOutNumber":"456","loanOutName":"456","performerType":"AFTRA Staff","performerCategory":"Actor","paymentType":"Deferred","wages":"500","subjectToPh":"500","phContrib":"500","contribPercent":"1000.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"},{"fileDetailRecordId":2,"detailRecordId":"341697P2","signatoryName":"comp name","signatoryId":"comp aftra","contract":"Theatrical","sagId":"aftra id","title":"Project 1","principalPhotoDate":"2019-12-13","madeFor":"Foreign TV","ppe":"2019-12-13","sessionDateTv":"2019-12-13","marketPaid":"Home Video","commercialTitle":"Project 1","sessionDate":"2019-12-13","useType":"Clip Use","ssn":"123","firstName":"last name 2","middleName":"last name 2","lastName":"last name 2","loanOutNumber":"456","loanOutName":"456","performerType":"AFTRA Staff","performerCategory":"Dance Coreographer","paymentType":"Deferred","wages":"800","subjectToPh":"800","phContrib":"800","contribPercent":"50.0000","detailStatus":"DRAFT","earningsFileId":341697,"detailStatusId":{"parentRefId":32,"activeFlag":true,"refCode":"detail_processing","refValue":"Processing","comments":"detail is being processed","createdBy":"NS","createdAt":"2018-10-04T01:33:18.000+0000","updatedBy":"NS","updatedAt":"2019-06-19T17:45:39.000+0000","cmsProcessEfDetailLogList":[],"referenceId":33,"cmsEarningsFileList":[],"cmsEarningsFileList1":[]},"createdBy":"UI","updatedBy":"UI"}]
projectFormTypes.forEach(function(type) {
Object.values(type.sections).forEach(function(section) {
section.fields.forEach(function(field) {
field.value = projectData[0][field.name];
});
});
});
console.log('result', projectFormTypes);

How to get "title" from AJV error?

I've got a JSON Schema that looks like this:
{
"required": [
"instructions"
],
"properties": {
"instructions": {
"title": "Instructions",
"minItems": 3,
"type": "array",
"items": {
"required": [
"typeId",
"teamId",
"disciplineId"
],
"properties": {
"typeId": {
"minimum": 1,
"title": "Appointment Type",
"type": "integer"
},
"teamId": {
"minimum": 1,
"title": "Team",
"type": "integer"
},
"disciplineId": {
"minimum": 1,
"title": "Discipline",
"type": "integer"
},
"prefClinicianId": {
"title": "Pref. Clinician",
"anyOf": [
{
"type": "null"
},
{
"minimum": 1,
"type": "integer"
}
]
},
"prefTime": {
"title": "Pref. Time",
"anyOf": [
{
"type": "null"
},
{
"type": "integer"
}
]
},
"childRequired": {
"title": "Child Req'd",
"type": "boolean"
}
},
"type": "object"
}
}
},
"type": "object"
}
As you can see, I've added titles to all the properties. However, the error object I get back looks like:
[
{
"keyword": "minItems",
"dataPath": ".instructions",
"schemaPath": "#/properties/instructions/minItems",
"params": {
"limit": 3
},
"message": "should NOT have less than 3 items"
},
{
"keyword": "type",
"dataPath": ".instructions[0].typeId",
"schemaPath": "#/properties/instructions/items/properties/typeId/type",
"params": {
"type": "integer"
},
"message": "should be integer"
},
{
"keyword": "type",
"dataPath": ".instructions[0].teamId",
"schemaPath": "#/properties/instructions/items/properties/teamId/type",
"params": {
"type": "integer"
},
"message": "should be integer"
},
{
"keyword": "type",
"dataPath": ".instructions[0].disciplineId",
"schemaPath": "#/properties/instructions/items/properties/disciplineId/type",
"params": {
"type": "integer"
},
"message": "should be integer"
}
]
As you can see, the title is not in there. How can I get the titles with the errors?
Please note that this question is specific to AJV.
When you create your AJV object set the verbose option to true.
This will add a parentSchema property to the ajv error with the original schema. It will also add a schema property that contains the specific schema attribute that caused the validation failure.
Here's an example:
var ajv = new Ajv({
$data: true,
verbose: true
});
let schema = {
title: 'object title',
type: 'object',
properties: {
str: {
title: "A string property",
type: "string"
}
}
};
let data = {
str: 3
};
ajv.validate(schema, data)
console.log('ERRORS: ', this.ajv.errors)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/5.3.0/ajv.bundle.js"></script>

Loopback relations

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(...

Strongloop property from relation in parent

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.

Categories