How to POST a record with fk data in Strapi? - relational-database

I have a problem when I try to POST a new record.
I have a legacy app that I'm consuming with Strapi, so I didn't let Strapi create the tables, it just only use what is on the DDBB.
This one of the collection type: (enfermedadrepeticion) (go to repeticion_id)
{
"kind": "collectionType",
"connection": "atdbconnection",
"collectionName": "enfermedadrepeticion",
"info": {
"name": "RepeticionEnfermedad"
},
"options": {
"increments": false,
"timestamps": false
},
"attributes": {
"valor": {
"type": "decimal"
},
"enfermedad_id": {
"type": "integer"
},
"valor_1": {
"type": "string"
},
"valor_2": {
"type": "string"
},
"fechaTomaDato": {
"type": "string"
},
"repeticion_id": {
"via": "repeticion_enfermedads",
"model": "repeticion"
}
}
}
this is the model "repeticion" ... go to repeticion_enfermedads...
{
"kind": "collectionType",
"connection": "atdbconnection",
"collectionName": "repeticiones",
"info": {
"name": "Repeticion"
},
"options": {
"increments": false,
"timestamps": false
},
"attributes": {
"activa": {
"type": "boolean"
},
"altura": {
"type": "string"
},
"linea_id": {
"model": "linea"
},
"ensayo_id": {
"type": "integer"
},
"esp": {
"type": "string"
},
... bunch of fields ...
"repeticion_enfermedads": {
"collection": "repeticion-enfermedad",
"via": "repeticion_id"
}
}
}
My relation is: One Repeticion have 0 or N repeticion-enfermedad, via repeticion_id field.
Using this relation, when I fetch data from "Repeticion" I get one or more "Repeticion-Enfermedad".
So, when I need to update una entry in "Repeticion-Enfermedad" I PUT the data with this body :
body {
enfermedad_id: 1,
fechaTomaDato: '2021-05-05 03:29:29',
fechaUltCambio: '2021-05-05 03:29:29',
repeticion_id: { id: 392571 },
valor: 60,
valor_1: '60',
valor_2: 'S'
}
and everything works fine!!
But when I try to create a record using POST, with the same body, I get a 500 error.
error Error: ER_NO_DEFAULT_FOR_FIELD: Field 'repeticion_id' doesn't have a default value
I try to send the body using Swagger, and I get the same error.
I try, sending "repeticion_id" field, using integer, string, object, etc, but I cant't make it work...
My Strapi version is 3.0.6
I really don't want to alter the model definition, it's working fine...
Any suggestion ??
Best Regards

OK, I found the problem.
There is nothing to do with Strapi, I have to add NOT NULL to the field in RepeticionEnfermedad table.
It looks like Strapi first tries to create a record in that table, and then updates the recently added record...
I leave my answer in case someone got the same problem.
Chiao!

Related

How to operate with json list in CMake?

I have the following code which I'm trying to read in CMake.
{
"demo": [
{
"name": "foo0",
"url": "url1",
"verify_ssl": true
},
{
"name": "foo1",
"url": "url1",
"verify_ssl": true
},
{
"name": "foo2",
"url": "url2",
"verify_ssl": true
}
]
}
I'm trying to access a member from the list above, for example demo[0].name without success, what I'm doing wrong?
file(READ "${CONAN_CACHE}/demo.json" MY_JSON_STRING)
string(JSON CUR_NAME GET ${MY_JSON_STRING} demo[0].name)
One at a time.
string(JSON CUR_NAME GET ${MY_JSON_STRING} demo 0 name)

In Logic Apps JSON Array while parsing throwing error for single object but for multiple objects it is working fine

While parsing JSON in Azure Logic App in my array I can get single or multiple values/objects (Box as shown in below example)
Both type of inputs are correct but when only single object is coming then it is throwing an error "Invalid type. Expected Array but got Object "
Input 1 (Throwing error) : -
{
"MyBoxCollection":
{
"Box":{
"BoxName": "Box 1"
}
}
}
Input 2 (Working Fine) : -
{
"MyBoxCollection":
[
{
"Box":{
"BoxName": "Box 1"
},
"Box":{
"BoxName": "Box 2"
}
}]
}
JSON Schema :
"MyBoxCollection": {
"type": "object",
"properties": {
"box": {
"type": "array",
items": {
"type": "object",
"properties": {
"BoxName": {
"type": "string"
},
......
.....
..
}
Error Details :-
[
{
"message": "Invalid type. Expected Array but got Object .",
"lineNumber": 0,
"linePosition": 0,
"path": "Order.MyBoxCollection.Box",
"schemaId": "#/properties/Root/properties/MyBoxCollection/properties/Box",
"errorType": "type",
"childErrors": []
}
]
I used to use the trick of injecting a couple of dummy rows in the resultset as suggested by the other posts, but I recently found a better way. Kudos to Thomas Prokov for providing the inspiration in his NETWORG blog post.
The JSON parse schema accepts multiple choices as type, so simply replace
"type": "array"
with
"type": ["array","object"]
and your parse step will happily parse either an array or a single value (or no value at all).
You may then need to identify which scenario you're in: 0, 1 or multiple records in the resultset? I'm pasting below how you can create a variable (ResultsetSize) which takes one of 3 values (rs_0, rs_1 or rs_n) for your switch:
"Initialize_ResultsetSize": {
"inputs": {
"variables": [
{
"name": "ResultsetSize",
"type": "string",
"value": "rs_n"
}
]
},
"runAfter": {
"<replace_with_name_of_previous_action>": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Check_if_resultset_is_0_or_1_records": {
"actions": {
"Set_ResultsetSize_to_0": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_0"
},
"runAfter": {},
"type": "SetVariable"
}
},
"else": {
"actions": {
"Set_ResultsetSize_to_1": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_1"
},
"runAfter": {},
"type": "SetVariable"
}
}
},
"expression": {
"and": [
{
"equals": [
"#string(body('<replace_with_name_of_Parse_JSON_action>')?['<replace_with_name_of_root_element>']?['<replace_with_name_of_list_container_element>']?['<replace_with_name_of_item_element>']?['<replace_with_non_null_element_or_attribute>'])",
""
]
}
]
},
"runAfter": {
"Initialize_ResultsetSize": [
"Succeeded"
]
},
"type": "If"
},
"Process_resultset_depending_on_ResultsetSize": {
"cases": {
"Case_no_record": {
"actions": {
},
"case": "rs_0"
},
"Case_one_record_only": {
"actions": {
},
"case": "rs_1"
}
},
"default": {
"actions": {
}
},
"expression": "#variables('ResultsetSize')",
"runAfter": {
"Check_if_resultset_is_0_or_1_records": [
"Succeeded",
"Failed",
"Skipped",
"TimedOut"
]
},
"type": "Switch"
}
For this problem, I met another stack overflow post which is similar to this problem. While there is one "Box", it will be shown as {key/value pair} but not [array] when we convert it to json format. I think it is caused by design, so maybe we can just add a record "Box" at the source of your xml data such as:
<Box>specific_test</Box>
And do some operation to escape the "specific_test" in the next steps.
Another workaround for your reference:
If your json data has only one array, we can use it to do a judgment. We can judge the json data if it contains "[" character. If it contains "[", the return value is the index of the "[" character. If not contains, the return value is -1.
The expression shows as below:
indexOf('{"MyBoxCollection":{"Box":[aaa,bbb]}}', '[')
The screenshot above is the situation when it doesn't contain "[", it return -1.
Then we can add a "If" condition. If >0, do "Parse JSON" with one of the schema. If =-1, do "Parse JSON" with the other schema.
Hope it would be helpful to your problem~
We faced a similar issue. The only solution we find is by manipulating the XML before conversion. We updated XML nodes which needs to be an array even when we have single element using this. We used a Azure function to update the required XML attributes and then returned the XML for conversion in Logic Apps. Hope this helps someone.

How to add TimeInstant, CreationDate and ModifiedDate into CrateDB with Orion Context Broker?

I'm setting up a Firmware-Framework, where I unforutunately have to add historically Sensor Values. But I also need the creationDate and the modificationDate for other usecases.
Therefore I add the Attribute "Metadata" with the variable "TimeInstant". Then I create an Entity, create an Orion-Subscription for that Entity and update the Entity with my old Sensor-Valses.
The Json-File I send to the Orion-Context Broker to update the Attribute looks like this:
{
"metadata": {
"TimeInstant": {
"type": "DateTime",
"value": "2015-02-02T11:35:25.0000Z"
}
},
"type": "Number",
"value": 0.0132361 }
The Output in my Mongo-DB like this:
"_id": {
"id": "urn:ngsi-ld:SensorB-K1200____",
"type": "Sensor",
"servicePath": "/test/servicepath"
},
"attrNames": [
"Sensor_value"
],
"attrs": {
"Sensor_value": {
"value": 0.01632361,
"type": "Number",
"md": {
"TimeInstant": {
"type": "DateTime",
"value": 1422876989
}
},
"mdNames": [
"TimeInstant"
],
"creDate": 1568712813,
"modDate": 1568735930
}
},
"creDate": 1568712813,
"modDate": 1568735930,
"lastCorrelator": "0a129232-d964-11e9-8e5a-0242ac130009" }
But my Crate-DB only has the columns:
entity_id entity_type fiware_servicepath sensor_value time_index
My Subscription File looks like this:
{
"expires": "2019-12-24T18:00:00",
"notification": {
"http": {
"url": "http://quantumleap:8668/v2/notify"
},
"metadata": [
"dateCreated",
"dateModified",
"TimeInstant"
]
},
"subject": {
"entities": [
{
"id": "urn:ngsi-ld:SensorB-K1200____",
"type": "Sensor"
}
]
},
"throttling": 0 }
I've tried changing the "Metadata" Attributes in the Subscription-File, also tried restartig Crate-DB, ContextBroker e.g..
I excpect the CrateDb to show all three values: "dateCreated", "dateModified" and "TimeInstant".
Did you check what's the message notification actually sent by Orion to QuantumLeap?
As regards the payload I would try as follow:
{
"TimeInstant": {
"type": "DateTime",
"value": "2015-02-02T11:35:25.0000Z"
},
"type": "Number",
"value": 0.0132361
}
Internally we usually use as attribute name for this type of scenario dateObserved, but it would not make any difference w.r.t. TimeInstant.
I am not actually sure you can attach metadata to the root of NGSI message, I believe they are supposed to be attached only to attributes.
Anyhow, QuantumLeaps does not support NGSI metadata (i.e. metadata attached to NGSI attributes). Still it support time indexing based on them.
The way Quantum Leap handles TimeInstant metadata and other time metadata is via time_index. See documentation here: https://quantumleap.readthedocs.io/en/latest/user/#time-index

Postman: schema validation passes even with a wrong response

I have the following schema for bellow happy path response
var responseSchema =
{
"type": "object",
"properties": {
"value": {
"type": "object",
"properties":{
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"patientGuid": {"type": "string" },
"givenName": {"type": "string" },
"familyName": {"type": "string" } ,
"combinedName" : {"type": "string" }
},
"required": ["patientGuid","givenName","familyName"]
}
}
}
}
}
};
Happy path response:
{
"value": {
"items": [
{
"patientGuid": "e9530cd5-72e4-4ebf-add8-8df51739d15f",
"givenName": "Vajira",
"familyName": "Kalum",
"combinedName": "Vajira Kalum"
}
],
"href": "http://something.co",
"previous": null,
"next": null,
"limit": 10,
"offset": 0,
"total": 1
},
"businessRuleResults": [],
"valid": true
}
I check if condition to validate whether response schema is correct:
if(responseBody !== null & responseBody.length >0)
{
var responseObject = JSON.parse(responseBody);
if(tv4.validate(responseObject, responseSchema))
{
// do something
}
else
{
// log some msg
}
}
Schema validation condition (nested if) get passed even when I get bellow bad response.
{
"value": {
"items": [],
"href": "http://something.co",
"previous": null,
"next": null,
"limit": 10,
"offset": 0,
"total": 0
},
"businessRuleResults": [],
"valid": true
}
Why response schema validation not failed as a response not has required fields?
When I ran your code and removed a property from the response data it seemed to work ok:
I would suggest a couple of things though - The tv4 module is not fantastic, it's not actively being worked on (for a couple of years I think) and there's a bunch of complaints/issue raised on the Postman project about how poor it is and asking for it to be replaced in the native app.
Have you considered creating a test to check the schema instead? This is very basic and could be easily refactored and wrapped in some logic but it would check the different value types of your response.
pm.test("Response data format is correct", () => {
var jsonData = pm.response.json()
// Check the type are correct
pm.expect(jsonData).to.be.an('object')
pm.expect(jsonData.value).to.be.an('object')
pm.expect(jsonData.value.items).to.be.an('array')
pm.expect(jsonData.value.items[0]).to.be.an('object')
// Check the value types are correct
pm.expect(jsonData.value.items[0].combinedName).to.be.a('string')
pm.expect(jsonData.value.items[0].givenName).to.be.a('string')
pm.expect(jsonData.value.items[0].familyName).to.be.a('string')
pm.expect(jsonData.value.items[0].patientGuid).to.a('string')
});
Also the syntax that you're using is the older style now and you don't need to JSON.parse(responseBody) the response body anymore as pm.response.json() is basically doing the same thing.

Loopback : adding data in relation table of type "hasMany"

I have a table representing profil of users. This profils can follow others profils.
Here is a part of my profil.json
"relations": {
"followers": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "publisherId",
"keyThrough": "subscriberId",
"through": "Subscribing"
},
"subscribings": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "subscriberId",
"keyThrough": "publisherId",
"through": "Subscribing"
},
Well this works fine, but now I want to known the date when the profil was subscribed.
So I updated the subscribing.json relation table to add a date
{
"name": "Subscribing",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"subscriptionDate": {
"type": "date",
"required": true,
"default": "$now"
}
},
"validations": [],
"relations": {
"subscriber": {
"type": "belongsTo",
"model": "Profil",
"as": "subscriber"
},
"publisher": {
"type": "belongsTo",
"model": "Profil",
"as": "publisher"
}
},
"acls": [],
"methods": {}
}
Now I would like to be able to query the data of this profils while having the possibility to retrieve the date of subscription in the same time. Ideally, I would to keep the relation table non public.
Thanks
Try add subscribers manually.
First, add the next to subscribing.json
"scope": {
"include": [
"subscriber"
]
}
Next, create the hook observer in profil.js (I use lodash for mapping but you can use your prefer library)
Profil.observe('loaded', function(ctx, next){
const Subscribing = Profil.app.models.Subscribing;
Subscribing.find({
where: {
publisherId: ctx.data.id
}
}, function (err, subscribings) {
if(err) return next(err)
ctx.data.subscribings = _.map(subscribings, function(subscribing){
return {
date: subscribing.subscriptionDate,
subscriber: subscribing.subscriber
}
});
next();
});
})
And it's all! Try this and comment if you have problems.
You can use the "include" filter to add the relation data to the response.
So in your case this should get a user's 'subscribings':
Profil.find({include: 'subscribings'}, function() { /* ... */ });
One way is mentioned by #rigobcastro in his answer.
Alternatively, you need to create a new relation.
Let's say we call it SubscribingMetaData
In your profile.json, add following
"subscribingMetaData": {
"type": "hasMany",
"model": "Subscribing",
"foreignKey": "subscriberId"
}
Now you can include it in your query,
Profil.find({include: ['subscribings', 'subscribingMetaData']}, function() { /* ... */ });
// process subscribingMetaData and map subscriptionDate with subscribings to get desired output.